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