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 15199 : struct smb2_session *smb2_session_init(struct smb2_transport *transport,
37 : struct gensec_settings *settings,
38 : TALLOC_CTX *parent_ctx)
39 : {
40 807 : struct smb2_session *session;
41 807 : NTSTATUS status;
42 :
43 15199 : session = talloc_zero(parent_ctx, struct smb2_session);
44 15199 : if (!session) {
45 0 : return NULL;
46 : }
47 15199 : session->transport = talloc_steal(session, transport);
48 :
49 15199 : session->smbXcli = smbXcli_session_create(session, transport->conn);
50 15199 : if (session->smbXcli == NULL) {
51 0 : talloc_free(session);
52 0 : return NULL;
53 : }
54 :
55 : /* prepare a gensec context for later use */
56 15199 : status = gensec_client_start(session, &session->gensec,
57 : settings);
58 15199 : if (!NT_STATUS_IS_OK(status)) {
59 0 : talloc_free(session);
60 0 : return NULL;
61 : }
62 :
63 15199 : gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
64 :
65 15199 : 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 16288 : 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 16288 : struct smb2_transport *transport = session->transport;
141 998 : struct tevent_req *req;
142 998 : struct smb2_session_setup_spnego_state *state;
143 998 : uint64_t current_session_id;
144 998 : const char *chosen_oid;
145 998 : NTSTATUS status;
146 998 : const DATA_BLOB *server_gss_blob;
147 998 : struct timeval endtime;
148 998 : bool ok;
149 :
150 16288 : req = tevent_req_create(mem_ctx, &state,
151 : struct smb2_session_setup_spnego_state);
152 16288 : if (req == NULL) {
153 0 : return NULL;
154 : }
155 16288 : state->ev = ev;
156 16288 : state->session = session;
157 16288 : state->credentials = credentials;
158 16288 : state->previous_session_id = previous_session_id;
159 16288 : state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
160 16288 : state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
161 :
162 16288 : endtime = timeval_current_ofs(transport->options.request_timeout, 0);
163 :
164 16288 : ok = tevent_req_set_endtime(req, ev, endtime);
165 16288 : if (!ok) {
166 0 : return tevent_req_post(req, ev);
167 : }
168 :
169 16288 : current_session_id = smb2cli_session_current_id(state->session->smbXcli);
170 16288 : if (state->session->needs_bind) {
171 1816 : state->session_bind = true;
172 14472 : } else if (current_session_id != 0) {
173 1013 : state->reauth = true;
174 : }
175 16288 : server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
176 16288 : if (server_gss_blob) {
177 16288 : state->out_secblob = *server_gss_blob;
178 : }
179 :
180 16288 : status = gensec_set_credentials(session->gensec, credentials);
181 16288 : if (tevent_req_nterror(req, status)) {
182 0 : return tevent_req_post(req, ev);
183 : }
184 :
185 16288 : status = gensec_set_target_hostname(session->gensec,
186 16288 : smbXcli_conn_remote_name(session->transport->conn));
187 16288 : if (tevent_req_nterror(req, status)) {
188 0 : return tevent_req_post(req, ev);
189 : }
190 :
191 16288 : status = gensec_set_target_service(session->gensec, "cifs");
192 16288 : if (tevent_req_nterror(req, status)) {
193 0 : return tevent_req_post(req, ev);
194 : }
195 :
196 16288 : if (state->out_secblob.length > 0) {
197 16288 : chosen_oid = GENSEC_OID_SPNEGO;
198 16288 : status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
199 16288 : 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 16288 : 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 16288 : smb2_session_setup_spnego_gensec_next(req);
233 16288 : if (!tevent_req_is_in_progress(req)) {
234 0 : return tevent_req_post(req, ev);
235 : }
236 :
237 15290 : return req;
238 : }
239 :
240 36918 : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
241 : {
242 1854 : struct smb2_session_setup_spnego_state *state =
243 36918 : tevent_req_data(req,
244 : struct smb2_session_setup_spnego_state);
245 36918 : struct smb2_session *session = state->session;
246 36918 : struct tevent_req *subreq = NULL;
247 :
248 36918 : 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 36918 : subreq = gensec_update_send(state, state->ev,
254 : session->gensec,
255 : state->out_secblob);
256 36918 : if (tevent_req_nomem(subreq, req)) {
257 0 : return;
258 : }
259 36918 : tevent_req_set_callback(subreq,
260 : smb2_session_setup_spnego_gensec_done,
261 : req);
262 : }
263 :
264 36918 : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
265 : {
266 1854 : struct tevent_req *req =
267 36918 : tevent_req_callback_data(subreq,
268 : struct tevent_req);
269 1854 : struct smb2_session_setup_spnego_state *state =
270 36918 : tevent_req_data(req,
271 : struct smb2_session_setup_spnego_state);
272 1854 : NTSTATUS status;
273 :
274 36918 : status = gensec_update_recv(subreq, state,
275 : &state->in_secblob);
276 36918 : state->gensec_status = status;
277 36918 : state->out_secblob = data_blob_null;
278 36918 : if (!NT_STATUS_IS_OK(status) &&
279 23240 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
280 20 : tevent_req_nterror(req, status);
281 20 : return;
282 : }
283 :
284 36898 : if (NT_STATUS_IS_OK(state->remote_status) &&
285 11752 : NT_STATUS_IS_OK(state->gensec_status)) {
286 12443 : smb2_session_setup_spnego_both_ready(req);
287 12443 : return;
288 : }
289 :
290 24455 : smb2_session_setup_spnego_smb2_next(req);
291 : }
292 :
293 24455 : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
294 : {
295 1163 : struct smb2_session_setup_spnego_state *state =
296 24455 : tevent_req_data(req,
297 : struct smb2_session_setup_spnego_state);
298 24455 : struct smb2_session *session = state->session;
299 1163 : uint32_t timeout_msec;
300 24455 : uint8_t in_flags = 0;
301 24455 : struct tevent_req *subreq = NULL;
302 :
303 24455 : 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 24455 : timeout_msec = session->transport->options.request_timeout * 1000;
309 :
310 24455 : if (state->session_bind) {
311 2412 : in_flags |= SMB2_SESSION_FLAG_BINDING;
312 : }
313 :
314 25618 : subreq = smb2cli_session_setup_send(state, state->ev,
315 23292 : 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 24455 : &state->in_secblob);
323 24455 : if (tevent_req_nomem(subreq, req)) {
324 0 : return;
325 : }
326 24455 : 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 24455 : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
335 : {
336 1163 : struct tevent_req *req =
337 24455 : tevent_req_callback_data(subreq,
338 : struct tevent_req);
339 1163 : struct smb2_session_setup_spnego_state *state =
340 24455 : tevent_req_data(req,
341 : struct smb2_session_setup_spnego_state);
342 1163 : NTSTATUS status;
343 :
344 24455 : status = smb2cli_session_setup_recv(subreq, state,
345 : &state->recv_iov,
346 : &state->out_secblob);
347 24455 : state->remote_status = status;
348 24455 : state->in_secblob = data_blob_null;
349 24455 : if (!NT_STATUS_IS_OK(status) &&
350 11468 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
351 3753 : tevent_req_nterror(req, status);
352 3753 : return;
353 : }
354 :
355 20702 : if (NT_STATUS_IS_OK(state->remote_status) &&
356 12515 : NT_STATUS_IS_OK(state->gensec_status)) {
357 72 : smb2_session_setup_spnego_both_ready(req);
358 72 : return;
359 : }
360 :
361 20630 : smb2_session_setup_spnego_gensec_next(req);
362 : }
363 :
364 12515 : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
365 : {
366 691 : struct smb2_session_setup_spnego_state *state =
367 12515 : tevent_req_data(req,
368 : struct smb2_session_setup_spnego_state);
369 12515 : struct smb2_session *session = state->session;
370 691 : NTSTATUS status;
371 691 : DATA_BLOB session_key;
372 :
373 12515 : if (state->out_secblob.length != 0) {
374 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
375 0 : return;
376 : }
377 :
378 12515 : if (state->in_secblob.length != 0) {
379 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
380 0 : return;
381 : }
382 :
383 12515 : if (state->reauth) {
384 138 : tevent_req_done(req);
385 138 : return;
386 : }
387 :
388 12377 : if (cli_credentials_is_anonymous(state->credentials) &&
389 425 : !state->session->anonymous_session_key)
390 : {
391 : /*
392 : * Windows server does not set the
393 : * SMB2_SESSION_FLAG_IS_GUEST nor
394 : * SMB2_SESSION_FLAG_IS_NULL flag.
395 : *
396 : * This fix makes sure we do not try
397 : * to verify a signature on the final
398 : * session setup response.
399 : */
400 365 : tevent_req_done(req);
401 365 : return;
402 : }
403 :
404 12012 : if (state->session->forced_session_key.length != 0) {
405 24 : session_key = state->session->forced_session_key;
406 : } else {
407 11988 : status = gensec_session_key(session->gensec, state,
408 : &session_key);
409 11988 : if (tevent_req_nterror(req, status)) {
410 0 : return;
411 : }
412 : }
413 :
414 12012 : if (state->session_bind) {
415 950 : status = smb2cli_session_set_channel_key(session->smbXcli,
416 : session_key,
417 950 : state->recv_iov);
418 950 : if (tevent_req_nterror(req, status)) {
419 0 : return;
420 : }
421 950 : session->needs_bind = false;
422 : } else {
423 11062 : status = smb2cli_session_set_session_key(session->smbXcli,
424 : session_key,
425 11062 : state->recv_iov);
426 11062 : if (tevent_req_nterror(req, status)) {
427 0 : return;
428 : }
429 : }
430 12012 : tevent_req_done(req);
431 12012 : return;
432 : }
433 :
434 : /*
435 : receive a composite session setup reply
436 : */
437 16288 : NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
438 : {
439 16288 : return tevent_req_simple_recv_ntstatus(req);
440 : }
441 :
442 : /*
443 : sync version of smb2_session_setup_spnego
444 : */
445 4905 : NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
446 : struct cli_credentials *credentials,
447 : uint64_t previous_session_id)
448 : {
449 363 : struct tevent_req *subreq;
450 363 : NTSTATUS status;
451 363 : bool ok;
452 4905 : TALLOC_CTX *frame = talloc_stackframe();
453 4905 : struct tevent_context *ev = session->transport->ev;
454 :
455 4905 : if (frame == NULL) {
456 0 : return NT_STATUS_NO_MEMORY;
457 : }
458 :
459 4905 : subreq = smb2_session_setup_spnego_send(frame, ev,
460 : session, credentials,
461 : previous_session_id);
462 4905 : if (subreq == NULL) {
463 0 : TALLOC_FREE(frame);
464 0 : return NT_STATUS_NO_MEMORY;
465 : }
466 :
467 4905 : ok = tevent_req_poll(subreq, ev);
468 4905 : if (!ok) {
469 0 : status = map_nt_error_from_unix_common(errno);
470 0 : TALLOC_FREE(frame);
471 0 : return status;
472 : }
473 :
474 4905 : status = smb2_session_setup_spnego_recv(subreq);
475 4905 : TALLOC_FREE(subreq);
476 4905 : if (!NT_STATUS_IS_OK(status)) {
477 3731 : TALLOC_FREE(frame);
478 3731 : return status;
479 : }
480 :
481 1174 : TALLOC_FREE(frame);
482 1174 : return NT_STATUS_OK;
483 : }
|