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 = cli_request_setup_session(session, cmd, wct, buflen); \
26 if (!req) return NULL; \
29 /****************************************************************************
30 Initialize the session context
31 ****************************************************************************/
32 struct cli_session *cli_session_init(struct cli_transport *transport)
34 struct cli_session *session;
35 TALLOC_CTX *mem_ctx = talloc_init("cli_session");
36 if (mem_ctx == NULL) {
40 session = talloc_zero(mem_ctx, sizeof(*session));
42 talloc_destroy(mem_ctx);
46 session->mem_ctx = mem_ctx;
47 session->transport = transport;
48 session->pid = (uint16_t)getpid();
49 session->vuid = UID_FIELD_INVALID;
50 session->transport->reference_count++;
55 /****************************************************************************
56 reduce reference_count and destroy is <= 0
57 ****************************************************************************/
58 void cli_session_close(struct cli_session *session)
60 session->reference_count--;
61 if (session->reference_count <= 0) {
62 cli_transport_close(session->transport);
63 talloc_destroy(session->mem_ctx);
67 /****************************************************************************
68 Perform a session setup (async send)
69 ****************************************************************************/
70 struct cli_request *smb_raw_session_setup_send(struct cli_session *session, union smb_sesssetup *parms)
72 struct cli_request *req;
74 switch (parms->generic.level) {
75 case RAW_SESSSETUP_GENERIC:
76 /* handled elsewhere */
79 case RAW_SESSSETUP_OLD:
80 SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
81 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
82 SSVAL(req->out.vwv, VWV(1), 0);
83 SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
84 SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
85 SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
86 SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
87 SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
88 cli_req_append_blob(req, &parms->old.in.password);
89 cli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
90 cli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
91 cli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
92 cli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
95 case RAW_SESSSETUP_NT1:
96 SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
97 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
98 SSVAL(req->out.vwv, VWV(1), 0);
99 SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
100 SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
101 SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
102 SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
103 SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
104 SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
105 SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
106 SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
107 cli_req_append_blob(req, &parms->nt1.in.password1);
108 cli_req_append_blob(req, &parms->nt1.in.password2);
109 cli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
110 cli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
111 cli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
112 cli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
115 case RAW_SESSSETUP_SPNEGO:
116 SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
117 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
118 SSVAL(req->out.vwv, VWV(1), 0);
119 SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
120 SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
121 SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
122 SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
123 SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
124 SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
125 SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
126 cli_req_append_blob(req, &parms->spnego.in.secblob);
127 cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
128 cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
129 cli_req_append_string(req, parms->spnego.in.domain, STR_TERMINATE);
133 if (!cli_request_send(req)) {
134 cli_request_destroy(req);
142 /****************************************************************************
143 Perform a session setup (async recv)
144 ****************************************************************************/
145 NTSTATUS smb_raw_session_setup_recv(struct cli_request *req,
147 union smb_sesssetup *parms)
152 if (!cli_request_receive(req)) {
153 return cli_request_destroy(req);
156 if (!NT_STATUS_IS_OK(req->status) &&
157 !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
158 return cli_request_destroy(req);
161 switch (parms->generic.level) {
162 case RAW_SESSSETUP_GENERIC:
163 /* handled elsewhere */
164 return NT_STATUS_INVALID_LEVEL;
166 case RAW_SESSSETUP_OLD:
167 CLI_CHECK_WCT(req, 3);
168 ZERO_STRUCT(parms->old.out);
169 parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
170 parms->old.out.action = SVAL(req->in.vwv, VWV(2));
173 p += cli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
174 p += cli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
175 p += cli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
179 case RAW_SESSSETUP_NT1:
180 CLI_CHECK_WCT(req, 3);
181 ZERO_STRUCT(parms->nt1.out);
182 parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
183 parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
186 p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
187 p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
188 if (p < (req->in.data + req->in.data_size)) {
189 p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
194 case RAW_SESSSETUP_SPNEGO:
195 CLI_CHECK_WCT(req, 4);
196 ZERO_STRUCT(parms->spnego.out);
197 parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
198 parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
199 len = SVAL(req->in.vwv, VWV(3));
205 parms->spnego.out.secblob = cli_req_pull_blob(req, mem_ctx, p, len);
206 p += parms->spnego.out.secblob.length;
207 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
208 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
209 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
214 return cli_request_destroy(req);
218 form an encrypted lanman password from a plaintext password
219 and the server supplied challenge
221 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
223 DATA_BLOB blob = data_blob(NULL, 24);
224 SMBencrypt(pass, challenge.data, blob.data);
229 form an encrypted NT password from a plaintext password
230 and the server supplied challenge
232 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
234 DATA_BLOB blob = data_blob(NULL, 24);
235 SMBNTencrypt(pass, challenge.data, blob.data);
240 store the user session key for a transport
242 void cli_session_set_user_session_key(struct cli_session *session,
243 const DATA_BLOB *session_key)
245 session->user_session_key = data_blob_talloc(session->mem_ctx,
247 session_key->length);
251 setup signing for a NT1 style session setup
253 static void use_nt1_session_keys(struct cli_session *session,
254 const char *password, const DATA_BLOB *nt_response)
256 struct cli_transport *transport = session->transport;
258 DATA_BLOB session_key = data_blob(NULL, 16);
260 E_md4hash(password, nt_hash);
261 SMBsesskeygen_ntv1(nt_hash, session_key.data);
263 cli_transport_simple_set_signing(transport, session_key, *nt_response, 0);
265 cli_session_set_user_session_key(session, &session_key);
266 data_blob_free(&session_key);
269 /****************************************************************************
270 Perform a session setup (sync interface) using generic interface and the old
272 ****************************************************************************/
273 static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session,
275 union smb_sesssetup *parms)
278 union smb_sesssetup s2;
280 /* use the old interface */
281 s2.generic.level = RAW_SESSSETUP_OLD;
282 s2.old.in.bufsize = ~0;
283 s2.old.in.mpx_max = 50;
284 s2.old.in.vc_num = 1;
285 s2.old.in.sesskey = parms->generic.in.sesskey;
286 s2.old.in.domain = parms->generic.in.domain;
287 s2.old.in.user = parms->generic.in.user;
288 s2.old.in.os = "Unix";
289 s2.old.in.lanman = "Samba";
291 if (!parms->generic.in.password) {
292 s2.old.in.password = data_blob(NULL, 0);
293 } else if (session->transport->negotiate.sec_mode &
294 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
295 s2.old.in.password = lanman_blob(parms->generic.in.password,
296 session->transport->negotiate.secblob);
298 s2.old.in.password = data_blob(parms->generic.in.password,
299 strlen(parms->generic.in.password));
302 status = smb_raw_session_setup(session, mem_ctx, &s2);
304 data_blob_free(&s2.old.in.password);
306 if (!NT_STATUS_IS_OK(status)) {
310 parms->generic.out.vuid = s2.old.out.vuid;
311 parms->generic.out.os = s2.old.out.os;
312 parms->generic.out.lanman = s2.old.out.lanman;
313 parms->generic.out.domain = s2.old.out.domain;
318 /****************************************************************************
319 Perform a session setup (sync interface) using generic interface and the NT1
321 ****************************************************************************/
322 static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session,
324 union smb_sesssetup *parms)
327 union smb_sesssetup s2;
329 s2.generic.level = RAW_SESSSETUP_NT1;
330 s2.nt1.in.bufsize = ~0;
331 s2.nt1.in.mpx_max = 50;
332 s2.nt1.in.vc_num = 1;
333 s2.nt1.in.sesskey = parms->generic.in.sesskey;
334 s2.nt1.in.capabilities = parms->generic.in.capabilities;
335 s2.nt1.in.domain = parms->generic.in.domain;
336 s2.nt1.in.user = parms->generic.in.user;
337 s2.nt1.in.os = "Unix";
338 s2.nt1.in.lanman = "Samba";
340 if (!parms->generic.in.password) {
341 s2.nt1.in.password1 = data_blob(NULL, 0);
342 s2.nt1.in.password2 = data_blob(NULL, 0);
343 } else if (session->transport->negotiate.sec_mode &
344 NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
345 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password,
346 session->transport->negotiate.secblob);
347 s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
348 session->transport->negotiate.secblob);
349 use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
352 s2.nt1.in.password1 = data_blob(parms->generic.in.password,
353 strlen(parms->generic.in.password));
354 s2.nt1.in.password2 = data_blob(NULL, 0);
357 status = smb_raw_session_setup(session, mem_ctx, &s2);
359 data_blob_free(&s2.nt1.in.password1);
360 data_blob_free(&s2.nt1.in.password2);
362 if (!NT_STATUS_IS_OK(status)) {
366 parms->generic.out.vuid = s2.nt1.out.vuid;
367 parms->generic.out.os = s2.nt1.out.os;
368 parms->generic.out.lanman = s2.nt1.out.lanman;
369 parms->generic.out.domain = s2.nt1.out.domain;
374 /****************************************************************************
375 Perform a session setup (sync interface) using generic interface and the SPNEGO
377 ****************************************************************************/
378 static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session,
380 union smb_sesssetup *parms)
383 union smb_sesssetup s2;
384 DATA_BLOB session_key = data_blob(NULL, 0);
385 DATA_BLOB null_data_blob = data_blob(NULL, 0);
387 s2.generic.level = RAW_SESSSETUP_SPNEGO;
388 s2.spnego.in.bufsize = ~0;
389 s2.spnego.in.mpx_max = 50;
390 s2.spnego.in.vc_num = 1;
391 s2.spnego.in.sesskey = parms->generic.in.sesskey;
392 s2.spnego.in.capabilities = parms->generic.in.capabilities;
393 s2.spnego.in.domain = parms->generic.in.domain;
394 s2.spnego.in.os = "Unix";
395 s2.spnego.in.lanman = "Samba";
396 s2.spnego.out.vuid = UID_FIELD_INVALID;
398 cli_temp_set_signing(session->transport);
400 status = gensec_client_start(&session->gensec);
401 if (!NT_STATUS_IS_OK(status)) {
402 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
406 status = gensec_set_domain(session->gensec, parms->generic.in.domain);
407 if (!NT_STATUS_IS_OK(status)) {
408 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n",
409 parms->generic.in.domain, nt_errstr(status)));
413 status = gensec_set_username(session->gensec, parms->generic.in.user);
414 if (!NT_STATUS_IS_OK(status)) {
415 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n",
416 parms->generic.in.user, nt_errstr(status)));
420 status = gensec_set_password(session->gensec, parms->generic.in.password);
421 if (!NT_STATUS_IS_OK(status)) {
422 DEBUG(1, ("Failed to start set GENSEC client password: %s\n",
427 status = gensec_start_mech_by_oid(session->gensec, OID_SPNEGO);
428 if (!NT_STATUS_IS_OK(status)) {
429 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n",
434 status = gensec_update(session->gensec, mem_ctx,
435 session->transport->negotiate.secblob,
436 &s2.spnego.in.secblob);
439 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
443 status = gensec_session_key(session->gensec, &session_key);
444 if (NT_STATUS_IS_OK(status)) {
445 cli_transport_simple_set_signing(session->transport, session_key, null_data_blob, 0);
448 session->vuid = s2.spnego.out.vuid;
449 status = smb_raw_session_setup(session, mem_ctx, &s2);
450 session->vuid = UID_FIELD_INVALID;
451 if (!NT_STATUS_IS_OK(status) &&
452 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
456 status = gensec_update(session->gensec, mem_ctx,
457 s2.spnego.out.secblob,
458 &s2.spnego.in.secblob);
460 if (NT_STATUS_IS_OK(status)) {
466 if (NT_STATUS_IS_OK(status)) {
467 status = gensec_session_key(session->gensec, &session_key);
468 if (!NT_STATUS_IS_OK(status)) {
472 cli_transport_simple_set_signing(session->transport, session_key, null_data_blob, 2 /* two legs on last packet */);
474 cli_session_set_user_session_key(session, &session_key);
476 parms->generic.out.vuid = s2.spnego.out.vuid;
477 parms->generic.out.os = s2.spnego.out.os;
478 parms->generic.out.lanman = s2.spnego.out.lanman;
479 parms->generic.out.domain = s2.spnego.out.domain;
485 /****************************************************************************
486 Perform a session setup (sync interface) using generic interface
487 ****************************************************************************/
488 static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session,
490 union smb_sesssetup *parms)
492 if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
493 /* no session setup at all in earliest protocols */
494 ZERO_STRUCT(parms->generic.out);
498 /* see if we need to use the original session setup interface */
499 if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
500 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
503 /* see if we should use the NT1 interface */
504 if (!(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) {
505 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
508 /* default to using SPNEGO/NTLMSSP */
509 return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms);
513 /****************************************************************************
514 Perform a session setup (sync interface)
515 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
516 setup variant based on negotiated protocol options
517 ****************************************************************************/
518 NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx,
519 union smb_sesssetup *parms)
521 struct cli_request *req;
523 if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
524 return smb_raw_session_setup_generic(session, mem_ctx, parms);
527 req = smb_raw_session_setup_send(session, parms);
528 return smb_raw_session_setup_recv(req, mem_ctx, parms);
532 /****************************************************************************
533 Send a uloggoff (async send)
534 *****************************************************************************/
535 struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
537 struct cli_request *req;
539 SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
541 SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
542 SSVAL(req->out.vwv, VWV(1), 0);
544 if (!cli_request_send(req)) {
545 cli_request_destroy(req);
552 /****************************************************************************
553 Send a uloggoff (sync interface)
554 *****************************************************************************/
555 NTSTATUS smb_raw_ulogoff(struct cli_session *session)
557 struct cli_request *req = smb_raw_ulogoff_send(session);
558 return cli_request_simple_recv(req);
562 /****************************************************************************
564 ****************************************************************************/
565 NTSTATUS smb_raw_exit(struct cli_session *session)
567 struct cli_request *req;
569 req = cli_request_setup_session(session, SMBexit, 0, 0);
571 if (cli_request_send(req)) {
572 cli_request_receive(req);
574 return cli_request_destroy(req);