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