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