s4:libcli/smb_composite: make the additional gensec_update steps async
[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 <tevent.h>
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/raw/raw_proto.h"
27 #include "libcli/composite/composite.h"
28 #include "libcli/smb_composite/smb_composite.h"
29 #include "libcli/auth/libcli_auth.h"
30 #include "auth/auth.h"
31 #include "auth/gensec/gensec.h"
32 #include "auth/credentials/credentials.h"
33 #include "version.h"
34 #include "param/param.h"
35 #include "libcli/smb/smbXcli_base.h"
36
37 struct sesssetup_state {
38         struct smbcli_session *session;
39         union smb_sesssetup setup;
40         const char *chosen_oid;
41         NTSTATUS remote_status;
42         NTSTATUS gensec_status;
43         struct smb_composite_sesssetup *io;
44         struct smbcli_request *req;
45         struct smbcli_request *check_req;
46         unsigned int logon_retries;
47 };
48
49 static int sesssetup_state_destructor(struct sesssetup_state *state)
50 {
51         if (state->req) {
52                 talloc_free(state->req);
53                 state->req = NULL;
54         }
55
56         return 0;
57 }
58
59 static NTSTATUS session_setup_old(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_nt1(struct composite_context *c,
64                                   struct smbcli_session *session, 
65                                   struct smb_composite_sesssetup *io,
66                                   struct smbcli_request **req); 
67 static NTSTATUS session_setup_spnego_restart(struct composite_context *c,
68                                              struct smbcli_session *session,
69                                              struct smb_composite_sesssetup *io);
70 static NTSTATUS session_setup_spnego(struct composite_context *c,
71                                      struct smbcli_session *session, 
72                                      struct smb_composite_sesssetup *io,
73                                      struct smbcli_request **req);
74 static void smb_composite_sesssetup_spnego_done1(struct tevent_req *subreq);
75 static void smb_composite_sesssetup_spnego_done2(struct tevent_req *subreq);
76
77
78 /*
79   handler for completion of a smbcli_request sub-request
80 */
81 static void request_handler(struct smbcli_request *req)
82 {
83         struct composite_context *c = (struct composite_context *)req->async.private_data;
84         struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
85         struct smbcli_session *session = req->session;
86         DATA_BLOB null_data_blob = data_blob(NULL, 0);
87         NTSTATUS session_key_err, nt_status;
88         struct smbcli_request *check_req = NULL;
89         const char *os = NULL;
90         const char *lanman = NULL;
91
92         if (req->sign_caller_checks) {
93                 req->do_not_free = true;
94                 check_req = req;
95         }
96
97         state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup);
98         c->status = state->remote_status;
99         state->req = NULL;
100
101         /*
102          * we only need to check the signature if the
103          * NT_STATUS_OK is returned
104          */
105         if (!NT_STATUS_IS_OK(state->remote_status)) {
106                 talloc_free(check_req);
107                 check_req = NULL;
108         }
109
110         switch (state->setup.old.level) {
111         case RAW_SESSSETUP_OLD:
112                 state->io->out.vuid = state->setup.old.out.vuid;
113                 /* This doesn't work, as this only happens on old
114                  * protocols, where this comparison won't match. */
115                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
116                         /* we neet to reset the vuid for a new try */
117                         session->vuid = 0;
118                         if (cli_credentials_wrong_password(state->io->in.credentials)) {
119                                 nt_status = session_setup_old(c, session, 
120                                                               state->io, 
121                                                               &state->req);
122                                 if (NT_STATUS_IS_OK(nt_status)) {
123                                         talloc_free(check_req);
124                                         c->status = nt_status;
125                                         composite_continue_smb(c, state->req, request_handler, c);
126                                         return;
127                                 }
128                         }
129                 }
130                 if (!NT_STATUS_IS_OK(c->status)) {
131                         composite_error(c, c->status);
132                         return;
133                 }
134                 os = state->setup.old.out.os;
135                 lanman = state->setup.old.out.lanman;
136                 break;
137
138         case RAW_SESSSETUP_NT1:
139                 state->io->out.vuid = state->setup.nt1.out.vuid;
140                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
141                         /* we need to reset the vuid for a new try */
142                         session->vuid = 0;
143                         if (cli_credentials_wrong_password(state->io->in.credentials)) {
144                                 nt_status = session_setup_nt1(c, session, 
145                                                               state->io, 
146                                                               &state->req);
147                                 if (NT_STATUS_IS_OK(nt_status)) {
148                                         talloc_free(check_req);
149                                         c->status = nt_status;
150                                         composite_continue_smb(c, state->req, request_handler, c);
151                                         return;
152                                 }
153                         }
154                 }
155                 if (!NT_STATUS_IS_OK(c->status)) {
156                         composite_error(c, c->status);
157                         return;
158                 }
159                 os = state->setup.nt1.out.os;
160                 lanman = state->setup.nt1.out.lanman;
161                 break;
162
163         case RAW_SESSSETUP_SPNEGO:
164                 state->io->out.vuid = state->setup.spnego.out.vuid;
165                 if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
166                         const char *principal;
167
168                         /* we need to reset the vuid for a new try */
169                         session->vuid = 0;
170
171                         principal = gensec_get_target_principal(session->gensec);
172                         if (principal == NULL) {
173                                 const char *hostname = gensec_get_target_hostname(session->gensec);
174                                 const char *service  = gensec_get_target_service(session->gensec);
175                                 if (hostname != NULL && service != NULL) {
176                                         principal = talloc_asprintf(state, "%s/%s", service, hostname);
177                                 }
178                         }
179                         if (cli_credentials_failed_kerberos_login(state->io->in.credentials, principal, &state->logon_retries) ||
180                             cli_credentials_wrong_password(state->io->in.credentials)) {
181                                 struct tevent_req *subreq = NULL;
182
183                                 nt_status = session_setup_spnego_restart(c, session, state->io);
184                                 if (!NT_STATUS_IS_OK(nt_status)) {
185                                         DEBUG(1, ("session_setup_spnego_restart() failed: %s\n",
186                                                   nt_errstr(nt_status)));
187                                         c->status = nt_status;
188                                         composite_error(c, c->status);
189                                         return;
190                                 }
191
192                                 subreq = gensec_update_send(state, c->event_ctx,
193                                                             session->gensec,
194                                                             state->setup.spnego.out.secblob);
195                                 if (composite_nomem(subreq, c)) {
196                                         return;
197                                 }
198                                 tevent_req_set_callback(subreq,
199                                                         smb_composite_sesssetup_spnego_done1,
200                                                         c);
201                                 return;
202                         }
203                 }
204                 if (GENSEC_UPDATE_IS_NTERROR(c->status)) {
205                         composite_error(c, c->status);
206                         return;
207                 }
208                 if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
209                         struct tevent_req *subreq = NULL;
210
211                         /* The status value here, from the earlier pass at GENSEC is
212                          * vital to the security of the system.  Even if the other end
213                          * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then
214                          * you must keep feeding it blobs, or else the remote
215                          * host/attacker might avoid mutal authentication
216                          * requirements */
217
218                         subreq = gensec_update_send(state, c->event_ctx,
219                                                     session->gensec,
220                                                     state->setup.spnego.out.secblob);
221                         if (composite_nomem(subreq, c)) {
222                                 return;
223                         }
224                         tevent_req_set_callback(subreq,
225                                                 smb_composite_sesssetup_spnego_done2,
226                                                 c);
227                         if (NT_STATUS_IS_OK(state->remote_status)) {
228                                 state->check_req = check_req;
229                         } else {
230                                 TALLOC_FREE(check_req);
231                         }
232                         return;
233                 } else {
234                         state->setup.spnego.in.secblob = data_blob(NULL, 0);
235                 }
236
237                 if (cli_credentials_is_anonymous(state->io->in.credentials)) {
238                         /*
239                          * anonymous => no signing
240                          */
241                 } else if (NT_STATUS_IS_OK(state->remote_status)) {
242                         DATA_BLOB session_key;
243
244                         if (state->setup.spnego.in.secblob.length) {
245                                 c->status = NT_STATUS_INTERNAL_ERROR;
246                                 composite_error(c, c->status);
247                                 return;
248                         }
249                         session_key_err = gensec_session_key(session->gensec, session, &session_key);
250                         if (NT_STATUS_IS_OK(session_key_err)) {
251                                 smb1cli_conn_activate_signing(session->transport->conn,
252                                                               session_key,
253                                                               null_data_blob);
254                         }
255
256                         c->status = smb1cli_session_set_session_key(session->smbXcli,
257                                                                     session_key);
258                         data_blob_free(&session_key);
259                         if (!NT_STATUS_IS_OK(c->status)) {
260                                 composite_error(c, c->status);
261                                 return;
262                         }
263                 }
264
265                 os = state->setup.spnego.out.os;
266                 lanman = state->setup.spnego.out.lanman;
267                 break;
268
269         case RAW_SESSSETUP_SMB2:
270                 c->status = NT_STATUS_INTERNAL_ERROR;
271                 composite_error(c, c->status);
272                 return;
273         }
274
275         if (check_req) {
276                 bool ok;
277
278                 check_req->sign_caller_checks = false;
279
280                 ok = smb1cli_conn_check_signing(check_req->transport->conn,
281                                                 check_req->in.buffer, 1);
282                 TALLOC_FREE(check_req);
283                 if (!ok) {
284                         c->status = NT_STATUS_ACCESS_DENIED;
285                         composite_error(c, c->status);
286                         return;
287                 }
288         }
289
290         if (!NT_STATUS_IS_OK(c->status)) {
291                 composite_error(c, c->status);
292                 return;
293         }
294
295         if (os) {
296                 session->os = talloc_strdup(session, os);
297                 if (composite_nomem(session->os, c)) return;
298         } else {
299                 session->os = NULL;
300         }
301         if (lanman) {
302                 session->lanman = talloc_strdup(session, lanman);
303                 if (composite_nomem(session->lanman, c)) return;
304         } else {
305                 session->lanman = NULL;
306         }
307
308         composite_done(c);
309 }
310
311
312 /*
313   send a nt1 style session setup
314 */
315 static NTSTATUS session_setup_nt1(struct composite_context *c,
316                                   struct smbcli_session *session, 
317                                   struct smb_composite_sesssetup *io,
318                                   struct smbcli_request **req) 
319 {
320         NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
321         struct sesssetup_state *state = talloc_get_type(c->private_data,
322                                                         struct sesssetup_state);
323         const char *domain = cli_credentials_get_domain(io->in.credentials);
324
325         /*
326          * domain controllers tend to reject the NTLM v2 blob
327          * if the netbiosname is not valid (e.g. IP address or FQDN)
328          * so just leave it away (as Windows client do)
329          */
330         DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, NULL, domain);
331
332         DATA_BLOB session_key = data_blob(NULL, 0);
333         int flags = CLI_CRED_NTLM_AUTH;
334
335         if (session->options.lanman_auth) {
336                 flags |= CLI_CRED_LANMAN_AUTH;
337         }
338
339         if (session->options.ntlmv2_auth) {
340                 flags |= CLI_CRED_NTLMv2_AUTH;
341         }
342
343         state->setup.nt1.level           = RAW_SESSSETUP_NT1;
344         state->setup.nt1.in.bufsize      = session->transport->options.max_xmit;
345         state->setup.nt1.in.mpx_max      = session->transport->options.max_mux;
346         state->setup.nt1.in.vc_num       = 1;
347         state->setup.nt1.in.sesskey      = io->in.sesskey;
348         state->setup.nt1.in.capabilities = io->in.capabilities;
349         state->setup.nt1.in.os           = "Unix";
350         state->setup.nt1.in.lanman       = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
351
352         cli_credentials_get_ntlm_username_domain(io->in.credentials, state, 
353                                                  &state->setup.nt1.in.user,
354                                                  &state->setup.nt1.in.domain);
355         
356
357         if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
358                 if (!cli_credentials_is_anonymous(io->in.credentials) &&
359                     session->options.ntlmv2_auth &&
360                     session->transport->options.use_spnego)
361                 {
362                         /*
363                          * Don't send an NTLMv2_RESPONSE without NTLMSSP
364                          * if we want to use spnego
365                          */
366                         return NT_STATUS_INVALID_PARAMETER;
367                 }
368
369                 nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, 
370                                                               &flags, 
371                                                               session->transport->negotiate.secblob, 
372                                                               NULL, /* server_timestamp */
373                                                               names_blob,
374                                                               &state->setup.nt1.in.password1,
375                                                               &state->setup.nt1.in.password2,
376                                                               NULL, &session_key);
377                 NT_STATUS_NOT_OK_RETURN(nt_status);
378         } else if (session->options.plaintext_auth) {
379                 const char *password = cli_credentials_get_password(io->in.credentials);
380                 state->setup.nt1.in.password1 = data_blob_talloc(state, password, strlen(password));
381                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
382         } else {
383                 /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
384                 return NT_STATUS_INVALID_PARAMETER;
385         }
386
387         *req = smb_raw_sesssetup_send(session, &state->setup);
388         if (!*req) {
389                 return NT_STATUS_NO_MEMORY;
390         }
391
392         if (!NT_STATUS_IS_OK(nt_status)) {
393                 /*
394                  * plain text => no signing
395                  */
396                 return (*req)->status;
397         }
398
399         if (cli_credentials_is_anonymous(io->in.credentials)) {
400                 /*
401                  * anonymous => no signing
402                  */
403                 return (*req)->status;
404         }
405
406         smb1cli_conn_activate_signing(session->transport->conn,
407                                       session_key,
408                                       state->setup.nt1.in.password2);
409
410         nt_status = smb1cli_session_set_session_key(session->smbXcli,
411                                                     session_key);
412         data_blob_free(&session_key);
413         if (!NT_STATUS_IS_OK(nt_status)) {
414                 return nt_status;
415         }
416
417         return (*req)->status;
418 }
419
420
421 /*
422   old style session setup (pre NT1 protocol level)
423 */
424 static NTSTATUS session_setup_old(struct composite_context *c,
425                                   struct smbcli_session *session, 
426                                   struct smb_composite_sesssetup *io,
427                                   struct smbcli_request **req) 
428 {
429         NTSTATUS nt_status;
430         struct sesssetup_state *state = talloc_get_type(c->private_data,
431                                                         struct sesssetup_state);
432         const char *password = cli_credentials_get_password(io->in.credentials);
433
434         /*
435          * domain controllers tend to reject the NTLM v2 blob
436          * if the netbiosname is not valid (e.g. IP address or FQDN)
437          * so just leave it away (as Windows client do)
438          */
439         DATA_BLOB session_key;
440
441         state->setup.old.level      = RAW_SESSSETUP_OLD;
442         state->setup.old.in.bufsize = session->transport->options.max_xmit;
443         state->setup.old.in.mpx_max = session->transport->options.max_mux;
444         state->setup.old.in.vc_num  = 1;
445         state->setup.old.in.sesskey = io->in.sesskey;
446         state->setup.old.in.os      = "Unix";
447         state->setup.old.in.lanman  = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
448         cli_credentials_get_ntlm_username_domain(io->in.credentials, state, 
449                                                  &state->setup.old.in.user,
450                                                  &state->setup.old.in.domain);
451         
452         if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
453                 DATA_BLOB names_blob = data_blob_null;
454                 int flags = 0;
455
456                 if (!cli_credentials_is_anonymous(io->in.credentials) &&
457                     !session->options.lanman_auth)
458                 {
459                         return NT_STATUS_INVALID_PARAMETER;
460                 }
461
462                 flags |= CLI_CRED_LANMAN_AUTH;
463
464                 nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, 
465                                                               &flags, 
466                                                               session->transport->negotiate.secblob, 
467                                                               NULL, /* server_timestamp */
468                                                               names_blob,
469                                                               &state->setup.old.in.password,
470                                                               NULL,
471                                                               NULL, &session_key);
472                 NT_STATUS_NOT_OK_RETURN(nt_status);
473
474                 nt_status = smb1cli_session_set_session_key(session->smbXcli,
475                                                             session_key);
476                 data_blob_free(&session_key);
477                 if (!NT_STATUS_IS_OK(nt_status)) {
478                         return nt_status;
479                 }
480         } else if (session->options.plaintext_auth) {
481                 state->setup.old.in.password = data_blob_talloc(state, password, strlen(password));
482         } else {
483                 /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
484                 return NT_STATUS_INVALID_PARAMETER;
485         }
486         
487         *req = smb_raw_sesssetup_send(session, &state->setup);
488         if (!*req) {
489                 return NT_STATUS_NO_MEMORY;
490         }
491         return (*req)->status;
492 }
493
494 static NTSTATUS session_setup_spnego_restart(struct composite_context *c,
495                                              struct smbcli_session *session,
496                                              struct smb_composite_sesssetup *io)
497 {
498         struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
499         NTSTATUS status;
500
501         status = gensec_client_start(session, &session->gensec,
502                                      io->in.gensec_settings);
503         if (!NT_STATUS_IS_OK(status)) {
504                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
505                 return status;
506         }
507
508         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
509
510         status = gensec_set_credentials(session->gensec, io->in.credentials);
511         if (!NT_STATUS_IS_OK(status)) {
512                 DEBUG(1, ("Failed to start set GENSEC client credentials: %s\n", 
513                           nt_errstr(status)));
514                 return status;
515         }
516
517         status = gensec_set_target_hostname(session->gensec,
518                         smbXcli_conn_remote_name(session->transport->conn));
519         if (!NT_STATUS_IS_OK(status)) {
520                 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
521                           nt_errstr(status)));
522                 return status;
523         }
524
525         status = gensec_set_target_service(session->gensec, "cifs");
526         if (!NT_STATUS_IS_OK(status)) {
527                 DEBUG(1, ("Failed to start set GENSEC target service: %s\n", 
528                           nt_errstr(status)));
529                 return status;
530         }
531
532         state->setup.spnego.out.secblob =
533                         session->transport->negotiate.secblob;
534         if (session->transport->negotiate.secblob.length) {
535                 state->chosen_oid = GENSEC_OID_SPNEGO;
536                 status = gensec_start_mech_by_oid(session->gensec,
537                                                   state->chosen_oid);
538                 if (!NT_STATUS_IS_OK(status)) {
539                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
540                                   gensec_get_name_by_oid(session->gensec,
541                                                          state->chosen_oid),
542                                   nt_errstr(status)));
543                         state->setup.spnego.out.secblob = data_blob_null;
544                         state->chosen_oid = GENSEC_OID_NTLMSSP;
545                         status = gensec_start_mech_by_oid(session->gensec,
546                                                           state->chosen_oid);
547                         if (!NT_STATUS_IS_OK(status)) {
548                                 DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
549                                           gensec_get_name_by_oid(session->gensec,
550                                                                  state->chosen_oid),
551                                           nt_errstr(status)));
552                                 return status;
553                         }
554                 }
555         } else {
556                 /* without a sec blob, means raw NTLMSSP */
557                 state->chosen_oid = GENSEC_OID_NTLMSSP;
558                 status = gensec_start_mech_by_oid(session->gensec,
559                                                   state->chosen_oid);
560                 if (!NT_STATUS_IS_OK(status)) {
561                         DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
562                                   gensec_get_name_by_oid(session->gensec,
563                                                          state->chosen_oid),
564                                   nt_errstr(status)));
565                         return status;
566                 }
567         }
568
569         state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
570         state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
571         return NT_STATUS_OK;
572 }
573
574 /*
575   Modern, all singing, all dancing extended security (and possibly SPNEGO) request
576 */
577 static NTSTATUS session_setup_spnego(struct composite_context *c,
578                                      struct smbcli_session *session,
579                                      struct smb_composite_sesssetup *io,
580                                      struct smbcli_request **req)
581 {
582         struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
583
584         state->setup.spnego.level           = RAW_SESSSETUP_SPNEGO;
585         state->setup.spnego.in.bufsize      = session->transport->options.max_xmit;
586         state->setup.spnego.in.mpx_max      = session->transport->options.max_mux;
587         state->setup.spnego.in.vc_num       = 1;
588         state->setup.spnego.in.sesskey      = io->in.sesskey;
589         state->setup.spnego.in.capabilities = io->in.capabilities;
590         state->setup.spnego.in.os           = "Unix";
591         state->setup.spnego.in.lanman       = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
592         state->setup.spnego.in.workgroup    = io->in.workgroup;
593
594         *req = smb_raw_sesssetup_send(session, &state->setup);
595         if (!*req) {
596                 return NT_STATUS_NO_MEMORY;
597         }
598
599         /*
600          * we need to check the signature ourself
601          * as the session key might be the acceptor subkey
602          * which comes within the response itself
603          */
604         if (!smb1cli_conn_signing_is_active((*req)->transport->conn)) {
605                 (*req)->sign_caller_checks = true;
606         }
607
608         return (*req)->status;
609 }
610
611
612 /*
613   composite session setup function that hides the details of all the
614   different session setup varients, including the multi-pass nature of
615   the spnego varient
616 */
617 struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session, 
618                                                        struct smb_composite_sesssetup *io)
619 {
620         struct composite_context *c;
621         struct sesssetup_state *state;
622         NTSTATUS status;
623
624         c = composite_create(session, session->transport->ev);
625         if (c == NULL) return NULL;
626
627         state = talloc_zero(c, struct sesssetup_state);
628         if (composite_nomem(state, c)) return c;
629         c->private_data = state;
630
631         state->session = session;
632         state->io = io;
633
634         talloc_set_destructor(state, sesssetup_state_destructor);
635
636         /* no session setup at all in earliest protocol varients */
637         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
638                 ZERO_STRUCT(io->out);
639                 composite_done(c);
640                 return c;
641         }
642
643         /* see what session setup interface we will use */
644         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
645                 status = session_setup_old(c, session, io, &state->req);
646         } else if (!session->transport->options.use_spnego ||
647                    !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
648                 status = session_setup_nt1(c, session, io, &state->req);
649         } else {
650                 struct tevent_req *subreq = NULL;
651
652                 status = session_setup_spnego_restart(c, session, io);
653                 if (!NT_STATUS_IS_OK(status)) {
654                         DEBUG(1, ("session_setup_spnego_restart() failed: %s\n",
655                                   nt_errstr(status)));
656                         c->status = status;
657                         composite_error(c, c->status);
658                         return c;
659                 }
660
661                 subreq = gensec_update_send(state, c->event_ctx,
662                                             session->gensec,
663                                             state->setup.spnego.out.secblob);
664                 if (composite_nomem(subreq, c)) {
665                         return c;
666                 }
667                 tevent_req_set_callback(subreq,
668                                         smb_composite_sesssetup_spnego_done1,
669                                         c);
670                 return c;
671         }
672
673         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || 
674             NT_STATUS_IS_OK(status)) {
675                 composite_continue_smb(c, state->req, request_handler, c);      
676                 return c;
677         }
678
679         composite_error(c, status);
680         return c;
681 }
682
683 static void smb_composite_sesssetup_spnego_done1(struct tevent_req *subreq)
684 {
685         struct composite_context *c =
686                 tevent_req_callback_data(subreq,
687                 struct composite_context);
688         struct sesssetup_state *state =
689                 talloc_get_type_abort(c->private_data,
690                 struct sesssetup_state);
691         NTSTATUS status;
692
693         status = gensec_update_recv(subreq, state,
694                                     &state->setup.spnego.in.secblob);
695         TALLOC_FREE(subreq);
696         if (GENSEC_UPDATE_IS_NTERROR(status)) {
697                 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
698                           gensec_get_name_by_oid(state->session->gensec,
699                                                  state->chosen_oid),
700                           nt_errstr(status)));
701                 c->status = status;
702                 composite_error(c, c->status);
703                 return;
704         }
705         state->gensec_status = status;
706
707         status = session_setup_spnego(c, state->session, state->io, &state->req);
708         if (!NT_STATUS_IS_OK(status)) {
709                 c->status = status;
710                 composite_error(c, c->status);
711                 return;
712         }
713
714         composite_continue_smb(c, state->req, request_handler, c);
715 }
716
717 static void smb_composite_sesssetup_spnego_done2(struct tevent_req *subreq)
718 {
719         struct composite_context *c =
720                 tevent_req_callback_data(subreq,
721                 struct composite_context);
722         struct sesssetup_state *state =
723                 talloc_get_type_abort(c->private_data,
724                 struct sesssetup_state);
725         struct smbcli_session *session = state->session;
726         NTSTATUS status;
727         const char *os = NULL;
728         const char *lanman = NULL;
729
730         status = gensec_update_recv(subreq, state,
731                                     &state->setup.spnego.in.secblob);
732         TALLOC_FREE(subreq);
733         if (GENSEC_UPDATE_IS_NTERROR(status)) {
734                 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
735                           gensec_get_name_by_oid(state->session->gensec,
736                                                  state->chosen_oid),
737                           nt_errstr(status)));
738                 c->status = status;
739                 composite_error(c, c->status);
740                 return;
741         }
742         state->gensec_status = status;
743
744         if (NT_STATUS_IS_OK(state->remote_status)) {
745                 if (state->setup.spnego.in.secblob.length) {
746                         c->status = NT_STATUS_INTERNAL_ERROR;
747                         composite_error(c, c->status);
748                         return;
749                 }
750         }
751
752         if (state->setup.spnego.in.secblob.length) {
753                 /*
754                  * set the session->vuid value only for calling
755                  * smb_raw_sesssetup_send()
756                  */
757                 uint16_t vuid = session->vuid;
758                 session->vuid = state->io->out.vuid;
759                 state->req = smb_raw_sesssetup_send(session, &state->setup);
760                 session->vuid = vuid;
761                 if (state->req &&
762                     !smb1cli_conn_signing_is_active(state->req->transport->conn)) {
763                         state->req->sign_caller_checks = true;
764                 }
765                 composite_continue_smb(c, state->req, request_handler, c);
766                 return;
767         }
768
769         if (cli_credentials_is_anonymous(state->io->in.credentials)) {
770                 /*
771                  * anonymous => no signing
772                  */
773         } else if (NT_STATUS_IS_OK(state->remote_status)) {
774                 NTSTATUS session_key_err;
775                 DATA_BLOB session_key;
776
777                 session_key_err = gensec_session_key(session->gensec, session, &session_key);
778                 if (NT_STATUS_IS_OK(session_key_err)) {
779                         smb1cli_conn_activate_signing(session->transport->conn,
780                                                       session_key,
781                                                       data_blob_null);
782                 }
783
784                 c->status = smb1cli_session_set_session_key(session->smbXcli,
785                                                             session_key);
786                 data_blob_free(&session_key);
787                 if (!NT_STATUS_IS_OK(c->status)) {
788                         composite_error(c, c->status);
789                         return;
790                 }
791         }
792
793         os = state->setup.spnego.out.os;
794         lanman = state->setup.spnego.out.lanman;
795
796         if (state->check_req) {
797                 struct smbcli_request *check_req = state->check_req;
798                 bool ok;
799
800                 check_req->sign_caller_checks = false;
801
802                 ok = smb1cli_conn_check_signing(check_req->transport->conn,
803                                                 check_req->in.buffer, 1);
804                 TALLOC_FREE(check_req);
805                 if (!ok) {
806                         c->status = NT_STATUS_ACCESS_DENIED;
807                         composite_error(c, c->status);
808                         return;
809                 }
810         }
811
812         if (os) {
813                 session->os = talloc_strdup(session, os);
814                 if (composite_nomem(session->os, c)) return;
815         } else {
816                 session->os = NULL;
817         }
818         if (lanman) {
819                 session->lanman = talloc_strdup(session, lanman);
820                 if (composite_nomem(session->lanman, c)) return;
821         } else {
822                 session->lanman = NULL;
823         }
824
825         composite_done(c);
826 }
827
828 /*
829   receive a composite session setup reply
830 */
831 NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c)
832 {
833         NTSTATUS status;
834         status = composite_wait(c);
835         talloc_free(c);
836         return status;
837 }
838
839 /*
840   sync version of smb_composite_sesssetup 
841 */
842 NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
843 {
844         struct composite_context *c = smb_composite_sesssetup_send(session, io);
845         return smb_composite_sesssetup_recv(c);
846 }