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