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