auth/spnego: move the output generation to the end of gensec_spnego_create_negTokenInit()
[vlendec/samba-autobuild/.git] / auth / gensec / spnego.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    RFC2478 Compliant SPNEGO implementation
5
6    Copyright (C) Jim McDonough <jmcd@us.ibm.com>      2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8    Copyright (C) Stefan Metzmacher <metze@samba.org>  2004-2008
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include <tevent.h>
27 #include "lib/util/tevent_ntstatus.h"
28 #include "../libcli/auth/spnego.h"
29 #include "librpc/gen_ndr/ndr_dcerpc.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "auth/gensec/gensec_internal.h"
33 #include "param/param.h"
34 #include "lib/util/asn1.h"
35 #include "lib/util/base64.h"
36
37 #undef strcasecmp
38
39 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
40
41 enum spnego_state_position {
42         SPNEGO_SERVER_START,
43         SPNEGO_CLIENT_START,
44         SPNEGO_SERVER_TARG,
45         SPNEGO_CLIENT_TARG,
46         SPNEGO_FALLBACK,
47         SPNEGO_DONE
48 };
49
50 struct spnego_state {
51         enum spnego_message_type expected_packet;
52         enum spnego_state_position state_position;
53         struct gensec_security *sub_sec_security;
54         bool sub_sec_ready;
55
56         const char *neg_oid;
57
58         DATA_BLOB mech_types;
59         size_t num_targs;
60         bool downgraded;
61         bool mic_requested;
62         bool needs_mic_sign;
63         bool needs_mic_check;
64         bool may_skip_mic_check;
65         bool done_mic_check;
66
67         bool simulate_w2k;
68
69         /*
70          * The following is used to implement
71          * the update token fragmentation
72          */
73         size_t in_needed;
74         DATA_BLOB in_frag;
75         size_t out_max_length;
76         DATA_BLOB out_frag;
77         NTSTATUS out_status;
78 };
79
80 static void gensec_spnego_update_sub_abort(struct spnego_state *spnego_state)
81 {
82         spnego_state->sub_sec_ready = false;
83         TALLOC_FREE(spnego_state->sub_sec_security);
84 }
85
86 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
87 {
88         struct spnego_state *spnego_state;
89
90         spnego_state = talloc_zero(gensec_security, struct spnego_state);
91         if (!spnego_state) {
92                 return NT_STATUS_NO_MEMORY;
93         }
94
95         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
96         spnego_state->state_position = SPNEGO_CLIENT_START;
97         spnego_state->sub_sec_security = NULL;
98         spnego_state->sub_sec_ready = false;
99         spnego_state->mech_types = data_blob_null;
100         spnego_state->out_max_length = gensec_max_update_size(gensec_security);
101         spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
102
103         spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
104                                                 "spnego", "simulate_w2k", false);
105
106         gensec_security->private_data = spnego_state;
107         return NT_STATUS_OK;
108 }
109
110 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
111 {
112         struct spnego_state *spnego_state;
113
114         spnego_state = talloc_zero(gensec_security, struct spnego_state);
115         if (!spnego_state) {
116                 return NT_STATUS_NO_MEMORY;
117         }
118
119         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
120         spnego_state->state_position = SPNEGO_SERVER_START;
121         spnego_state->sub_sec_security = NULL;
122         spnego_state->sub_sec_ready = false;
123         spnego_state->mech_types = data_blob_null;
124         spnego_state->out_max_length = gensec_max_update_size(gensec_security);
125         spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
126
127         spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
128                                                 "spnego", "simulate_w2k", false);
129
130         gensec_security->private_data = spnego_state;
131         return NT_STATUS_OK;
132 }
133
134 /** Fallback to another GENSEC mechanism, based on magic strings 
135  *
136  * This is the 'fallback' case, where we don't get SPNEGO, and have to
137  * try all the other options (and hope they all have a magic string
138  * they check)
139 */
140
141 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, 
142                                                   struct spnego_state *spnego_state,
143                                                   TALLOC_CTX *mem_ctx,
144                                                   const DATA_BLOB in)
145 {
146         int i,j;
147         const struct gensec_security_ops **all_ops;
148
149         all_ops = gensec_security_mechs(gensec_security, mem_ctx);
150
151         for (i=0; all_ops && all_ops[i]; i++) {
152                 bool is_spnego;
153                 NTSTATUS nt_status;
154
155                 if (gensec_security != NULL &&
156                     !gensec_security_ops_enabled(all_ops[i], gensec_security))
157                 {
158                         continue;
159                 }
160
161                 if (!all_ops[i]->oid) {
162                         continue;
163                 }
164
165                 is_spnego = false;
166                 for (j=0; all_ops[i]->oid[j]; j++) {
167                         if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
168                                 is_spnego = true;
169                         }
170                 }
171                 if (is_spnego) {
172                         continue;
173                 }
174
175                 if (!all_ops[i]->magic) {
176                         continue;
177                 }
178
179                 nt_status = all_ops[i]->magic(gensec_security, &in);
180                 if (!NT_STATUS_IS_OK(nt_status)) {
181                         continue;
182                 }
183
184                 spnego_state->state_position = SPNEGO_FALLBACK;
185
186                 nt_status = gensec_subcontext_start(spnego_state, 
187                                                     gensec_security, 
188                                                     &spnego_state->sub_sec_security);
189
190                 if (!NT_STATUS_IS_OK(nt_status)) {
191                         return nt_status;
192                 }
193                 /* select the sub context */
194                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
195                                                      all_ops[i]);
196                 if (!NT_STATUS_IS_OK(nt_status)) {
197                         return nt_status;
198                 }
199
200                 return NT_STATUS_OK;
201         }
202         DEBUG(1, ("Failed to parse SPNEGO request\n"));
203         return NT_STATUS_INVALID_PARAMETER;
204 }
205
206 /** create a negTokenInit 
207  *
208  * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
209 */
210 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security, 
211                                                   struct spnego_state *spnego_state,
212                                                   TALLOC_CTX *out_mem_ctx, 
213                                                   struct tevent_context *ev,
214                                                   DATA_BLOB *out)
215 {
216         int i;
217         NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
218         const char **mechTypes = NULL;
219         DATA_BLOB unwrapped_out = data_blob_null;
220         const struct gensec_security_ops_wrapper *all_sec;
221         const char **send_mech_types = NULL;
222         struct spnego_data spnego_out;
223         bool ok;
224
225         mechTypes = gensec_security_oids(gensec_security, 
226                                          out_mem_ctx, GENSEC_OID_SPNEGO);
227
228         all_sec = gensec_security_by_oid_list(gensec_security, 
229                                               out_mem_ctx, 
230                                               mechTypes,
231                                               GENSEC_OID_SPNEGO);
232         for (i=0; all_sec && all_sec[i].op; i++) {
233                 nt_status = gensec_subcontext_start(spnego_state,
234                                                     gensec_security,
235                                                     &spnego_state->sub_sec_security);
236                 if (!NT_STATUS_IS_OK(nt_status)) {
237                         return nt_status;
238                 }
239                 /* select the sub context */
240                 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
241                                                      all_sec[i].op);
242                 if (!NT_STATUS_IS_OK(nt_status)) {
243                         gensec_spnego_update_sub_abort(spnego_state);
244                         continue;
245                 }
246
247                 /* In the client, try and produce the first (optimistic) packet */
248                 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
249                         nt_status = gensec_update_ev(spnego_state->sub_sec_security,
250                                                   out_mem_ctx, 
251                                                   ev,
252                                                   data_blob_null,
253                                                   &unwrapped_out);
254                         if (NT_STATUS_IS_OK(nt_status)) {
255                                 spnego_state->sub_sec_ready = true;
256                         }
257
258                         if (GENSEC_UPDATE_IS_NTERROR(nt_status)) {
259                                 const char *next = NULL;
260                                 const char *principal = NULL;
261                                 int dbg_level = DBGLVL_WARNING;
262
263                                 if (all_sec[i+1].op != NULL) {
264                                         next = all_sec[i+1].op->name;
265                                         dbg_level = DBGLVL_NOTICE;
266                                 }
267
268                                 if (gensec_security->target.principal != NULL) {
269                                         principal = gensec_security->target.principal;
270                                 } else if (gensec_security->target.service != NULL &&
271                                            gensec_security->target.hostname != NULL)
272                                 {
273                                         principal = talloc_asprintf(spnego_state->sub_sec_security,
274                                                                     "%s/%s",
275                                                                     gensec_security->target.service,
276                                                                     gensec_security->target.hostname);
277                                 } else {
278                                         principal = gensec_security->target.hostname;
279                                 }
280
281                                 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
282                                           spnego_state->sub_sec_security->ops->name,
283                                           principal,
284                                           next, nt_errstr(nt_status)));
285
286                                 /*
287                                  * Pretend we never started it
288                                  */
289                                 gensec_spnego_update_sub_abort(spnego_state);
290                                 continue;
291                         }
292                 }
293
294                 goto reply;
295         }
296         gensec_spnego_update_sub_abort(spnego_state);
297
298         DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
299         return nt_status;
300
301 reply:
302         spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
303
304         send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
305                                                                 &all_sec[i]);
306
307         ok = spnego_write_mech_types(spnego_state,
308                                      send_mech_types,
309                                      &spnego_state->mech_types);
310         if (!ok) {
311                 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
312                 return NT_STATUS_NO_MEMORY;
313         }
314
315         /* List the remaining mechs as options */
316         spnego_out.negTokenInit.mechTypes = send_mech_types;
317         spnego_out.negTokenInit.reqFlags = data_blob_null;
318         spnego_out.negTokenInit.reqFlagsPadding = 0;
319
320         if (spnego_state->state_position == SPNEGO_SERVER_START) {
321                 spnego_out.negTokenInit.mechListMIC
322                         = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
323         } else {
324                 spnego_out.negTokenInit.mechListMIC = data_blob_null;
325         }
326
327         spnego_out.negTokenInit.mechToken = unwrapped_out;
328
329         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
330                 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
331                         return NT_STATUS_INVALID_PARAMETER;
332         }
333
334         /* set next state */
335         spnego_state->neg_oid = all_sec[i].oid;
336
337         if (spnego_state->state_position == SPNEGO_SERVER_START) {
338                 spnego_state->state_position = SPNEGO_SERVER_START;
339                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
340         } else {
341                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
342                 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
343         }
344
345         return NT_STATUS_MORE_PROCESSING_REQUIRED;
346 }
347
348 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security,
349                                                   struct spnego_state *spnego_state,
350                                                   struct tevent_context *ev,
351                                                   struct spnego_data *spnego_in,
352                                                   TALLOC_CTX *out_mem_ctx,
353                                                   DATA_BLOB *out)
354 {
355         TALLOC_CTX *frame = talloc_stackframe();
356         DATA_BLOB sub_out = data_blob_null;
357         const char *tp = NULL;
358         const char * const *mech_types = NULL;
359         size_t all_idx = 0;
360         const struct gensec_security_ops_wrapper *all_sec = NULL;
361         struct spnego_data spnego_out;
362         const char *my_mechs[] = {NULL, NULL};
363         NTSTATUS status;
364         bool ok;
365
366         *out = data_blob_null;
367
368         /* The server offers a list of mechanisms */
369
370         tp = spnego_in->negTokenInit.targetPrincipal;
371         if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
372                 DBG_INFO("Server claims it's principal name is %s\n", tp);
373                 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
374                         gensec_set_target_principal(gensec_security, tp);
375                 }
376         }
377
378         mech_types = spnego_in->negTokenInit.mechTypes;
379         if (mech_types == NULL) {
380                 TALLOC_FREE(frame);
381                 return NT_STATUS_INVALID_PARAMETER;
382         }
383
384         all_sec = gensec_security_by_oid_list(gensec_security,
385                                               frame, mech_types,
386                                               GENSEC_OID_SPNEGO);
387         if (all_sec == NULL) {
388                 DBG_WARNING("gensec_security_by_oid_list() failed\n");
389                 TALLOC_FREE(frame);
390                 return NT_STATUS_INVALID_PARAMETER;
391         }
392
393         for (; all_sec[all_idx].op; all_idx++) {
394                 const struct gensec_security_ops_wrapper *cur_sec =
395                         &all_sec[all_idx];
396                 const char *next = NULL;
397                 const char *principal = NULL;
398                 int dbg_level = DBGLVL_WARNING;
399                 bool allow_fallback = false;
400
401                 status = gensec_subcontext_start(spnego_state,
402                                                  gensec_security,
403                                                  &spnego_state->sub_sec_security);
404                 if (!NT_STATUS_IS_OK(status)) {
405                         TALLOC_FREE(frame);
406                         return status;
407                 }
408
409                 /* select the sub context */
410                 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
411                                                   cur_sec->op);
412                 if (!NT_STATUS_IS_OK(status)) {
413                         /*
414                          * Pretend we never started it.
415                          */
416                         gensec_spnego_update_sub_abort(spnego_state);
417                         continue;
418                 }
419
420                 spnego_state->neg_oid = cur_sec->oid;
421
422                 /*
423                  * As client we don't use an optimistic token from the server.
424                  */
425                 status = gensec_update_ev(spnego_state->sub_sec_security,
426                                           frame, ev, data_blob_null, &sub_out);
427                 if (NT_STATUS_IS_OK(status)) {
428                         spnego_state->sub_sec_ready = true;
429                 }
430
431                 if (!GENSEC_UPDATE_IS_NTERROR(status)) {
432                         /* OK or MORE_PROCESSING_REQUIRED */
433                         goto reply;
434                 }
435
436                 /*
437                  * it is likely that a NULL input token will
438                  * not be liked by most server mechs, but if
439                  * we are in the client, we want the first
440                  * update packet to be able to abort the use
441                  * of this mech
442                  */
443                 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
444                     NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) ||
445                     NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
446                     NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
447                 {
448                         allow_fallback = true;
449                 }
450
451                 if (allow_fallback && cur_sec[1].op != NULL) {
452                         next = cur_sec[1].op->name;
453                         dbg_level = DBGLVL_NOTICE;
454                 }
455
456                 if (gensec_security->target.principal != NULL) {
457                         principal = gensec_security->target.principal;
458                 } else if (gensec_security->target.service != NULL &&
459                            gensec_security->target.hostname != NULL)
460                 {
461                         principal = talloc_asprintf(spnego_state->sub_sec_security,
462                                                     "%s/%s",
463                                                     gensec_security->target.service,
464                                                     gensec_security->target.hostname);
465                 } else {
466                         principal = gensec_security->target.hostname;
467                 }
468
469                 DBG_PREFIX(dbg_level, (
470                            "%s: creating NEG_TOKEN_INIT "
471                            "for %s failed (next[%s]): %s\n",
472                            spnego_state->sub_sec_security->ops->name,
473                            principal, next, nt_errstr(status)));
474
475                 if (next == NULL) {
476                         /*
477                          * A hard error without a possible fallback.
478                          */
479                         TALLOC_FREE(frame);
480                         return status;
481                 }
482
483                 /*
484                  * Pretend we never started it.
485                  */
486                 gensec_spnego_update_sub_abort(spnego_state);
487         }
488
489         DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
490         TALLOC_FREE(frame);
491         return NT_STATUS_INVALID_PARAMETER;
492
493  reply:
494         my_mechs[0] = spnego_state->neg_oid;
495         /* compose reply */
496         spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
497         spnego_out.negTokenInit.mechTypes = my_mechs;
498         spnego_out.negTokenInit.reqFlags = data_blob_null;
499         spnego_out.negTokenInit.reqFlagsPadding = 0;
500         spnego_out.negTokenInit.mechListMIC = data_blob_null;
501         spnego_out.negTokenInit.mechToken = sub_out;
502
503         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
504                 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
505                 TALLOC_FREE(frame);
506                 return NT_STATUS_INVALID_PARAMETER;
507         }
508
509         ok = spnego_write_mech_types(spnego_state,
510                                      my_mechs,
511                                      &spnego_state->mech_types);
512         if (!ok) {
513                 DBG_ERR("failed to write mechTypes\n");
514                 TALLOC_FREE(frame);
515                 return NT_STATUS_NO_MEMORY;
516         }
517
518         /* set next state */
519         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
520         spnego_state->state_position = SPNEGO_CLIENT_TARG;
521
522         TALLOC_FREE(frame);
523         return NT_STATUS_MORE_PROCESSING_REQUIRED;
524 }
525
526 static NTSTATUS gensec_spnego_client_negTokenTarg(struct gensec_security *gensec_security,
527                                                   struct spnego_state *spnego_state,
528                                                   struct tevent_context *ev,
529                                                   struct spnego_data *spnego_in,
530                                                   TALLOC_CTX *out_mem_ctx,
531                                                   DATA_BLOB *out)
532 {
533         struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
534         DATA_BLOB sub_in = ta->responseToken;
535         DATA_BLOB mech_list_mic = data_blob_null;
536         DATA_BLOB sub_out = data_blob_null;
537         struct spnego_data spnego_out;
538         NTSTATUS status;
539
540         *out = data_blob_null;
541
542         spnego_state->num_targs++;
543
544         if (ta->negResult == SPNEGO_REJECT) {
545                 return NT_STATUS_LOGON_FAILURE;
546         }
547
548         if (ta->negResult == SPNEGO_REQUEST_MIC) {
549                 spnego_state->mic_requested = true;
550         }
551
552         if (ta->mechListMIC.length > 0) {
553                 DATA_BLOB *m = &ta->mechListMIC;
554                 const DATA_BLOB *r = &ta->responseToken;
555
556                 /*
557                  * Windows 2000 has a bug, it repeats the
558                  * responseToken in the mechListMIC field.
559                  */
560                 if (m->length == r->length) {
561                         int cmp;
562
563                         cmp = memcmp(m->data, r->data, m->length);
564                         if (cmp == 0) {
565                                 data_blob_free(m);
566                         }
567                 }
568         }
569
570         /* Server didn't like our choice of mech, and chose something else */
571         if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
572              (ta->negResult == SPNEGO_REQUEST_MIC)) &&
573             ta->supportedMech != NULL &&
574             strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
575         {
576                 const char *client_mech = NULL;
577                 const char *client_oid = NULL;
578                 const char *server_mech = NULL;
579                 const char *server_oid = NULL;
580
581                 client_mech = gensec_get_name_by_oid(gensec_security,
582                                                      spnego_state->neg_oid);
583                 client_oid = spnego_state->neg_oid;
584                 server_mech = gensec_get_name_by_oid(gensec_security,
585                                                      ta->supportedMech);
586                 server_oid = ta->supportedMech;
587
588                 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
589                            "server wants: %s[%s]\n",
590                            client_mech, client_oid, server_mech, server_oid);
591
592                 spnego_state->downgraded = true;
593                 gensec_spnego_update_sub_abort(spnego_state);
594
595                 status = gensec_subcontext_start(spnego_state,
596                                                  gensec_security,
597                                                  &spnego_state->sub_sec_security);
598                 if (!NT_STATUS_IS_OK(status)) {
599                         return status;
600                 }
601
602                 /* select the sub context */
603                 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
604                                                   ta->supportedMech);
605                 if (!NT_STATUS_IS_OK(status)) {
606                         return status;
607                 }
608
609                 spnego_state->neg_oid = talloc_strdup(spnego_state,
610                                         ta->supportedMech);
611                 if (spnego_state->neg_oid == NULL) {
612                         return NT_STATUS_NO_MEMORY;
613                 }
614         }
615
616         if (ta->mechListMIC.length > 0) {
617                 if (spnego_state->sub_sec_ready) {
618                         spnego_state->needs_mic_check = true;
619                 }
620         }
621
622         if (spnego_state->needs_mic_check) {
623                 if (ta->responseToken.length != 0) {
624                         DBG_WARNING("non empty response token not expected\n");
625                         return NT_STATUS_INVALID_PARAMETER;
626                 }
627
628                 if (ta->mechListMIC.length == 0
629                     && spnego_state->may_skip_mic_check) {
630                         /*
631                          * In this case we don't require
632                          * a mechListMIC from the server.
633                          *
634                          * This works around bugs in the Azure
635                          * and Apple spnego implementations.
636                          *
637                          * See
638                          * https://bugzilla.samba.org/show_bug.cgi?id=11994
639                          */
640                         spnego_state->needs_mic_check = false;
641                         status = NT_STATUS_OK;
642                         goto client_response;
643                 }
644
645                 status = gensec_check_packet(spnego_state->sub_sec_security,
646                                              spnego_state->mech_types.data,
647                                              spnego_state->mech_types.length,
648                                              spnego_state->mech_types.data,
649                                              spnego_state->mech_types.length,
650                                              &ta->mechListMIC);
651                 if (!NT_STATUS_IS_OK(status)) {
652                         DBG_WARNING("failed to verify mechListMIC: %s\n",
653                                     nt_errstr(status));
654                         return status;
655                 }
656                 spnego_state->needs_mic_check = false;
657                 spnego_state->done_mic_check = true;
658                 goto client_response;
659         }
660
661         if (!spnego_state->sub_sec_ready) {
662                 status = gensec_update_ev(spnego_state->sub_sec_security,
663                                           out_mem_ctx, ev,
664                                           sub_in,
665                                           &sub_out);
666                 if (NT_STATUS_IS_OK(status)) {
667                         spnego_state->sub_sec_ready = true;
668                 }
669                 if (!NT_STATUS_IS_OK(status)) {
670                         goto client_response;
671                 }
672         } else {
673                 status = NT_STATUS_OK;
674         }
675
676         if (!spnego_state->done_mic_check) {
677                 bool have_sign = true;
678                 bool new_spnego = false;
679
680                 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
681                                                 GENSEC_FEATURE_SIGN);
682                 if (spnego_state->simulate_w2k) {
683                         have_sign = false;
684                 }
685                 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
686                                                  GENSEC_FEATURE_NEW_SPNEGO);
687
688                 switch (ta->negResult) {
689                 case SPNEGO_ACCEPT_COMPLETED:
690                 case SPNEGO_NONE_RESULT:
691                         if (spnego_state->num_targs == 1) {
692                                 /*
693                                  * the first exchange doesn't require
694                                  * verification
695                                  */
696                                 new_spnego = false;
697                         }
698
699                         break;
700
701                 case SPNEGO_ACCEPT_INCOMPLETE:
702                         if (ta->mechListMIC.length > 0) {
703                                 new_spnego = true;
704                                 break;
705                         }
706
707                         if (spnego_state->downgraded) {
708                                 /*
709                                  * A downgrade should be protected if
710                                  * supported
711                                  */
712                                 break;
713                         }
714
715                         /*
716                          * The caller may just asked for
717                          * GENSEC_FEATURE_SESSION_KEY, this
718                          * is only reflected in the want_features.
719                          *
720                          * As it will imply
721                          * gensec_have_features(GENSEC_FEATURE_SIGN)
722                          * to return true.
723                          */
724                         if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
725                                 break;
726                         }
727                         if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
728                                 break;
729                         }
730                         /*
731                          * Here we're sure our preferred mech was
732                          * selected by the server and our caller doesn't
733                          * need GENSEC_FEATURE_SIGN nor
734                          * GENSEC_FEATURE_SEAL support.
735                          *
736                          * In this case we don't require
737                          * a mechListMIC from the server.
738                          *
739                          * This works around bugs in the Azure
740                          * and Apple spnego implementations.
741                          *
742                          * See
743                          * https://bugzilla.samba.org/show_bug.cgi?id=11994
744                          */
745                         spnego_state->may_skip_mic_check = true;
746                         break;
747
748                 case SPNEGO_REQUEST_MIC:
749                         if (ta->mechListMIC.length > 0) {
750                                 new_spnego = true;
751                         }
752                         break;
753                 default:
754                         break;
755                 }
756
757                 if (spnego_state->mic_requested) {
758                         if (have_sign) {
759                                 new_spnego = true;
760                         }
761                 }
762
763                 if (have_sign && new_spnego) {
764                         spnego_state->needs_mic_check = true;
765                         spnego_state->needs_mic_sign = true;
766                 }
767         }
768
769         if (ta->mechListMIC.length > 0) {
770                 status = gensec_check_packet(spnego_state->sub_sec_security,
771                                              spnego_state->mech_types.data,
772                                              spnego_state->mech_types.length,
773                                              spnego_state->mech_types.data,
774                                              spnego_state->mech_types.length,
775                                              &ta->mechListMIC);
776                 if (!NT_STATUS_IS_OK(status)) {
777                         DBG_WARNING("failed to verify mechListMIC: %s\n",
778                                     nt_errstr(status));
779                         return status;
780                 }
781                 spnego_state->needs_mic_check = false;
782                 spnego_state->done_mic_check = true;
783         }
784
785         if (spnego_state->needs_mic_sign) {
786                 status = gensec_sign_packet(spnego_state->sub_sec_security,
787                                             out_mem_ctx,
788                                             spnego_state->mech_types.data,
789                                             spnego_state->mech_types.length,
790                                             spnego_state->mech_types.data,
791                                             spnego_state->mech_types.length,
792                                             &mech_list_mic);
793                 if (!NT_STATUS_IS_OK(status)) {
794                         DBG_WARNING("failed to sign mechListMIC: %s\n",
795                                     nt_errstr(status));
796                         return status;
797                 }
798                 spnego_state->needs_mic_sign = false;
799         }
800
801         if (spnego_state->needs_mic_check) {
802                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
803         }
804
805  client_response:
806         if (GENSEC_UPDATE_IS_NTERROR(status)) {
807                 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
808                             spnego_state->sub_sec_security->ops->name,
809                             nt_errstr(status));
810                 return status;
811         }
812
813         if (sub_out.length || mech_list_mic.length) {
814                 /* compose reply */
815                 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
816                 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
817                 spnego_out.negTokenTarg.supportedMech = NULL;
818                 spnego_out.negTokenTarg.responseToken = sub_out;
819                 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
820
821                 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
822                         DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
823                         return NT_STATUS_INVALID_PARAMETER;
824                 }
825
826                 spnego_state->num_targs++;
827                 spnego_state->state_position = SPNEGO_CLIENT_TARG;
828                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
829         } else {
830
831                 /* all done - server has accepted, and we agree */
832                 *out = data_blob_null;
833
834                 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
835                         /* unless of course it did not accept */
836                         DBG_WARNING("gensec_update ok but not accepted\n");
837                         status = NT_STATUS_INVALID_PARAMETER;
838                 }
839
840                 spnego_state->state_position = SPNEGO_DONE;
841         }
842
843         return status;
844 }
845
846 /** create a server negTokenTarg 
847  *
848  * This is the case, where the client is the first one who sends data
849 */
850
851 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
852                                               TALLOC_CTX *out_mem_ctx,
853                                               NTSTATUS nt_status,
854                                               const DATA_BLOB unwrapped_out,
855                                               DATA_BLOB mech_list_mic,
856                                               DATA_BLOB *out)
857 {
858         struct spnego_data spnego_out;
859
860         /* compose reply */
861         spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
862         spnego_out.negTokenTarg.responseToken = unwrapped_out;
863         spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
864         spnego_out.negTokenTarg.supportedMech = NULL;
865
866         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {   
867                 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
868                 if (spnego_state->mic_requested) {
869                         spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
870                         spnego_state->mic_requested = false;
871                 } else {
872                         spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
873                 }
874                 spnego_state->state_position = SPNEGO_SERVER_TARG;
875         } else if (NT_STATUS_IS_OK(nt_status)) {
876                 if (unwrapped_out.data) {
877                         spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
878                 }
879                 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
880                 spnego_state->state_position = SPNEGO_DONE;
881         } else {
882                 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
883                 spnego_out.negTokenTarg.mechListMIC = data_blob_null;
884                 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
885                 spnego_state->state_position = SPNEGO_DONE;
886         }
887
888         if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
889                 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
890                 return NT_STATUS_INVALID_PARAMETER;
891         }
892
893         spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
894         spnego_state->num_targs++;
895
896         return nt_status;
897 }
898
899 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
900                                                   struct spnego_state *spnego_state,
901                                                   struct tevent_context *ev,
902                                                   struct spnego_data *spnego_in,
903                                                   TALLOC_CTX *out_mem_ctx,
904                                                   DATA_BLOB *out)
905 {
906         TALLOC_CTX *frame = talloc_stackframe();
907         DATA_BLOB sub_out = data_blob_null;
908         DATA_BLOB mech_list_mic = data_blob_null;
909         const char * const *mech_types = NULL;
910         size_t all_idx = 0;
911         const struct gensec_security_ops_wrapper *all_sec = NULL;
912         size_t mech_idx = 0;
913         NTSTATUS status;
914         bool ok;
915
916         mech_types = spnego_in->negTokenInit.mechTypes;
917         if (mech_types == NULL) {
918                 TALLOC_FREE(frame);
919                 return NT_STATUS_INVALID_PARAMETER;
920         }
921
922         all_sec = gensec_security_by_oid_list(gensec_security, frame,
923                                               mech_types, GENSEC_OID_SPNEGO);
924         if (all_sec == NULL) {
925                 DBG_WARNING("gensec_security_by_oid_list() failed\n");
926                 TALLOC_FREE(frame);
927                 return NT_STATUS_INVALID_PARAMETER;
928         }
929
930         ok = spnego_write_mech_types(spnego_state, mech_types,
931                                      &spnego_state->mech_types);
932         if (!ok) {
933                 DBG_ERR("Failed to write mechTypes\n");
934                 TALLOC_FREE(frame);
935                 return NT_STATUS_NO_MEMORY;
936         }
937
938         /*
939          * First try the preferred mechs from the client.
940          */
941         for (; mech_types[mech_idx]; mech_idx++) {
942                 const char *cur_mech = mech_types[mech_idx];
943                 const struct gensec_security_ops_wrapper *cur_sec = NULL;
944                 DATA_BLOB sub_in = data_blob_null;
945
946                 for (all_idx = 0; all_sec[all_idx].op; all_idx++) {
947                         if (strcmp(cur_mech, all_sec[all_idx].oid) == 0) {
948                                 cur_sec = &all_sec[all_idx];
949                                 break;
950                         }
951                 }
952
953                 if (cur_sec == NULL) {
954                         continue;
955                 }
956
957                 status = gensec_subcontext_start(spnego_state,
958                                                  gensec_security,
959                                                  &spnego_state->sub_sec_security);
960                 if (!NT_STATUS_IS_OK(status)) {
961                         TALLOC_FREE(frame);
962                         return status;
963                 }
964
965                 /* select the sub context */
966                 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
967                                                   cur_sec->op);
968                 if (!NT_STATUS_IS_OK(status)) {
969                         /*
970                          * Pretend we never started it
971                          */
972                         gensec_spnego_update_sub_abort(spnego_state);
973                         continue;
974                 }
975
976                 if (mech_idx > 0) {
977                         /*
978                          * Indicate the downgrade and request a
979                          * mic.
980                          */
981                         spnego_state->downgraded = true;
982                         spnego_state->mic_requested = true;
983                         /* no optimistic token */
984                         spnego_state->neg_oid = cur_sec->oid;
985                         sub_out = data_blob_null;
986                         status = NT_STATUS_MORE_PROCESSING_REQUIRED;
987                         goto reply;
988                 }
989
990                 /*
991                  * Try the optimistic token from the client
992                  */
993                 sub_in = spnego_in->negTokenInit.mechToken;
994                 status = gensec_update_ev(spnego_state->sub_sec_security,
995                                           frame, ev, sub_in, &sub_out);
996                 if (NT_STATUS_IS_OK(status)) {
997                         spnego_state->sub_sec_ready = true;
998                 }
999                 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1000                     NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1001
1002                         DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1003                                     cur_sec->op->name, nt_errstr(status));
1004
1005                         /*
1006                          * Pretend we never started it
1007                          */
1008                         gensec_spnego_update_sub_abort(spnego_state);
1009                         continue;
1010                 }
1011
1012                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1013                         DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1014                                     cur_sec->op->name, nt_errstr(status));
1015                         goto reply;
1016                 }
1017
1018                 spnego_state->neg_oid = cur_sec->oid;
1019                 goto reply; /* OK or MORE PROCESSING */
1020         }
1021
1022         DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1023         status = NT_STATUS_INVALID_PARAMETER;
1024
1025  reply:
1026         if (spnego_state->simulate_w2k) {
1027                 /*
1028                  * Windows 2000 returns the unwrapped token
1029                  * also in the mech_list_mic field.
1030                  *
1031                  * In order to verify our client code,
1032                  * we need a way to have a server with this
1033                  * broken behaviour
1034                  */
1035                 mech_list_mic = sub_out;
1036         }
1037
1038         status = gensec_spnego_server_response(spnego_state,
1039                                                out_mem_ctx,
1040                                                status,
1041                                                sub_out,
1042                                                mech_list_mic,
1043                                                out);
1044         TALLOC_FREE(frame);
1045         return status;
1046 }
1047
1048 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1049                                                   struct spnego_state *spnego_state,
1050                                                   struct tevent_context *ev,
1051                                                   struct spnego_data *spnego_in,
1052                                                   TALLOC_CTX *out_mem_ctx,
1053                                                   DATA_BLOB *out)
1054 {
1055         const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1056         DATA_BLOB sub_in = ta->responseToken;
1057         DATA_BLOB mech_list_mic = data_blob_null;
1058         DATA_BLOB sub_out = data_blob_null;
1059         NTSTATUS status;
1060         bool have_sign = true;
1061         bool new_spnego = false;
1062
1063         spnego_state->num_targs++;
1064
1065         if (spnego_state->sub_sec_security == NULL) {
1066                 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1067                 return NT_STATUS_INVALID_PARAMETER;
1068         }
1069
1070         if (spnego_state->needs_mic_check) {
1071                 if (ta->responseToken.length != 0) {
1072                         DBG_WARNING("non empty response token not expected\n");
1073                         return NT_STATUS_INVALID_PARAMETER;
1074                 }
1075
1076                 status = gensec_check_packet(spnego_state->sub_sec_security,
1077                                              spnego_state->mech_types.data,
1078                                              spnego_state->mech_types.length,
1079                                              spnego_state->mech_types.data,
1080                                              spnego_state->mech_types.length,
1081                                              &ta->mechListMIC);
1082                 if (!NT_STATUS_IS_OK(status)) {
1083                         DBG_WARNING("failed to verify mechListMIC: %s\n",
1084                                     nt_errstr(status));
1085                         goto server_response;
1086                 }
1087
1088                 spnego_state->needs_mic_check = false;
1089                 spnego_state->done_mic_check = true;
1090                 goto server_response;
1091         }
1092
1093         if (!spnego_state->sub_sec_ready) {
1094                 status = gensec_update_ev(spnego_state->sub_sec_security,
1095                                           out_mem_ctx, ev,
1096                                           sub_in, &sub_out);
1097                 if (NT_STATUS_IS_OK(status)) {
1098                         spnego_state->sub_sec_ready = true;
1099                 }
1100                 if (!NT_STATUS_IS_OK(status)) {
1101                         goto server_response;
1102                 }
1103         } else {
1104                 status = NT_STATUS_OK;
1105         }
1106
1107         have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1108                                         GENSEC_FEATURE_SIGN);
1109         if (spnego_state->simulate_w2k) {
1110                 have_sign = false;
1111         }
1112         new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1113                                          GENSEC_FEATURE_NEW_SPNEGO);
1114         if (ta->mechListMIC.length > 0) {
1115                 new_spnego = true;
1116         }
1117
1118         if (have_sign && new_spnego) {
1119                 spnego_state->needs_mic_check = true;
1120                 spnego_state->needs_mic_sign = true;
1121         }
1122
1123         if (have_sign && ta->mechListMIC.length > 0) {
1124                 status = gensec_check_packet(spnego_state->sub_sec_security,
1125                                              spnego_state->mech_types.data,
1126                                              spnego_state->mech_types.length,
1127                                              spnego_state->mech_types.data,
1128                                              spnego_state->mech_types.length,
1129                                              &ta->mechListMIC);
1130                 if (!NT_STATUS_IS_OK(status)) {
1131                         DBG_WARNING("failed to verify mechListMIC: %s\n",
1132                                     nt_errstr(status));
1133                         goto server_response;
1134                 }
1135
1136                 spnego_state->needs_mic_check = false;
1137                 spnego_state->done_mic_check = true;
1138         }
1139
1140         if (spnego_state->needs_mic_sign) {
1141                 status = gensec_sign_packet(spnego_state->sub_sec_security,
1142                                             out_mem_ctx,
1143                                             spnego_state->mech_types.data,
1144                                             spnego_state->mech_types.length,
1145                                             spnego_state->mech_types.data,
1146                                             spnego_state->mech_types.length,
1147                                             &mech_list_mic);
1148                 if (!NT_STATUS_IS_OK(status)) {
1149                         DBG_WARNING("failed to sign mechListMIC: %s\n",
1150                                     nt_errstr(status));
1151                         return status;
1152                 }
1153                 spnego_state->needs_mic_sign = false;
1154         }
1155
1156         if (spnego_state->needs_mic_check) {
1157                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1158         }
1159
1160  server_response:
1161         return gensec_spnego_server_response(spnego_state,
1162                                              out_mem_ctx,
1163                                              status,
1164                                              sub_out,
1165                                              mech_list_mic,
1166                                              out);
1167 }
1168
1169 struct gensec_spnego_update_state {
1170         struct tevent_context *ev;
1171         struct gensec_security *gensec;
1172         struct spnego_state *spnego;
1173
1174         DATA_BLOB full_in;
1175         struct spnego_data _spnego_in;
1176         struct spnego_data *spnego_in;
1177
1178         struct {
1179                 bool needed;
1180                 DATA_BLOB in;
1181                 NTSTATUS status;
1182                 DATA_BLOB out;
1183         } sub;
1184
1185         NTSTATUS status;
1186         DATA_BLOB out;
1187 };
1188
1189 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1190                                          enum tevent_req_state req_state)
1191 {
1192         struct gensec_spnego_update_state *state =
1193                 tevent_req_data(req,
1194                 struct gensec_spnego_update_state);
1195
1196         switch (req_state) {
1197         case TEVENT_REQ_USER_ERROR:
1198         case TEVENT_REQ_TIMED_OUT:
1199         case TEVENT_REQ_NO_MEMORY:
1200                 /*
1201                  * A fatal error, further updates are not allowed.
1202                  */
1203                 state->spnego->state_position = SPNEGO_DONE;
1204                 break;
1205         default:
1206                 break;
1207         }
1208 }
1209
1210 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1211                                         const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1212                                         DATA_BLOB *full_in);
1213 static void gensec_spnego_update_pre(struct tevent_req *req);
1214 static void gensec_spnego_update_done(struct tevent_req *subreq);
1215 static void gensec_spnego_update_post(struct tevent_req *req);
1216 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1217                                          TALLOC_CTX *out_mem_ctx,
1218                                          DATA_BLOB *_out);
1219
1220 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1221                                                     struct tevent_context *ev,
1222                                                     struct gensec_security *gensec_security,
1223                                                     const DATA_BLOB in)
1224 {
1225         struct spnego_state *spnego_state =
1226                 talloc_get_type_abort(gensec_security->private_data,
1227                 struct spnego_state);
1228         struct tevent_req *req = NULL;
1229         struct gensec_spnego_update_state *state = NULL;
1230         NTSTATUS status;
1231         ssize_t len;
1232
1233         req = tevent_req_create(mem_ctx, &state,
1234                                 struct gensec_spnego_update_state);
1235         if (req == NULL) {
1236                 return NULL;
1237         }
1238         state->ev = ev;
1239         state->gensec = gensec_security;
1240         state->spnego = spnego_state;
1241         tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1242
1243         if (spnego_state->out_frag.length > 0) {
1244                 if (in.length > 0) {
1245                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1246                         return tevent_req_post(req, ev);
1247                 }
1248
1249                 status = gensec_spnego_update_out(gensec_security,
1250                                                   state, &state->out);
1251                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1252                         tevent_req_nterror(req, status);
1253                         return tevent_req_post(req, ev);
1254                 }
1255
1256                 state->status = status;
1257                 tevent_req_done(req);
1258                 return tevent_req_post(req, ev);
1259         }
1260
1261         status = gensec_spnego_update_in(gensec_security, in,
1262                                          state, &state->full_in);
1263         state->status = status;
1264         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1265                 tevent_req_done(req);
1266                 return tevent_req_post(req, ev);
1267         }
1268         if (tevent_req_nterror(req, status)) {
1269                 return tevent_req_post(req, ev);
1270         }
1271
1272         /* Check if we got a valid SPNEGO blob... */
1273
1274         switch (spnego_state->state_position) {
1275         case SPNEGO_FALLBACK:
1276                 break;
1277
1278         case SPNEGO_CLIENT_TARG:
1279         case SPNEGO_SERVER_TARG:
1280                 if (state->full_in.length == 0) {
1281                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1282                         return tevent_req_post(req, ev);
1283                 }
1284
1285                 /* fall through */
1286         case SPNEGO_CLIENT_START:
1287         case SPNEGO_SERVER_START:
1288
1289                 if (state->full_in.length == 0) {
1290                         /* create_negTokenInit later */
1291                         break;
1292                 }
1293
1294                 len = spnego_read_data(state,
1295                                        state->full_in,
1296                                        &state->_spnego_in);
1297                 if (len == -1) {
1298                         if (spnego_state->state_position != SPNEGO_SERVER_START) {
1299                                 DEBUG(1, ("Invalid SPNEGO request:\n"));
1300                                 dump_data(1, state->full_in.data,
1301                                           state->full_in.length);
1302                                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1303                                 return tevent_req_post(req, ev);
1304                         }
1305
1306                         /*
1307                          * This is the 'fallback' case, where we don't get
1308                          * SPNEGO, and have to try all the other options (and
1309                          * hope they all have a magic string they check)
1310                          */
1311                         status = gensec_spnego_server_try_fallback(gensec_security,
1312                                                                    spnego_state,
1313                                                                    state,
1314                                                                    state->full_in);
1315                         if (tevent_req_nterror(req, status)) {
1316                                 return tevent_req_post(req, ev);
1317                         }
1318
1319                         /*
1320                          * We'll continue with SPNEGO_FALLBACK below...
1321                          */
1322                         break;
1323                 }
1324                 state->spnego_in = &state->_spnego_in;
1325
1326                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1327                 if (state->spnego_in->type != spnego_state->expected_packet) {
1328                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1329                                   state->spnego_in->type,
1330                                   spnego_state->expected_packet));
1331                         dump_data(1, state->full_in.data,
1332                                   state->full_in.length);
1333                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1334                         return tevent_req_post(req, ev);
1335                 }
1336
1337                 break;
1338
1339         default:
1340                 smb_panic(__location__);
1341                 return NULL;
1342         }
1343
1344         gensec_spnego_update_pre(req);
1345         if (!tevent_req_is_in_progress(req)) {
1346                 return tevent_req_post(req, ev);
1347         }
1348
1349         if (state->sub.needed) {
1350                 struct tevent_req *subreq = NULL;
1351
1352                 /*
1353                  * We may need one more roundtrip...
1354                  */
1355                 subreq = gensec_update_send(state, state->ev,
1356                                             spnego_state->sub_sec_security,
1357                                             state->sub.in);
1358                 if (tevent_req_nomem(subreq, req)) {
1359                         return tevent_req_post(req, ev);
1360                 }
1361                 tevent_req_set_callback(subreq,
1362                                         gensec_spnego_update_done,
1363                                         req);
1364                 state->sub.needed = false;
1365                 return req;
1366         }
1367
1368         gensec_spnego_update_post(req);
1369         if (!tevent_req_is_in_progress(req)) {
1370                 return tevent_req_post(req, ev);
1371         }
1372
1373         return req;
1374 }
1375
1376 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1377                                         const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1378                                         DATA_BLOB *full_in)
1379 {
1380         struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1381         size_t expected;
1382         bool ok;
1383
1384         *full_in = data_blob_null;
1385
1386         switch (spnego_state->state_position) {
1387         case SPNEGO_FALLBACK:
1388                 *full_in = in;
1389                 spnego_state->in_needed = 0;
1390                 return NT_STATUS_OK;
1391
1392         case SPNEGO_CLIENT_START:
1393         case SPNEGO_CLIENT_TARG:
1394         case SPNEGO_SERVER_START:
1395         case SPNEGO_SERVER_TARG:
1396                 break;
1397
1398         case SPNEGO_DONE:
1399         default:
1400                 return NT_STATUS_INVALID_PARAMETER;
1401         }
1402
1403         if (spnego_state->in_needed == 0) {
1404                 size_t size = 0;
1405                 int ret;
1406
1407                 /*
1408                  * try to work out the size of the full
1409                  * input token, it might be fragmented
1410                  */
1411                 ret = asn1_peek_full_tag(in,  ASN1_APPLICATION(0), &size);
1412                 if ((ret != 0) && (ret != EAGAIN)) {
1413                         ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1414                 }
1415
1416                 if ((ret == 0) || (ret == EAGAIN)) {
1417                         spnego_state->in_needed = size;
1418                 } else {
1419                         /*
1420                          * If it is not an asn1 message
1421                          * just call the next layer.
1422                          */
1423                         spnego_state->in_needed = in.length;
1424                 }
1425         }
1426
1427         if (spnego_state->in_needed > UINT16_MAX) {
1428                 /*
1429                  * limit the incoming message to 0xFFFF
1430                  * to avoid DoS attacks.
1431                  */
1432                 return NT_STATUS_INVALID_BUFFER_SIZE;
1433         }
1434
1435         if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1436                 /*
1437                  * If we reach this, we know we got at least
1438                  * part of an asn1 message, getting 0 means
1439                  * the remote peer wants us to spin.
1440                  */
1441                 return NT_STATUS_INVALID_PARAMETER;
1442         }
1443
1444         expected = spnego_state->in_needed - spnego_state->in_frag.length;
1445         if (in.length > expected) {
1446                 /*
1447                  * we got more than expected
1448                  */
1449                 return NT_STATUS_INVALID_PARAMETER;
1450         }
1451
1452         if (in.length == spnego_state->in_needed) {
1453                 /*
1454                  * if the in.length contains the full blob
1455                  * we are done.
1456                  *
1457                  * Note: this implies spnego_state->in_frag.length == 0,
1458                  *       but we do not need to check this explicitly
1459                  *       because we already know that we did not get
1460                  *       more than expected.
1461                  */
1462                 *full_in = in;
1463                 spnego_state->in_needed = 0;
1464                 return NT_STATUS_OK;
1465         }
1466
1467         ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1468                               in.data, in.length);
1469         if (!ok) {
1470                 return NT_STATUS_NO_MEMORY;
1471         }
1472
1473         if (spnego_state->in_needed > spnego_state->in_frag.length) {
1474                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1475         }
1476
1477         *full_in = spnego_state->in_frag;
1478         talloc_steal(mem_ctx, full_in->data);
1479         spnego_state->in_frag = data_blob_null;
1480         spnego_state->in_needed = 0;
1481         return NT_STATUS_OK;
1482 }
1483
1484 static void gensec_spnego_update_pre(struct tevent_req *req)
1485 {
1486         struct gensec_spnego_update_state *state =
1487                 tevent_req_data(req,
1488                 struct gensec_spnego_update_state);
1489         struct gensec_security *gensec_security = state->gensec;
1490         struct spnego_state *spnego_state = state->spnego;
1491         struct tevent_context *ev = state->ev;
1492         NTSTATUS status;
1493
1494         state->sub.needed = false;
1495         state->sub.in = data_blob_null;
1496         state->sub.status = NT_STATUS_INTERNAL_ERROR;
1497         state->sub.out = data_blob_null;
1498
1499         if (spnego_state->state_position == SPNEGO_FALLBACK) {
1500                 state->sub.in = state->full_in;
1501                 state->full_in = data_blob_null;
1502                 state->sub.needed = true;
1503                 return;
1504         }
1505
1506         switch (spnego_state->state_position) {
1507         case SPNEGO_CLIENT_START:
1508                 if (state->spnego_in == NULL) {
1509                         /* client to produce negTokenInit */
1510                         status = gensec_spnego_create_negTokenInit(gensec_security,
1511                                                         spnego_state, state, ev,
1512                                                         &spnego_state->out_frag);
1513                         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1514                                 tevent_req_nterror(req, status);
1515                                 return;
1516                         }
1517                         break;
1518                 }
1519
1520                 status = gensec_spnego_client_negTokenInit(gensec_security,
1521                                                         spnego_state, ev,
1522                                                         state->spnego_in, state,
1523                                                         &spnego_state->out_frag);
1524                 break;
1525
1526         case SPNEGO_CLIENT_TARG:
1527                 status = gensec_spnego_client_negTokenTarg(gensec_security,
1528                                                         spnego_state, ev,
1529                                                         state->spnego_in, state,
1530                                                         &spnego_state->out_frag);
1531                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1532                         tevent_req_nterror(req, status);
1533                         return;
1534                 }
1535                 break;
1536
1537         case SPNEGO_SERVER_START:
1538                 if (state->spnego_in == NULL) {
1539                         /* server to produce negTokenInit */
1540                         status = gensec_spnego_create_negTokenInit(gensec_security,
1541                                                         spnego_state, state, ev,
1542                                                         &spnego_state->out_frag);
1543                         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1544                                 tevent_req_nterror(req, status);
1545                                 return;
1546                         }
1547                         break;
1548                 }
1549
1550                 status = gensec_spnego_server_negTokenInit(gensec_security,
1551                                                         spnego_state, ev,
1552                                                         state->spnego_in, state,
1553                                                         &spnego_state->out_frag);
1554                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1555                         tevent_req_nterror(req, status);
1556                         return;
1557                 }
1558                 break;
1559
1560         case SPNEGO_SERVER_TARG:
1561                 status = gensec_spnego_server_negTokenTarg(gensec_security,
1562                                                         spnego_state, ev,
1563                                                         state->spnego_in, state,
1564                                                         &spnego_state->out_frag);
1565                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1566                         tevent_req_nterror(req, status);
1567                         return;
1568                 }
1569                 break;
1570
1571         default:
1572                 smb_panic(__location__);
1573                 return;
1574         }
1575
1576         spnego_state->out_status = status;
1577 }
1578
1579 static void gensec_spnego_update_done(struct tevent_req *subreq)
1580 {
1581         struct tevent_req *req =
1582                 tevent_req_callback_data(subreq,
1583                 struct tevent_req);
1584         struct gensec_spnego_update_state *state =
1585                 tevent_req_data(req,
1586                 struct gensec_spnego_update_state);
1587         struct spnego_state *spnego_state = state->spnego;
1588
1589         state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1590         TALLOC_FREE(subreq);
1591         if (NT_STATUS_IS_OK(state->sub.status)) {
1592                 spnego_state->sub_sec_ready = true;
1593         }
1594
1595         gensec_spnego_update_post(req);
1596 }
1597
1598 static void gensec_spnego_update_post(struct tevent_req *req)
1599 {
1600         struct gensec_spnego_update_state *state =
1601                 tevent_req_data(req,
1602                 struct gensec_spnego_update_state);
1603         struct spnego_state *spnego_state = state->spnego;
1604         NTSTATUS status;
1605
1606         state->sub.in = data_blob_null;
1607         state->sub.needed = false;
1608
1609         if (spnego_state->state_position == SPNEGO_FALLBACK) {
1610                 status = state->sub.status;
1611                 spnego_state->out_frag = state->sub.out;
1612                 talloc_steal(spnego_state, spnego_state->out_frag.data);
1613                 state->sub.out = data_blob_null;
1614                 goto respond;
1615         }
1616
1617         /*
1618          * For now just handle the sync processing done
1619          * in gensec_spnego_update_pre()
1620          */
1621         status = spnego_state->out_status;
1622
1623         if (NT_STATUS_IS_OK(status)) {
1624                 bool reset_full = true;
1625
1626                 reset_full = !spnego_state->done_mic_check;
1627
1628                 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1629                                                  reset_full);
1630                 if (tevent_req_nterror(req, status)) {
1631                         return;
1632                 }
1633         }
1634
1635 respond:
1636         spnego_state->out_status = status;
1637
1638         status = gensec_spnego_update_out(state->gensec,
1639                                           state, &state->out);
1640         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1641                 tevent_req_nterror(req, status);
1642                 return;
1643         }
1644
1645         state->status = status;
1646         tevent_req_done(req);
1647         return;
1648 }
1649
1650 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1651                                          TALLOC_CTX *out_mem_ctx,
1652                                          DATA_BLOB *_out)
1653 {
1654         struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1655         DATA_BLOB out = data_blob_null;
1656         bool ok;
1657
1658         *_out = data_blob_null;
1659
1660         if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1661                 /*
1662                  * Fast path, we can deliver everything
1663                  */
1664
1665                 *_out = spnego_state->out_frag;
1666                 if (spnego_state->out_frag.length > 0) {
1667                         talloc_steal(out_mem_ctx, _out->data);
1668                         spnego_state->out_frag = data_blob_null;
1669                 }
1670
1671                 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1672                         return spnego_state->out_status;
1673                 }
1674
1675                 /*
1676                  * We're completely done, further updates are not allowed.
1677                  */
1678                 spnego_state->state_position = SPNEGO_DONE;
1679                 return gensec_child_ready(gensec_security,
1680                                           spnego_state->sub_sec_security);
1681         }
1682
1683         out = spnego_state->out_frag;
1684
1685         /*
1686          * copy the remaining bytes
1687          */
1688         spnego_state->out_frag = data_blob_talloc(spnego_state,
1689                                         out.data + spnego_state->out_max_length,
1690                                         out.length - spnego_state->out_max_length);
1691         if (spnego_state->out_frag.data == NULL) {
1692                 return NT_STATUS_NO_MEMORY;
1693         }
1694
1695         /*
1696          * truncate the buffer
1697          */
1698         ok = data_blob_realloc(spnego_state, &out,
1699                                spnego_state->out_max_length);
1700         if (!ok) {
1701                 return NT_STATUS_NO_MEMORY;
1702         }
1703
1704         talloc_steal(out_mem_ctx, out.data);
1705         *_out = out;
1706         return NT_STATUS_MORE_PROCESSING_REQUIRED;
1707 }
1708
1709 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1710                                           TALLOC_CTX *out_mem_ctx,
1711                                           DATA_BLOB *out)
1712 {
1713         struct gensec_spnego_update_state *state =
1714                 tevent_req_data(req,
1715                 struct gensec_spnego_update_state);
1716         NTSTATUS status;
1717
1718         *out = data_blob_null;
1719
1720         if (tevent_req_is_nterror(req, &status)) {
1721                 tevent_req_received(req);
1722                 return status;
1723         }
1724
1725         *out = state->out;
1726         talloc_steal(out_mem_ctx, state->out.data);
1727         status = state->status;
1728         tevent_req_received(req);
1729         return status;
1730 }
1731
1732 static const char *gensec_spnego_oids[] = { 
1733         GENSEC_OID_SPNEGO,
1734         NULL 
1735 };
1736
1737 static const struct gensec_security_ops gensec_spnego_security_ops = {
1738         .name             = "spnego",
1739         .sasl_name        = "GSS-SPNEGO",
1740         .auth_type        = DCERPC_AUTH_TYPE_SPNEGO,
1741         .oid              = gensec_spnego_oids,
1742         .client_start     = gensec_spnego_client_start,
1743         .server_start     = gensec_spnego_server_start,
1744         .update_send      = gensec_spnego_update_send,
1745         .update_recv      = gensec_spnego_update_recv,
1746         .seal_packet      = gensec_child_seal_packet,
1747         .sign_packet      = gensec_child_sign_packet,
1748         .sig_size         = gensec_child_sig_size,
1749         .max_wrapped_size = gensec_child_max_wrapped_size,
1750         .max_input_size   = gensec_child_max_input_size,
1751         .check_packet     = gensec_child_check_packet,
1752         .unseal_packet    = gensec_child_unseal_packet,
1753         .wrap             = gensec_child_wrap,
1754         .unwrap           = gensec_child_unwrap,
1755         .session_key      = gensec_child_session_key,
1756         .session_info     = gensec_child_session_info,
1757         .want_feature     = gensec_child_want_feature,
1758         .have_feature     = gensec_child_have_feature,
1759         .expire_time      = gensec_child_expire_time,
1760         .final_auth_type  = gensec_child_final_auth_type,
1761         .enabled          = true,
1762         .priority         = GENSEC_SPNEGO
1763 };
1764
1765 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1766 {
1767         NTSTATUS ret;
1768         ret = gensec_register(ctx, &gensec_spnego_security_ops);
1769         if (!NT_STATUS_IS_OK(ret)) {
1770                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1771                         gensec_spnego_security_ops.name));
1772                 return ret;
1773         }
1774
1775         return ret;
1776 }