r1602: make sure we honor the use_spnego flag
[garming/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_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(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);
130                 break;
131         }
132
133         if (!cli_request_send(req)) {
134                 cli_request_destroy(req);
135                 return NULL;
136         }
137
138         return req;
139 }
140
141
142 /****************************************************************************
143  Perform a session setup (async recv)
144 ****************************************************************************/
145 NTSTATUS smb_raw_session_setup_recv(struct cli_request *req, 
146                                     TALLOC_CTX *mem_ctx, 
147                                     union smb_sesssetup *parms) 
148 {
149         uint16_t len;
150         char *p;
151
152         if (!cli_request_receive(req)) {
153                 return cli_request_destroy(req);
154         }
155         
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);
159         }
160
161         switch (parms->generic.level) {
162         case RAW_SESSSETUP_GENERIC:
163                 /* handled elsewhere */
164                 return NT_STATUS_INVALID_LEVEL;
165
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));
171                 p = req->in.data;
172                 if (p) {
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);
176                 }
177                 break;
178
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));
184                 p = req->in.data;
185                 if (p) {
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);
190                         }
191                 }
192                 break;
193
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));
200                 p = req->in.data;
201                 if (!p) {
202                         break;
203                 }
204
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);
210                 break;
211         }
212
213 failed:
214         return cli_request_destroy(req);
215 }
216
217 /*
218   form an encrypted lanman password from a plaintext password
219   and the server supplied challenge
220 */
221 static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
222 {
223         DATA_BLOB blob = data_blob(NULL, 24);
224         SMBencrypt(pass, challenge.data, blob.data);
225         return blob;
226 }
227
228 /*
229   form an encrypted NT password from a plaintext password
230   and the server supplied challenge
231 */
232 static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
233 {
234         DATA_BLOB blob = data_blob(NULL, 24);
235         SMBNTencrypt(pass, challenge.data, blob.data);
236         return blob;
237 }
238
239 /*
240   store the user session key for a transport
241 */
242 void cli_session_set_user_session_key(struct cli_session *session,
243                                    const DATA_BLOB *session_key)
244 {
245         session->user_session_key = data_blob_talloc(session->mem_ctx, 
246                                                      session_key->data, 
247                                                      session_key->length);
248 }
249
250 /*
251   setup signing for a NT1 style session setup
252 */
253 static void use_nt1_session_keys(struct cli_session *session, 
254                                  const char *password, const DATA_BLOB  *nt_response)
255 {
256         struct cli_transport *transport = session->transport; 
257         uint8_t nt_hash[16];
258         DATA_BLOB session_key = data_blob(NULL, 16);
259
260         E_md4hash(password, nt_hash);
261         SMBsesskeygen_ntv1(nt_hash, session_key.data);
262
263         cli_transport_simple_set_signing(transport, session_key, *nt_response);
264
265         cli_session_set_user_session_key(session, &session_key);
266         data_blob_free(&session_key);
267 }
268
269 /****************************************************************************
270  Perform a session setup (sync interface) using generic interface and the old
271  style sesssetup call
272 ****************************************************************************/
273 static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session, 
274                                                   TALLOC_CTX *mem_ctx, 
275                                                   union smb_sesssetup *parms) 
276 {
277         NTSTATUS status;
278         union smb_sesssetup s2;
279
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";
290         
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);
297         } else {
298                 s2.old.in.password = data_blob(parms->generic.in.password, 
299                                                strlen(parms->generic.in.password));
300         }
301         
302         status = smb_raw_session_setup(session, mem_ctx, &s2);
303         
304         data_blob_free(&s2.old.in.password);
305         
306         if (!NT_STATUS_IS_OK(status)) {
307                 return status;
308         }
309         
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;
314         
315         return NT_STATUS_OK;
316 }
317
318 /****************************************************************************
319  Perform a session setup (sync interface) using generic interface and the NT1
320  style sesssetup call
321 ****************************************************************************/
322 static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, 
323                                                   TALLOC_CTX *mem_ctx,
324                                                   union smb_sesssetup *parms) 
325 {
326         NTSTATUS status;
327         union smb_sesssetup s2;
328
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";
339
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);
350
351         } else {
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);
355         }
356
357         status = smb_raw_session_setup(session, mem_ctx, &s2);
358                 
359         data_blob_free(&s2.nt1.in.password1);
360         data_blob_free(&s2.nt1.in.password2);
361                 
362         if (!NT_STATUS_IS_OK(status)) {
363                 return status;
364         }
365
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;
370
371         return NT_STATUS_OK;
372 }
373
374 /****************************************************************************
375  Perform a session setup (sync interface) using generic interface and the SPNEGO
376  style sesssetup call
377 ****************************************************************************/
378 static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session, 
379                                                   TALLOC_CTX *mem_ctx,
380                                                   union smb_sesssetup *parms) 
381 {
382         NTSTATUS status;
383         NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY;
384         union smb_sesssetup s2;
385         DATA_BLOB session_key = data_blob(NULL, 0);
386         DATA_BLOB null_data_blob = data_blob(NULL, 0);
387
388         s2.generic.level = RAW_SESSSETUP_SPNEGO;
389         s2.spnego.in.bufsize = ~0;
390         s2.spnego.in.mpx_max = 50;
391         s2.spnego.in.vc_num = 1;
392         s2.spnego.in.sesskey = parms->generic.in.sesskey;
393         s2.spnego.in.capabilities = parms->generic.in.capabilities;
394         s2.spnego.in.domain = parms->generic.in.domain;
395         s2.spnego.in.os = "Unix";
396         s2.spnego.in.lanman = "Samba";
397         s2.spnego.out.vuid = UID_FIELD_INVALID;
398
399         cli_temp_set_signing(session->transport);
400
401         status = gensec_client_start(&session->gensec);
402         if (!NT_STATUS_IS_OK(status)) {
403                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
404                 goto done;
405         }
406
407         status = gensec_set_domain(session->gensec, parms->generic.in.domain);
408         if (!NT_STATUS_IS_OK(status)) {
409                 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", 
410                           parms->generic.in.domain, nt_errstr(status)));
411                 goto done;
412         }
413
414         status = gensec_set_username(session->gensec, parms->generic.in.user);
415         if (!NT_STATUS_IS_OK(status)) {
416                 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", 
417                           parms->generic.in.user, nt_errstr(status)));
418                 goto done;
419         }
420
421         status = gensec_set_password(session->gensec, parms->generic.in.password);
422         if (!NT_STATUS_IS_OK(status)) {
423                 DEBUG(1, ("Failed to start set GENSEC client password: %s\n", 
424                           nt_errstr(status)));
425                 goto done;
426         }
427
428         status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
429         if (!NT_STATUS_IS_OK(status)) {
430                 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
431                           nt_errstr(status)));
432                 goto done;
433         }
434
435         status = gensec_start_mech_by_oid(session->gensec, OID_SPNEGO);
436         if (!NT_STATUS_IS_OK(status)) {
437                 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n",
438                           nt_errstr(status)));
439                 goto done;
440         }
441
442         status = gensec_update(session->gensec, mem_ctx,
443                                session->transport->negotiate.secblob,
444                                &s2.spnego.in.secblob);
445
446         while(1) {
447                 if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) {
448                         break;
449                 }
450                 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
451                         break;
452                 }
453
454                 if (!NT_STATUS_IS_OK(session_key_err)) {
455                         session_key_err = gensec_session_key(session->gensec, &session_key);
456                 }
457                 if (NT_STATUS_IS_OK(session_key_err)) {
458                         cli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
459                 }
460                 
461                 session->vuid = s2.spnego.out.vuid;
462                 status = smb_raw_session_setup(session, mem_ctx, &s2);
463                 session->vuid = UID_FIELD_INVALID;
464                 if (!NT_STATUS_IS_OK(status) &&
465                     !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
466                         break;
467                 }
468
469                 status = gensec_update(session->gensec, mem_ctx,
470                                        s2.spnego.out.secblob,
471                                        &s2.spnego.in.secblob);
472
473         }
474
475 done:
476         if (NT_STATUS_IS_OK(status)) {
477                 if (!NT_STATUS_IS_OK(session_key_err)) {
478                         DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err)));
479                         return session_key_err;
480                 }
481
482                 cli_session_set_user_session_key(session, &session_key);
483
484                 parms->generic.out.vuid = s2.spnego.out.vuid;
485                 parms->generic.out.os = s2.spnego.out.os;
486                 parms->generic.out.lanman = s2.spnego.out.lanman;
487                 parms->generic.out.domain = s2.spnego.out.domain;
488         } else {
489                 DEBUG(1, ("Failed to login with SPNEGO: %s\n", nt_errstr(status)));
490                 return status;
491         }
492
493         return status;
494 }
495
496 /****************************************************************************
497  Perform a session setup (sync interface) using generic interface
498 ****************************************************************************/
499 static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, 
500                                               TALLOC_CTX *mem_ctx,
501                                               union smb_sesssetup *parms) 
502 {
503         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
504                 /* no session setup at all in earliest protocols */
505                 ZERO_STRUCT(parms->generic.out);
506                 return NT_STATUS_OK;
507         }
508
509         /* see if we need to use the original session setup interface */
510         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
511                 return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
512         }
513
514         /* see if we should use the NT1 interface */
515         if (!session->transport->options.use_spnego ||
516             !(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) {
517                 return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
518         }
519
520         /* default to using SPNEGO/NTLMSSP */
521         return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms);
522 }
523
524
525 /****************************************************************************
526  Perform a session setup (sync interface)
527 this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
528 setup variant based on negotiated protocol options
529 ****************************************************************************/
530 NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx, 
531                                union smb_sesssetup *parms) 
532 {
533         struct cli_request *req;
534
535         if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
536                 NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms);
537
538                 if (NT_STATUS_IS_OK(ret) 
539                     && parms->generic.in.user 
540                     && *parms->generic.in.user) {
541                         if (!session->transport->negotiate.sign_info.doing_signing 
542                             && session->transport->negotiate.sign_info.mandatory_signing) {
543                                 DEBUG(0, ("SMB signing required, but server does not support it\n"));
544                                 return NT_STATUS_ACCESS_DENIED;
545                         }
546                 }
547                 return ret;
548         }
549
550         req = smb_raw_session_setup_send(session, parms);
551         return smb_raw_session_setup_recv(req, mem_ctx, parms);
552 }
553
554
555 /****************************************************************************
556  Send a uloggoff (async send)
557 *****************************************************************************/
558 struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
559 {
560         struct cli_request *req;
561
562         SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
563
564         SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
565         SSVAL(req->out.vwv, VWV(1), 0);
566
567         if (!cli_request_send(req)) {
568                 cli_request_destroy(req);
569                 return NULL;
570         }
571
572         return req;
573 }
574
575 /****************************************************************************
576  Send a uloggoff (sync interface)
577 *****************************************************************************/
578 NTSTATUS smb_raw_ulogoff(struct cli_session *session)
579 {
580         struct cli_request *req = smb_raw_ulogoff_send(session);
581         return cli_request_simple_recv(req);
582 }
583
584
585 /****************************************************************************
586  Send a SMBexit
587 ****************************************************************************/
588 NTSTATUS smb_raw_exit(struct cli_session *session)
589 {
590         struct cli_request *req;
591
592         req = cli_request_setup_session(session, SMBexit, 0, 0);
593
594         if (cli_request_send(req)) {
595                 cli_request_receive(req);
596         }
597         return cli_request_destroy(req);
598 }