ctdb-scripts: Move connection tracking to 10.interface
[samba4-gss.git] / source4 / libcli / smb2 / session.c
blob322a7bd6860aed501cc46a6780a6f19ea3d29ba5
1 /*
2 Unix SMB/CIFS implementation.
4 SMB2 client session handling
6 Copyright (C) Andrew Tridgell 2005
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.
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.
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/>.
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"
33 /**
34 initialise a smb2_session structure
36 struct smb2_session *smb2_session_init(struct smb2_transport *transport,
37 struct gensec_settings *settings,
38 TALLOC_CTX *parent_ctx)
40 struct smb2_session *session;
41 NTSTATUS status;
43 session = talloc_zero(parent_ctx, struct smb2_session);
44 if (!session) {
45 return NULL;
47 session->transport = talloc_steal(session, transport);
49 session->smbXcli = smbXcli_session_create(session, transport->conn);
50 if (session->smbXcli == NULL) {
51 talloc_free(session);
52 return NULL;
55 /* prepare a gensec context for later use */
56 status = gensec_client_start(session, &session->gensec,
57 settings);
58 if (!NT_STATUS_IS_OK(status)) {
59 talloc_free(session);
60 return NULL;
63 gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
65 return session;
69 * Note: that the caller needs to keep 'transport' around as
70 * long as the returned session is active!
72 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)
77 struct smb2_session *session;
78 NTSTATUS status;
80 session = talloc_zero(parent_ctx, struct smb2_session);
81 if (!session) {
82 return NULL;
84 session->transport = transport;
86 status = smb2cli_session_create_channel(session,
87 base_session->smbXcli,
88 transport->conn,
89 &session->smbXcli);
90 if (!NT_STATUS_IS_OK(status)) {
91 talloc_free(session);
92 return NULL;
95 session->needs_bind = true;
97 /* prepare a gensec context for later use */
98 status = gensec_client_start(session, &session->gensec,
99 settings);
100 if (!NT_STATUS_IS_OK(status)) {
101 talloc_free(session);
102 return NULL;
105 gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
107 return session;
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;
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);
131 a composite function that does a full SPNEGO session setup
133 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)
140 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;
150 req = tevent_req_create(mem_ctx, &state,
151 struct smb2_session_setup_spnego_state);
152 if (req == NULL) {
153 return NULL;
155 state->ev = ev;
156 state->session = session;
157 state->credentials = credentials;
158 state->previous_session_id = previous_session_id;
159 state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
160 state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
162 endtime = timeval_current_ofs(transport->options.request_timeout, 0);
164 ok = tevent_req_set_endtime(req, ev, endtime);
165 if (!ok) {
166 return tevent_req_post(req, ev);
169 current_session_id = smb2cli_session_current_id(state->session->smbXcli);
170 if (state->session->needs_bind) {
171 state->session_bind = true;
172 } else if (current_session_id != 0) {
173 state->reauth = true;
175 server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
176 if (server_gss_blob) {
177 state->out_secblob = *server_gss_blob;
180 status = gensec_set_credentials(session->gensec, credentials);
181 if (tevent_req_nterror(req, status)) {
182 return tevent_req_post(req, ev);
185 status = gensec_set_target_hostname(session->gensec,
186 smbXcli_conn_remote_name(session->transport->conn));
187 if (tevent_req_nterror(req, status)) {
188 return tevent_req_post(req, ev);
191 status = gensec_set_target_service(session->gensec, "cifs");
192 if (tevent_req_nterror(req, status)) {
193 return tevent_req_post(req, ev);
196 if (state->out_secblob.length > 0) {
197 chosen_oid = GENSEC_OID_SPNEGO;
198 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
199 if (!NT_STATUS_IS_OK(status)) {
200 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 state->out_secblob = data_blob_null;
205 chosen_oid = GENSEC_OID_NTLMSSP;
206 status = gensec_start_mech_by_oid(session->gensec,
207 chosen_oid);
208 if (!NT_STATUS_IS_OK(status)) {
209 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)));
215 if (tevent_req_nterror(req, status)) {
216 return tevent_req_post(req, ev);
218 } else {
219 chosen_oid = GENSEC_OID_NTLMSSP;
220 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
221 if (!NT_STATUS_IS_OK(status)) {
222 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)));
227 if (tevent_req_nterror(req, status)) {
228 return tevent_req_post(req, ev);
232 smb2_session_setup_spnego_gensec_next(req);
233 if (!tevent_req_is_in_progress(req)) {
234 return tevent_req_post(req, ev);
237 return req;
240 static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
242 struct smb2_session_setup_spnego_state *state =
243 tevent_req_data(req,
244 struct smb2_session_setup_spnego_state);
245 struct smb2_session *session = state->session;
246 struct tevent_req *subreq = NULL;
248 if (NT_STATUS_IS_OK(state->gensec_status)) {
249 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
250 return;
253 subreq = gensec_update_send(state, state->ev,
254 session->gensec,
255 state->out_secblob);
256 if (tevent_req_nomem(subreq, req)) {
257 return;
259 tevent_req_set_callback(subreq,
260 smb2_session_setup_spnego_gensec_done,
261 req);
264 static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
266 struct tevent_req *req =
267 tevent_req_callback_data(subreq,
268 struct tevent_req);
269 struct smb2_session_setup_spnego_state *state =
270 tevent_req_data(req,
271 struct smb2_session_setup_spnego_state);
272 NTSTATUS status;
274 status = gensec_update_recv(subreq, state,
275 &state->in_secblob);
276 state->gensec_status = status;
277 state->out_secblob = data_blob_null;
278 if (!NT_STATUS_IS_OK(status) &&
279 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
280 tevent_req_nterror(req, status);
281 return;
284 if (NT_STATUS_IS_OK(state->remote_status) &&
285 NT_STATUS_IS_OK(state->gensec_status)) {
286 smb2_session_setup_spnego_both_ready(req);
287 return;
290 smb2_session_setup_spnego_smb2_next(req);
293 static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
295 struct smb2_session_setup_spnego_state *state =
296 tevent_req_data(req,
297 struct smb2_session_setup_spnego_state);
298 struct smb2_session *session = state->session;
299 uint32_t timeout_msec;
300 uint8_t in_flags = 0;
301 struct tevent_req *subreq = NULL;
303 if (NT_STATUS_IS_OK(state->remote_status)) {
304 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
305 return;
308 timeout_msec = session->transport->options.request_timeout * 1000;
310 if (state->session_bind) {
311 in_flags |= SMB2_SESSION_FLAG_BINDING;
314 subreq = smb2cli_session_setup_send(state, state->ev,
315 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 &state->in_secblob);
323 if (tevent_req_nomem(subreq, req)) {
324 return;
326 tevent_req_set_callback(subreq,
327 smb2_session_setup_spnego_smb2_done,
328 req);
332 handle continuations of the spnego session setup
334 static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
336 struct tevent_req *req =
337 tevent_req_callback_data(subreq,
338 struct tevent_req);
339 struct smb2_session_setup_spnego_state *state =
340 tevent_req_data(req,
341 struct smb2_session_setup_spnego_state);
342 NTSTATUS status;
344 status = smb2cli_session_setup_recv(subreq, state,
345 &state->recv_iov,
346 &state->out_secblob);
347 state->remote_status = status;
348 state->in_secblob = data_blob_null;
349 if (!NT_STATUS_IS_OK(status) &&
350 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
351 tevent_req_nterror(req, status);
352 return;
355 if (NT_STATUS_IS_OK(state->remote_status) &&
356 NT_STATUS_IS_OK(state->gensec_status)) {
357 smb2_session_setup_spnego_both_ready(req);
358 return;
361 smb2_session_setup_spnego_gensec_next(req);
364 static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
366 struct smb2_session_setup_spnego_state *state =
367 tevent_req_data(req,
368 struct smb2_session_setup_spnego_state);
369 struct smb2_session *session = state->session;
370 NTSTATUS status;
371 DATA_BLOB session_key;
373 if (state->out_secblob.length != 0) {
374 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
375 return;
378 if (state->in_secblob.length != 0) {
379 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
380 return;
383 if (state->reauth) {
384 tevent_req_done(req);
385 return;
388 if (cli_credentials_is_anonymous(state->credentials) &&
389 !state->session->anonymous_session_key)
392 * Windows server does not set the
393 * SMB2_SESSION_FLAG_IS_GUEST nor
394 * SMB2_SESSION_FLAG_IS_NULL flag.
396 * This fix makes sure we do not try
397 * to verify a signature on the final
398 * session setup response.
400 tevent_req_done(req);
401 return;
404 if (state->session->forced_session_key.length != 0) {
405 session_key = state->session->forced_session_key;
406 } else {
407 status = gensec_session_key(session->gensec, state,
408 &session_key);
409 if (tevent_req_nterror(req, status)) {
410 return;
414 if (state->session_bind) {
415 status = smb2cli_session_set_channel_key(session->smbXcli,
416 session_key,
417 state->recv_iov);
418 if (tevent_req_nterror(req, status)) {
419 return;
421 session->needs_bind = false;
422 } else {
423 status = smb2cli_session_set_session_key(session->smbXcli,
424 session_key,
425 state->recv_iov);
426 if (tevent_req_nterror(req, status)) {
427 return;
430 tevent_req_done(req);
431 return;
435 receive a composite session setup reply
437 NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
439 return tevent_req_simple_recv_ntstatus(req);
443 sync version of smb2_session_setup_spnego
445 NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
446 struct cli_credentials *credentials,
447 uint64_t previous_session_id)
449 struct tevent_req *subreq;
450 NTSTATUS status;
451 bool ok;
452 TALLOC_CTX *frame = talloc_stackframe();
453 struct tevent_context *ev = session->transport->ev;
455 if (frame == NULL) {
456 return NT_STATUS_NO_MEMORY;
459 subreq = smb2_session_setup_spnego_send(frame, ev,
460 session, credentials,
461 previous_session_id);
462 if (subreq == NULL) {
463 TALLOC_FREE(frame);
464 return NT_STATUS_NO_MEMORY;
467 ok = tevent_req_poll(subreq, ev);
468 if (!ok) {
469 status = map_nt_error_from_unix_common(errno);
470 TALLOC_FREE(frame);
471 return status;
474 status = smb2_session_setup_spnego_recv(subreq);
475 TALLOC_FREE(subreq);
476 if (!NT_STATUS_IS_OK(status)) {
477 TALLOC_FREE(frame);
478 return status;
481 TALLOC_FREE(frame);
482 return NT_STATUS_OK;