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 4538 : struct smb2_session *smb2_session_init(struct smb2_transport *transport,
37 : struct gensec_settings *settings,
38 : TALLOC_CTX *parent_ctx)
39 : {
40 : struct smb2_session *session;
41 : NTSTATUS status;
42 :
43 4538 : session = talloc_zero(parent_ctx, struct smb2_session);
44 4538 : if (!session) {
45 0 : return NULL;
46 : }
47 4538 : session->transport = talloc_steal(session, transport);
48 :
49 4538 : session->smbXcli = smbXcli_session_create(session, transport->conn);
50 4538 : if (session->smbXcli == NULL) {
51 0 : talloc_free(session);
52 0 : return NULL;
53 : }
54 :
55 : /* prepare a gensec context for later use */
56 4538 : status = gensec_client_start(session, &session->gensec,
57 : settings);
58 4538 : if (!NT_STATUS_IS_OK(status)) {
59 0 : talloc_free(session);
60 0 : return NULL;
61 : }
62 :
63 4538 : gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
64 :
65 4538 : 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 244 : 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 : struct smb2_session *session;
78 : NTSTATUS status;
79 :
80 244 : session = talloc_zero(parent_ctx, struct smb2_session);
81 244 : if (!session) {
82 0 : return NULL;
83 : }
84 244 : session->transport = transport;
85 :
86 244 : status = smb2cli_session_create_channel(session,
87 : base_session->smbXcli,
88 : transport->conn,
89 : &session->smbXcli);
90 244 : if (!NT_STATUS_IS_OK(status)) {
91 0 : talloc_free(session);
92 0 : return NULL;
93 : }
94 :
95 244 : session->needs_bind = true;
96 :
97 : /* prepare a gensec context for later use */
98 244 : status = gensec_client_start(session, &session->gensec,
99 : settings);
100 244 : if (!NT_STATUS_IS_OK(status)) {
101 0 : talloc_free(session);
102 0 : return NULL;
103 : }
104 :
105 244 : gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
106 :
107 244 : 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 4730 : 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 4730 : struct smb2_transport *transport = session->transport;
141 : struct tevent_req *req;
142 : struct smb2_session_setup_spnego_state *state;
143 : uint64_t current_session_id;
144 : const char *chosen_oid;
145 : NTSTATUS status;
146 : const DATA_BLOB *server_gss_blob;
147 : struct timeval endtime;
148 : bool ok;
149 :
150 4730 : req = tevent_req_create(mem_ctx, &state,
151 : struct smb2_session_setup_spnego_state);
152 4730 : if (req == NULL) {
153 0 : return NULL;
154 : }
155 4730 : state->ev = ev;
156 4730 : state->session = session;
157 4730 : state->credentials = credentials;
158 4730 : state->previous_session_id = previous_session_id;
159 4730 : state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
160 4730 : state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
161 :
162 4730 : endtime = timeval_current_ofs(transport->options.request_timeout, 0);
163 :
164 4730 : ok = tevent_req_set_endtime(req, ev, endtime);
165 4730 : if (!ok) {
166 0 : return tevent_req_post(req, ev);
167 : }
168 :
169 4730 : current_session_id = smb2cli_session_current_id(state->session->smbXcli);
170 4730 : if (state->session->needs_bind) {
171 166 : state->session_bind = true;
172 4564 : } else if (current_session_id != 0) {
173 180 : state->reauth = true;
174 : }
175 4730 : server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
176 4730 : if (server_gss_blob) {
177 4730 : state->out_secblob = *server_gss_blob;
178 : }
179 :
180 4730 : status = gensec_set_credentials(session->gensec, credentials);
181 4730 : if (tevent_req_nterror(req, status)) {
182 0 : return tevent_req_post(req, ev);
183 : }
184 :
185 4730 : status = gensec_set_target_hostname(session->gensec,
186 4730 : smbXcli_conn_remote_name(session->transport->conn));
187 4730 : if (tevent_req_nterror(req, status)) {
188 0 : return tevent_req_post(req, ev);
189 : }
190 :
191 4730 : status = gensec_set_target_service(session->gensec, "cifs");
192 4730 : if (tevent_req_nterror(req, status)) {
193 0 : return tevent_req_post(req, ev);
194 : }
195 :
196 4730 : if (state->out_secblob.length > 0) {
197 4730 : chosen_oid = GENSEC_OID_SPNEGO;
198 4730 : status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
199 4730 : if (!NT_STATUS_IS_OK(status)) {
200 24 : 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 24 : state->out_secblob = data_blob_null;
205 24 : chosen_oid = GENSEC_OID_NTLMSSP;
206 24 : status = gensec_start_mech_by_oid(session->gensec,
207 : chosen_oid);
208 24 : 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 4730 : 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 4730 : smb2_session_setup_spnego_gensec_next(req);
233 4730 : if (!tevent_req_is_in_progress(req)) {
234 0 : return tevent_req_post(req, ev);
235 : }
236 :
237 4730 : return req;
238 : }
239 :
240 9701 : static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
241 : {
242 7393 : struct smb2_session_setup_spnego_state *state =
243 9701 : tevent_req_data(req,
244 : struct smb2_session_setup_spnego_state);
245 9701 : struct smb2_session *session = state->session;
246 9701 : struct tevent_req *subreq = NULL;
247 :
248 9701 : 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 9701 : subreq = gensec_update_send(state, state->ev,
254 : session->gensec,
255 : state->out_secblob);
256 9701 : if (tevent_req_nomem(subreq, req)) {
257 0 : return;
258 : }
259 9701 : tevent_req_set_callback(subreq,
260 : smb2_session_setup_spnego_gensec_done,
261 : req);
262 : }
263 :
264 9701 : static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
265 : {
266 7393 : struct tevent_req *req =
267 9701 : tevent_req_callback_data(subreq,
268 : struct tevent_req);
269 7393 : struct smb2_session_setup_spnego_state *state =
270 9701 : tevent_req_data(req,
271 : struct smb2_session_setup_spnego_state);
272 : NTSTATUS status;
273 :
274 9701 : status = gensec_update_recv(subreq, state,
275 : &state->in_secblob);
276 9701 : state->gensec_status = status;
277 9701 : state->out_secblob = data_blob_null;
278 13856 : if (!NT_STATUS_IS_OK(status) &&
279 5398 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
280 18 : tevent_req_nterror(req, status);
281 18 : return;
282 : }
283 :
284 12897 : if (NT_STATUS_IS_OK(state->remote_status) &&
285 4279 : NT_STATUS_IS_OK(state->gensec_status)) {
286 4279 : smb2_session_setup_spnego_both_ready(req);
287 4279 : return;
288 : }
289 :
290 5404 : smb2_session_setup_spnego_smb2_next(req);
291 : }
292 :
293 5404 : static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
294 : {
295 4161 : struct smb2_session_setup_spnego_state *state =
296 5404 : tevent_req_data(req,
297 : struct smb2_session_setup_spnego_state);
298 5404 : struct smb2_session *session = state->session;
299 : uint32_t timeout_msec;
300 5404 : uint8_t in_flags = 0;
301 5404 : struct tevent_req *subreq = NULL;
302 :
303 5404 : 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 5404 : timeout_msec = session->transport->options.request_timeout * 1000;
309 :
310 5404 : if (state->session_bind) {
311 168 : in_flags |= SMB2_SESSION_FLAG_BINDING;
312 : }
313 :
314 9565 : subreq = smb2cli_session_setup_send(state, state->ev,
315 5404 : 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 5404 : &state->in_secblob);
323 5404 : if (tevent_req_nomem(subreq, req)) {
324 0 : return;
325 : }
326 5404 : 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 5404 : static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
335 : {
336 4161 : struct tevent_req *req =
337 5404 : tevent_req_callback_data(subreq,
338 : struct tevent_req);
339 4161 : struct smb2_session_setup_spnego_state *state =
340 5404 : tevent_req_data(req,
341 : struct smb2_session_setup_spnego_state);
342 : NTSTATUS status;
343 :
344 5404 : status = smb2cli_session_setup_recv(subreq, state,
345 : &state->recv_iov,
346 : &state->out_secblob);
347 5404 : state->remote_status = status;
348 5404 : state->in_secblob = data_blob_null;
349 6327 : if (!NT_STATUS_IS_OK(status) &&
350 1101 : !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
351 409 : tevent_req_nterror(req, status);
352 409 : return;
353 : }
354 :
355 8233 : if (NT_STATUS_IS_OK(state->remote_status) &&
356 4303 : NT_STATUS_IS_OK(state->gensec_status)) {
357 24 : smb2_session_setup_spnego_both_ready(req);
358 24 : return;
359 : }
360 :
361 4971 : smb2_session_setup_spnego_gensec_next(req);
362 : }
363 :
364 4303 : static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
365 : {
366 3238 : struct smb2_session_setup_spnego_state *state =
367 4303 : tevent_req_data(req,
368 : struct smb2_session_setup_spnego_state);
369 4303 : struct smb2_session *session = state->session;
370 : NTSTATUS status;
371 : DATA_BLOB session_key;
372 :
373 4303 : if (state->out_secblob.length != 0) {
374 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
375 0 : return;
376 : }
377 :
378 4303 : if (state->in_secblob.length != 0) {
379 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
380 0 : return;
381 : }
382 :
383 4303 : if (state->reauth) {
384 30 : tevent_req_done(req);
385 30 : return;
386 : }
387 :
388 4273 : 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 177 : tevent_req_done(req);
399 177 : return;
400 : }
401 :
402 4096 : status = gensec_session_key(session->gensec, state,
403 : &session_key);
404 4096 : if (tevent_req_nterror(req, status)) {
405 0 : return;
406 : }
407 :
408 4096 : if (state->session_bind) {
409 16 : status = smb2cli_session_set_channel_key(session->smbXcli,
410 : session_key,
411 16 : state->recv_iov);
412 16 : if (tevent_req_nterror(req, status)) {
413 0 : return;
414 : }
415 16 : session->needs_bind = false;
416 : } else {
417 4080 : status = smb2cli_session_set_session_key(session->smbXcli,
418 : session_key,
419 4080 : state->recv_iov);
420 4080 : if (tevent_req_nterror(req, status)) {
421 0 : return;
422 : }
423 : }
424 4096 : tevent_req_done(req);
425 4096 : return;
426 : }
427 :
428 : /*
429 : receive a composite session setup reply
430 : */
431 4730 : NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
432 : {
433 4730 : return tevent_req_simple_recv_ntstatus(req);
434 : }
435 :
436 : /*
437 : sync version of smb2_session_setup_spnego
438 : */
439 449 : NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
440 : struct cli_credentials *credentials,
441 : uint64_t previous_session_id)
442 : {
443 : struct tevent_req *subreq;
444 : NTSTATUS status;
445 : bool ok;
446 449 : TALLOC_CTX *frame = talloc_stackframe();
447 449 : struct tevent_context *ev = session->transport->ev;
448 :
449 449 : if (frame == NULL) {
450 0 : return NT_STATUS_NO_MEMORY;
451 : }
452 :
453 449 : subreq = smb2_session_setup_spnego_send(frame, ev,
454 : session, credentials,
455 : previous_session_id);
456 449 : if (subreq == NULL) {
457 0 : TALLOC_FREE(frame);
458 0 : return NT_STATUS_NO_MEMORY;
459 : }
460 :
461 449 : ok = tevent_req_poll(subreq, ev);
462 449 : if (!ok) {
463 0 : status = map_nt_error_from_unix_common(errno);
464 0 : TALLOC_FREE(frame);
465 0 : return status;
466 : }
467 :
468 449 : status = smb2_session_setup_spnego_recv(subreq);
469 449 : TALLOC_FREE(subreq);
470 449 : if (!NT_STATUS_IS_OK(status)) {
471 399 : TALLOC_FREE(frame);
472 399 : return status;
473 : }
474 :
475 50 : TALLOC_FREE(frame);
476 50 : return NT_STATUS_OK;
477 : }
|