Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : SMB2 client session handling
5 :
6 : Copyright (C) Andrew Tridgell 2005
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "system/network.h"
24 : #include <tevent.h>
25 : #include "lib/util/tevent_ntstatus.h"
26 : #include "libcli/raw/libcliraw.h"
27 : #include "libcli/smb2/smb2.h"
28 : #include "libcli/smb2/smb2_calls.h"
29 : #include "auth/gensec/gensec.h"
30 : #include "auth/credentials/credentials.h"
31 : #include "../libcli/smb/smbXcli_base.h"
32 :
33 : /**
34 : initialise a smb2_session structure
35 : */
36 15206 : struct smb2_session *smb2_session_init(struct smb2_transport *transport,
37 : struct gensec_settings *settings,
38 : TALLOC_CTX *parent_ctx)
39 : {
40 781 : struct smb2_session *session;
41 781 : NTSTATUS status;
42 :
43 15206 : session = talloc_zero(parent_ctx, struct smb2_session);
44 15206 : if (!session) {
45 0 : return NULL;
46 : }
47 15206 : session->transport = talloc_steal(session, transport);
48 :
49 15206 : session->smbXcli = smbXcli_session_create(session, transport->conn);
50 15206 : if (session->smbXcli == NULL) {
51 0 : talloc_free(session);
52 0 : return NULL;
53 : }
54 :
55 : /* prepare a gensec context for later use */
56 15206 : status = gensec_client_start(session, &session->gensec,
57 : settings);
58 15206 : if (!NT_STATUS_IS_OK(status)) {
59 0 : talloc_free(session);
60 0 : return NULL;
61 : }
62 :
63 15206 : gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
64 :
65 15206 : return session;
66 : }
67 :
68 : /*
69 : * Note: that the caller needs to keep 'transport' around as
70 : * long as the returned session is active!
71 : */
72 2262 : struct smb2_session *smb2_session_channel(struct smb2_transport *transport,
73 : struct gensec_settings *settings,
74 : TALLOC_CTX *parent_ctx,
75 : struct smb2_session *base_session)
76 : {
77 248 : struct smb2_session *session;
78 248 : NTSTATUS status;
79 :
80 2262 : session = talloc_zero(parent_ctx, struct smb2_session);
81 2262 : if (!session) {
82 0 : return NULL;
83 : }
84 2262 : session->transport = transport;
85 :
86 2262 : status = smb2cli_session_create_channel(session,
87 : base_session->smbXcli,
88 : transport->conn,
89 : &session->smbXcli);
90 2262 : if (!NT_STATUS_IS_OK(status)) {
91 0 : talloc_free(session);
92 0 : return NULL;
93 : }
94 :
95 2262 : session->needs_bind = true;
96 :
97 : /* prepare a gensec context for later use */
98 2262 : status = gensec_client_start(session, &session->gensec,
99 : settings);
100 2262 : if (!NT_STATUS_IS_OK(status)) {
101 0 : talloc_free(session);
102 0 : return NULL;
103 : }
104 :
105 2262 : gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
106 :
107 2262 : return session;
108 : }
109 :
110 : struct smb2_session_setup_spnego_state {
111 : struct tevent_context *ev;
112 : struct smb2_session *session;
113 : struct cli_credentials *credentials;
114 : uint64_t previous_session_id;
115 : bool session_bind;
116 : bool reauth;
117 : NTSTATUS gensec_status;
118 : NTSTATUS remote_status;
119 : DATA_BLOB in_secblob;
120 : DATA_BLOB out_secblob;
121 : struct iovec *recv_iov;
122 : };
123 :
124 : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req);
125 : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq);
126 : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req);
127 : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq);
128 : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req);
129 :
130 : /*
131 : a composite function that does a full SPNEGO session setup
132 : */
133 16307 : struct tevent_req *smb2_session_setup_spnego_send(
134 : TALLOC_CTX *mem_ctx,
135 : struct tevent_context *ev,
136 : struct smb2_session *session,
137 : struct cli_credentials *credentials,
138 : uint64_t previous_session_id)
139 : {
140 16307 : struct smb2_transport *transport = session->transport;
141 974 : struct tevent_req *req;
142 974 : struct smb2_session_setup_spnego_state *state;
143 974 : uint64_t current_session_id;
144 974 : const char *chosen_oid;
145 974 : NTSTATUS status;
146 974 : const DATA_BLOB *server_gss_blob;
147 974 : struct timeval endtime;
148 974 : bool ok;
149 :
150 16307 : req = tevent_req_create(mem_ctx, &state,
151 : struct smb2_session_setup_spnego_state);
152 16307 : if (req == NULL) {
153 0 : return NULL;
154 : }
155 16307 : state->ev = ev;
156 16307 : state->session = session;
157 16307 : state->credentials = credentials;
158 16307 : state->previous_session_id = previous_session_id;
159 16307 : state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
160 16307 : state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
161 :
162 16307 : endtime = timeval_current_ofs(transport->options.request_timeout, 0);
163 :
164 16307 : ok = tevent_req_set_endtime(req, ev, endtime);
165 16307 : if (!ok) {
166 0 : return tevent_req_post(req, ev);
167 : }
168 :
169 16307 : current_session_id = smb2cli_session_current_id(state->session->smbXcli);
170 16307 : if (state->session->needs_bind) {
171 1816 : state->session_bind = true;
172 14491 : } else if (current_session_id != 0) {
173 1013 : state->reauth = true;
174 : }
175 16307 : server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
176 16307 : if (server_gss_blob) {
177 16307 : state->out_secblob = *server_gss_blob;
178 : }
179 :
180 16307 : status = gensec_set_credentials(session->gensec, credentials);
181 16307 : if (tevent_req_nterror(req, status)) {
182 0 : return tevent_req_post(req, ev);
183 : }
184 :
185 16307 : status = gensec_set_target_hostname(session->gensec,
186 16307 : smbXcli_conn_remote_name(session->transport->conn));
187 16307 : if (tevent_req_nterror(req, status)) {
188 0 : return tevent_req_post(req, ev);
189 : }
190 :
191 16307 : status = gensec_set_target_service(session->gensec, "cifs");
192 16307 : if (tevent_req_nterror(req, status)) {
193 0 : return tevent_req_post(req, ev);
194 : }
195 :
196 16307 : if (state->out_secblob.length > 0) {
197 16307 : chosen_oid = GENSEC_OID_SPNEGO;
198 16307 : status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
199 16307 : if (!NT_STATUS_IS_OK(status)) {
200 72 : DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
201 : gensec_get_name_by_oid(session->gensec,
202 : chosen_oid),
203 : nt_errstr(status)));
204 72 : state->out_secblob = data_blob_null;
205 72 : chosen_oid = GENSEC_OID_NTLMSSP;
206 72 : status = gensec_start_mech_by_oid(session->gensec,
207 : chosen_oid);
208 72 : if (!NT_STATUS_IS_OK(status)) {
209 0 : DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
210 : gensec_get_name_by_oid(session->gensec,
211 : chosen_oid),
212 : nt_errstr(status)));
213 : }
214 : }
215 16307 : if (tevent_req_nterror(req, status)) {
216 0 : return tevent_req_post(req, ev);
217 : }
218 : } else {
219 0 : chosen_oid = GENSEC_OID_NTLMSSP;
220 0 : status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
221 0 : if (!NT_STATUS_IS_OK(status)) {
222 0 : DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
223 : gensec_get_name_by_oid(session->gensec,
224 : chosen_oid),
225 : nt_errstr(status)));
226 : }
227 0 : if (tevent_req_nterror(req, status)) {
228 0 : return tevent_req_post(req, ev);
229 : }
230 : }
231 :
232 16307 : smb2_session_setup_spnego_gensec_next(req);
233 16307 : if (!tevent_req_is_in_progress(req)) {
234 0 : return tevent_req_post(req, ev);
235 : }
236 :
237 15333 : return req;
238 : }
239 :
240 36839 : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
241 : {
242 1789 : struct smb2_session_setup_spnego_state *state =
243 36839 : tevent_req_data(req,
244 : struct smb2_session_setup_spnego_state);
245 36839 : struct smb2_session *session = state->session;
246 36839 : struct tevent_req *subreq = NULL;
247 :
248 36839 : if (NT_STATUS_IS_OK(state->gensec_status)) {
249 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
250 0 : return;
251 : }
252 :
253 36839 : subreq = gensec_update_send(state, state->ev,
254 : session->gensec,
255 : state->out_secblob);
256 36839 : if (tevent_req_nomem(subreq, req)) {
257 0 : return;
258 : }
259 36839 : tevent_req_set_callback(subreq,
260 : smb2_session_setup_spnego_gensec_done,
261 : req);
262 : }
263 :
264 36839 : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
265 : {
266 1789 : struct tevent_req *req =
267 36839 : tevent_req_callback_data(subreq,
268 : struct tevent_req);
269 1789 : struct smb2_session_setup_spnego_state *state =
270 36839 : tevent_req_data(req,
271 : struct smb2_session_setup_spnego_state);
272 1789 : NTSTATUS status;
273 :
274 36839 : status = gensec_update_recv(subreq, state,
275 : &state->in_secblob);
276 36839 : state->gensec_status = status;
277 36839 : state->out_secblob = data_blob_null;
278 36839 : if (!NT_STATUS_IS_OK(status) &&
279 23098 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
280 20 : tevent_req_nterror(req, status);
281 20 : return;
282 : }
283 :
284 36819 : if (NT_STATUS_IS_OK(state->remote_status) &&
285 11880 : NT_STATUS_IS_OK(state->gensec_status)) {
286 12547 : smb2_session_setup_spnego_both_ready(req);
287 12547 : return;
288 : }
289 :
290 24272 : smb2_session_setup_spnego_smb2_next(req);
291 : }
292 :
293 24272 : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
294 : {
295 1122 : struct smb2_session_setup_spnego_state *state =
296 24272 : tevent_req_data(req,
297 : struct smb2_session_setup_spnego_state);
298 24272 : struct smb2_session *session = state->session;
299 1122 : uint32_t timeout_msec;
300 24272 : uint8_t in_flags = 0;
301 24272 : struct tevent_req *subreq = NULL;
302 :
303 24272 : if (NT_STATUS_IS_OK(state->remote_status)) {
304 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
305 0 : return;
306 : }
307 :
308 24272 : timeout_msec = session->transport->options.request_timeout * 1000;
309 :
310 24272 : if (state->session_bind) {
311 2412 : in_flags |= SMB2_SESSION_FLAG_BINDING;
312 : }
313 :
314 25394 : subreq = smb2cli_session_setup_send(state, state->ev,
315 23150 : session->transport->conn,
316 : timeout_msec,
317 : session->smbXcli,
318 : in_flags,
319 : 0, /* in_capabilities */
320 : 0, /* in_channel */
321 : state->previous_session_id,
322 24272 : &state->in_secblob);
323 24272 : if (tevent_req_nomem(subreq, req)) {
324 0 : return;
325 : }
326 24272 : tevent_req_set_callback(subreq,
327 : smb2_session_setup_spnego_smb2_done,
328 : req);
329 : }
330 :
331 : /*
332 : handle continuations of the spnego session setup
333 : */
334 24272 : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
335 : {
336 1122 : struct tevent_req *req =
337 24272 : tevent_req_callback_data(subreq,
338 : struct tevent_req);
339 1122 : struct smb2_session_setup_spnego_state *state =
340 24272 : tevent_req_data(req,
341 : struct smb2_session_setup_spnego_state);
342 1122 : NTSTATUS status;
343 :
344 24272 : status = smb2cli_session_setup_recv(subreq, state,
345 : &state->recv_iov,
346 : &state->out_secblob);
347 24272 : state->remote_status = status;
348 24272 : state->in_secblob = data_blob_null;
349 24272 : if (!NT_STATUS_IS_OK(status) &&
350 11198 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
351 3668 : tevent_req_nterror(req, status);
352 3668 : return;
353 : }
354 :
355 20604 : if (NT_STATUS_IS_OK(state->remote_status) &&
356 12619 : NT_STATUS_IS_OK(state->gensec_status)) {
357 72 : smb2_session_setup_spnego_both_ready(req);
358 72 : return;
359 : }
360 :
361 20532 : smb2_session_setup_spnego_gensec_next(req);
362 : }
363 :
364 12619 : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
365 : {
366 667 : struct smb2_session_setup_spnego_state *state =
367 12619 : tevent_req_data(req,
368 : struct smb2_session_setup_spnego_state);
369 12619 : struct smb2_session *session = state->session;
370 667 : NTSTATUS status;
371 667 : DATA_BLOB session_key;
372 :
373 12619 : if (state->out_secblob.length != 0) {
374 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
375 0 : return;
376 : }
377 :
378 12619 : if (state->in_secblob.length != 0) {
379 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
380 0 : return;
381 : }
382 :
383 12619 : if (state->reauth) {
384 138 : tevent_req_done(req);
385 138 : return;
386 : }
387 :
388 12481 : if (cli_credentials_is_anonymous(state->credentials)) {
389 : /*
390 : * Windows server does not set the
391 : * SMB2_SESSION_FLAG_IS_GUEST nor
392 : * SMB2_SESSION_FLAG_IS_NULL flag.
393 : *
394 : * This fix makes sure we do not try
395 : * to verify a signature on the final
396 : * session setup response.
397 : */
398 365 : tevent_req_done(req);
399 365 : return;
400 : }
401 :
402 12116 : status = gensec_session_key(session->gensec, state,
403 : &session_key);
404 12116 : if (tevent_req_nterror(req, status)) {
405 0 : return;
406 : }
407 :
408 12116 : if (state->session_bind) {
409 950 : status = smb2cli_session_set_channel_key(session->smbXcli,
410 : session_key,
411 950 : state->recv_iov);
412 950 : if (tevent_req_nterror(req, status)) {
413 0 : return;
414 : }
415 950 : session->needs_bind = false;
416 : } else {
417 11166 : status = smb2cli_session_set_session_key(session->smbXcli,
418 : session_key,
419 11166 : state->recv_iov);
420 11166 : if (tevent_req_nterror(req, status)) {
421 0 : return;
422 : }
423 : }
424 12116 : tevent_req_done(req);
425 12116 : return;
426 : }
427 :
428 : /*
429 : receive a composite session setup reply
430 : */
431 16307 : NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
432 : {
433 16307 : return tevent_req_simple_recv_ntstatus(req);
434 : }
435 :
436 : /*
437 : sync version of smb2_session_setup_spnego
438 : */
439 4762 : NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
440 : struct cli_credentials *credentials,
441 : uint64_t previous_session_id)
442 : {
443 353 : struct tevent_req *subreq;
444 353 : NTSTATUS status;
445 353 : bool ok;
446 4762 : TALLOC_CTX *frame = talloc_stackframe();
447 4762 : struct tevent_context *ev = session->transport->ev;
448 :
449 4762 : if (frame == NULL) {
450 0 : return NT_STATUS_NO_MEMORY;
451 : }
452 :
453 4762 : subreq = smb2_session_setup_spnego_send(frame, ev,
454 : session, credentials,
455 : previous_session_id);
456 4762 : if (subreq == NULL) {
457 0 : TALLOC_FREE(frame);
458 0 : return NT_STATUS_NO_MEMORY;
459 : }
460 :
461 4762 : ok = tevent_req_poll(subreq, ev);
462 4762 : if (!ok) {
463 0 : status = map_nt_error_from_unix_common(errno);
464 0 : TALLOC_FREE(frame);
465 0 : return status;
466 : }
467 :
468 4762 : status = smb2_session_setup_spnego_recv(subreq);
469 4762 : TALLOC_FREE(subreq);
470 4762 : if (!NT_STATUS_IS_OK(status)) {
471 3648 : TALLOC_FREE(frame);
472 3648 : return status;
473 : }
474 :
475 1114 : TALLOC_FREE(frame);
476 1114 : return NT_STATUS_OK;
477 : }
|