r100: remember the user session key during session setup so it can be used in various...
[samba.git] / source4 / libcli / raw / clisession.c
1 /* 
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>
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
24 #define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
25         req = cli_request_setup_session(session, cmd, wct, buflen); \
26         if (!req) return NULL; \
27 } while (0)
28
29 /****************************************************************************
30  Initialize the session context
31 ****************************************************************************/
32 struct cli_session *cli_session_init(struct cli_transport *transport)
33 {
34         struct cli_session *session;
35         TALLOC_CTX *mem_ctx = talloc_init("cli_session");
36         if (mem_ctx == NULL) {
37                 return NULL;
38         }
39
40         session = talloc_zero(mem_ctx, sizeof(*session));
41         if (!session) {
42                 talloc_destroy(mem_ctx);
43                 return NULL;
44         }
45
46         session->mem_ctx = mem_ctx;
47         session->transport = transport;
48         session->pid = (uint16)getpid();
49         session->vuid = UID_FIELD_INVALID;
50         session->transport->reference_count++;
51
52         return session;
53 }
54
55 /****************************************************************************
56 reduce reference_count and destroy is <= 0
57 ****************************************************************************/
58 void cli_session_close(struct cli_session *session)
59 {
60         session->reference_count--;
61         if (session->reference_count <= 0) {
62                 cli_transport_close(session->transport);
63                 talloc_destroy(session->mem_ctx);
64         }
65 }
66
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) 
71 {
72         struct cli_request *req;
73
74         switch (parms->generic.level) {
75         case RAW_SESSSETUP_GENERIC:
76                 /* handled elsewhere */
77                 return NULL;
78
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);
93                 break;
94
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);
113                 break;
114
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(10), parms->spnego.in.capabilities);
125                 cli_req_append_blob(req, &parms->spnego.in.secblob);
126                 cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
127                 cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
128                 break;
129         }
130
131         if (!cli_request_send(req)) {
132                 cli_request_destroy(req);
133                 return NULL;
134         }
135
136         return req;
137 }
138
139
140 /****************************************************************************
141  Perform a session setup (async recv)
142 ****************************************************************************/
143 NTSTATUS smb_raw_session_setup_recv(struct cli_request *req, 
144                                     TALLOC_CTX *mem_ctx, 
145                                     union smb_sesssetup *parms) 
146 {
147         uint16 len;
148         char *p;
149
150         if (!cli_request_receive(req)) {
151                 return cli_request_destroy(req);
152         }
153         
154         if (!NT_STATUS_IS_OK(req->status) &&
155             !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
156                 return cli_request_destroy(req);
157         }
158
159         switch (parms->generic.level) {
160         case RAW_SESSSETUP_GENERIC:
161                 /* handled elsewhere */
162                 return NT_STATUS_INVALID_LEVEL;
163
164         case RAW_SESSSETUP_OLD:
165                 CLI_CHECK_WCT(req, 3);
166                 ZERO_STRUCT(parms->old.out);
167                 parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
168                 parms->old.out.action = SVAL(req->in.vwv, VWV(2));
169                 p = req->in.data;
170                 if (p) {
171                         p += cli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
172                         p += cli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
173                         p += cli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
174                 }
175                 break;
176
177         case RAW_SESSSETUP_NT1:
178                 CLI_CHECK_WCT(req, 3);
179                 ZERO_STRUCT(parms->nt1.out);
180                 parms->nt1.out.vuid   = SVAL(req->in.hdr, HDR_UID);
181                 parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
182                 p = req->in.data;
183                 if (p) {
184                         p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
185                         p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
186                         if (p < (req->in.data + req->in.data_size)) {
187                                 p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
188                         }
189                 }
190                 break;
191
192         case RAW_SESSSETUP_SPNEGO:
193                 CLI_CHECK_WCT(req, 4);
194                 ZERO_STRUCT(parms->spnego.out);
195                 parms->spnego.out.vuid   = SVAL(req->in.hdr, HDR_UID);
196                 parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
197                 len                      = SVAL(req->in.vwv, VWV(3));
198                 p = req->in.data;
199                 if (!p) {
200                         break;
201                 }
202
203                 parms->spnego.out.secblob = cli_req_pull_blob(req, mem_ctx, p, len);
204                 p += parms->spnego.out.secblob.length;
205                 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
206                 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
207                 p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
208                 break;
209         }
210
211 failed:
212         return cli_request_destroy(req);
213 }
214
215 /*
216   form an encrypted lanman password from a plaintext password
217   and the server supplied challenge
218 */
219 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
220 {
221         DATA_BLOB blob = data_blob(NULL, 24);
222         SMBencrypt(pass, challenge.data, blob.data);
223         return blob;
224 }
225
226 /*
227   form an encrypted NT password from a plaintext password
228   and the server supplied challenge
229 */
230 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
231 {
232         DATA_BLOB blob = data_blob(NULL, 24);
233         SMBNTencrypt(pass, challenge.data, blob.data);
234         return blob;
235 }
236
237 /*
238   setup signing for a NT1 style session setup
239 */
240 static void setup_nt1_signing(struct cli_transport *transport, const char *password)
241 {
242         uchar nt_hash[16];
243         uchar session_key[16];
244         DATA_BLOB nt_response;
245
246         E_md4hash(password, nt_hash);
247         SMBsesskeygen_ntv1(nt_hash, NULL, session_key);
248         nt_response = nt_blob(password, transport->negotiate.secblob);
249
250         cli_transport_set_session_key(transport, session_key);
251
252         cli_transport_simple_set_signing(transport, session_key, nt_response);
253 }
254
255 /****************************************************************************
256  Perform a session setup (sync interface) using generic interface and the old
257  style sesssetup call
258 ****************************************************************************/
259 static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session, 
260                                                   TALLOC_CTX *mem_ctx, 
261                                                   union smb_sesssetup *parms) 
262 {
263         NTSTATUS status;
264         union smb_sesssetup s2;
265
266         /* use the old interface */
267         s2.generic.level = RAW_SESSSETUP_OLD;
268         s2.old.in.bufsize = ~0;
269         s2.old.in.mpx_max = 50;
270         s2.old.in.vc_num = 1;
271         s2.old.in.sesskey = parms->generic.in.sesskey;
272         s2.old.in.domain = parms->generic.in.domain;
273         s2.old.in.user = parms->generic.in.user;
274         s2.old.in.os = "Unix";
275         s2.old.in.lanman = "Samba";
276         
277         if (!parms->generic.in.password) {
278                 s2.old.in.password = data_blob(NULL, 0);
279         } else if (session->transport->negotiate.sec_mode & 
280                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
281                 s2.old.in.password = lanman_blob(parms->generic.in.password, 
282                                                  session->transport->negotiate.secblob);
283         } else {
284                 s2.old.in.password = data_blob(parms->generic.in.password, 
285                                                strlen(parms->generic.in.password));
286         }
287         
288         status = smb_raw_session_setup(session, mem_ctx, &s2);
289         
290         data_blob_free(&s2.old.in.password);
291         
292         if (!NT_STATUS_IS_OK(status)) {
293                 return status;
294         }
295         
296         parms->generic.out.vuid = s2.old.out.vuid;
297         parms->generic.out.os = s2.old.out.os;
298         parms->generic.out.lanman = s2.old.out.lanman;
299         parms->generic.out.domain = s2.old.out.domain;
300         
301         return NT_STATUS_OK;
302 }
303
304 /****************************************************************************
305  Perform a session setup (sync interface) using generic interface and the NT1
306  style sesssetup call
307 ****************************************************************************/
308 static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, 
309                                                   TALLOC_CTX *mem_ctx,
310                                                   union smb_sesssetup *parms) 
311 {
312         NTSTATUS status;
313         union smb_sesssetup s2;
314
315         s2.generic.level = RAW_SESSSETUP_NT1;
316         s2.nt1.in.bufsize = ~0;
317         s2.nt1.in.mpx_max = 50;
318         s2.nt1.in.vc_num = 1;
319         s2.nt1.in.sesskey = parms->generic.in.sesskey;
320         s2.nt1.in.capabilities = parms->generic.in.capabilities;
321         s2.nt1.in.domain = parms->generic.in.domain;
322         s2.nt1.in.user = parms->generic.in.user;
323         s2.nt1.in.os = "Unix";
324         s2.nt1.in.lanman = "Samba";
325
326         if (!parms->generic.in.password) {
327                 s2.nt1.in.password1 = data_blob(NULL, 0);
328                 s2.nt1.in.password2 = data_blob(NULL, 0);
329         } else if (session->transport->negotiate.sec_mode & 
330                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
331                 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password, 
332                                                   session->transport->negotiate.secblob);
333                 s2.nt1.in.password2 = nt_blob(parms->generic.in.password, 
334                                               session->transport->negotiate.secblob);
335                 setup_nt1_signing(session->transport, parms->generic.in.password);
336         } else {
337                 s2.nt1.in.password1 = data_blob(parms->generic.in.password, 
338                                                 strlen(parms->generic.in.password));
339                 s2.nt1.in.password2 = data_blob(NULL, 0);
340         }
341
342         status = smb_raw_session_setup(session, mem_ctx, &s2);
343                 
344         data_blob_free(&s2.nt1.in.password1);
345         data_blob_free(&s2.nt1.in.password2);
346                 
347         if (!NT_STATUS_IS_OK(status)) {
348                 return status;
349         }
350
351         parms->generic.out.vuid = s2.nt1.out.vuid;
352         parms->generic.out.os = s2.nt1.out.os;
353         parms->generic.out.lanman = s2.nt1.out.lanman;
354         parms->generic.out.domain = s2.nt1.out.domain;
355
356         return NT_STATUS_OK;
357 }
358
359
360 /****************************************************************************
361  Perform a session setup (sync interface) using generic interface
362 ****************************************************************************/
363 static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, 
364                                               TALLOC_CTX *mem_ctx,
365                                               union smb_sesssetup *parms) 
366 {
367         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
368                 /* no session setup at all in earliest protocols */
369                 ZERO_STRUCT(parms->generic.out);
370                 return NT_STATUS_OK;
371         }
372
373         /* see if we need to use the original session setup interface */
374         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
375                 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
376         }
377
378         /* see if we should use the NT1 interface */
379         if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) ||
380             !session->transport->options.use_spnego) {
381                 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
382         }
383
384         /* default to using SPNEGO/NTLMSSP */
385         DEBUG(0,("Need to add client SPNEGO code back in\n"));
386         return NT_STATUS_UNSUCCESSFUL;
387 }
388
389
390 /****************************************************************************
391  Perform a session setup (sync interface)
392 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
393 setup varient based on negotiated protocol options
394 ****************************************************************************/
395 NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx, 
396                                union smb_sesssetup *parms) 
397 {
398         struct cli_request *req;
399
400         if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
401                 return smb_raw_session_setup_generic(session, mem_ctx, parms);
402         }
403
404         req = smb_raw_session_setup_send(session, parms);
405         return smb_raw_session_setup_recv(req, mem_ctx, parms);
406 }
407
408
409 /****************************************************************************
410  Send a uloggoff (async send)
411 *****************************************************************************/
412 struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
413 {
414         struct cli_request *req;
415
416         SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
417
418         SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
419         SSVAL(req->out.vwv, VWV(1), 0);
420
421         if (!cli_request_send(req)) {
422                 cli_request_destroy(req);
423                 return NULL;
424         }
425
426         return req;
427 }
428
429 /****************************************************************************
430  Send a uloggoff (sync interface)
431 *****************************************************************************/
432 NTSTATUS smb_raw_ulogoff(struct cli_session *session)
433 {
434         struct cli_request *req = smb_raw_ulogoff_send(session);
435         return cli_request_simple_recv(req);
436 }
437
438
439 /****************************************************************************
440  Send a SMBexit
441 ****************************************************************************/
442 NTSTATUS smb_raw_exit(struct cli_session *session)
443 {
444         struct cli_request *req;
445
446         req = cli_request_setup_session(session, SMBexit, 0, 0);
447
448         if (cli_request_send(req)) {
449                 cli_request_receive(req);
450         }
451         return cli_request_destroy(req);
452 }