* fixed a segv when -U is not used in smbtorture.
[gd/samba-autobuild/.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_simple_set_signing(transport, session_key, nt_response);
251 }
252
253 /****************************************************************************
254  Perform a session setup (sync interface) using generic interface and the old
255  style sesssetup call
256 ****************************************************************************/
257 static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session, 
258                                                   TALLOC_CTX *mem_ctx, 
259                                                   union smb_sesssetup *parms) 
260 {
261         NTSTATUS status;
262         union smb_sesssetup s2;
263
264         /* use the old interface */
265         s2.generic.level = RAW_SESSSETUP_OLD;
266         s2.old.in.bufsize = ~0;
267         s2.old.in.mpx_max = 50;
268         s2.old.in.vc_num = 1;
269         s2.old.in.sesskey = parms->generic.in.sesskey;
270         s2.old.in.domain = parms->generic.in.domain;
271         s2.old.in.user = parms->generic.in.user;
272         s2.old.in.os = "Unix";
273         s2.old.in.lanman = "Samba";
274         
275         if (!parms->generic.in.password) {
276                 s2.old.in.password = data_blob(NULL, 0);
277         } else if (session->transport->negotiate.sec_mode & 
278                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
279                 s2.old.in.password = lanman_blob(parms->generic.in.password, 
280                                                  session->transport->negotiate.secblob);
281         } else {
282                 s2.old.in.password = data_blob(parms->generic.in.password, 
283                                                strlen(parms->generic.in.password));
284         }
285         
286         status = smb_raw_session_setup(session, mem_ctx, &s2);
287         
288         data_blob_free(&s2.old.in.password);
289         
290         if (!NT_STATUS_IS_OK(status)) {
291                 return status;
292         }
293         
294         parms->generic.out.vuid = s2.old.out.vuid;
295         parms->generic.out.os = s2.old.out.os;
296         parms->generic.out.lanman = s2.old.out.lanman;
297         parms->generic.out.domain = s2.old.out.domain;
298         
299         return NT_STATUS_OK;
300 }
301
302 /****************************************************************************
303  Perform a session setup (sync interface) using generic interface and the NT1
304  style sesssetup call
305 ****************************************************************************/
306 static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, 
307                                                   TALLOC_CTX *mem_ctx,
308                                                   union smb_sesssetup *parms) 
309 {
310         NTSTATUS status;
311         union smb_sesssetup s2;
312
313         s2.generic.level = RAW_SESSSETUP_NT1;
314         s2.nt1.in.bufsize = ~0;
315         s2.nt1.in.mpx_max = 50;
316         s2.nt1.in.vc_num = 1;
317         s2.nt1.in.sesskey = parms->generic.in.sesskey;
318         s2.nt1.in.capabilities = parms->generic.in.capabilities;
319         s2.nt1.in.domain = parms->generic.in.domain;
320         s2.nt1.in.user = parms->generic.in.user;
321         s2.nt1.in.os = "Unix";
322         s2.nt1.in.lanman = "Samba";
323
324         if (!parms->generic.in.password) {
325                 s2.nt1.in.password1 = data_blob(NULL, 0);
326                 s2.nt1.in.password2 = data_blob(NULL, 0);
327         } else if (session->transport->negotiate.sec_mode & 
328                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
329                 s2.nt1.in.password1 = lanman_blob(parms->generic.in.password, 
330                                                   session->transport->negotiate.secblob);
331                 s2.nt1.in.password2 = nt_blob(parms->generic.in.password, 
332                                               session->transport->negotiate.secblob);
333                 setup_nt1_signing(session->transport, parms->generic.in.password);
334         } else {
335                 s2.nt1.in.password1 = data_blob(parms->generic.in.password, 
336                                                 strlen(parms->generic.in.password));
337                 s2.nt1.in.password2 = data_blob(NULL, 0);
338         }
339
340         status = smb_raw_session_setup(session, mem_ctx, &s2);
341                 
342         data_blob_free(&s2.nt1.in.password1);
343         data_blob_free(&s2.nt1.in.password2);
344                 
345         if (!NT_STATUS_IS_OK(status)) {
346                 return status;
347         }
348
349         parms->generic.out.vuid = s2.nt1.out.vuid;
350         parms->generic.out.os = s2.nt1.out.os;
351         parms->generic.out.lanman = s2.nt1.out.lanman;
352         parms->generic.out.domain = s2.nt1.out.domain;
353
354         return NT_STATUS_OK;
355 }
356
357
358 /****************************************************************************
359  Perform a session setup (sync interface) using generic interface
360 ****************************************************************************/
361 static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, 
362                                               TALLOC_CTX *mem_ctx,
363                                               union smb_sesssetup *parms) 
364 {
365         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
366                 /* no session setup at all in earliest protocols */
367                 ZERO_STRUCT(parms->generic.out);
368                 return NT_STATUS_OK;
369         }
370
371         /* see if we need to use the original session setup interface */
372         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
373                 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
374         }
375
376         /* see if we should use the NT1 interface */
377         if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) ||
378             !session->transport->options.use_spnego) {
379                 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
380         }
381
382         /* default to using SPNEGO/NTLMSSP */
383         DEBUG(0,("Need to add client SPNEGO code back in\n"));
384         return NT_STATUS_UNSUCCESSFUL;
385 }
386
387
388 /****************************************************************************
389  Perform a session setup (sync interface)
390 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
391 setup varient based on negotiated protocol options
392 ****************************************************************************/
393 NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx, 
394                                union smb_sesssetup *parms) 
395 {
396         struct cli_request *req;
397
398         if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
399                 return smb_raw_session_setup_generic(session, mem_ctx, parms);
400         }
401
402         req = smb_raw_session_setup_send(session, parms);
403         return smb_raw_session_setup_recv(req, mem_ctx, parms);
404 }
405
406
407 /****************************************************************************
408  Send a uloggoff (async send)
409 *****************************************************************************/
410 struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
411 {
412         struct cli_request *req;
413
414         SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
415
416         SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
417         SSVAL(req->out.vwv, VWV(1), 0);
418
419         if (!cli_request_send(req)) {
420                 cli_request_destroy(req);
421                 return NULL;
422         }
423
424         return req;
425 }
426
427 /****************************************************************************
428  Send a uloggoff (sync interface)
429 *****************************************************************************/
430 NTSTATUS smb_raw_ulogoff(struct cli_session *session)
431 {
432         struct cli_request *req = smb_raw_ulogoff_send(session);
433         return cli_request_simple_recv(req);
434 }
435
436
437 /****************************************************************************
438  Send a SMBexit
439 ****************************************************************************/
440 NTSTATUS smb_raw_exit(struct cli_session *session)
441 {
442         struct cli_request *req;
443
444         req = cli_request_setup_session(session, SMBexit, 0, 0);
445
446         if (cli_request_send(req)) {
447                 cli_request_receive(req);
448         }
449         return cli_request_destroy(req);
450 }