auth/spnego: set state_position = SPNEGO_DONE in gensec_spnego_update_cleanup()
[Samba.git] / auth / gensec / spnego.c
blobcb2c227cd604f56973117bfa5edb1ace7b919271
1 /*
2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
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
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.
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.
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/>.
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"
37 #undef strcasecmp
39 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
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
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;
56 const char *neg_oid;
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;
67 bool simulate_w2k;
70 * The following is used to implement
71 * the update token fragmentation
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;
81 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
83 struct spnego_state *spnego_state;
85 spnego_state = talloc_zero(gensec_security, struct spnego_state);
86 if (!spnego_state) {
87 return NT_STATUS_NO_MEMORY;
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;
98 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
99 "spnego", "simulate_w2k", false);
101 gensec_security->private_data = spnego_state;
102 return NT_STATUS_OK;
105 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
107 struct spnego_state *spnego_state;
109 spnego_state = talloc_zero(gensec_security, struct spnego_state);
110 if (!spnego_state) {
111 return NT_STATUS_NO_MEMORY;
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;
122 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
123 "spnego", "simulate_w2k", false);
125 gensec_security->private_data = spnego_state;
126 return NT_STATUS_OK;
129 /** Fallback to another GENSEC mechanism, based on magic strings
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)
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)
142 int i,j;
143 const struct gensec_security_ops **all_ops;
145 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
147 for (i=0; all_ops && all_ops[i]; i++) {
148 bool is_spnego;
149 NTSTATUS nt_status;
151 if (gensec_security != NULL &&
152 !gensec_security_ops_enabled(all_ops[i], gensec_security))
153 continue;
155 if (!all_ops[i]->oid) {
156 continue;
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;
165 if (is_spnego) {
166 continue;
169 if (!all_ops[i]->magic) {
170 continue;
173 nt_status = all_ops[i]->magic(gensec_security, &in);
174 if (!NT_STATUS_IS_OK(nt_status)) {
175 continue;
178 spnego_state->state_position = SPNEGO_FALLBACK;
180 nt_status = gensec_subcontext_start(spnego_state,
181 gensec_security,
182 &spnego_state->sub_sec_security);
184 if (!NT_STATUS_IS_OK(nt_status)) {
185 return nt_status;
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;
193 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
194 out_mem_ctx, ev, in, out);
195 return nt_status;
197 DEBUG(1, ("Failed to parse SPNEGO request\n"));
198 return NT_STATUS_INVALID_PARAMETER;
202 Parse the netTokenInit, either from the client, to the server, or
203 from the server to the client.
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)
213 int i;
214 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
215 bool ok;
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);
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;
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;
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;
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;
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;
260 * Indicate the downgrade and request a
261 * mic.
263 spnego_state->downgraded = true;
264 spnego_state->mic_requested = true;
265 break;
268 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
269 out_mem_ctx,
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) */
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;
284 spnego_state->neg_oid = all_sec[i].oid;
285 break;
287 if (spnego_state->sub_sec_security) {
288 break;
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;
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 */
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;
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;
319 spnego_state->neg_oid = all_sec[i].oid;
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,
325 data_blob_null,
326 unwrapped_out);
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;
342 if (all_sec[i+1].op != NULL) {
343 next = all_sec[i+1].op->name;
344 dbg_level = DBGLVL_NOTICE;
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)
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;
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)));
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;
372 break;
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 */
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;
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;
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;
404 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
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 */
413 return nt_status;
416 /** create a negTokenInit
418 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
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)
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;
432 mechTypes = gensec_security_oids(gensec_security,
433 out_mem_ctx, GENSEC_OID_SPNEGO);
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;
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;
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;
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,
464 data_blob_null,
465 &unwrapped_out);
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;
473 if (all_sec[i+1].op != NULL) {
474 next = all_sec[i+1].op->name;
475 dbg_level = DBGLVL_NOTICE;
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)
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;
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) */
499 continue;
503 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
505 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
506 &all_sec[i]);
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;
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;
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;
528 spnego_out.negTokenInit.mechToken = unwrapped_out;
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;
535 /* set next state */
536 spnego_state->neg_oid = all_sec[i].oid;
538 if (NT_STATUS_IS_OK(nt_status)) {
539 spnego_state->no_response_expected = true;
542 return NT_STATUS_MORE_PROCESSING_REQUIRED;
544 talloc_free(spnego_state->sub_sec_security);
545 spnego_state->sub_sec_security = NULL;
547 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
548 return nt_status;
552 /** create a server negTokenTarg
554 * This is the case, where the client is the first one who sends data
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)
564 struct spnego_data spnego_out;
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;
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;
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;
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;
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;
599 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
600 spnego_state->num_targs++;
602 return nt_status;
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)
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;
616 ssize_t len;
618 *out = data_blob_null;
620 if (!out_mem_ctx) {
621 out_mem_ctx = spnego_state;
624 /* and switch into the state machine */
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:
632 NTSTATUS nt_status;
633 if (in.length) {
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);
640 /* client sent NegTargetInit, we send NegTokenTarg */
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;
651 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
652 spnego_state,
653 out_mem_ctx,
655 spnego.negTokenInit.mechTypes,
656 spnego.negTokenInit.mechToken,
657 &unwrapped_out);
659 if (spnego_state->simulate_w2k) {
661 * Windows 2000 returns the unwrapped token
662 * also in the mech_list_mic field.
664 * In order to verify our client code,
665 * we need a way to have a server with this
666 * broken behaviour
668 mech_list_mic = unwrapped_out;
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);
678 spnego_free_data(&spnego);
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;
690 case SPNEGO_CLIENT_START:
692 /* The server offers a list of mechanisms */
694 const char *my_mechs[] = {NULL, NULL};
695 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
696 bool ok;
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;
707 len = spnego_read_data(gensec_security, in, &spnego);
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;
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;
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);
732 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
733 spnego_state,
734 out_mem_ctx,
736 spnego.negTokenInit.mechTypes,
737 spnego.negTokenInit.mechToken,
738 &unwrapped_out);
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;
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;
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;
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;
767 /* set next state */
768 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
769 spnego_state->state_position = SPNEGO_CLIENT_TARG;
771 if (NT_STATUS_IS_OK(nt_status)) {
772 spnego_state->no_response_expected = true;
775 spnego_free_data(&spnego);
776 return NT_STATUS_MORE_PROCESSING_REQUIRED;
778 case SPNEGO_SERVER_TARG:
780 NTSTATUS nt_status;
781 bool have_sign = true;
782 bool new_spnego = false;
784 if (!in.length) {
785 return NT_STATUS_INVALID_PARAMETER;
788 len = spnego_read_data(gensec_security, in, &spnego);
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;
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;
805 spnego_state->num_targs++;
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;
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;
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)));
833 goto server_response;
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;
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;
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;
855 if (have_sign && new_spnego) {
856 spnego_state->needs_mic_check = true;
857 spnego_state->needs_mic_sign = true;
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;
873 spnego_state->needs_mic_check = false;
874 spnego_state->done_mic_check = true;
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;
890 spnego_state->needs_mic_sign = false;
893 if (spnego_state->needs_mic_check) {
894 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
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);
905 spnego_free_data(&spnego);
907 return nt_status;
909 case SPNEGO_CLIENT_TARG:
911 NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
913 if (!in.length) {
914 return NT_STATUS_INVALID_PARAMETER;
917 len = spnego_read_data(gensec_security, in, &spnego);
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;
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;
934 spnego_state->num_targs++;
936 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
937 spnego_free_data(&spnego);
938 return NT_STATUS_LOGON_FAILURE;
941 if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
942 spnego_state->mic_requested = true;
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;
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;
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;
979 if (spnego.negTokenTarg.mechListMIC.length > 0) {
980 DATA_BLOB *m = &spnego.negTokenTarg.mechListMIC;
981 const DATA_BLOB *r = &spnego.negTokenTarg.responseToken;
984 * Windows 2000 has a bug, it repeats the
985 * responseToken in the mechListMIC field.
987 if (m->length == r->length) {
988 int cmp;
990 cmp = memcmp(m->data, r->data, m->length);
991 if (cmp == 0) {
992 data_blob_free(m);
997 if (spnego.negTokenTarg.mechListMIC.length > 0) {
998 if (spnego_state->no_response_expected) {
999 spnego_state->needs_mic_check = true;
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;
1010 if (spnego.negTokenTarg.mechListMIC.length == 0
1011 && spnego_state->may_skip_mic_check) {
1013 * In this case we don't require
1014 * a mechListMIC from the server.
1016 * This works around bugs in the Azure
1017 * and Apple spnego implementations.
1019 * See
1020 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1022 spnego_state->needs_mic_check = false;
1023 nt_status = NT_STATUS_OK;
1024 goto client_response;
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;
1039 spnego_state->needs_mic_check = false;
1040 spnego_state->done_mic_check = true;
1041 goto client_response;
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;
1053 spnego_state->no_response_expected = true;
1054 } else {
1055 nt_status = NT_STATUS_OK;
1058 if (spnego_state->no_response_expected &&
1059 !spnego_state->done_mic_check)
1061 bool have_sign = true;
1062 bool new_spnego = false;
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;
1069 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1070 GENSEC_FEATURE_NEW_SPNEGO);
1072 switch (spnego.negTokenTarg.negResult) {
1073 case SPNEGO_ACCEPT_COMPLETED:
1074 case SPNEGO_NONE_RESULT:
1075 if (spnego_state->num_targs == 1) {
1077 * the first exchange doesn't require
1078 * verification
1080 new_spnego = false;
1083 break;
1085 case SPNEGO_ACCEPT_INCOMPLETE:
1086 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1087 new_spnego = true;
1088 break;
1091 if (spnego_state->downgraded) {
1093 * A downgrade should be protected if
1094 * supported
1096 break;
1100 * The caller may just asked for
1101 * GENSEC_FEATURE_SESSION_KEY, this
1102 * is only reflected in the want_features.
1104 * As it will imply
1105 * gensec_have_features(GENSEC_FEATURE_SIGN)
1106 * to return true.
1108 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
1109 break;
1111 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
1112 break;
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.
1120 * In this case we don't require
1121 * a mechListMIC from the server.
1123 * This works around bugs in the Azure
1124 * and Apple spnego implementations.
1126 * See
1127 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1129 spnego_state->may_skip_mic_check = true;
1130 break;
1132 case SPNEGO_REQUEST_MIC:
1133 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1134 new_spnego = true;
1136 break;
1137 default:
1138 break;
1141 if (spnego_state->mic_requested) {
1142 if (have_sign) {
1143 new_spnego = true;
1147 if (have_sign && new_spnego) {
1148 spnego_state->needs_mic_check = true;
1149 spnego_state->needs_mic_sign = true;
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;
1166 spnego_state->needs_mic_check = false;
1167 spnego_state->done_mic_check = true;
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;
1184 spnego_state->needs_mic_sign = false;
1187 if (spnego_state->needs_mic_check) {
1188 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1191 client_response:
1192 spnego_free_data(&spnego);
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;
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;
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;
1215 spnego_state->num_targs++;
1216 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1217 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1218 } else {
1220 /* all done - server has accepted, and we agree */
1221 *out = data_blob_null;
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;
1229 spnego_state->state_position = SPNEGO_DONE;
1232 return nt_status;
1234 case SPNEGO_DONE:
1235 /* We should not be called after we are 'done' */
1236 return NT_STATUS_INVALID_PARAMETER;
1238 return NT_STATUS_INVALID_PARAMETER;
1241 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1242 const DATA_BLOB in, DATA_BLOB *full_in)
1244 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1245 size_t expected;
1246 bool ok;
1248 *full_in = data_blob_null;
1250 if (spnego_state->in_needed == 0) {
1251 size_t size = 0;
1252 int ret;
1255 * try to work out the size of the full
1256 * input token, it might be fragmented
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);
1263 if ((ret == 0) || (ret == EAGAIN)) {
1264 spnego_state->in_needed = size;
1265 } else {
1267 * If it is not an asn1 message
1268 * just call the next layer.
1270 spnego_state->in_needed = in.length;
1274 if (spnego_state->in_needed > UINT16_MAX) {
1276 * limit the incoming message to 0xFFFF
1277 * to avoid DoS attacks.
1279 return NT_STATUS_INVALID_BUFFER_SIZE;
1282 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
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.
1288 return NT_STATUS_INVALID_PARAMETER;
1291 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1292 if (in.length > expected) {
1294 * we got more than expected
1296 return NT_STATUS_INVALID_PARAMETER;
1299 if (in.length == spnego_state->in_needed) {
1301 * if the in.length contains the full blob
1302 * we are done.
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.
1309 *full_in = in;
1310 return NT_STATUS_OK;
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;
1319 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1320 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1323 *full_in = spnego_state->in_frag;
1324 return NT_STATUS_OK;
1327 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1328 TALLOC_CTX *out_mem_ctx,
1329 DATA_BLOB *_out)
1331 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1332 DATA_BLOB out = data_blob_null;
1333 bool ok;
1335 *_out = data_blob_null;
1337 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1339 * Fast path, we can deliver everything
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;
1348 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1349 return spnego_state->out_status;
1353 * We're completely done, further updates are not allowed.
1355 spnego_state->state_position = SPNEGO_DONE;
1356 return gensec_child_ready(gensec_security,
1357 spnego_state->sub_sec_security);
1360 out = spnego_state->out_frag;
1363 * copy the remaining bytes
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;
1373 * truncate the buffer
1375 ok = data_blob_realloc(spnego_state, &out,
1376 spnego_state->out_max_length);
1377 if (!ok) {
1378 return NT_STATUS_NO_MEMORY;
1381 talloc_steal(out_mem_ctx, out.data);
1382 *_out = out;
1383 return NT_STATUS_MORE_PROCESSING_REQUIRED;
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;
1394 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1395 enum tevent_req_state req_state)
1397 struct gensec_spnego_update_state *state =
1398 tevent_req_data(req,
1399 struct gensec_spnego_update_state);
1401 switch (req_state) {
1402 case TEVENT_REQ_USER_ERROR:
1403 case TEVENT_REQ_TIMED_OUT:
1404 case TEVENT_REQ_NO_MEMORY:
1406 * A fatal error, further updates are not allowed.
1408 state->spnego->state_position = SPNEGO_DONE;
1409 break;
1410 default:
1411 break;
1415 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1416 struct tevent_context *ev,
1417 struct gensec_security *gensec_security,
1418 const DATA_BLOB in)
1420 struct spnego_state *spnego_state =
1421 talloc_get_type_abort(gensec_security->private_data,
1422 struct spnego_state);
1423 struct tevent_req *req = NULL;
1424 struct gensec_spnego_update_state *state = NULL;
1425 NTSTATUS status;
1427 req = tevent_req_create(mem_ctx, &state,
1428 struct gensec_spnego_update_state);
1429 if (req == NULL) {
1430 return NULL;
1432 state->gensec = gensec_security;
1433 state->spnego = spnego_state;
1434 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1436 if (spnego_state->out_frag.length > 0) {
1437 if (in.length > 0) {
1438 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1439 return tevent_req_post(req, ev);
1442 status = gensec_spnego_update_out(gensec_security,
1443 state, &state->out);
1444 state->status = status;
1445 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1446 tevent_req_done(req);
1447 return tevent_req_post(req, ev);
1449 if (tevent_req_nterror(req, status)) {
1450 return tevent_req_post(req, ev);
1453 tevent_req_done(req);
1454 return tevent_req_post(req, ev);
1457 status = gensec_spnego_update_in(gensec_security,
1458 in, &state->full_in);
1459 state->status = status;
1460 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1461 tevent_req_done(req);
1462 return tevent_req_post(req, ev);
1464 if (tevent_req_nterror(req, status)) {
1465 return tevent_req_post(req, ev);
1468 status = gensec_spnego_update(gensec_security,
1469 state, ev,
1470 state->full_in,
1471 &spnego_state->out_frag);
1472 data_blob_free(&spnego_state->in_frag);
1473 spnego_state->in_needed = 0;
1474 if (NT_STATUS_IS_OK(status)) {
1475 bool reset_full = true;
1477 reset_full = !spnego_state->done_mic_check;
1479 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1480 reset_full);
1482 if (!NT_STATUS_IS_OK(status) &&
1483 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1484 tevent_req_nterror(req, status);
1485 return tevent_req_post(req, ev);
1488 spnego_state->out_status = status;
1490 status = gensec_spnego_update_out(gensec_security,
1491 state, &state->out);
1492 state->status = status;
1493 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1494 tevent_req_done(req);
1495 return tevent_req_post(req, ev);
1497 if (tevent_req_nterror(req, status)) {
1498 return tevent_req_post(req, ev);
1501 tevent_req_done(req);
1502 return tevent_req_post(req, ev);
1505 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1506 TALLOC_CTX *out_mem_ctx,
1507 DATA_BLOB *out)
1509 struct gensec_spnego_update_state *state =
1510 tevent_req_data(req,
1511 struct gensec_spnego_update_state);
1512 NTSTATUS status;
1514 *out = data_blob_null;
1516 if (tevent_req_is_nterror(req, &status)) {
1517 tevent_req_received(req);
1518 return status;
1521 *out = state->out;
1522 talloc_steal(out_mem_ctx, state->out.data);
1523 status = state->status;
1524 tevent_req_received(req);
1525 return status;
1528 static const char *gensec_spnego_oids[] = {
1529 GENSEC_OID_SPNEGO,
1530 NULL
1533 static const struct gensec_security_ops gensec_spnego_security_ops = {
1534 .name = "spnego",
1535 .sasl_name = "GSS-SPNEGO",
1536 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1537 .oid = gensec_spnego_oids,
1538 .client_start = gensec_spnego_client_start,
1539 .server_start = gensec_spnego_server_start,
1540 .update_send = gensec_spnego_update_send,
1541 .update_recv = gensec_spnego_update_recv,
1542 .seal_packet = gensec_child_seal_packet,
1543 .sign_packet = gensec_child_sign_packet,
1544 .sig_size = gensec_child_sig_size,
1545 .max_wrapped_size = gensec_child_max_wrapped_size,
1546 .max_input_size = gensec_child_max_input_size,
1547 .check_packet = gensec_child_check_packet,
1548 .unseal_packet = gensec_child_unseal_packet,
1549 .wrap = gensec_child_wrap,
1550 .unwrap = gensec_child_unwrap,
1551 .session_key = gensec_child_session_key,
1552 .session_info = gensec_child_session_info,
1553 .want_feature = gensec_child_want_feature,
1554 .have_feature = gensec_child_have_feature,
1555 .expire_time = gensec_child_expire_time,
1556 .final_auth_type = gensec_child_final_auth_type,
1557 .enabled = true,
1558 .priority = GENSEC_SPNEGO
1561 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1563 NTSTATUS ret;
1564 ret = gensec_register(ctx, &gensec_spnego_security_ops);
1565 if (!NT_STATUS_IS_OK(ret)) {
1566 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1567 gensec_spnego_security_ops.name));
1568 return ret;
1571 return ret;