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"
26 #define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
27 req = smbcli_request_setup_session(session, cmd, wct, buflen); \
28 if (!req) return NULL; \
32 /****************************************************************************
33 Initialize the session context
34 ****************************************************************************/
35 struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport)
37 struct smbcli_session *session;
39 uint32_t capabilities;
41 session = talloc_zero(transport, struct smbcli_session);
46 session->transport = talloc_reference(session, transport);
47 session->pid = (uint16_t)getpid();
48 session->vuid = UID_FIELD_INVALID;
50 capabilities = transport->negotiate.capabilities;
52 flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES;
54 if (capabilities & CAP_UNICODE) {
55 flags2 |= FLAGS2_UNICODE_STRINGS;
57 if (capabilities & CAP_STATUS32) {
58 flags2 |= FLAGS2_32_BIT_ERROR_CODES;
60 if (capabilities & CAP_EXTENDED_SECURITY) {
61 flags2 |= FLAGS2_EXTENDED_SECURITY;
63 if (session->transport->negotiate.sign_info.doing_signing) {
64 flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
67 session->flags2 = flags2;
72 /****************************************************************************
73 Perform a session setup (async send)
74 ****************************************************************************/
75 struct smbcli_request *smb_raw_session_setup_send(struct smbcli_session *session, union smb_sesssetup *parms)
77 struct smbcli_request *req = NULL;
79 switch (parms->generic.level) {
80 case RAW_SESSSETUP_GENERIC:
81 /* handled elsewhere */
84 case RAW_SESSSETUP_OLD:
85 SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
86 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
87 SSVAL(req->out.vwv, VWV(1), 0);
88 SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
89 SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
90 SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
91 SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
92 SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
93 SIVAL(req->out.vwv,VWV(8), 0); /* reserved */
94 smbcli_req_append_blob(req, &parms->old.in.password);
95 smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
96 smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
97 smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
98 smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
101 case RAW_SESSSETUP_NT1:
102 SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
103 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
104 SSVAL(req->out.vwv, VWV(1), 0);
105 SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
106 SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
107 SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
108 SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
109 SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
110 SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
111 SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
112 SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
113 smbcli_req_append_blob(req, &parms->nt1.in.password1);
114 smbcli_req_append_blob(req, &parms->nt1.in.password2);
115 smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
116 smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
117 smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
118 smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
121 case RAW_SESSSETUP_SPNEGO:
122 SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
123 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
124 SSVAL(req->out.vwv, VWV(1), 0);
125 SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
126 SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
127 SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
128 SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
129 SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
130 SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
131 SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
132 smbcli_req_append_blob(req, &parms->spnego.in.secblob);
133 smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
134 smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
135 smbcli_req_append_string(req, parms->spnego.in.domain, STR_TERMINATE);
139 if (!smbcli_request_send(req)) {
140 smbcli_request_destroy(req);
148 /****************************************************************************
149 Perform a session setup (async recv)
150 ****************************************************************************/
151 NTSTATUS smb_raw_session_setup_recv(struct smbcli_request *req,
153 union smb_sesssetup *parms)
158 if (!smbcli_request_receive(req)) {
159 return smbcli_request_destroy(req);
162 if (!NT_STATUS_IS_OK(req->status) &&
163 !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
164 return smbcli_request_destroy(req);
167 switch (parms->generic.level) {
168 case RAW_SESSSETUP_GENERIC:
169 /* handled elsewhere */
170 return NT_STATUS_INVALID_LEVEL;
172 case RAW_SESSSETUP_OLD:
173 SMBCLI_CHECK_WCT(req, 3);
174 ZERO_STRUCT(parms->old.out);
175 parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
176 parms->old.out.action = SVAL(req->in.vwv, VWV(2));
179 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
180 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
181 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
185 case RAW_SESSSETUP_NT1:
186 SMBCLI_CHECK_WCT(req, 3);
187 ZERO_STRUCT(parms->nt1.out);
188 parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
189 parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
192 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
193 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
194 if (p < (req->in.data + req->in.data_size)) {
195 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
200 case RAW_SESSSETUP_SPNEGO:
201 SMBCLI_CHECK_WCT(req, 4);
202 ZERO_STRUCT(parms->spnego.out);
203 parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
204 parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
205 len = SVAL(req->in.vwv, VWV(3));
211 parms->spnego.out.secblob = smbcli_req_pull_blob(req, mem_ctx, p, len);
212 p += parms->spnego.out.secblob.length;
213 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
214 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
215 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
220 return smbcli_request_destroy(req);
224 form an encrypted lanman password from a plaintext password
225 and the server supplied challenge
227 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
229 DATA_BLOB blob = data_blob(NULL, 24);
230 SMBencrypt(pass, challenge.data, blob.data);
235 form an encrypted NT password from a plaintext password
236 and the server supplied challenge
238 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
240 DATA_BLOB blob = data_blob(NULL, 24);
241 SMBNTencrypt(pass, challenge.data, blob.data);
246 store the user session key for a transport
248 void smbcli_session_set_user_session_key(struct smbcli_session *session,
249 const DATA_BLOB *session_key)
251 session->user_session_key = data_blob_talloc(session,
253 session_key->length);
257 setup signing for a NT1 style session setup
259 void smb_session_use_nt1_session_keys(struct smbcli_session *session,
260 const char *password, const DATA_BLOB *nt_response)
262 struct smbcli_transport *transport = session->transport;
264 DATA_BLOB session_key = data_blob(NULL, 16);
266 E_md4hash(password, nt_hash);
267 SMBsesskeygen_ntv1(nt_hash, session_key.data);
269 smbcli_transport_simple_set_signing(transport, session_key, *nt_response);
271 smbcli_session_set_user_session_key(session, &session_key);
272 data_blob_free(&session_key);
275 /****************************************************************************
276 Perform a session setup (sync interface) using generic interface and the old
278 ****************************************************************************/
279 static NTSTATUS smb_raw_session_setup_generic_old(struct smbcli_session *session,
281 union smb_sesssetup *parms)
284 union smb_sesssetup s2;
286 /* use the old interface */
287 s2.generic.level = RAW_SESSSETUP_OLD;
288 s2.old.in.bufsize = session->transport->options.max_xmit;
289 s2.old.in.mpx_max = session->transport->options.max_mux;
290 s2.old.in.vc_num = 1;
291 s2.old.in.sesskey = parms->generic.in.sesskey;
292 s2.old.in.domain = parms->generic.in.domain;
293 s2.old.in.user = parms->generic.in.user;
294 s2.old.in.os = "Unix";
295 s2.old.in.lanman = "Samba";
297 if (!parms->generic.in.password) {
298 s2.old.in.password = data_blob(NULL, 0);
299 } else if (session->transport->negotiate.sec_mode &
300 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
301 s2.old.in.password = lanman_blob(parms->generic.in.password,
302 session->transport->negotiate.secblob);
304 s2.old.in.password = data_blob(parms->generic.in.password,
305 strlen(parms->generic.in.password));
308 status = smb_raw_session_setup(session, mem_ctx, &s2);
310 data_blob_free(&s2.old.in.password);
312 if (!NT_STATUS_IS_OK(status)) {
316 parms->generic.out.vuid = s2.old.out.vuid;
317 parms->generic.out.os = s2.old.out.os;
318 parms->generic.out.lanman = s2.old.out.lanman;
319 parms->generic.out.domain = s2.old.out.domain;
324 /****************************************************************************
325 Perform a session setup (sync interface) using generic interface and the NT1
327 ****************************************************************************/
328 static NTSTATUS smb_raw_session_setup_generic_nt1(struct smbcli_session *session,
330 union smb_sesssetup *parms)
333 union smb_sesssetup s2;
335 s2.generic.level = RAW_SESSSETUP_NT1;
336 s2.nt1.in.bufsize = session->transport->options.max_xmit;
337 s2.nt1.in.mpx_max = session->transport->options.max_mux;
338 s2.nt1.in.vc_num = 1;
339 s2.nt1.in.sesskey = parms->generic.in.sesskey;
340 s2.nt1.in.capabilities = parms->generic.in.capabilities;
341 s2.nt1.in.domain = parms->generic.in.domain;
342 s2.nt1.in.user = parms->generic.in.user;
343 s2.nt1.in.os = "Unix";
344 s2.nt1.in.lanman = "Samba";
346 if (!parms->generic.in.password) {
347 s2.nt1.in.password1 = data_blob(NULL, 0);
348 s2.nt1.in.password2 = data_blob(NULL, 0);
349 } else if (session->transport->negotiate.sec_mode &
350 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
351 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password,
352 session->transport->negotiate.secblob);
353 s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
354 session->transport->negotiate.secblob);
355 smb_session_use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
358 s2.nt1.in.password1 = data_blob(parms->generic.in.password,
359 strlen(parms->generic.in.password));
360 s2.nt1.in.password2 = data_blob(NULL, 0);
363 status = smb_raw_session_setup(session, mem_ctx, &s2);
365 data_blob_free(&s2.nt1.in.password1);
366 data_blob_free(&s2.nt1.in.password2);
368 if (!NT_STATUS_IS_OK(status)) {
372 parms->generic.out.vuid = s2.nt1.out.vuid;
373 parms->generic.out.os = s2.nt1.out.os;
374 parms->generic.out.lanman = s2.nt1.out.lanman;
375 parms->generic.out.domain = s2.nt1.out.domain;
380 /****************************************************************************
381 Perform a session setup (sync interface) using generic interface and the SPNEGO
383 ****************************************************************************/
384 static NTSTATUS smb_raw_session_setup_generic_spnego(struct smbcli_session *session,
386 union smb_sesssetup *parms)
389 NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY;
390 union smb_sesssetup s2;
391 DATA_BLOB session_key = data_blob(NULL, 0);
392 DATA_BLOB null_data_blob = data_blob(NULL, 0);
393 const char *chosen_oid = NULL;
395 s2.generic.level = RAW_SESSSETUP_SPNEGO;
396 s2.spnego.in.bufsize = session->transport->options.max_xmit;
397 s2.spnego.in.mpx_max = session->transport->options.max_mux;
398 s2.spnego.in.vc_num = 1;
399 s2.spnego.in.sesskey = parms->generic.in.sesskey;
400 s2.spnego.in.capabilities = parms->generic.in.capabilities;
401 s2.spnego.in.domain = parms->generic.in.domain;
402 s2.spnego.in.os = "Unix";
403 s2.spnego.in.lanman = "Samba";
404 s2.spnego.out.vuid = session->vuid;
406 smbcli_temp_set_signing(session->transport);
408 status = gensec_client_start(session, &session->gensec);
409 if (!NT_STATUS_IS_OK(status)) {
410 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
414 gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
416 status = gensec_set_domain(session->gensec, parms->generic.in.domain);
417 if (!NT_STATUS_IS_OK(status)) {
418 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n",
419 parms->generic.in.domain, nt_errstr(status)));
423 status = gensec_set_username(session->gensec, parms->generic.in.user);
424 if (!NT_STATUS_IS_OK(status)) {
425 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n",
426 parms->generic.in.user, nt_errstr(status)));
430 status = gensec_set_password(session->gensec, parms->generic.in.password);
431 if (!NT_STATUS_IS_OK(status)) {
432 DEBUG(1, ("Failed to start set GENSEC client password: %s\n",
437 status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
438 if (!NT_STATUS_IS_OK(status)) {
439 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
444 if (session->transport->negotiate.secblob.length) {
445 chosen_oid = GENSEC_OID_SPNEGO;
447 /* without a sec blob, means raw NTLMSSP */
448 chosen_oid = GENSEC_OID_NTLMSSP;
451 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
452 if (!NT_STATUS_IS_OK(status)) {
453 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
454 gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
458 status = gensec_update(session->gensec, mem_ctx,
459 session->transport->negotiate.secblob,
460 &s2.spnego.in.secblob);
463 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
467 if (!NT_STATUS_IS_OK(session_key_err)) {
468 session_key_err = gensec_session_key(session->gensec, &session_key);
470 if (NT_STATUS_IS_OK(session_key_err)) {
471 smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
474 if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) {
478 session->vuid = s2.spnego.out.vuid;
479 status = smb_raw_session_setup(session, mem_ctx, &s2);
480 session->vuid = UID_FIELD_INVALID;
481 if (!NT_STATUS_IS_OK(status) &&
482 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
486 status = gensec_update(session->gensec, mem_ctx,
487 s2.spnego.out.secblob,
488 &s2.spnego.in.secblob);
493 if (NT_STATUS_IS_OK(status)) {
494 if (!NT_STATUS_IS_OK(session_key_err)) {
495 DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err)));
496 return session_key_err;
499 smbcli_session_set_user_session_key(session, &session_key);
501 parms->generic.out.vuid = s2.spnego.out.vuid;
502 parms->generic.out.os = s2.spnego.out.os;
503 parms->generic.out.lanman = s2.spnego.out.lanman;
504 parms->generic.out.domain = s2.spnego.out.domain;
506 talloc_free(session->gensec);
507 session->gensec = NULL;
508 DEBUG(1, ("Failed to login with %s: %s\n", gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
515 /****************************************************************************
516 Perform a session setup (sync interface) using generic interface
517 ****************************************************************************/
518 static NTSTATUS smb_raw_session_setup_generic(struct smbcli_session *session,
520 union smb_sesssetup *parms)
522 if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
523 /* no session setup at all in earliest protocols */
524 ZERO_STRUCT(parms->generic.out);
528 /* see if we need to use the original session setup interface */
529 if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
530 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
533 /* see if we should use the NT1 interface */
534 if (!session->transport->options.use_spnego ||
535 !(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) {
536 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
539 /* default to using SPNEGO/NTLMSSP */
540 return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms);
544 /****************************************************************************
545 Perform a session setup (sync interface)
546 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
547 setup variant based on negotiated protocol options
548 ****************************************************************************/
549 NTSTATUS smb_raw_session_setup(struct smbcli_session *session, TALLOC_CTX *mem_ctx,
550 union smb_sesssetup *parms)
552 struct smbcli_request *req;
554 if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
555 NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms);
557 if (NT_STATUS_IS_OK(ret)
558 && parms->generic.in.user
559 && *parms->generic.in.user) {
560 if (!session->transport->negotiate.sign_info.doing_signing
561 && session->transport->negotiate.sign_info.mandatory_signing) {
562 DEBUG(0, ("SMB signing required, but server does not support it\n"));
563 return NT_STATUS_ACCESS_DENIED;
569 req = smb_raw_session_setup_send(session, parms);
570 return smb_raw_session_setup_recv(req, mem_ctx, parms);
574 /****************************************************************************
575 Send a uloggoff (async send)
576 *****************************************************************************/
577 struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session)
579 struct smbcli_request *req;
581 SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
583 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
584 SSVAL(req->out.vwv, VWV(1), 0);
586 if (!smbcli_request_send(req)) {
587 smbcli_request_destroy(req);
594 /****************************************************************************
595 Send a uloggoff (sync interface)
596 *****************************************************************************/
597 NTSTATUS smb_raw_ulogoff(struct smbcli_session *session)
599 struct smbcli_request *req = smb_raw_ulogoff_send(session);
600 return smbcli_request_simple_recv(req);
604 /****************************************************************************
605 Send a exit (async send)
606 *****************************************************************************/
607 struct smbcli_request *smb_raw_exit_send(struct smbcli_session *session)
609 struct smbcli_request *req;
611 SETUP_REQUEST_SESSION(SMBexit, 0, 0);
613 if (!smbcli_request_send(req)) {
614 smbcli_request_destroy(req);
621 /****************************************************************************
622 Send a exit (sync interface)
623 *****************************************************************************/
624 NTSTATUS smb_raw_exit(struct smbcli_session *session)
626 struct smbcli_request *req = smb_raw_exit_send(session);
627 return smbcli_request_simple_recv(req);