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