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/>.
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"
39 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
41 enum spnego_state_position {
51 struct spnego_neg_ops;
52 struct spnego_neg_state;
54 struct spnego_neg_state {
55 const struct spnego_neg_ops *ops;
56 const struct gensec_security_ops_wrapper *all_sec;
58 const char * const *mech_types;
62 struct spnego_neg_ops {
65 * The start hook does the initial processing on the incoming paket and
66 * may starts the first possible subcontext. It indicates that
67 * gensec_update() is required on the subcontext by returning
68 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
69 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
70 * caller should treat 'in_next' as const and don't attempt to free the
71 * content. NT_STATUS_OK indicates the finish hook should be invoked
72 * directly withing the need of gensec_update() on the subcontext.
73 * Every other error indicates an error that's returned to the caller.
75 NTSTATUS (*start_fn)(struct gensec_security *gensec_security,
76 struct spnego_state *spnego_state,
77 struct spnego_neg_state *n,
78 struct spnego_data *spnego_in,
79 TALLOC_CTX *in_mem_ctx,
82 * The step hook processes the result of a failed gensec_update() and
83 * can decide to ignore a failure and continue the negotiation by
84 * setting up the next possible subcontext. It indicates that
85 * gensec_update() is required on the subcontext by returning
86 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
87 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
88 * caller should treat 'in_next' as const and don't attempt to free the
89 * content. NT_STATUS_OK indicates the finish hook should be invoked
90 * directly withing the need of gensec_update() on the subcontext.
91 * Every other error indicates an error that's returned to the caller.
93 NTSTATUS (*step_fn)(struct gensec_security *gensec_security,
94 struct spnego_state *spnego_state,
95 struct spnego_neg_state *n,
96 struct spnego_data *spnego_in,
98 TALLOC_CTX *in_mem_ctx,
101 * The finish hook processes the result of a successful gensec_update()
102 * (NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED). It forms the
103 * response pdu that will be returned from the toplevel gensec_update()
104 * together with NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED. It
105 * may also alter the state machine to prepare receiving the next pdu
108 NTSTATUS (*finish_fn)(struct gensec_security *gensec_security,
109 struct spnego_state *spnego_state,
110 struct spnego_neg_state *n,
111 struct spnego_data *spnego_in,
113 const DATA_BLOB sub_out,
114 TALLOC_CTX *out_mem_ctx,
118 struct spnego_state {
119 enum spnego_message_type expected_packet;
120 enum spnego_state_position state_position;
121 struct gensec_security *sub_sec_security;
126 DATA_BLOB mech_types;
131 bool needs_mic_check;
132 bool may_skip_mic_check;
138 * The following is used to implement
139 * the update token fragmentation
143 size_t out_max_length;
148 static struct spnego_neg_state *gensec_spnego_neg_state(TALLOC_CTX *mem_ctx,
149 const struct spnego_neg_ops *ops)
151 struct spnego_neg_state *n = NULL;
153 n = talloc_zero(mem_ctx, struct spnego_neg_state);
162 static NTSTATUS gensec_spnego_neg_loop(struct gensec_security *gensec_security,
163 struct spnego_state *spnego_state,
164 const const struct spnego_neg_ops *ops,
165 struct tevent_context *ev,
166 struct spnego_data *spnego_in,
167 TALLOC_CTX *out_mem_ctx,
170 struct spnego_neg_state *n = NULL;
172 DATA_BLOB sub_in = data_blob_null;
173 DATA_BLOB sub_out = data_blob_null;
175 *out = data_blob_null;
177 n = gensec_spnego_neg_state(out_mem_ctx, ops);
179 return NT_STATUS_NO_MEMORY;
182 status = n->ops->start_fn(gensec_security, spnego_state, n,
183 spnego_in, n, &sub_in);
184 if (GENSEC_UPDATE_IS_NTERROR(status)) {
189 while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
190 status = gensec_update_ev(spnego_state->sub_sec_security,
191 n, ev, sub_in, &sub_out);
192 sub_in = data_blob_null;
193 if (NT_STATUS_IS_OK(status)) {
194 spnego_state->sub_sec_ready = true;
196 if (!GENSEC_UPDATE_IS_NTERROR(status)) {
199 sub_out = data_blob_null;
201 status = n->ops->step_fn(gensec_security, spnego_state, n,
202 spnego_in, status, n, &sub_in);
203 if (GENSEC_UPDATE_IS_NTERROR(status)) {
209 status = n->ops->finish_fn(gensec_security, spnego_state, n,
210 spnego_in, status, sub_out,
216 static void gensec_spnego_update_sub_abort(struct spnego_state *spnego_state)
218 spnego_state->sub_sec_ready = false;
219 TALLOC_FREE(spnego_state->sub_sec_security);
222 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
224 struct spnego_state *spnego_state;
226 spnego_state = talloc_zero(gensec_security, struct spnego_state);
228 return NT_STATUS_NO_MEMORY;
231 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
232 spnego_state->state_position = SPNEGO_CLIENT_START;
233 spnego_state->sub_sec_security = NULL;
234 spnego_state->sub_sec_ready = false;
235 spnego_state->mech_types = data_blob_null;
236 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
237 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
239 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
240 "spnego", "simulate_w2k", false);
242 gensec_security->private_data = spnego_state;
246 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
248 struct spnego_state *spnego_state;
250 spnego_state = talloc_zero(gensec_security, struct spnego_state);
252 return NT_STATUS_NO_MEMORY;
255 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
256 spnego_state->state_position = SPNEGO_SERVER_START;
257 spnego_state->sub_sec_security = NULL;
258 spnego_state->sub_sec_ready = false;
259 spnego_state->mech_types = data_blob_null;
260 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
261 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
263 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
264 "spnego", "simulate_w2k", false);
266 gensec_security->private_data = spnego_state;
270 /** Fallback to another GENSEC mechanism, based on magic strings
272 * This is the 'fallback' case, where we don't get SPNEGO, and have to
273 * try all the other options (and hope they all have a magic string
277 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
278 struct spnego_state *spnego_state,
283 const struct gensec_security_ops **all_ops;
285 all_ops = gensec_security_mechs(gensec_security, mem_ctx);
287 for (i=0; all_ops && all_ops[i]; i++) {
291 if (gensec_security != NULL &&
292 !gensec_security_ops_enabled(all_ops[i], gensec_security))
297 if (!all_ops[i]->oid) {
302 for (j=0; all_ops[i]->oid[j]; j++) {
303 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
311 if (!all_ops[i]->magic) {
315 nt_status = all_ops[i]->magic(gensec_security, &in);
316 if (!NT_STATUS_IS_OK(nt_status)) {
320 spnego_state->state_position = SPNEGO_FALLBACK;
322 nt_status = gensec_subcontext_start(spnego_state,
324 &spnego_state->sub_sec_security);
326 if (!NT_STATUS_IS_OK(nt_status)) {
329 /* select the sub context */
330 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
332 if (!NT_STATUS_IS_OK(nt_status)) {
338 DEBUG(1, ("Failed to parse SPNEGO request\n"));
339 return NT_STATUS_INVALID_PARAMETER;
342 static NTSTATUS gensec_spnego_create_negTokenInit_start(
343 struct gensec_security *gensec_security,
344 struct spnego_state *spnego_state,
345 struct spnego_neg_state *n,
346 struct spnego_data *spnego_in,
347 TALLOC_CTX *in_mem_ctx,
351 n->mech_types = gensec_security_oids(gensec_security, n,
353 if (n->mech_types == NULL) {
354 DBG_WARNING("gensec_security_oids() failed\n");
355 return NT_STATUS_NO_MEMORY;
359 n->all_sec = gensec_security_by_oid_list(gensec_security,
362 if (n->all_sec == NULL) {
363 DBG_WARNING("gensec_security_by_oid_list() failed\n");
364 return NT_STATUS_NO_MEMORY;
367 return n->ops->step_fn(gensec_security, spnego_state, n,
368 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
371 static NTSTATUS gensec_spnego_create_negTokenInit_step(
372 struct gensec_security *gensec_security,
373 struct spnego_state *spnego_state,
374 struct spnego_neg_state *n,
375 struct spnego_data *spnego_in,
376 NTSTATUS last_status,
377 TALLOC_CTX *in_mem_ctx,
380 if (!NT_STATUS_IS_OK(last_status)) {
381 const struct gensec_security_ops_wrapper *cur_sec =
382 &n->all_sec[n->all_idx];
383 const struct gensec_security_ops_wrapper *next_sec = NULL;
384 const char *next = NULL;
385 const char *principal = NULL;
386 int dbg_level = DBGLVL_WARNING;
387 NTSTATUS status = last_status;
389 if (cur_sec[1].op != NULL) {
390 next_sec = &cur_sec[1];
393 if (next_sec != NULL) {
394 next = next_sec->op->name;
395 dbg_level = DBGLVL_NOTICE;
398 if (gensec_security->target.principal != NULL) {
399 principal = gensec_security->target.principal;
400 } else if (gensec_security->target.service != NULL &&
401 gensec_security->target.hostname != NULL)
403 principal = talloc_asprintf(spnego_state->sub_sec_security,
405 gensec_security->target.service,
406 gensec_security->target.hostname);
408 principal = gensec_security->target.hostname;
411 DBG_PREFIX(dbg_level, (
412 "%s: creating NEG_TOKEN_INIT for %s failed "
413 "(next[%s]): %s\n", cur_sec->op->name,
414 principal, next, nt_errstr(status)));
418 * A hard error without a possible fallback.
424 * Pretend we never started it
426 gensec_spnego_update_sub_abort(spnego_state);
429 * And try the next one...
434 for (; n->all_sec[n->all_idx].op != NULL; n->all_idx++) {
435 const struct gensec_security_ops_wrapper *cur_sec =
436 &n->all_sec[n->all_idx];
439 status = gensec_subcontext_start(spnego_state,
441 &spnego_state->sub_sec_security);
442 if (!NT_STATUS_IS_OK(status)) {
446 /* select the sub context */
447 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
449 if (!NT_STATUS_IS_OK(status)) {
450 gensec_spnego_update_sub_abort(spnego_state);
454 /* In the client, try and produce the first (optimistic) packet */
455 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
456 *in_next = data_blob_null;
457 return NT_STATUS_MORE_PROCESSING_REQUIRED;
460 *in_next = data_blob_null;
464 DBG_WARNING("Failed to setup SPNEGO negTokenInit request\n");
465 return NT_STATUS_INVALID_PARAMETER;
468 static NTSTATUS gensec_spnego_create_negTokenInit_finish(
469 struct gensec_security *gensec_security,
470 struct spnego_state *spnego_state,
471 struct spnego_neg_state *n,
472 struct spnego_data *spnego_in,
474 const DATA_BLOB sub_out,
475 TALLOC_CTX *out_mem_ctx,
478 const struct gensec_security_ops_wrapper *cur_sec =
479 &n->all_sec[n->all_idx];
480 struct spnego_data spnego_out;
483 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
485 n->mech_types = gensec_security_oids_from_ops_wrapped(n, cur_sec);
486 if (n->mech_types == NULL) {
487 DBG_WARNING("gensec_security_oids_from_ops_wrapped() failed\n");
488 return NT_STATUS_NO_MEMORY;
491 ok = spnego_write_mech_types(spnego_state,
493 &spnego_state->mech_types);
495 DBG_ERR("Failed to write mechTypes\n");
496 return NT_STATUS_NO_MEMORY;
499 /* List the remaining mechs as options */
500 spnego_out.negTokenInit.mechTypes = n->mech_types;
501 spnego_out.negTokenInit.reqFlags = data_blob_null;
502 spnego_out.negTokenInit.reqFlagsPadding = 0;
504 if (spnego_state->state_position == SPNEGO_SERVER_START) {
505 spnego_out.negTokenInit.mechListMIC
506 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
508 spnego_out.negTokenInit.mechListMIC = data_blob_null;
511 spnego_out.negTokenInit.mechToken = sub_out;
513 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
514 DBG_ERR("Failed to write NEG_TOKEN_INIT\n");
515 return NT_STATUS_INVALID_PARAMETER;
519 * Note that 'cur_sec' is temporary memory, but
520 * cur_sec->oid points to a const string in the
521 * backends gensec_security_ops structure.
523 spnego_state->neg_oid = cur_sec->oid;
526 if (spnego_state->state_position == SPNEGO_SERVER_START) {
527 spnego_state->state_position = SPNEGO_SERVER_START;
528 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
530 spnego_state->state_position = SPNEGO_CLIENT_TARG;
531 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
534 return NT_STATUS_MORE_PROCESSING_REQUIRED;
537 static const struct spnego_neg_ops gensec_spnego_create_negTokenInit_ops = {
538 .name = "create_negTokenInit",
539 .start_fn = gensec_spnego_create_negTokenInit_start,
540 .step_fn = gensec_spnego_create_negTokenInit_step,
541 .finish_fn = gensec_spnego_create_negTokenInit_finish,
544 /** create a negTokenInit
546 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
548 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
549 struct spnego_state *spnego_state,
550 TALLOC_CTX *out_mem_ctx,
551 struct tevent_context *ev,
554 struct spnego_data *spnego_in = NULL;
555 return gensec_spnego_neg_loop(gensec_security, spnego_state,
556 &gensec_spnego_create_negTokenInit_ops,
557 ev, spnego_in, out_mem_ctx, out);
560 static NTSTATUS gensec_spnego_client_negTokenInit_start(
561 struct gensec_security *gensec_security,
562 struct spnego_state *spnego_state,
563 struct spnego_neg_state *n,
564 struct spnego_data *spnego_in,
565 TALLOC_CTX *in_mem_ctx,
568 const char *tp = NULL;
570 /* The server offers a list of mechanisms */
572 tp = spnego_in->negTokenInit.targetPrincipal;
573 if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
574 DBG_INFO("Server claims it's principal name is %s\n", tp);
575 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
576 gensec_set_target_principal(gensec_security, tp);
581 n->mech_types = spnego_in->negTokenInit.mechTypes;
582 if (n->mech_types == NULL) {
583 return NT_STATUS_INVALID_PARAMETER;
587 n->all_sec = gensec_security_by_oid_list(gensec_security,
590 if (n->all_sec == NULL) {
591 DBG_WARNING("gensec_security_by_oid_list() failed\n");
592 return NT_STATUS_INVALID_PARAMETER;
595 return n->ops->step_fn(gensec_security, spnego_state, n,
596 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
599 static NTSTATUS gensec_spnego_client_negTokenInit_step(
600 struct gensec_security *gensec_security,
601 struct spnego_state *spnego_state,
602 struct spnego_neg_state *n,
603 struct spnego_data *spnego_in,
604 NTSTATUS last_status,
605 TALLOC_CTX *in_mem_ctx,
608 if (!NT_STATUS_IS_OK(last_status)) {
609 const struct gensec_security_ops_wrapper *cur_sec =
610 &n->all_sec[n->all_idx];
611 const struct gensec_security_ops_wrapper *next_sec = NULL;
612 const char *next = NULL;
613 const char *principal = NULL;
614 int dbg_level = DBGLVL_WARNING;
615 bool allow_fallback = false;
616 NTSTATUS status = last_status;
618 if (cur_sec[1].op != NULL) {
619 next_sec = &cur_sec[1];
623 * it is likely that a NULL input token will
624 * not be liked by most server mechs, but if
625 * we are in the client, we want the first
626 * update packet to be able to abort the use
629 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
630 NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) ||
631 NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
632 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
634 allow_fallback = true;
637 if (allow_fallback && next_sec != NULL) {
638 next = next_sec->op->name;
639 dbg_level = DBGLVL_NOTICE;
642 if (gensec_security->target.principal != NULL) {
643 principal = gensec_security->target.principal;
644 } else if (gensec_security->target.service != NULL &&
645 gensec_security->target.hostname != NULL)
647 principal = talloc_asprintf(spnego_state->sub_sec_security,
649 gensec_security->target.service,
650 gensec_security->target.hostname);
652 principal = gensec_security->target.hostname;
655 DBG_PREFIX(dbg_level, (
656 "%s: creating NEG_TOKEN_INIT for %s failed "
657 "(next[%s]): %s\n", cur_sec->op->name,
658 principal, next, nt_errstr(status)));
662 * A hard error without a possible fallback.
668 * Pretend we never started it.
670 gensec_spnego_update_sub_abort(spnego_state);
673 * And try the next one...
678 for (; n->all_sec[n->all_idx].op != NULL; n->all_idx++) {
679 const struct gensec_security_ops_wrapper *cur_sec =
680 &n->all_sec[n->all_idx];
683 status = gensec_subcontext_start(spnego_state,
685 &spnego_state->sub_sec_security);
686 if (!NT_STATUS_IS_OK(status)) {
690 /* select the sub context */
691 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
693 if (!NT_STATUS_IS_OK(status)) {
694 gensec_spnego_update_sub_abort(spnego_state);
699 * Note that 'cur_sec' is temporary memory, but
700 * cur_sec->oid points to a const string in the
701 * backends gensec_security_ops structure.
703 spnego_state->neg_oid = cur_sec->oid;
706 * As client we don't use an optimistic token from the server.
707 * But try to produce one for the server.
709 *in_next = data_blob_null;
710 return NT_STATUS_MORE_PROCESSING_REQUIRED;
713 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
714 return NT_STATUS_INVALID_PARAMETER;
717 static NTSTATUS gensec_spnego_client_negTokenInit_finish(
718 struct gensec_security *gensec_security,
719 struct spnego_state *spnego_state,
720 struct spnego_neg_state *n,
721 struct spnego_data *spnego_in,
723 const DATA_BLOB sub_out,
724 TALLOC_CTX *out_mem_ctx,
727 struct spnego_data spnego_out;
728 const char *my_mechs[] = {NULL, NULL};
731 my_mechs[0] = spnego_state->neg_oid;
733 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
734 spnego_out.negTokenInit.mechTypes = my_mechs;
735 spnego_out.negTokenInit.reqFlags = data_blob_null;
736 spnego_out.negTokenInit.reqFlagsPadding = 0;
737 spnego_out.negTokenInit.mechListMIC = data_blob_null;
738 spnego_out.negTokenInit.mechToken = sub_out;
740 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
741 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
742 return NT_STATUS_INVALID_PARAMETER;
745 ok = spnego_write_mech_types(spnego_state,
747 &spnego_state->mech_types);
749 DBG_ERR("failed to write mechTypes\n");
750 return NT_STATUS_NO_MEMORY;
754 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
755 spnego_state->state_position = SPNEGO_CLIENT_TARG;
757 return NT_STATUS_MORE_PROCESSING_REQUIRED;
760 static const struct spnego_neg_ops gensec_spnego_client_negTokenInit_ops = {
761 .name = "client_negTokenInit",
762 .start_fn = gensec_spnego_client_negTokenInit_start,
763 .step_fn = gensec_spnego_client_negTokenInit_step,
764 .finish_fn = gensec_spnego_client_negTokenInit_finish,
767 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security,
768 struct spnego_state *spnego_state,
769 struct tevent_context *ev,
770 struct spnego_data *spnego_in,
771 TALLOC_CTX *out_mem_ctx,
774 return gensec_spnego_neg_loop(gensec_security, spnego_state,
775 &gensec_spnego_client_negTokenInit_ops,
776 ev, spnego_in, out_mem_ctx, out);
779 static NTSTATUS gensec_spnego_client_negTokenTarg(struct gensec_security *gensec_security,
780 struct spnego_state *spnego_state,
781 struct tevent_context *ev,
782 struct spnego_data *spnego_in,
783 TALLOC_CTX *out_mem_ctx,
786 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
787 DATA_BLOB sub_in = ta->responseToken;
788 DATA_BLOB mech_list_mic = data_blob_null;
789 DATA_BLOB sub_out = data_blob_null;
790 struct spnego_data spnego_out;
793 *out = data_blob_null;
795 spnego_state->num_targs++;
797 if (ta->negResult == SPNEGO_REJECT) {
798 return NT_STATUS_LOGON_FAILURE;
801 if (ta->negResult == SPNEGO_REQUEST_MIC) {
802 spnego_state->mic_requested = true;
805 if (ta->mechListMIC.length > 0) {
806 DATA_BLOB *m = &ta->mechListMIC;
807 const DATA_BLOB *r = &ta->responseToken;
810 * Windows 2000 has a bug, it repeats the
811 * responseToken in the mechListMIC field.
813 if (m->length == r->length) {
816 cmp = memcmp(m->data, r->data, m->length);
823 /* Server didn't like our choice of mech, and chose something else */
824 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
825 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
826 ta->supportedMech != NULL &&
827 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
829 const char *client_mech = NULL;
830 const char *client_oid = NULL;
831 const char *server_mech = NULL;
832 const char *server_oid = NULL;
834 client_mech = gensec_get_name_by_oid(gensec_security,
835 spnego_state->neg_oid);
836 client_oid = spnego_state->neg_oid;
837 server_mech = gensec_get_name_by_oid(gensec_security,
839 server_oid = ta->supportedMech;
841 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
842 "server wants: %s[%s]\n",
843 client_mech, client_oid, server_mech, server_oid);
845 spnego_state->downgraded = true;
846 gensec_spnego_update_sub_abort(spnego_state);
848 status = gensec_subcontext_start(spnego_state,
850 &spnego_state->sub_sec_security);
851 if (!NT_STATUS_IS_OK(status)) {
855 /* select the sub context */
856 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
858 if (!NT_STATUS_IS_OK(status)) {
862 spnego_state->neg_oid = talloc_strdup(spnego_state,
864 if (spnego_state->neg_oid == NULL) {
865 return NT_STATUS_NO_MEMORY;
869 if (ta->mechListMIC.length > 0) {
870 if (spnego_state->sub_sec_ready) {
871 spnego_state->needs_mic_check = true;
875 if (spnego_state->needs_mic_check) {
876 if (ta->responseToken.length != 0) {
877 DBG_WARNING("non empty response token not expected\n");
878 return NT_STATUS_INVALID_PARAMETER;
881 if (ta->mechListMIC.length == 0
882 && spnego_state->may_skip_mic_check) {
884 * In this case we don't require
885 * a mechListMIC from the server.
887 * This works around bugs in the Azure
888 * and Apple spnego implementations.
891 * https://bugzilla.samba.org/show_bug.cgi?id=11994
893 spnego_state->needs_mic_check = false;
894 status = NT_STATUS_OK;
895 goto client_response;
898 status = gensec_check_packet(spnego_state->sub_sec_security,
899 spnego_state->mech_types.data,
900 spnego_state->mech_types.length,
901 spnego_state->mech_types.data,
902 spnego_state->mech_types.length,
904 if (!NT_STATUS_IS_OK(status)) {
905 DBG_WARNING("failed to verify mechListMIC: %s\n",
909 spnego_state->needs_mic_check = false;
910 spnego_state->done_mic_check = true;
911 goto client_response;
914 if (!spnego_state->sub_sec_ready) {
915 status = gensec_update_ev(spnego_state->sub_sec_security,
919 if (NT_STATUS_IS_OK(status)) {
920 spnego_state->sub_sec_ready = true;
922 if (!NT_STATUS_IS_OK(status)) {
923 goto client_response;
926 status = NT_STATUS_OK;
929 if (!spnego_state->done_mic_check) {
930 bool have_sign = true;
931 bool new_spnego = false;
933 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
934 GENSEC_FEATURE_SIGN);
935 if (spnego_state->simulate_w2k) {
938 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
939 GENSEC_FEATURE_NEW_SPNEGO);
941 switch (ta->negResult) {
942 case SPNEGO_ACCEPT_COMPLETED:
943 case SPNEGO_NONE_RESULT:
944 if (spnego_state->num_targs == 1) {
946 * the first exchange doesn't require
954 case SPNEGO_ACCEPT_INCOMPLETE:
955 if (ta->mechListMIC.length > 0) {
960 if (spnego_state->downgraded) {
962 * A downgrade should be protected if
969 * The caller may just asked for
970 * GENSEC_FEATURE_SESSION_KEY, this
971 * is only reflected in the want_features.
974 * gensec_have_features(GENSEC_FEATURE_SIGN)
977 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
980 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
984 * Here we're sure our preferred mech was
985 * selected by the server and our caller doesn't
986 * need GENSEC_FEATURE_SIGN nor
987 * GENSEC_FEATURE_SEAL support.
989 * In this case we don't require
990 * a mechListMIC from the server.
992 * This works around bugs in the Azure
993 * and Apple spnego implementations.
996 * https://bugzilla.samba.org/show_bug.cgi?id=11994
998 spnego_state->may_skip_mic_check = true;
1001 case SPNEGO_REQUEST_MIC:
1002 if (ta->mechListMIC.length > 0) {
1010 if (spnego_state->mic_requested) {
1016 if (have_sign && new_spnego) {
1017 spnego_state->needs_mic_check = true;
1018 spnego_state->needs_mic_sign = true;
1022 if (ta->mechListMIC.length > 0) {
1023 status = gensec_check_packet(spnego_state->sub_sec_security,
1024 spnego_state->mech_types.data,
1025 spnego_state->mech_types.length,
1026 spnego_state->mech_types.data,
1027 spnego_state->mech_types.length,
1029 if (!NT_STATUS_IS_OK(status)) {
1030 DBG_WARNING("failed to verify mechListMIC: %s\n",
1034 spnego_state->needs_mic_check = false;
1035 spnego_state->done_mic_check = true;
1038 if (spnego_state->needs_mic_sign) {
1039 status = gensec_sign_packet(spnego_state->sub_sec_security,
1041 spnego_state->mech_types.data,
1042 spnego_state->mech_types.length,
1043 spnego_state->mech_types.data,
1044 spnego_state->mech_types.length,
1046 if (!NT_STATUS_IS_OK(status)) {
1047 DBG_WARNING("failed to sign mechListMIC: %s\n",
1051 spnego_state->needs_mic_sign = false;
1055 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1056 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
1057 spnego_state->sub_sec_security->ops->name,
1062 if (sub_out.length == 0 && mech_list_mic.length == 0) {
1063 *out = data_blob_null;
1065 if (!spnego_state->sub_sec_ready) {
1066 /* somethings wrong here... */
1067 DBG_ERR("gensec_update not ready without output\n");
1068 return NT_STATUS_INTERNAL_ERROR;
1071 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
1072 /* unless of course it did not accept */
1073 DBG_WARNING("gensec_update ok but not accepted\n");
1074 return NT_STATUS_INVALID_PARAMETER;
1077 if (!spnego_state->needs_mic_check) {
1078 spnego_state->state_position = SPNEGO_DONE;
1079 return NT_STATUS_OK;
1084 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1085 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1086 spnego_out.negTokenTarg.supportedMech = NULL;
1087 spnego_out.negTokenTarg.responseToken = sub_out;
1088 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1090 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1091 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
1092 return NT_STATUS_INVALID_PARAMETER;
1095 spnego_state->num_targs++;
1097 /* set next state */
1098 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1099 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1101 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1104 /** create a server negTokenTarg
1106 * This is the case, where the client is the first one who sends data
1109 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
1110 TALLOC_CTX *out_mem_ctx,
1112 const DATA_BLOB unwrapped_out,
1113 DATA_BLOB mech_list_mic,
1116 struct spnego_data spnego_out;
1119 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1120 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1121 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1122 spnego_out.negTokenTarg.supportedMech = NULL;
1124 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1125 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1126 if (spnego_state->mic_requested) {
1127 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
1128 spnego_state->mic_requested = false;
1130 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1132 spnego_state->state_position = SPNEGO_SERVER_TARG;
1133 } else if (NT_STATUS_IS_OK(nt_status)) {
1134 if (unwrapped_out.data) {
1135 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1137 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1138 spnego_state->state_position = SPNEGO_DONE;
1141 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1142 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1143 return NT_STATUS_INVALID_PARAMETER;
1146 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1147 spnego_state->num_targs++;
1152 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
1153 struct spnego_state *spnego_state,
1154 struct tevent_context *ev,
1155 struct spnego_data *spnego_in,
1156 TALLOC_CTX *out_mem_ctx,
1159 TALLOC_CTX *frame = talloc_stackframe();
1160 DATA_BLOB sub_out = data_blob_null;
1161 DATA_BLOB mech_list_mic = data_blob_null;
1162 const char * const *mech_types = NULL;
1164 const struct gensec_security_ops_wrapper *all_sec = NULL;
1165 size_t mech_idx = 0;
1169 mech_types = spnego_in->negTokenInit.mechTypes;
1170 if (mech_types == NULL) {
1172 return NT_STATUS_INVALID_PARAMETER;
1175 all_sec = gensec_security_by_oid_list(gensec_security, frame,
1176 mech_types, GENSEC_OID_SPNEGO);
1177 if (all_sec == NULL) {
1178 DBG_WARNING("gensec_security_by_oid_list() failed\n");
1180 return NT_STATUS_INVALID_PARAMETER;
1183 ok = spnego_write_mech_types(spnego_state, mech_types,
1184 &spnego_state->mech_types);
1186 DBG_ERR("Failed to write mechTypes\n");
1188 return NT_STATUS_NO_MEMORY;
1192 * First try the preferred mechs from the client.
1194 for (; mech_types[mech_idx]; mech_idx++) {
1195 const char *cur_mech = mech_types[mech_idx];
1196 const struct gensec_security_ops_wrapper *cur_sec = NULL;
1197 DATA_BLOB sub_in = data_blob_null;
1199 for (all_idx = 0; all_sec[all_idx].op; all_idx++) {
1200 if (strcmp(cur_mech, all_sec[all_idx].oid) == 0) {
1201 cur_sec = &all_sec[all_idx];
1206 if (cur_sec == NULL) {
1210 status = gensec_subcontext_start(spnego_state,
1212 &spnego_state->sub_sec_security);
1213 if (!NT_STATUS_IS_OK(status)) {
1218 /* select the sub context */
1219 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
1221 if (!NT_STATUS_IS_OK(status)) {
1223 * Pretend we never started it
1225 gensec_spnego_update_sub_abort(spnego_state);
1231 * Indicate the downgrade and request a
1234 spnego_state->downgraded = true;
1235 spnego_state->mic_requested = true;
1236 /* no optimistic token */
1237 spnego_state->neg_oid = cur_sec->oid;
1238 sub_out = data_blob_null;
1239 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1244 * Try the optimistic token from the client
1246 sub_in = spnego_in->negTokenInit.mechToken;
1247 status = gensec_update_ev(spnego_state->sub_sec_security,
1248 frame, ev, sub_in, &sub_out);
1249 if (NT_STATUS_IS_OK(status)) {
1250 spnego_state->sub_sec_ready = true;
1252 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1253 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1255 DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1256 cur_sec->op->name, nt_errstr(status));
1259 * Pretend we never started it
1261 gensec_spnego_update_sub_abort(spnego_state);
1265 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1266 DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1267 cur_sec->op->name, nt_errstr(status));
1272 spnego_state->neg_oid = cur_sec->oid;
1273 goto reply; /* OK or MORE PROCESSING */
1276 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1278 return NT_STATUS_INVALID_PARAMETER;
1281 if (spnego_state->simulate_w2k) {
1283 * Windows 2000 returns the unwrapped token
1284 * also in the mech_list_mic field.
1286 * In order to verify our client code,
1287 * we need a way to have a server with this
1290 mech_list_mic = sub_out;
1293 status = gensec_spnego_server_response(spnego_state,
1303 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1304 struct spnego_state *spnego_state,
1305 struct tevent_context *ev,
1306 struct spnego_data *spnego_in,
1307 TALLOC_CTX *out_mem_ctx,
1310 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1311 DATA_BLOB sub_in = ta->responseToken;
1312 DATA_BLOB mech_list_mic = data_blob_null;
1313 DATA_BLOB sub_out = data_blob_null;
1315 bool have_sign = true;
1316 bool new_spnego = false;
1318 spnego_state->num_targs++;
1320 if (spnego_state->sub_sec_security == NULL) {
1321 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1322 return NT_STATUS_INVALID_PARAMETER;
1325 if (spnego_state->needs_mic_check) {
1326 if (ta->responseToken.length != 0) {
1327 DBG_WARNING("non empty response token not expected\n");
1328 return NT_STATUS_INVALID_PARAMETER;
1331 status = gensec_check_packet(spnego_state->sub_sec_security,
1332 spnego_state->mech_types.data,
1333 spnego_state->mech_types.length,
1334 spnego_state->mech_types.data,
1335 spnego_state->mech_types.length,
1337 if (!NT_STATUS_IS_OK(status)) {
1338 DBG_WARNING("failed to verify mechListMIC: %s\n",
1343 spnego_state->needs_mic_check = false;
1344 spnego_state->done_mic_check = true;
1345 goto server_response;
1348 if (!spnego_state->sub_sec_ready) {
1349 status = gensec_update_ev(spnego_state->sub_sec_security,
1352 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1353 DEBUG(2, ("SPNEGO login failed: %s\n",
1354 nt_errstr(status)));
1357 if (NT_STATUS_IS_OK(status)) {
1358 spnego_state->sub_sec_ready = true;
1360 if (!NT_STATUS_IS_OK(status)) {
1361 goto server_response;
1364 status = NT_STATUS_OK;
1367 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1368 GENSEC_FEATURE_SIGN);
1369 if (spnego_state->simulate_w2k) {
1372 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1373 GENSEC_FEATURE_NEW_SPNEGO);
1374 if (ta->mechListMIC.length > 0) {
1378 if (have_sign && new_spnego) {
1379 spnego_state->needs_mic_check = true;
1380 spnego_state->needs_mic_sign = true;
1383 if (have_sign && ta->mechListMIC.length > 0) {
1384 status = gensec_check_packet(spnego_state->sub_sec_security,
1385 spnego_state->mech_types.data,
1386 spnego_state->mech_types.length,
1387 spnego_state->mech_types.data,
1388 spnego_state->mech_types.length,
1390 if (!NT_STATUS_IS_OK(status)) {
1391 DBG_WARNING("failed to verify mechListMIC: %s\n",
1396 spnego_state->needs_mic_check = false;
1397 spnego_state->done_mic_check = true;
1400 if (spnego_state->needs_mic_sign) {
1401 status = gensec_sign_packet(spnego_state->sub_sec_security,
1403 spnego_state->mech_types.data,
1404 spnego_state->mech_types.length,
1405 spnego_state->mech_types.data,
1406 spnego_state->mech_types.length,
1408 if (!NT_STATUS_IS_OK(status)) {
1409 DBG_WARNING("failed to sign mechListMIC: %s\n",
1413 spnego_state->needs_mic_sign = false;
1416 if (spnego_state->needs_mic_check) {
1417 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1421 return gensec_spnego_server_response(spnego_state,
1429 struct gensec_spnego_update_state {
1430 struct tevent_context *ev;
1431 struct gensec_security *gensec;
1432 struct spnego_state *spnego;
1435 struct spnego_data _spnego_in;
1436 struct spnego_data *spnego_in;
1449 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1450 enum tevent_req_state req_state)
1452 struct gensec_spnego_update_state *state =
1453 tevent_req_data(req,
1454 struct gensec_spnego_update_state);
1456 switch (req_state) {
1457 case TEVENT_REQ_USER_ERROR:
1458 case TEVENT_REQ_TIMED_OUT:
1459 case TEVENT_REQ_NO_MEMORY:
1461 * A fatal error, further updates are not allowed.
1463 state->spnego->state_position = SPNEGO_DONE;
1470 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1471 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1472 DATA_BLOB *full_in);
1473 static void gensec_spnego_update_pre(struct tevent_req *req);
1474 static void gensec_spnego_update_done(struct tevent_req *subreq);
1475 static void gensec_spnego_update_post(struct tevent_req *req);
1476 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1477 TALLOC_CTX *out_mem_ctx,
1480 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1481 struct tevent_context *ev,
1482 struct gensec_security *gensec_security,
1485 struct spnego_state *spnego_state =
1486 talloc_get_type_abort(gensec_security->private_data,
1487 struct spnego_state);
1488 struct tevent_req *req = NULL;
1489 struct gensec_spnego_update_state *state = NULL;
1493 req = tevent_req_create(mem_ctx, &state,
1494 struct gensec_spnego_update_state);
1499 state->gensec = gensec_security;
1500 state->spnego = spnego_state;
1501 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1503 if (spnego_state->out_frag.length > 0) {
1504 if (in.length > 0) {
1505 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1506 return tevent_req_post(req, ev);
1509 status = gensec_spnego_update_out(gensec_security,
1510 state, &state->out);
1511 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1512 tevent_req_nterror(req, status);
1513 return tevent_req_post(req, ev);
1516 state->status = status;
1517 tevent_req_done(req);
1518 return tevent_req_post(req, ev);
1521 status = gensec_spnego_update_in(gensec_security, in,
1522 state, &state->full_in);
1523 state->status = status;
1524 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1525 tevent_req_done(req);
1526 return tevent_req_post(req, ev);
1528 if (tevent_req_nterror(req, status)) {
1529 return tevent_req_post(req, ev);
1532 /* Check if we got a valid SPNEGO blob... */
1534 switch (spnego_state->state_position) {
1535 case SPNEGO_FALLBACK:
1538 case SPNEGO_CLIENT_TARG:
1539 case SPNEGO_SERVER_TARG:
1540 if (state->full_in.length == 0) {
1541 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1542 return tevent_req_post(req, ev);
1546 case SPNEGO_CLIENT_START:
1547 case SPNEGO_SERVER_START:
1549 if (state->full_in.length == 0) {
1550 /* create_negTokenInit later */
1554 len = spnego_read_data(state,
1556 &state->_spnego_in);
1558 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1559 DEBUG(1, ("Invalid SPNEGO request:\n"));
1560 dump_data(1, state->full_in.data,
1561 state->full_in.length);
1562 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1563 return tevent_req_post(req, ev);
1567 * This is the 'fallback' case, where we don't get
1568 * SPNEGO, and have to try all the other options (and
1569 * hope they all have a magic string they check)
1571 status = gensec_spnego_server_try_fallback(gensec_security,
1575 if (tevent_req_nterror(req, status)) {
1576 return tevent_req_post(req, ev);
1580 * We'll continue with SPNEGO_FALLBACK below...
1584 state->spnego_in = &state->_spnego_in;
1586 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1587 if (state->spnego_in->type != spnego_state->expected_packet) {
1588 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1589 state->spnego_in->type,
1590 spnego_state->expected_packet));
1591 dump_data(1, state->full_in.data,
1592 state->full_in.length);
1593 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1594 return tevent_req_post(req, ev);
1600 smb_panic(__location__);
1604 gensec_spnego_update_pre(req);
1605 if (!tevent_req_is_in_progress(req)) {
1606 return tevent_req_post(req, ev);
1609 if (state->sub.needed) {
1610 struct tevent_req *subreq = NULL;
1613 * We may need one more roundtrip...
1615 subreq = gensec_update_send(state, state->ev,
1616 spnego_state->sub_sec_security,
1618 if (tevent_req_nomem(subreq, req)) {
1619 return tevent_req_post(req, ev);
1621 tevent_req_set_callback(subreq,
1622 gensec_spnego_update_done,
1624 state->sub.needed = false;
1628 gensec_spnego_update_post(req);
1629 if (!tevent_req_is_in_progress(req)) {
1630 return tevent_req_post(req, ev);
1636 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1637 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1640 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1644 *full_in = data_blob_null;
1646 switch (spnego_state->state_position) {
1647 case SPNEGO_FALLBACK:
1649 spnego_state->in_needed = 0;
1650 return NT_STATUS_OK;
1652 case SPNEGO_CLIENT_START:
1653 case SPNEGO_CLIENT_TARG:
1654 case SPNEGO_SERVER_START:
1655 case SPNEGO_SERVER_TARG:
1660 return NT_STATUS_INVALID_PARAMETER;
1663 if (spnego_state->in_needed == 0) {
1668 * try to work out the size of the full
1669 * input token, it might be fragmented
1671 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1672 if ((ret != 0) && (ret != EAGAIN)) {
1673 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1676 if ((ret == 0) || (ret == EAGAIN)) {
1677 spnego_state->in_needed = size;
1680 * If it is not an asn1 message
1681 * just call the next layer.
1683 spnego_state->in_needed = in.length;
1687 if (spnego_state->in_needed > UINT16_MAX) {
1689 * limit the incoming message to 0xFFFF
1690 * to avoid DoS attacks.
1692 return NT_STATUS_INVALID_BUFFER_SIZE;
1695 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1697 * If we reach this, we know we got at least
1698 * part of an asn1 message, getting 0 means
1699 * the remote peer wants us to spin.
1701 return NT_STATUS_INVALID_PARAMETER;
1704 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1705 if (in.length > expected) {
1707 * we got more than expected
1709 return NT_STATUS_INVALID_PARAMETER;
1712 if (in.length == spnego_state->in_needed) {
1714 * if the in.length contains the full blob
1717 * Note: this implies spnego_state->in_frag.length == 0,
1718 * but we do not need to check this explicitly
1719 * because we already know that we did not get
1720 * more than expected.
1723 spnego_state->in_needed = 0;
1724 return NT_STATUS_OK;
1727 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1728 in.data, in.length);
1730 return NT_STATUS_NO_MEMORY;
1733 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1734 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1737 *full_in = spnego_state->in_frag;
1738 talloc_steal(mem_ctx, full_in->data);
1739 spnego_state->in_frag = data_blob_null;
1740 spnego_state->in_needed = 0;
1741 return NT_STATUS_OK;
1744 static void gensec_spnego_update_pre(struct tevent_req *req)
1746 struct gensec_spnego_update_state *state =
1747 tevent_req_data(req,
1748 struct gensec_spnego_update_state);
1749 struct gensec_security *gensec_security = state->gensec;
1750 struct spnego_state *spnego_state = state->spnego;
1751 struct tevent_context *ev = state->ev;
1754 state->sub.needed = false;
1755 state->sub.in = data_blob_null;
1756 state->sub.status = NT_STATUS_INTERNAL_ERROR;
1757 state->sub.out = data_blob_null;
1759 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1760 state->sub.in = state->full_in;
1761 state->full_in = data_blob_null;
1762 state->sub.needed = true;
1766 switch (spnego_state->state_position) {
1767 case SPNEGO_CLIENT_START:
1768 if (state->spnego_in == NULL) {
1769 /* client to produce negTokenInit */
1770 status = gensec_spnego_create_negTokenInit(gensec_security,
1771 spnego_state, state, ev,
1772 &spnego_state->out_frag);
1773 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1774 tevent_req_nterror(req, status);
1780 status = gensec_spnego_client_negTokenInit(gensec_security,
1782 state->spnego_in, state,
1783 &spnego_state->out_frag);
1786 case SPNEGO_CLIENT_TARG:
1787 status = gensec_spnego_client_negTokenTarg(gensec_security,
1789 state->spnego_in, state,
1790 &spnego_state->out_frag);
1791 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1792 tevent_req_nterror(req, status);
1797 case SPNEGO_SERVER_START:
1798 if (state->spnego_in == NULL) {
1799 /* server to produce negTokenInit */
1800 status = gensec_spnego_create_negTokenInit(gensec_security,
1801 spnego_state, state, ev,
1802 &spnego_state->out_frag);
1803 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1804 tevent_req_nterror(req, status);
1810 status = gensec_spnego_server_negTokenInit(gensec_security,
1812 state->spnego_in, state,
1813 &spnego_state->out_frag);
1814 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1815 tevent_req_nterror(req, status);
1820 case SPNEGO_SERVER_TARG:
1821 status = gensec_spnego_server_negTokenTarg(gensec_security,
1823 state->spnego_in, state,
1824 &spnego_state->out_frag);
1825 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1826 tevent_req_nterror(req, status);
1832 smb_panic(__location__);
1836 spnego_state->out_status = status;
1839 static void gensec_spnego_update_done(struct tevent_req *subreq)
1841 struct tevent_req *req =
1842 tevent_req_callback_data(subreq,
1844 struct gensec_spnego_update_state *state =
1845 tevent_req_data(req,
1846 struct gensec_spnego_update_state);
1847 struct spnego_state *spnego_state = state->spnego;
1849 state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1850 TALLOC_FREE(subreq);
1851 if (NT_STATUS_IS_OK(state->sub.status)) {
1852 spnego_state->sub_sec_ready = true;
1855 gensec_spnego_update_post(req);
1858 static void gensec_spnego_update_post(struct tevent_req *req)
1860 struct gensec_spnego_update_state *state =
1861 tevent_req_data(req,
1862 struct gensec_spnego_update_state);
1863 struct spnego_state *spnego_state = state->spnego;
1866 state->sub.in = data_blob_null;
1867 state->sub.needed = false;
1869 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1870 status = state->sub.status;
1871 spnego_state->out_frag = state->sub.out;
1872 talloc_steal(spnego_state, spnego_state->out_frag.data);
1873 state->sub.out = data_blob_null;
1878 * For now just handle the sync processing done
1879 * in gensec_spnego_update_pre()
1881 status = spnego_state->out_status;
1883 if (NT_STATUS_IS_OK(status)) {
1884 bool reset_full = true;
1886 reset_full = !spnego_state->done_mic_check;
1888 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1890 if (tevent_req_nterror(req, status)) {
1896 spnego_state->out_status = status;
1898 status = gensec_spnego_update_out(state->gensec,
1899 state, &state->out);
1900 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1901 tevent_req_nterror(req, status);
1905 state->status = status;
1906 tevent_req_done(req);
1910 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1911 TALLOC_CTX *out_mem_ctx,
1914 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1915 DATA_BLOB out = data_blob_null;
1918 *_out = data_blob_null;
1920 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1922 * Fast path, we can deliver everything
1925 *_out = spnego_state->out_frag;
1926 if (spnego_state->out_frag.length > 0) {
1927 talloc_steal(out_mem_ctx, _out->data);
1928 spnego_state->out_frag = data_blob_null;
1931 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1932 return spnego_state->out_status;
1936 * We're completely done, further updates are not allowed.
1938 spnego_state->state_position = SPNEGO_DONE;
1939 return gensec_child_ready(gensec_security,
1940 spnego_state->sub_sec_security);
1943 out = spnego_state->out_frag;
1946 * copy the remaining bytes
1948 spnego_state->out_frag = data_blob_talloc(spnego_state,
1949 out.data + spnego_state->out_max_length,
1950 out.length - spnego_state->out_max_length);
1951 if (spnego_state->out_frag.data == NULL) {
1952 return NT_STATUS_NO_MEMORY;
1956 * truncate the buffer
1958 ok = data_blob_realloc(spnego_state, &out,
1959 spnego_state->out_max_length);
1961 return NT_STATUS_NO_MEMORY;
1964 talloc_steal(out_mem_ctx, out.data);
1966 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1969 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1970 TALLOC_CTX *out_mem_ctx,
1973 struct gensec_spnego_update_state *state =
1974 tevent_req_data(req,
1975 struct gensec_spnego_update_state);
1978 *out = data_blob_null;
1980 if (tevent_req_is_nterror(req, &status)) {
1981 tevent_req_received(req);
1986 talloc_steal(out_mem_ctx, state->out.data);
1987 status = state->status;
1988 tevent_req_received(req);
1992 static const char *gensec_spnego_oids[] = {
1997 static const struct gensec_security_ops gensec_spnego_security_ops = {
1999 .sasl_name = "GSS-SPNEGO",
2000 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
2001 .oid = gensec_spnego_oids,
2002 .client_start = gensec_spnego_client_start,
2003 .server_start = gensec_spnego_server_start,
2004 .update_send = gensec_spnego_update_send,
2005 .update_recv = gensec_spnego_update_recv,
2006 .seal_packet = gensec_child_seal_packet,
2007 .sign_packet = gensec_child_sign_packet,
2008 .sig_size = gensec_child_sig_size,
2009 .max_wrapped_size = gensec_child_max_wrapped_size,
2010 .max_input_size = gensec_child_max_input_size,
2011 .check_packet = gensec_child_check_packet,
2012 .unseal_packet = gensec_child_unseal_packet,
2013 .wrap = gensec_child_wrap,
2014 .unwrap = gensec_child_unwrap,
2015 .session_key = gensec_child_session_key,
2016 .session_info = gensec_child_session_info,
2017 .want_feature = gensec_child_want_feature,
2018 .have_feature = gensec_child_have_feature,
2019 .expire_time = gensec_child_expire_time,
2020 .final_auth_type = gensec_child_final_auth_type,
2022 .priority = GENSEC_SPNEGO
2025 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
2028 ret = gensec_register(ctx, &gensec_spnego_security_ops);
2029 if (!NT_STATUS_IS_OK(ret)) {
2030 DEBUG(0,("Failed to register '%s' gensec backend!\n",
2031 gensec_spnego_security_ops.name));