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.
24 #define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
25 req = smbcli_request_setup_session(session, cmd, wct, buflen); \
26 if (!req) return NULL; \
29 /****************************************************************************
30 Initialize the session context
31 ****************************************************************************/
32 struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport)
34 struct smbcli_session *session;
36 session = talloc_p(transport, struct smbcli_session);
41 ZERO_STRUCTP(session);
42 session->transport = transport;
43 session->pid = (uint16_t)getpid();
44 session->vuid = UID_FIELD_INVALID;
45 session->transport->reference_count++;
50 /****************************************************************************
51 reduce reference_count and destroy is <= 0
52 ****************************************************************************/
53 void smbcli_session_close(struct smbcli_session *session)
55 session->reference_count--;
56 if (session->reference_count <= 0) {
57 smbcli_transport_close(session->transport);
61 /****************************************************************************
62 Perform a session setup (async send)
63 ****************************************************************************/
64 struct smbcli_request *smb_raw_session_setup_send(struct smbcli_session *session, union smb_sesssetup *parms)
66 struct smbcli_request *req = NULL;
68 switch (parms->generic.level) {
69 case RAW_SESSSETUP_GENERIC:
70 /* handled elsewhere */
73 case RAW_SESSSETUP_OLD:
74 SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
75 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
76 SSVAL(req->out.vwv, VWV(1), 0);
77 SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
78 SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
79 SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
80 SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
81 SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
82 SIVAL(req->out.vwv,VWV(8), 0); /* reserved */
83 smbcli_req_append_blob(req, &parms->old.in.password);
84 smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
85 smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
86 smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
87 smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
90 case RAW_SESSSETUP_NT1:
91 SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
92 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
93 SSVAL(req->out.vwv, VWV(1), 0);
94 SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
95 SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
96 SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
97 SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
98 SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
99 SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
100 SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
101 SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
102 smbcli_req_append_blob(req, &parms->nt1.in.password1);
103 smbcli_req_append_blob(req, &parms->nt1.in.password2);
104 smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
105 smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
106 smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
107 smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
110 case RAW_SESSSETUP_SPNEGO:
111 SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
112 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
113 SSVAL(req->out.vwv, VWV(1), 0);
114 SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
115 SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
116 SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
117 SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
118 SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
119 SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
120 SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
121 smbcli_req_append_blob(req, &parms->spnego.in.secblob);
122 smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
123 smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
124 smbcli_req_append_string(req, parms->spnego.in.domain, STR_TERMINATE);
128 if (!smbcli_request_send(req)) {
129 smbcli_request_destroy(req);
137 /****************************************************************************
138 Perform a session setup (async recv)
139 ****************************************************************************/
140 NTSTATUS smb_raw_session_setup_recv(struct smbcli_request *req,
142 union smb_sesssetup *parms)
147 if (!smbcli_request_receive(req)) {
148 return smbcli_request_destroy(req);
151 if (!NT_STATUS_IS_OK(req->status) &&
152 !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
153 return smbcli_request_destroy(req);
156 switch (parms->generic.level) {
157 case RAW_SESSSETUP_GENERIC:
158 /* handled elsewhere */
159 return NT_STATUS_INVALID_LEVEL;
161 case RAW_SESSSETUP_OLD:
162 SMBCLI_CHECK_WCT(req, 3);
163 ZERO_STRUCT(parms->old.out);
164 parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
165 parms->old.out.action = SVAL(req->in.vwv, VWV(2));
168 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
169 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
170 p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
174 case RAW_SESSSETUP_NT1:
175 SMBCLI_CHECK_WCT(req, 3);
176 ZERO_STRUCT(parms->nt1.out);
177 parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
178 parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
181 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
182 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
183 if (p < (req->in.data + req->in.data_size)) {
184 p += smbcli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
189 case RAW_SESSSETUP_SPNEGO:
190 SMBCLI_CHECK_WCT(req, 4);
191 ZERO_STRUCT(parms->spnego.out);
192 parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
193 parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
194 len = SVAL(req->in.vwv, VWV(3));
200 parms->spnego.out.secblob = smbcli_req_pull_blob(req, mem_ctx, p, len);
201 p += parms->spnego.out.secblob.length;
202 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
203 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
204 p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
209 return smbcli_request_destroy(req);
213 form an encrypted lanman password from a plaintext password
214 and the server supplied challenge
216 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
218 DATA_BLOB blob = data_blob(NULL, 24);
219 SMBencrypt(pass, challenge.data, blob.data);
224 form an encrypted NT password from a plaintext password
225 and the server supplied challenge
227 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
229 DATA_BLOB blob = data_blob(NULL, 24);
230 SMBNTencrypt(pass, challenge.data, blob.data);
235 store the user session key for a transport
237 void smbcli_session_set_user_session_key(struct smbcli_session *session,
238 const DATA_BLOB *session_key)
240 session->user_session_key = data_blob_talloc(session,
242 session_key->length);
246 setup signing for a NT1 style session setup
248 static void use_nt1_session_keys(struct smbcli_session *session,
249 const char *password, const DATA_BLOB *nt_response)
251 struct smbcli_transport *transport = session->transport;
253 DATA_BLOB session_key = data_blob(NULL, 16);
255 E_md4hash(password, nt_hash);
256 SMBsesskeygen_ntv1(nt_hash, session_key.data);
258 smbcli_transport_simple_set_signing(transport, session_key, *nt_response);
260 smbcli_session_set_user_session_key(session, &session_key);
261 data_blob_free(&session_key);
264 /****************************************************************************
265 Perform a session setup (sync interface) using generic interface and the old
267 ****************************************************************************/
268 static NTSTATUS smb_raw_session_setup_generic_old(struct smbcli_session *session,
270 union smb_sesssetup *parms)
273 union smb_sesssetup s2;
275 /* use the old interface */
276 s2.generic.level = RAW_SESSSETUP_OLD;
277 s2.old.in.bufsize = ~0;
278 s2.old.in.mpx_max = 50;
279 s2.old.in.vc_num = 1;
280 s2.old.in.sesskey = parms->generic.in.sesskey;
281 s2.old.in.domain = parms->generic.in.domain;
282 s2.old.in.user = parms->generic.in.user;
283 s2.old.in.os = "Unix";
284 s2.old.in.lanman = "Samba";
286 if (!parms->generic.in.password) {
287 s2.old.in.password = data_blob(NULL, 0);
288 } else if (session->transport->negotiate.sec_mode &
289 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
290 s2.old.in.password = lanman_blob(parms->generic.in.password,
291 session->transport->negotiate.secblob);
293 s2.old.in.password = data_blob(parms->generic.in.password,
294 strlen(parms->generic.in.password));
297 status = smb_raw_session_setup(session, mem_ctx, &s2);
299 data_blob_free(&s2.old.in.password);
301 if (!NT_STATUS_IS_OK(status)) {
305 parms->generic.out.vuid = s2.old.out.vuid;
306 parms->generic.out.os = s2.old.out.os;
307 parms->generic.out.lanman = s2.old.out.lanman;
308 parms->generic.out.domain = s2.old.out.domain;
313 /****************************************************************************
314 Perform a session setup (sync interface) using generic interface and the NT1
316 ****************************************************************************/
317 static NTSTATUS smb_raw_session_setup_generic_nt1(struct smbcli_session *session,
319 union smb_sesssetup *parms)
322 union smb_sesssetup s2;
324 s2.generic.level = RAW_SESSSETUP_NT1;
325 s2.nt1.in.bufsize = ~0;
326 s2.nt1.in.mpx_max = 50;
327 s2.nt1.in.vc_num = 1;
328 s2.nt1.in.sesskey = parms->generic.in.sesskey;
329 s2.nt1.in.capabilities = parms->generic.in.capabilities;
330 s2.nt1.in.domain = parms->generic.in.domain;
331 s2.nt1.in.user = parms->generic.in.user;
332 s2.nt1.in.os = "Unix";
333 s2.nt1.in.lanman = "Samba";
335 if (!parms->generic.in.password) {
336 s2.nt1.in.password1 = data_blob(NULL, 0);
337 s2.nt1.in.password2 = data_blob(NULL, 0);
338 } else if (session->transport->negotiate.sec_mode &
339 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
340 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password,
341 session->transport->negotiate.secblob);
342 s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
343 session->transport->negotiate.secblob);
344 use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
347 s2.nt1.in.password1 = data_blob(parms->generic.in.password,
348 strlen(parms->generic.in.password));
349 s2.nt1.in.password2 = data_blob(NULL, 0);
352 status = smb_raw_session_setup(session, mem_ctx, &s2);
354 data_blob_free(&s2.nt1.in.password1);
355 data_blob_free(&s2.nt1.in.password2);
357 if (!NT_STATUS_IS_OK(status)) {
361 parms->generic.out.vuid = s2.nt1.out.vuid;
362 parms->generic.out.os = s2.nt1.out.os;
363 parms->generic.out.lanman = s2.nt1.out.lanman;
364 parms->generic.out.domain = s2.nt1.out.domain;
369 /****************************************************************************
370 Perform a session setup (sync interface) using generic interface and the SPNEGO
372 ****************************************************************************/
373 static NTSTATUS smb_raw_session_setup_generic_spnego(struct smbcli_session *session,
375 union smb_sesssetup *parms)
378 NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY;
379 union smb_sesssetup s2;
380 DATA_BLOB session_key = data_blob(NULL, 0);
381 DATA_BLOB null_data_blob = data_blob(NULL, 0);
382 const char *chosen_oid;
384 s2.generic.level = RAW_SESSSETUP_SPNEGO;
385 s2.spnego.in.bufsize = ~0;
386 s2.spnego.in.mpx_max = 50;
387 s2.spnego.in.vc_num = 1;
388 s2.spnego.in.sesskey = parms->generic.in.sesskey;
389 s2.spnego.in.capabilities = parms->generic.in.capabilities;
390 s2.spnego.in.domain = parms->generic.in.domain;
391 s2.spnego.in.os = "Unix";
392 s2.spnego.in.lanman = "Samba";
393 s2.spnego.out.vuid = session->vuid;
395 smbcli_temp_set_signing(session->transport);
397 status = gensec_client_start(session, &session->gensec);
398 if (!NT_STATUS_IS_OK(status)) {
399 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
403 gensec_want_feature(session->gensec, GENSEC_WANT_SESSION_KEY);
405 status = gensec_set_domain(session->gensec, parms->generic.in.domain);
406 if (!NT_STATUS_IS_OK(status)) {
407 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n",
408 parms->generic.in.domain, nt_errstr(status)));
412 status = gensec_set_username(session->gensec, parms->generic.in.user);
413 if (!NT_STATUS_IS_OK(status)) {
414 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n",
415 parms->generic.in.user, nt_errstr(status)));
419 status = gensec_set_password(session->gensec, parms->generic.in.password);
420 if (!NT_STATUS_IS_OK(status)) {
421 DEBUG(1, ("Failed to start set GENSEC client password: %s\n",
426 status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
427 if (!NT_STATUS_IS_OK(status)) {
428 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
433 if (session->transport->negotiate.secblob.length) {
434 chosen_oid = OID_SPNEGO;
436 /* without a sec blob, means raw NTLMSSP */
437 chosen_oid = OID_NTLMSSP;
440 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
441 if (!NT_STATUS_IS_OK(status)) {
442 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
443 gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
447 status = gensec_update(session->gensec, mem_ctx,
448 session->transport->negotiate.secblob,
449 &s2.spnego.in.secblob);
452 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
456 if (!NT_STATUS_IS_OK(session_key_err)) {
457 session_key_err = gensec_session_key(session->gensec, &session_key);
459 if (NT_STATUS_IS_OK(session_key_err)) {
460 smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
463 if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) {
467 session->vuid = s2.spnego.out.vuid;
468 status = smb_raw_session_setup(session, mem_ctx, &s2);
469 session->vuid = UID_FIELD_INVALID;
470 if (!NT_STATUS_IS_OK(status) &&
471 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
475 status = gensec_update(session->gensec, mem_ctx,
476 s2.spnego.out.secblob,
477 &s2.spnego.in.secblob);
482 if (NT_STATUS_IS_OK(status)) {
483 if (!NT_STATUS_IS_OK(session_key_err)) {
484 DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err)));
485 return session_key_err;
488 smbcli_session_set_user_session_key(session, &session_key);
490 parms->generic.out.vuid = s2.spnego.out.vuid;
491 parms->generic.out.os = s2.spnego.out.os;
492 parms->generic.out.lanman = s2.spnego.out.lanman;
493 parms->generic.out.domain = s2.spnego.out.domain;
495 gensec_end(&session->gensec);
496 DEBUG(1, ("Failed to login with %s: %s\n", gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
503 /****************************************************************************
504 Perform a session setup (sync interface) using generic interface
505 ****************************************************************************/
506 static NTSTATUS smb_raw_session_setup_generic(struct smbcli_session *session,
508 union smb_sesssetup *parms)
510 if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
511 /* no session setup at all in earliest protocols */
512 ZERO_STRUCT(parms->generic.out);
516 /* see if we need to use the original session setup interface */
517 if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
518 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
521 /* see if we should use the NT1 interface */
522 if (!session->transport->options.use_spnego ||
523 !(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) {
524 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
527 /* default to using SPNEGO/NTLMSSP */
528 return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms);
532 /****************************************************************************
533 Perform a session setup (sync interface)
534 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
535 setup variant based on negotiated protocol options
536 ****************************************************************************/
537 NTSTATUS smb_raw_session_setup(struct smbcli_session *session, TALLOC_CTX *mem_ctx,
538 union smb_sesssetup *parms)
540 struct smbcli_request *req;
542 if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
543 NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms);
545 if (NT_STATUS_IS_OK(ret)
546 && parms->generic.in.user
547 && *parms->generic.in.user) {
548 if (!session->transport->negotiate.sign_info.doing_signing
549 && session->transport->negotiate.sign_info.mandatory_signing) {
550 DEBUG(0, ("SMB signing required, but server does not support it\n"));
551 return NT_STATUS_ACCESS_DENIED;
557 req = smb_raw_session_setup_send(session, parms);
558 return smb_raw_session_setup_recv(req, mem_ctx, parms);
562 /****************************************************************************
563 Send a uloggoff (async send)
564 *****************************************************************************/
565 struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session)
567 struct smbcli_request *req;
569 SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
571 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
572 SSVAL(req->out.vwv, VWV(1), 0);
574 if (!smbcli_request_send(req)) {
575 smbcli_request_destroy(req);
582 /****************************************************************************
583 Send a uloggoff (sync interface)
584 *****************************************************************************/
585 NTSTATUS smb_raw_ulogoff(struct smbcli_session *session)
587 struct smbcli_request *req = smb_raw_ulogoff_send(session);
588 return smbcli_request_simple_recv(req);
592 /****************************************************************************
594 ****************************************************************************/
595 NTSTATUS smb_raw_exit(struct smbcli_session *session)
597 struct smbcli_request *req;
599 req = smbcli_request_setup_session(session, SMBexit, 0, 0);
601 if (smbcli_request_send(req)) {
602 smbcli_request_receive(req);
604 return smbcli_request_destroy(req);