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