s4:libcli/smb_composite: don't try anonymous smb signing
[metze/samba/wip.git] / source4 / libcli / smb_composite / sesssetup.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2005
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 /*
20   a composite API for making handling a generic async session setup
21 */
22
23 #include "includes.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "libcli/composite/composite.h"
27 #include "libcli/smb_composite/smb_composite.h"
28 #include "libcli/auth/libcli_auth.h"
29 #include "auth/auth.h"
30 #include "auth/gensec/gensec.h"
31 #include "auth/credentials/credentials.h"
32 #include "version.h"
33 #include "param/param.h"
34 #include "libcli/smb/smbXcli_base.h"
35
36 struct sesssetup_state {
37         union smb_sesssetup setup;
38         NTSTATUS remote_status;
39         NTSTATUS gensec_status;
40         struct smb_composite_sesssetup *io;
41         struct smbcli_request *req;
42         unsigned int logon_retries;
43 };
44
45 static int sesssetup_state_destructor(struct sesssetup_state *state)
46 {
47         if (state->req) {
48                 talloc_free(state->req);
49                 state->req = NULL;
50         }
51
52         return 0;
53 }
54
55 static NTSTATUS session_setup_old(struct composite_context *c,
56                                   struct smbcli_session *session, 
57                                   struct smb_composite_sesssetup *io,
58                                   struct smbcli_request **req); 
59 static NTSTATUS session_setup_nt1(struct composite_context *c,
60                                   struct smbcli_session *session, 
61                                   struct smb_composite_sesssetup *io,
62                                   struct smbcli_request **req); 
63 static NTSTATUS session_setup_spnego(struct composite_context *c,
64                                      struct smbcli_session *session, 
65                                      struct smb_composite_sesssetup *io,
66                                      struct smbcli_request **req);
67
68 /*
69   handler for completion of a smbcli_request sub-request
70 */
71 static void request_handler(struct smbcli_request *req)
72 {
73         struct composite_context *c = (struct composite_context *)req->async.private_data;
74         struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
75         struct smbcli_session *session = req->session;
76         DATA_BLOB null_data_blob = data_blob(NULL, 0);
77         NTSTATUS session_key_err, nt_status;
78         struct smbcli_request *check_req = NULL;
79         const char *os = NULL;
80         const char *lanman = NULL;
81
82         if (req->sign_caller_checks) {
83                 req->do_not_free = true;
84                 check_req = req;
85         }
86
87         state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup);
88         c->status = state->remote_status;
89         state->req = NULL;
90
91         /*
92          * we only need to check the signature if the
93          * NT_STATUS_OK is returned
94          */
95         if (!NT_STATUS_IS_OK(state->remote_status)) {
96                 talloc_free(check_req);
97                 check_req = NULL;
98         }
99
100         switch (state->setup.old.level) {
101         case RAW_SESSSETUP_OLD:
102                 state->io->out.vuid = state->setup.old.out.vuid;
103                 /* This doesn't work, as this only happens on old
104                  * protocols, where this comparison won't match. */
105                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
106                         /* we neet to reset the vuid for a new try */
107                         session->vuid = 0;
108                         if (cli_credentials_wrong_password(state->io->in.credentials)) {
109                                 nt_status = session_setup_old(c, session, 
110                                                               state->io, 
111                                                               &state->req);
112                                 if (NT_STATUS_IS_OK(nt_status)) {
113                                         talloc_free(check_req);
114                                         c->status = nt_status;
115                                         composite_continue_smb(c, state->req, request_handler, c);
116                                         return;
117                                 }
118                         }
119                 }
120                 os = state->setup.old.out.os;
121                 lanman = state->setup.old.out.lanman;
122                 break;
123
124         case RAW_SESSSETUP_NT1:
125                 state->io->out.vuid = state->setup.nt1.out.vuid;
126                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
127                         /* we need to reset the vuid for a new try */
128                         session->vuid = 0;
129                         if (cli_credentials_wrong_password(state->io->in.credentials)) {
130                                 nt_status = session_setup_nt1(c, session, 
131                                                               state->io, 
132                                                               &state->req);
133                                 if (NT_STATUS_IS_OK(nt_status)) {
134                                         talloc_free(check_req);
135                                         c->status = nt_status;
136                                         composite_continue_smb(c, state->req, request_handler, c);
137                                         return;
138                                 }
139                         }
140                 }
141                 os = state->setup.nt1.out.os;
142                 lanman = state->setup.nt1.out.lanman;
143                 break;
144
145         case RAW_SESSSETUP_SPNEGO:
146                 state->io->out.vuid = state->setup.spnego.out.vuid;
147                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
148                         const char *principal;
149
150                         /* we need to reset the vuid for a new try */
151                         session->vuid = 0;
152
153                         principal = gensec_get_target_principal(session->gensec);
154                         if (principal == NULL) {
155                                 const char *hostname = gensec_get_target_hostname(session->gensec);
156                                 const char *service  = gensec_get_target_service(session->gensec);
157                                 if (hostname != NULL && service != NULL) {
158                                         principal = talloc_asprintf(state, "%s/%s", service, hostname);
159                                 }
160                         }
161                         if (cli_credentials_failed_kerberos_login(state->io->in.credentials, principal, &state->logon_retries) ||
162                             cli_credentials_wrong_password(state->io->in.credentials)) {
163                                 nt_status = session_setup_spnego(c, session, 
164                                                                       state->io, 
165                                                                       &state->req);
166                                 if (NT_STATUS_IS_OK(nt_status)) {
167                                         talloc_free(check_req);
168                                         c->status = nt_status;
169                                         composite_continue_smb(c, state->req, request_handler, c);
170                                         return;
171                                 }
172                         }
173                 }
174                 if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
175                     !NT_STATUS_IS_OK(c->status)) {
176                         break;
177                 }
178                 if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
179
180                         /* The status value here, from the earlier pass at GENSEC is
181                          * vital to the security of the system.  Even if the other end
182                          * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then
183                          * you must keep feeding it blobs, or else the remote
184                          * host/attacker might avoid mutal authentication
185                          * requirements */
186                         
187                         state->gensec_status = gensec_update_ev(session->gensec, state, c->event_ctx,
188                                                          state->setup.spnego.out.secblob,
189                                                          &state->setup.spnego.in.secblob);
190                         c->status = state->gensec_status;
191                         if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
192                             !NT_STATUS_IS_OK(c->status)) {
193                                 break;
194                         }
195                 } else {
196                         state->setup.spnego.in.secblob = data_blob(NULL, 0);
197                 }
198
199                 if (cli_credentials_is_anonymous(state->io->in.credentials)) {
200                         /*
201                          * anonymous => no signing
202                          */
203                 } else if (NT_STATUS_IS_OK(state->remote_status)) {
204                         DATA_BLOB session_key;
205
206                         if (state->setup.spnego.in.secblob.length) {
207                                 c->status = NT_STATUS_INTERNAL_ERROR;
208                                 break;
209                         }
210                         session_key_err = gensec_session_key(session->gensec, session, &session_key);
211                         if (NT_STATUS_IS_OK(session_key_err)) {
212                                 smb1cli_conn_activate_signing(session->transport->conn,
213                                                               session_key,
214                                                               null_data_blob);
215                         }
216
217                         c->status = smb1cli_session_set_session_key(session->smbXcli,
218                                                                     session_key);
219                         data_blob_free(&session_key);
220                         if (!NT_STATUS_IS_OK(c->status)) {
221                                 break;
222                         }
223                 }
224
225                 if (state->setup.spnego.in.secblob.length) {
226                         /* 
227                          * set the session->vuid value only for calling
228                          * smb_raw_sesssetup_send()
229                          */
230                         uint16_t vuid = session->vuid;
231                         session->vuid = state->io->out.vuid;
232                         state->req = smb_raw_sesssetup_send(session, &state->setup);
233                         session->vuid = vuid;
234                         if (state->req &&
235                             !smb1cli_conn_signing_is_active(state->req->transport->conn)) {
236                                 state->req->sign_caller_checks = true;
237                         }
238                         composite_continue_smb(c, state->req, request_handler, c);
239                         return;
240                 }
241                 os = state->setup.spnego.out.os;
242                 lanman = state->setup.spnego.out.lanman;
243                 break;
244
245         case RAW_SESSSETUP_SMB2:
246                 c->status = NT_STATUS_INTERNAL_ERROR;
247                 break;
248         }
249
250         if (check_req) {
251                 bool ok;
252
253                 check_req->sign_caller_checks = false;
254
255                 ok = smb1cli_conn_check_signing(check_req->transport->conn,
256                                                 check_req->in.buffer, 1);
257                 if (!ok) {
258                         c->status = NT_STATUS_ACCESS_DENIED;
259                 }
260                 talloc_free(check_req);
261                 check_req = NULL;
262         }
263
264         if (!NT_STATUS_IS_OK(c->status)) {
265                 composite_error(c, c->status);
266                 return;
267         }
268
269         if (os) {
270                 session->os = talloc_strdup(session, os);
271                 if (composite_nomem(session->os, c)) return;
272         } else {
273                 session->os = NULL;
274         }
275         if (lanman) {
276                 session->lanman = talloc_strdup(session, lanman);
277                 if (composite_nomem(session->lanman, c)) return;
278         } else {
279                 session->lanman = NULL;
280         }
281
282         composite_done(c);
283 }
284
285
286 /*
287   send a nt1 style session setup
288 */
289 static NTSTATUS session_setup_nt1(struct composite_context *c,
290                                   struct smbcli_session *session, 
291                                   struct smb_composite_sesssetup *io,
292                                   struct smbcli_request **req) 
293 {
294         NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
295         struct sesssetup_state *state = talloc_get_type(c->private_data,
296                                                         struct sesssetup_state);
297         const char *domain = cli_credentials_get_domain(io->in.credentials);
298
299         /*
300          * domain controllers tend to reject the NTLM v2 blob
301          * if the netbiosname is not valid (e.g. IP address or FQDN)
302          * so just leave it away (as Windows client do)
303          */
304         DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, NULL, domain);
305
306         DATA_BLOB session_key = data_blob(NULL, 0);
307         int flags = CLI_CRED_NTLM_AUTH;
308
309         if (session->options.lanman_auth) {
310                 flags |= CLI_CRED_LANMAN_AUTH;
311         }
312
313         if (session->options.ntlmv2_auth) {
314                 flags |= CLI_CRED_NTLMv2_AUTH;
315         }
316
317         state->setup.nt1.level           = RAW_SESSSETUP_NT1;
318         state->setup.nt1.in.bufsize      = session->transport->options.max_xmit;
319         state->setup.nt1.in.mpx_max      = session->transport->options.max_mux;
320         state->setup.nt1.in.vc_num       = 1;
321         state->setup.nt1.in.sesskey      = io->in.sesskey;
322         state->setup.nt1.in.capabilities = io->in.capabilities;
323         state->setup.nt1.in.os           = "Unix";
324         state->setup.nt1.in.lanman       = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
325
326         cli_credentials_get_ntlm_username_domain(io->in.credentials, state, 
327                                                  &state->setup.nt1.in.user,
328                                                  &state->setup.nt1.in.domain);
329         
330
331         if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
332                 nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, 
333                                                               &flags, 
334                                                               session->transport->negotiate.secblob, 
335                                                               names_blob,
336                                                               &state->setup.nt1.in.password1,
337                                                               &state->setup.nt1.in.password2,
338                                                               NULL, &session_key);
339                 NT_STATUS_NOT_OK_RETURN(nt_status);
340         } else if (session->options.plaintext_auth) {
341                 const char *password = cli_credentials_get_password(io->in.credentials);
342                 state->setup.nt1.in.password1 = data_blob_talloc(state, password, strlen(password));
343                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
344         } else {
345                 /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
346                 return NT_STATUS_INVALID_PARAMETER;
347         }
348
349         *req = smb_raw_sesssetup_send(session, &state->setup);
350         if (!*req) {
351                 return NT_STATUS_NO_MEMORY;
352         }
353
354         if (!NT_STATUS_IS_OK(nt_status)) {
355                 /*
356                  * plain text => no signing
357                  */
358                 return (*req)->status;
359         }
360
361         if (cli_credentials_is_anonymous(io->in.credentials)) {
362                 /*
363                  * anonymous => no signing
364                  */
365                 return (*req)->status;
366         }
367
368         smb1cli_conn_activate_signing(session->transport->conn,
369                                       session_key,
370                                       state->setup.nt1.in.password2);
371
372         nt_status = smb1cli_session_set_session_key(session->smbXcli,
373                                                     session_key);
374         data_blob_free(&session_key);
375         if (!NT_STATUS_IS_OK(nt_status)) {
376                 return nt_status;
377         }
378
379         return (*req)->status;
380 }
381
382
383 /*
384   old style session setup (pre NT1 protocol level)
385 */
386 static NTSTATUS session_setup_old(struct composite_context *c,
387                                   struct smbcli_session *session, 
388                                   struct smb_composite_sesssetup *io,
389                                   struct smbcli_request **req) 
390 {
391         NTSTATUS nt_status;
392         struct sesssetup_state *state = talloc_get_type(c->private_data,
393                                                         struct sesssetup_state);
394         const char *password = cli_credentials_get_password(io->in.credentials);
395         const char *domain = cli_credentials_get_domain(io->in.credentials);
396
397         /*
398          * domain controllers tend to reject the NTLM v2 blob
399          * if the netbiosname is not valid (e.g. IP address or FQDN)
400          * so just leave it away (as Windows client do)
401          */
402         DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, NULL, domain);
403
404         DATA_BLOB session_key;
405         int flags = 0;
406         if (session->options.lanman_auth) {
407                 flags |= CLI_CRED_LANMAN_AUTH;
408         }
409
410         if (session->options.ntlmv2_auth) {
411                 flags |= CLI_CRED_NTLMv2_AUTH;
412         }
413
414         state->setup.old.level      = RAW_SESSSETUP_OLD;
415         state->setup.old.in.bufsize = session->transport->options.max_xmit;
416         state->setup.old.in.mpx_max = session->transport->options.max_mux;
417         state->setup.old.in.vc_num  = 1;
418         state->setup.old.in.sesskey = io->in.sesskey;
419         state->setup.old.in.os      = "Unix";
420         state->setup.old.in.lanman  = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
421         cli_credentials_get_ntlm_username_domain(io->in.credentials, state, 
422                                                  &state->setup.old.in.user,
423                                                  &state->setup.old.in.domain);
424         
425         if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
426                 nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, 
427                                                               &flags, 
428                                                               session->transport->negotiate.secblob, 
429                                                               names_blob,
430                                                               &state->setup.old.in.password,
431                                                               NULL,
432                                                               NULL, &session_key);
433                 NT_STATUS_NOT_OK_RETURN(nt_status);
434
435                 nt_status = smb1cli_session_set_session_key(session->smbXcli,
436                                                             session_key);
437                 data_blob_free(&session_key);
438                 if (!NT_STATUS_IS_OK(nt_status)) {
439                         return nt_status;
440                 }
441         } else if (session->options.plaintext_auth) {
442                 state->setup.old.in.password = data_blob_talloc(state, password, strlen(password));
443         } else {
444                 /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
445                 return NT_STATUS_INVALID_PARAMETER;
446         }
447         
448         *req = smb_raw_sesssetup_send(session, &state->setup);
449         if (!*req) {
450                 return NT_STATUS_NO_MEMORY;
451         }
452         return (*req)->status;
453 }
454
455
456 /*
457   Modern, all singing, all dancing extended security (and possibly SPNEGO) request
458 */
459 static NTSTATUS session_setup_spnego(struct composite_context *c,
460                                      struct smbcli_session *session, 
461                                      struct smb_composite_sesssetup *io,
462                                      struct smbcli_request **req) 
463 {
464         struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
465         NTSTATUS status;
466         const char *chosen_oid = NULL;
467
468         state->setup.spnego.level           = RAW_SESSSETUP_SPNEGO;
469         state->setup.spnego.in.bufsize      = session->transport->options.max_xmit;
470         state->setup.spnego.in.mpx_max      = session->transport->options.max_mux;
471         state->setup.spnego.in.vc_num       = 1;
472         state->setup.spnego.in.sesskey      = io->in.sesskey;
473         state->setup.spnego.in.capabilities = io->in.capabilities;
474         state->setup.spnego.in.os           = "Unix";
475         state->setup.spnego.in.lanman       = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
476         state->setup.spnego.in.workgroup    = io->in.workgroup;
477
478         status = gensec_client_start(session, &session->gensec,
479                                      io->in.gensec_settings);
480         if (!NT_STATUS_IS_OK(status)) {
481                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
482                 return status;
483         }
484
485         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
486
487         status = gensec_set_credentials(session->gensec, io->in.credentials);
488         if (!NT_STATUS_IS_OK(status)) {
489                 DEBUG(1, ("Failed to start set GENSEC client credentials: %s\n", 
490                           nt_errstr(status)));
491                 return status;
492         }
493
494         status = gensec_set_target_hostname(session->gensec,
495                         smbXcli_conn_remote_name(session->transport->conn));
496         if (!NT_STATUS_IS_OK(status)) {
497                 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
498                           nt_errstr(status)));
499                 return status;
500         }
501
502         status = gensec_set_target_service(session->gensec, "cifs");
503         if (!NT_STATUS_IS_OK(status)) {
504                 DEBUG(1, ("Failed to start set GENSEC target service: %s\n", 
505                           nt_errstr(status)));
506                 return status;
507         }
508
509         if (session->transport->negotiate.secblob.length) {
510                 chosen_oid = GENSEC_OID_SPNEGO;
511                 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
512                 if (!NT_STATUS_IS_OK(status)) {
513                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
514                                   gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
515                         chosen_oid = GENSEC_OID_NTLMSSP;
516                         status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
517                         if (!NT_STATUS_IS_OK(status)) {
518                                 DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
519                                           gensec_get_name_by_oid(session->gensec, chosen_oid), 
520                                           nt_errstr(status)));
521                         return status;
522                         }
523                 }
524         } else {
525                 /* without a sec blob, means raw NTLMSSP */
526                 chosen_oid = GENSEC_OID_NTLMSSP;
527                 status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
528                 if (!NT_STATUS_IS_OK(status)) {
529                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
530                                   gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
531                 }
532         }
533
534         if (strequal(chosen_oid, GENSEC_OID_SPNEGO)) {
535                 status = gensec_update_ev(session->gensec, state,
536                                        c->event_ctx,
537                                        session->transport->negotiate.secblob,
538                                        &state->setup.spnego.in.secblob);
539         } else {
540                 status = gensec_update_ev(session->gensec, state,
541                                        c->event_ctx,
542                                        data_blob(NULL, 0),
543                                        &state->setup.spnego.in.secblob);
544
545         }
546
547         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
548             !NT_STATUS_IS_OK(status)) {
549                 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
550                           gensec_get_name_by_oid(session->gensec, chosen_oid), 
551                           nt_errstr(status)));
552                 return status;
553         }
554         state->gensec_status = status;
555
556         *req = smb_raw_sesssetup_send(session, &state->setup);
557         if (!*req) {
558                 return NT_STATUS_NO_MEMORY;
559         }
560
561         /*
562          * we need to check the signature ourself
563          * as the session key might be the acceptor subkey
564          * which comes within the response itself
565          */
566         if (!smb1cli_conn_signing_is_active((*req)->transport->conn)) {
567                 (*req)->sign_caller_checks = true;
568         }
569
570         return (*req)->status;
571 }
572
573
574 /*
575   composite session setup function that hides the details of all the
576   different session setup varients, including the multi-pass nature of
577   the spnego varient
578 */
579 struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session, 
580                                                        struct smb_composite_sesssetup *io)
581 {
582         struct composite_context *c;
583         struct sesssetup_state *state;
584         NTSTATUS status;
585
586         c = composite_create(session, session->transport->ev);
587         if (c == NULL) return NULL;
588
589         state = talloc_zero(c, struct sesssetup_state);
590         if (composite_nomem(state, c)) return c;
591         c->private_data = state;
592
593         state->io = io;
594
595         talloc_set_destructor(state, sesssetup_state_destructor);
596
597         /* no session setup at all in earliest protocol varients */
598         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
599                 ZERO_STRUCT(io->out);
600                 composite_done(c);
601                 return c;
602         }
603
604         /* see what session setup interface we will use */
605         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
606                 status = session_setup_old(c, session, io, &state->req);
607         } else if (!session->transport->options.use_spnego ||
608                    !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
609                 status = session_setup_nt1(c, session, io, &state->req);
610         } else {
611                 status = session_setup_spnego(c, session, io, &state->req);
612         }
613
614         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || 
615             NT_STATUS_IS_OK(status)) {
616                 composite_continue_smb(c, state->req, request_handler, c);      
617                 return c;
618         }
619
620         composite_error(c, status);
621         return c;
622 }
623
624
625 /*
626   receive a composite session setup reply
627 */
628 NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c)
629 {
630         NTSTATUS status;
631         status = composite_wait(c);
632         talloc_free(c);
633         return status;
634 }
635
636 /*
637   sync version of smb_composite_sesssetup 
638 */
639 NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
640 {
641         struct composite_context *c = smb_composite_sesssetup_send(session, io);
642         return smb_composite_sesssetup_recv(c);
643 }