0e37c3faf21fc72cf237d45938c23c1338489a3f
[sfrench/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
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
1172         DATA_BLOB full_in;
1173         struct spnego_data _spnego_in;
1174         struct spnego_data *spnego_in;
1175
1176         struct {
1177                 bool needed;
1178                 DATA_BLOB in;
1179                 NTSTATUS status;
1180                 DATA_BLOB out;
1181         } sub;
1182
1183         NTSTATUS status;
1184         DATA_BLOB out;
1185 };
1186
1187 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1188                                          enum tevent_req_state req_state)
1189 {
1190         struct gensec_spnego_update_state *state =
1191                 tevent_req_data(req,
1192                 struct gensec_spnego_update_state);
1193
1194         switch (req_state) {
1195         case TEVENT_REQ_USER_ERROR:
1196         case TEVENT_REQ_TIMED_OUT:
1197         case TEVENT_REQ_NO_MEMORY:
1198                 /*
1199                  * A fatal error, further updates are not allowed.
1200                  */
1201                 state->spnego->state_position = SPNEGO_DONE;
1202                 break;
1203         default:
1204                 break;
1205         }
1206 }
1207
1208 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1209                                         const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1210                                         DATA_BLOB *full_in);
1211 static void gensec_spnego_update_pre(struct tevent_req *req);
1212 static void gensec_spnego_update_done(struct tevent_req *subreq);
1213 static void gensec_spnego_update_post(struct tevent_req *req);
1214 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1215                                          TALLOC_CTX *out_mem_ctx,
1216                                          DATA_BLOB *_out);
1217
1218 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1219                                                     struct tevent_context *ev,
1220                                                     struct gensec_security *gensec_security,
1221                                                     const DATA_BLOB in)
1222 {
1223         struct spnego_state *spnego_state =
1224                 talloc_get_type_abort(gensec_security->private_data,
1225                 struct spnego_state);
1226         struct tevent_req *req = NULL;
1227         struct gensec_spnego_update_state *state = NULL;
1228         NTSTATUS status;
1229         ssize_t len;
1230
1231         req = tevent_req_create(mem_ctx, &state,
1232                                 struct gensec_spnego_update_state);
1233         if (req == NULL) {
1234                 return NULL;
1235         }
1236         state->ev = ev;
1237         state->gensec = gensec_security;
1238         state->spnego = spnego_state;
1239         tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1240
1241         if (spnego_state->out_frag.length > 0) {
1242                 if (in.length > 0) {
1243                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1244                         return tevent_req_post(req, ev);
1245                 }
1246
1247                 status = gensec_spnego_update_out(gensec_security,
1248                                                   state, &state->out);
1249                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1250                         tevent_req_nterror(req, status);
1251                         return tevent_req_post(req, ev);
1252                 }
1253
1254                 state->status = status;
1255                 tevent_req_done(req);
1256                 return tevent_req_post(req, ev);
1257         }
1258
1259         status = gensec_spnego_update_in(gensec_security, in,
1260                                          state, &state->full_in);
1261         state->status = status;
1262         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1263                 tevent_req_done(req);
1264                 return tevent_req_post(req, ev);
1265         }
1266         if (tevent_req_nterror(req, status)) {
1267                 return tevent_req_post(req, ev);
1268         }
1269
1270         /* Check if we got a valid SPNEGO blob... */
1271
1272         switch (spnego_state->state_position) {
1273         case SPNEGO_FALLBACK:
1274                 break;
1275
1276         case SPNEGO_CLIENT_TARG:
1277         case SPNEGO_SERVER_TARG:
1278                 if (state->full_in.length == 0) {
1279                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1280                         return tevent_req_post(req, ev);
1281                 }
1282
1283                 /* fall through */
1284         case SPNEGO_CLIENT_START:
1285         case SPNEGO_SERVER_START:
1286
1287                 if (state->full_in.length == 0) {
1288                         /* create_negTokenInit later */
1289                         break;
1290                 }
1291
1292                 len = spnego_read_data(state,
1293                                        state->full_in,
1294                                        &state->_spnego_in);
1295                 if (len == -1) {
1296                         if (spnego_state->state_position != SPNEGO_SERVER_START) {
1297                                 DEBUG(1, ("Invalid SPNEGO request:\n"));
1298                                 dump_data(1, state->full_in.data,
1299                                           state->full_in.length);
1300                                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1301                                 return tevent_req_post(req, ev);
1302                         }
1303
1304                         /*
1305                          * This is the 'fallback' case, where we don't get
1306                          * SPNEGO, and have to try all the other options (and
1307                          * hope they all have a magic string they check)
1308                          */
1309                         status = gensec_spnego_server_try_fallback(gensec_security,
1310                                                                    spnego_state,
1311                                                                    state,
1312                                                                    state->full_in);
1313                         if (tevent_req_nterror(req, status)) {
1314                                 return tevent_req_post(req, ev);
1315                         }
1316
1317                         /*
1318                          * We'll continue with SPNEGO_FALLBACK below...
1319                          */
1320                         break;
1321                 }
1322                 state->spnego_in = &state->_spnego_in;
1323
1324                 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1325                 if (state->spnego_in->type != spnego_state->expected_packet) {
1326                         DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1327                                   state->spnego_in->type,
1328                                   spnego_state->expected_packet));
1329                         dump_data(1, state->full_in.data,
1330                                   state->full_in.length);
1331                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1332                         return tevent_req_post(req, ev);
1333                 }
1334
1335                 break;
1336
1337         default:
1338                 smb_panic(__location__);
1339                 return NULL;
1340         }
1341
1342         gensec_spnego_update_pre(req);
1343         if (!tevent_req_is_in_progress(req)) {
1344                 return tevent_req_post(req, ev);
1345         }
1346
1347         if (state->sub.needed) {
1348                 struct tevent_req *subreq = NULL;
1349
1350                 /*
1351                  * We may need one more roundtrip...
1352                  */
1353                 subreq = gensec_update_send(state, state->ev,
1354                                             spnego_state->sub_sec_security,
1355                                             state->sub.in);
1356                 if (tevent_req_nomem(subreq, req)) {
1357                         return tevent_req_post(req, ev);
1358                 }
1359                 tevent_req_set_callback(subreq,
1360                                         gensec_spnego_update_done,
1361                                         req);
1362                 state->sub.needed = false;
1363                 return req;
1364         }
1365
1366         gensec_spnego_update_post(req);
1367         if (!tevent_req_is_in_progress(req)) {
1368                 return tevent_req_post(req, ev);
1369         }
1370
1371         return req;
1372 }
1373
1374 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1375                                         const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1376                                         DATA_BLOB *full_in)
1377 {
1378         struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1379         size_t expected;
1380         bool ok;
1381
1382         *full_in = data_blob_null;
1383
1384         switch (spnego_state->state_position) {
1385         case SPNEGO_FALLBACK:
1386                 *full_in = in;
1387                 spnego_state->in_needed = 0;
1388                 return NT_STATUS_OK;
1389
1390         case SPNEGO_CLIENT_START:
1391         case SPNEGO_CLIENT_TARG:
1392         case SPNEGO_SERVER_START:
1393         case SPNEGO_SERVER_TARG:
1394                 break;
1395
1396         case SPNEGO_DONE:
1397         default:
1398                 return NT_STATUS_INVALID_PARAMETER;
1399         }
1400
1401         if (spnego_state->in_needed == 0) {
1402                 size_t size = 0;
1403                 int ret;
1404
1405                 /*
1406                  * try to work out the size of the full
1407                  * input token, it might be fragmented
1408                  */
1409                 ret = asn1_peek_full_tag(in,  ASN1_APPLICATION(0), &size);
1410                 if ((ret != 0) && (ret != EAGAIN)) {
1411                         ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1412                 }
1413
1414                 if ((ret == 0) || (ret == EAGAIN)) {
1415                         spnego_state->in_needed = size;
1416                 } else {
1417                         /*
1418                          * If it is not an asn1 message
1419                          * just call the next layer.
1420                          */
1421                         spnego_state->in_needed = in.length;
1422                 }
1423         }
1424
1425         if (spnego_state->in_needed > UINT16_MAX) {
1426                 /*
1427                  * limit the incoming message to 0xFFFF
1428                  * to avoid DoS attacks.
1429                  */
1430                 return NT_STATUS_INVALID_BUFFER_SIZE;
1431         }
1432
1433         if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1434                 /*
1435                  * If we reach this, we know we got at least
1436                  * part of an asn1 message, getting 0 means
1437                  * the remote peer wants us to spin.
1438                  */
1439                 return NT_STATUS_INVALID_PARAMETER;
1440         }
1441
1442         expected = spnego_state->in_needed - spnego_state->in_frag.length;
1443         if (in.length > expected) {
1444                 /*
1445                  * we got more than expected
1446                  */
1447                 return NT_STATUS_INVALID_PARAMETER;
1448         }
1449
1450         if (in.length == spnego_state->in_needed) {
1451                 /*
1452                  * if the in.length contains the full blob
1453                  * we are done.
1454                  *
1455                  * Note: this implies spnego_state->in_frag.length == 0,
1456                  *       but we do not need to check this explicitly
1457                  *       because we already know that we did not get
1458                  *       more than expected.
1459                  */
1460                 *full_in = in;
1461                 spnego_state->in_needed = 0;
1462                 return NT_STATUS_OK;
1463         }
1464
1465         ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1466                               in.data, in.length);
1467         if (!ok) {
1468                 return NT_STATUS_NO_MEMORY;
1469         }
1470
1471         if (spnego_state->in_needed > spnego_state->in_frag.length) {
1472                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1473         }
1474
1475         *full_in = spnego_state->in_frag;
1476         talloc_steal(mem_ctx, full_in->data);
1477         spnego_state->in_frag = data_blob_null;
1478         spnego_state->in_needed = 0;
1479         return NT_STATUS_OK;
1480 }
1481
1482 static void gensec_spnego_update_pre(struct tevent_req *req)
1483 {
1484         struct gensec_spnego_update_state *state =
1485                 tevent_req_data(req,
1486                 struct gensec_spnego_update_state);
1487         struct gensec_security *gensec_security = state->gensec;
1488         struct spnego_state *spnego_state = state->spnego;
1489         struct tevent_context *ev = state->ev;
1490         NTSTATUS status;
1491
1492         state->sub.needed = false;
1493         state->sub.in = data_blob_null;
1494         state->sub.status = NT_STATUS_INTERNAL_ERROR;
1495         state->sub.out = data_blob_null;
1496
1497         if (spnego_state->state_position == SPNEGO_FALLBACK) {
1498                 state->sub.in = state->full_in;
1499                 state->full_in = data_blob_null;
1500                 state->sub.needed = true;
1501                 return;
1502         }
1503
1504         switch (spnego_state->state_position) {
1505         case SPNEGO_CLIENT_START:
1506                 if (state->spnego_in == NULL) {
1507                         /* client to produce negTokenInit */
1508                         status = gensec_spnego_create_negTokenInit(gensec_security,
1509                                                         spnego_state, state, ev,
1510                                                         &spnego_state->out_frag);
1511                         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1512                                 tevent_req_nterror(req, status);
1513                                 return;
1514                         }
1515                         break;
1516                 }
1517
1518                 status = gensec_spnego_client_negTokenInit(gensec_security,
1519                                                         spnego_state, ev,
1520                                                         state->spnego_in, state,
1521                                                         &spnego_state->out_frag);
1522                 break;
1523
1524         case SPNEGO_CLIENT_TARG:
1525                 status = gensec_spnego_client_negTokenTarg(gensec_security,
1526                                                         spnego_state, ev,
1527                                                         state->spnego_in, state,
1528                                                         &spnego_state->out_frag);
1529                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1530                         tevent_req_nterror(req, status);
1531                         return;
1532                 }
1533                 break;
1534
1535         case SPNEGO_SERVER_START:
1536                 if (state->spnego_in == NULL) {
1537                         /* server to produce negTokenInit */
1538                         status = gensec_spnego_create_negTokenInit(gensec_security,
1539                                                         spnego_state, state, ev,
1540                                                         &spnego_state->out_frag);
1541                         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1542                                 tevent_req_nterror(req, status);
1543                                 return;
1544                         }
1545                         break;
1546                 }
1547
1548                 status = gensec_spnego_server_negTokenInit(gensec_security,
1549                                                         spnego_state, ev,
1550                                                         state->spnego_in, state,
1551                                                         &spnego_state->out_frag);
1552                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1553                         tevent_req_nterror(req, status);
1554                         return;
1555                 }
1556                 break;
1557
1558         case SPNEGO_SERVER_TARG:
1559                 status = gensec_spnego_server_negTokenTarg(gensec_security,
1560                                                         spnego_state, ev,
1561                                                         state->spnego_in, state,
1562                                                         &spnego_state->out_frag);
1563                 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1564                         tevent_req_nterror(req, status);
1565                         return;
1566                 }
1567                 break;
1568
1569         default:
1570                 smb_panic(__location__);
1571                 return;
1572         }
1573
1574         spnego_state->out_status = status;
1575 }
1576
1577 static void gensec_spnego_update_done(struct tevent_req *subreq)
1578 {
1579         struct tevent_req *req =
1580                 tevent_req_callback_data(subreq,
1581                 struct tevent_req);
1582         struct gensec_spnego_update_state *state =
1583                 tevent_req_data(req,
1584                 struct gensec_spnego_update_state);
1585         struct spnego_state *spnego_state = state->spnego;
1586
1587         state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1588         TALLOC_FREE(subreq);
1589         if (NT_STATUS_IS_OK(state->sub.status)) {
1590                 spnego_state->sub_sec_ready = true;
1591         }
1592
1593         gensec_spnego_update_post(req);
1594 }
1595
1596 static void gensec_spnego_update_post(struct tevent_req *req)
1597 {
1598         struct gensec_spnego_update_state *state =
1599                 tevent_req_data(req,
1600                 struct gensec_spnego_update_state);
1601         struct spnego_state *spnego_state = state->spnego;
1602         NTSTATUS status;
1603
1604         state->sub.in = data_blob_null;
1605         state->sub.needed = false;
1606
1607         if (spnego_state->state_position == SPNEGO_FALLBACK) {
1608                 status = state->sub.status;
1609                 spnego_state->out_frag = state->sub.out;
1610                 talloc_steal(spnego_state, spnego_state->out_frag.data);
1611                 state->sub.out = data_blob_null;
1612                 goto respond;
1613         }
1614
1615         /*
1616          * For now just handle the sync processing done
1617          * in gensec_spnego_update_pre()
1618          */
1619         status = spnego_state->out_status;
1620
1621         if (NT_STATUS_IS_OK(status)) {
1622                 bool reset_full = true;
1623
1624                 reset_full = !spnego_state->done_mic_check;
1625
1626                 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1627                                                  reset_full);
1628                 if (tevent_req_nterror(req, status)) {
1629                         return;
1630                 }
1631         }
1632
1633 respond:
1634         spnego_state->out_status = status;
1635
1636         status = gensec_spnego_update_out(state->gensec,
1637                                           state, &state->out);
1638         if (GENSEC_UPDATE_IS_NTERROR(status)) {
1639                 tevent_req_nterror(req, status);
1640                 return;
1641         }
1642
1643         state->status = status;
1644         tevent_req_done(req);
1645         return;
1646 }
1647
1648 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1649                                          TALLOC_CTX *out_mem_ctx,
1650                                          DATA_BLOB *_out)
1651 {
1652         struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1653         DATA_BLOB out = data_blob_null;
1654         bool ok;
1655
1656         *_out = data_blob_null;
1657
1658         if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1659                 /*
1660                  * Fast path, we can deliver everything
1661                  */
1662
1663                 *_out = spnego_state->out_frag;
1664                 if (spnego_state->out_frag.length > 0) {
1665                         talloc_steal(out_mem_ctx, _out->data);
1666                         spnego_state->out_frag = data_blob_null;
1667                 }
1668
1669                 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1670                         return spnego_state->out_status;
1671                 }
1672
1673                 /*
1674                  * We're completely done, further updates are not allowed.
1675                  */
1676                 spnego_state->state_position = SPNEGO_DONE;
1677                 return gensec_child_ready(gensec_security,
1678                                           spnego_state->sub_sec_security);
1679         }
1680
1681         out = spnego_state->out_frag;
1682
1683         /*
1684          * copy the remaining bytes
1685          */
1686         spnego_state->out_frag = data_blob_talloc(spnego_state,
1687                                         out.data + spnego_state->out_max_length,
1688                                         out.length - spnego_state->out_max_length);
1689         if (spnego_state->out_frag.data == NULL) {
1690                 return NT_STATUS_NO_MEMORY;
1691         }
1692
1693         /*
1694          * truncate the buffer
1695          */
1696         ok = data_blob_realloc(spnego_state, &out,
1697                                spnego_state->out_max_length);
1698         if (!ok) {
1699                 return NT_STATUS_NO_MEMORY;
1700         }
1701
1702         talloc_steal(out_mem_ctx, out.data);
1703         *_out = out;
1704         return NT_STATUS_MORE_PROCESSING_REQUIRED;
1705 }
1706
1707 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1708                                           TALLOC_CTX *out_mem_ctx,
1709                                           DATA_BLOB *out)
1710 {
1711         struct gensec_spnego_update_state *state =
1712                 tevent_req_data(req,
1713                 struct gensec_spnego_update_state);
1714         NTSTATUS status;
1715
1716         *out = data_blob_null;
1717
1718         if (tevent_req_is_nterror(req, &status)) {
1719                 tevent_req_received(req);
1720                 return status;
1721         }
1722
1723         *out = state->out;
1724         talloc_steal(out_mem_ctx, state->out.data);
1725         status = state->status;
1726         tevent_req_received(req);
1727         return status;
1728 }
1729
1730 static const char *gensec_spnego_oids[] = { 
1731         GENSEC_OID_SPNEGO,
1732         NULL 
1733 };
1734
1735 static const struct gensec_security_ops gensec_spnego_security_ops = {
1736         .name             = "spnego",
1737         .sasl_name        = "GSS-SPNEGO",
1738         .auth_type        = DCERPC_AUTH_TYPE_SPNEGO,
1739         .oid              = gensec_spnego_oids,
1740         .client_start     = gensec_spnego_client_start,
1741         .server_start     = gensec_spnego_server_start,
1742         .update_send      = gensec_spnego_update_send,
1743         .update_recv      = gensec_spnego_update_recv,
1744         .seal_packet      = gensec_child_seal_packet,
1745         .sign_packet      = gensec_child_sign_packet,
1746         .sig_size         = gensec_child_sig_size,
1747         .max_wrapped_size = gensec_child_max_wrapped_size,
1748         .max_input_size   = gensec_child_max_input_size,
1749         .check_packet     = gensec_child_check_packet,
1750         .unseal_packet    = gensec_child_unseal_packet,
1751         .wrap             = gensec_child_wrap,
1752         .unwrap           = gensec_child_unwrap,
1753         .session_key      = gensec_child_session_key,
1754         .session_info     = gensec_child_session_info,
1755         .want_feature     = gensec_child_want_feature,
1756         .have_feature     = gensec_child_have_feature,
1757         .expire_time      = gensec_child_expire_time,
1758         .final_auth_type  = gensec_child_final_auth_type,
1759         .enabled          = true,
1760         .priority         = GENSEC_SPNEGO
1761 };
1762
1763 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1764 {
1765         NTSTATUS ret;
1766         ret = gensec_register(ctx, &gensec_spnego_security_ops);
1767         if (!NT_STATUS_IS_OK(ret)) {
1768                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1769                         gensec_spnego_security_ops.name));
1770                 return ret;
1771         }
1772
1773         return ret;
1774 }