2 Unix SMB/CIFS implementation.
3 SMB client session context management functions
4 Copyright (C) Andrew Tridgell 1994-1998
5 Copyright (C) James Myers 2003 <myersjj@samba.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "libcli/raw/libcliraw.h"
24 #include "auth/auth.h"
27 #define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
28 req = smbcli_request_setup_session(session, cmd, wct, buflen); \
29 if (!req) return NULL; \
33 /****************************************************************************
34 Initialize the session context
35 ****************************************************************************/
36 struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport)
38 struct smbcli_session *session;
40 uint32_t capabilities;
42 session = talloc_p(transport, struct smbcli_session);
47 ZERO_STRUCTP(session);
48 session->transport = talloc_reference(session, transport);
49 session->pid = (uint16_t)getpid();
50 session->vuid = UID_FIELD_INVALID;
53 capabilities = transport->negotiate.capabilities;
55 flags2 = FLAGS2_LONG_PATH_COMPONENTS;
57 if (capabilities & CAP_UNICODE) {
58 flags2 |= FLAGS2_UNICODE_STRINGS;
60 if (capabilities & CAP_STATUS32) {
61 flags2 |= FLAGS2_32_BIT_ERROR_CODES;
63 if (capabilities & CAP_EXTENDED_SECURITY) {
64 flags2 |= FLAGS2_EXTENDED_SECURITY;
66 if (session->transport->negotiate.sign_info.doing_signing) {
67 flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
70 session->flags2 = flags2;
75 /****************************************************************************
76 Perform a session setup (async send)
77 ****************************************************************************/
78 struct smbcli_request *smb_raw_session_setup_send(struct smbcli_session *session, union smb_sesssetup *parms)
80 struct smbcli_request *req = NULL;
82 switch (parms->generic.level) {
83 case RAW_SESSSETUP_GENERIC:
84 /* handled elsewhere */
87 case RAW_SESSSETUP_OLD:
88 SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
89 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
90 SSVAL(req->out.vwv, VWV(1), 0);
91 SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
92 SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
93 SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
94 SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
95 SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
96 SIVAL(req->out.vwv,VWV(8), 0); /* reserved */
97 smbcli_req_append_blob(req, &parms->old.in.password);
98 smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
99 smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
100 smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
101 smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
104 case RAW_SESSSETUP_NT1:
105 SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
106 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
107 SSVAL(req->out.vwv, VWV(1), 0);
108 SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
109 SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
110 SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
111 SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
112 SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
113 SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
114 SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
115 SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
116 smbcli_req_append_blob(req, &parms->nt1.in.password1);
117 smbcli_req_append_blob(req, &parms->nt1.in.password2);
118 smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
119 smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
120 smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
121 smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
124 case RAW_SESSSETUP_SPNEGO:
125 SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
126 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
127 SSVAL(req->out.vwv, VWV(1), 0);
128 SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
129 SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
130 SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
131 SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
132 SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
133 SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
134 SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
135 smbcli_req_append_blob(req, &parms->spnego.in.secblob);
136 smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
137 smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
138 smbcli_req_append_string(req, parms->spnego.in.domain, STR_TERMINATE);
142 if (!smbcli_request_send(req)) {
143 smbcli_request_destroy(req);
151 /****************************************************************************
152 Perform a session setup (async recv)
153 ****************************************************************************/
154 NTSTATUS smb_raw_session_setup_recv(struct smbcli_request *req,
156 union smb_sesssetup *parms)
161 if (!smbcli_request_receive(req)) {
162 return smbcli_request_destroy(req);
165 if (!NT_STATUS_IS_OK(req->status) &&
166 !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
167 return smbcli_request_destroy(req);
170 switch (parms->generic.level) {
171 case RAW_SESSSETUP_GENERIC:
172 /* handled elsewhere */
173 return NT_STATUS_INVALID_LEVEL;
175 case RAW_SESSSETUP_OLD:
176 SMBCLI_CHECK_WCT(req, 3);
177 ZERO_STRUCT(parms->old.out);
178 parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
179 parms->old.out.action = SVAL(req->in.vwv, VWV(2));
182 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
183 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
184 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
188 case RAW_SESSSETUP_NT1:
189 SMBCLI_CHECK_WCT(req, 3);
190 ZERO_STRUCT(parms->nt1.out);
191 parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
192 parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
195 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
196 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
197 if (p < (req->in.data + req->in.data_size)) {
198 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
203 case RAW_SESSSETUP_SPNEGO:
204 SMBCLI_CHECK_WCT(req, 4);
205 ZERO_STRUCT(parms->spnego.out);
206 parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
207 parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
208 len = SVAL(req->in.vwv, VWV(3));
214 parms->spnego.out.secblob = smbcli_req_pull_blob(req, mem_ctx, p, len);
215 p += parms->spnego.out.secblob.length;
216 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
217 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
218 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
223 return smbcli_request_destroy(req);
227 form an encrypted lanman password from a plaintext password
228 and the server supplied challenge
230 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
232 DATA_BLOB blob = data_blob(NULL, 24);
233 SMBencrypt(pass, challenge.data, blob.data);
238 form an encrypted NT password from a plaintext password
239 and the server supplied challenge
241 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
243 DATA_BLOB blob = data_blob(NULL, 24);
244 SMBNTencrypt(pass, challenge.data, blob.data);
249 store the user session key for a transport
251 void smbcli_session_set_user_session_key(struct smbcli_session *session,
252 const DATA_BLOB *session_key)
254 session->user_session_key = data_blob_talloc(session,
256 session_key->length);
260 setup signing for a NT1 style session setup
262 static void use_nt1_session_keys(struct smbcli_session *session,
263 const char *password, const DATA_BLOB *nt_response)
265 struct smbcli_transport *transport = session->transport;
267 DATA_BLOB session_key = data_blob(NULL, 16);
269 E_md4hash(password, nt_hash);
270 SMBsesskeygen_ntv1(nt_hash, session_key.data);
272 smbcli_transport_simple_set_signing(transport, session_key, *nt_response);
274 smbcli_session_set_user_session_key(session, &session_key);
275 data_blob_free(&session_key);
278 /****************************************************************************
279 Perform a session setup (sync interface) using generic interface and the old
281 ****************************************************************************/
282 static NTSTATUS smb_raw_session_setup_generic_old(struct smbcli_session *session,
284 union smb_sesssetup *parms)
287 union smb_sesssetup s2;
289 /* use the old interface */
290 s2.generic.level = RAW_SESSSETUP_OLD;
291 s2.old.in.bufsize = session->transport->options.max_xmit;
292 s2.old.in.mpx_max = session->transport->options.max_mux;
293 s2.old.in.vc_num = 1;
294 s2.old.in.sesskey = parms->generic.in.sesskey;
295 s2.old.in.domain = parms->generic.in.domain;
296 s2.old.in.user = parms->generic.in.user;
297 s2.old.in.os = "Unix";
298 s2.old.in.lanman = "Samba";
300 if (!parms->generic.in.password) {
301 s2.old.in.password = data_blob(NULL, 0);
302 } else if (session->transport->negotiate.sec_mode &
303 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
304 s2.old.in.password = lanman_blob(parms->generic.in.password,
305 session->transport->negotiate.secblob);
307 s2.old.in.password = data_blob(parms->generic.in.password,
308 strlen(parms->generic.in.password));
311 status = smb_raw_session_setup(session, mem_ctx, &s2);
313 data_blob_free(&s2.old.in.password);
315 if (!NT_STATUS_IS_OK(status)) {
319 parms->generic.out.vuid = s2.old.out.vuid;
320 parms->generic.out.os = s2.old.out.os;
321 parms->generic.out.lanman = s2.old.out.lanman;
322 parms->generic.out.domain = s2.old.out.domain;
327 /****************************************************************************
328 Perform a session setup (sync interface) using generic interface and the NT1
330 ****************************************************************************/
331 static NTSTATUS smb_raw_session_setup_generic_nt1(struct smbcli_session *session,
333 union smb_sesssetup *parms)
336 union smb_sesssetup s2;
338 s2.generic.level = RAW_SESSSETUP_NT1;
339 s2.nt1.in.bufsize = session->transport->options.max_xmit;
340 s2.nt1.in.mpx_max = session->transport->options.max_mux;
341 s2.nt1.in.vc_num = 1;
342 s2.nt1.in.sesskey = parms->generic.in.sesskey;
343 s2.nt1.in.capabilities = parms->generic.in.capabilities;
344 s2.nt1.in.domain = parms->generic.in.domain;
345 s2.nt1.in.user = parms->generic.in.user;
346 s2.nt1.in.os = "Unix";
347 s2.nt1.in.lanman = "Samba";
349 if (!parms->generic.in.password) {
350 s2.nt1.in.password1 = data_blob(NULL, 0);
351 s2.nt1.in.password2 = data_blob(NULL, 0);
352 } else if (session->transport->negotiate.sec_mode &
353 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
354 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password,
355 session->transport->negotiate.secblob);
356 s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
357 session->transport->negotiate.secblob);
358 use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
361 s2.nt1.in.password1 = data_blob(parms->generic.in.password,
362 strlen(parms->generic.in.password));
363 s2.nt1.in.password2 = data_blob(NULL, 0);
366 status = smb_raw_session_setup(session, mem_ctx, &s2);
368 data_blob_free(&s2.nt1.in.password1);
369 data_blob_free(&s2.nt1.in.password2);
371 if (!NT_STATUS_IS_OK(status)) {
375 parms->generic.out.vuid = s2.nt1.out.vuid;
376 parms->generic.out.os = s2.nt1.out.os;
377 parms->generic.out.lanman = s2.nt1.out.lanman;
378 parms->generic.out.domain = s2.nt1.out.domain;
383 /****************************************************************************
384 Perform a session setup (sync interface) using generic interface and the SPNEGO
386 ****************************************************************************/
387 static NTSTATUS smb_raw_session_setup_generic_spnego(struct smbcli_session *session,
389 union smb_sesssetup *parms)
392 NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY;
393 union smb_sesssetup s2;
394 DATA_BLOB session_key = data_blob(NULL, 0);
395 DATA_BLOB null_data_blob = data_blob(NULL, 0);
396 const char *chosen_oid;
398 s2.generic.level = RAW_SESSSETUP_SPNEGO;
399 s2.spnego.in.bufsize = session->transport->options.max_xmit;
400 s2.spnego.in.mpx_max = session->transport->options.max_mux;
401 s2.spnego.in.vc_num = 1;
402 s2.spnego.in.sesskey = parms->generic.in.sesskey;
403 s2.spnego.in.capabilities = parms->generic.in.capabilities;
404 s2.spnego.in.domain = parms->generic.in.domain;
405 s2.spnego.in.os = "Unix";
406 s2.spnego.in.lanman = "Samba";
407 s2.spnego.out.vuid = session->vuid;
409 smbcli_temp_set_signing(session->transport);
411 status = gensec_client_start(session, &session->gensec);
412 if (!NT_STATUS_IS_OK(status)) {
413 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
417 gensec_want_feature(session->gensec, GENSEC_WANT_SESSION_KEY);
419 status = gensec_set_domain(session->gensec, parms->generic.in.domain);
420 if (!NT_STATUS_IS_OK(status)) {
421 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n",
422 parms->generic.in.domain, nt_errstr(status)));
426 status = gensec_set_username(session->gensec, parms->generic.in.user);
427 if (!NT_STATUS_IS_OK(status)) {
428 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n",
429 parms->generic.in.user, nt_errstr(status)));
433 status = gensec_set_password(session->gensec, parms->generic.in.password);
434 if (!NT_STATUS_IS_OK(status)) {
435 DEBUG(1, ("Failed to start set GENSEC client password: %s\n",
440 status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
441 if (!NT_STATUS_IS_OK(status)) {
442 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
447 if (session->transport->negotiate.secblob.length) {
448 chosen_oid = OID_SPNEGO;
450 /* without a sec blob, means raw NTLMSSP */
451 chosen_oid = OID_NTLMSSP;
454 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
455 if (!NT_STATUS_IS_OK(status)) {
456 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
457 gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
461 status = gensec_update(session->gensec, mem_ctx,
462 session->transport->negotiate.secblob,
463 &s2.spnego.in.secblob);
466 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
470 if (!NT_STATUS_IS_OK(session_key_err)) {
471 session_key_err = gensec_session_key(session->gensec, &session_key);
473 if (NT_STATUS_IS_OK(session_key_err)) {
474 smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
477 if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) {
481 session->vuid = s2.spnego.out.vuid;
482 status = smb_raw_session_setup(session, mem_ctx, &s2);
483 session->vuid = UID_FIELD_INVALID;
484 if (!NT_STATUS_IS_OK(status) &&
485 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
489 status = gensec_update(session->gensec, mem_ctx,
490 s2.spnego.out.secblob,
491 &s2.spnego.in.secblob);
496 if (NT_STATUS_IS_OK(status)) {
497 if (!NT_STATUS_IS_OK(session_key_err)) {
498 DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err)));
499 return session_key_err;
502 smbcli_session_set_user_session_key(session, &session_key);
504 parms->generic.out.vuid = s2.spnego.out.vuid;
505 parms->generic.out.os = s2.spnego.out.os;
506 parms->generic.out.lanman = s2.spnego.out.lanman;
507 parms->generic.out.domain = s2.spnego.out.domain;
509 gensec_end(&session->gensec);
510 DEBUG(1, ("Failed to login with %s: %s\n", gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
517 /****************************************************************************
518 Perform a session setup (sync interface) using generic interface
519 ****************************************************************************/
520 static NTSTATUS smb_raw_session_setup_generic(struct smbcli_session *session,
522 union smb_sesssetup *parms)
524 if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
525 /* no session setup at all in earliest protocols */
526 ZERO_STRUCT(parms->generic.out);
530 /* see if we need to use the original session setup interface */
531 if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
532 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
535 /* see if we should use the NT1 interface */
536 if (!session->transport->options.use_spnego ||
537 !(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) {
538 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
541 /* default to using SPNEGO/NTLMSSP */
542 return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms);
546 /****************************************************************************
547 Perform a session setup (sync interface)
548 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
549 setup variant based on negotiated protocol options
550 ****************************************************************************/
551 NTSTATUS smb_raw_session_setup(struct smbcli_session *session, TALLOC_CTX *mem_ctx,
552 union smb_sesssetup *parms)
554 struct smbcli_request *req;
556 if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
557 NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms);
559 if (NT_STATUS_IS_OK(ret)
560 && parms->generic.in.user
561 && *parms->generic.in.user) {
562 if (!session->transport->negotiate.sign_info.doing_signing
563 && session->transport->negotiate.sign_info.mandatory_signing) {
564 DEBUG(0, ("SMB signing required, but server does not support it\n"));
565 return NT_STATUS_ACCESS_DENIED;
571 req = smb_raw_session_setup_send(session, parms);
572 return smb_raw_session_setup_recv(req, mem_ctx, parms);
576 /****************************************************************************
577 Send a uloggoff (async send)
578 *****************************************************************************/
579 struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session)
581 struct smbcli_request *req;
583 SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
585 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
586 SSVAL(req->out.vwv, VWV(1), 0);
588 if (!smbcli_request_send(req)) {
589 smbcli_request_destroy(req);
596 /****************************************************************************
597 Send a uloggoff (sync interface)
598 *****************************************************************************/
599 NTSTATUS smb_raw_ulogoff(struct smbcli_session *session)
601 struct smbcli_request *req = smb_raw_ulogoff_send(session);
602 return smbcli_request_simple_recv(req);
606 /****************************************************************************
607 Send a exit (async send)
608 *****************************************************************************/
609 struct smbcli_request *smb_raw_exit_send(struct smbcli_session *session)
611 struct smbcli_request *req;
613 SETUP_REQUEST_SESSION(SMBexit, 0, 0);
615 if (!smbcli_request_send(req)) {
616 smbcli_request_destroy(req);
623 /****************************************************************************
624 Send a exit (sync interface)
625 *****************************************************************************/
626 NTSTATUS smb_raw_exit(struct smbcli_session *session)
628 struct smbcli_request *req = smb_raw_exit_send(session);
629 return smbcli_request_simple_recv(req);