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