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