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_start(
780 struct gensec_security *gensec_security,
781 struct spnego_state *spnego_state,
782 struct spnego_neg_state *n,
783 struct spnego_data *spnego_in,
784 TALLOC_CTX *in_mem_ctx,
787 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
790 spnego_state->num_targs++;
792 if (ta->negResult == SPNEGO_REJECT) {
793 return NT_STATUS_LOGON_FAILURE;
796 if (ta->negResult == SPNEGO_REQUEST_MIC) {
797 spnego_state->mic_requested = true;
800 if (ta->mechListMIC.length > 0) {
801 DATA_BLOB *m = &ta->mechListMIC;
802 const DATA_BLOB *r = &ta->responseToken;
805 * Windows 2000 has a bug, it repeats the
806 * responseToken in the mechListMIC field.
808 if (m->length == r->length) {
811 cmp = memcmp(m->data, r->data, m->length);
818 /* Server didn't like our choice of mech, and chose something else */
819 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
820 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
821 ta->supportedMech != NULL &&
822 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
824 const char *client_mech = NULL;
825 const char *client_oid = NULL;
826 const char *server_mech = NULL;
827 const char *server_oid = NULL;
829 client_mech = gensec_get_name_by_oid(gensec_security,
830 spnego_state->neg_oid);
831 client_oid = spnego_state->neg_oid;
832 server_mech = gensec_get_name_by_oid(gensec_security,
834 server_oid = ta->supportedMech;
836 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
837 "server wants: %s[%s]\n",
838 client_mech, client_oid, server_mech, server_oid);
840 spnego_state->downgraded = true;
841 gensec_spnego_update_sub_abort(spnego_state);
843 status = gensec_subcontext_start(spnego_state,
845 &spnego_state->sub_sec_security);
846 if (!NT_STATUS_IS_OK(status)) {
850 /* select the sub context */
851 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
853 if (!NT_STATUS_IS_OK(status)) {
857 spnego_state->neg_oid = talloc_strdup(spnego_state,
859 if (spnego_state->neg_oid == NULL) {
860 return NT_STATUS_NO_MEMORY;
864 if (ta->mechListMIC.length > 0) {
865 if (spnego_state->sub_sec_ready) {
866 spnego_state->needs_mic_check = true;
870 if (spnego_state->needs_mic_check) {
871 if (ta->responseToken.length != 0) {
872 DBG_WARNING("non empty response token not expected\n");
873 return NT_STATUS_INVALID_PARAMETER;
876 if (ta->mechListMIC.length == 0
877 && spnego_state->may_skip_mic_check) {
879 * In this case we don't require
880 * a mechListMIC from the server.
882 * This works around bugs in the Azure
883 * and Apple spnego implementations.
886 * https://bugzilla.samba.org/show_bug.cgi?id=11994
888 spnego_state->needs_mic_check = false;
892 status = gensec_check_packet(spnego_state->sub_sec_security,
893 spnego_state->mech_types.data,
894 spnego_state->mech_types.length,
895 spnego_state->mech_types.data,
896 spnego_state->mech_types.length,
898 if (!NT_STATUS_IS_OK(status)) {
899 DBG_WARNING("failed to verify mechListMIC: %s\n",
903 spnego_state->needs_mic_check = false;
904 spnego_state->done_mic_check = true;
908 if (!spnego_state->sub_sec_ready) {
909 *in_next = ta->responseToken;
910 return NT_STATUS_MORE_PROCESSING_REQUIRED;
916 static NTSTATUS gensec_spnego_client_negTokenTarg_step(
917 struct gensec_security *gensec_security,
918 struct spnego_state *spnego_state,
919 struct spnego_neg_state *n,
920 struct spnego_data *spnego_in,
921 NTSTATUS last_status,
922 TALLOC_CTX *in_mem_ctx,
925 if (GENSEC_UPDATE_IS_NTERROR(last_status)) {
926 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
927 spnego_state->sub_sec_security->ops->name,
928 nt_errstr(last_status));
933 * This should never be reached!
934 * The step function is only called on errors!
936 smb_panic(__location__);
937 return NT_STATUS_INTERNAL_ERROR;
940 static NTSTATUS gensec_spnego_client_negTokenTarg_finish(
941 struct gensec_security *gensec_security,
942 struct spnego_state *spnego_state,
943 struct spnego_neg_state *n,
944 struct spnego_data *spnego_in,
946 const DATA_BLOB sub_out,
947 TALLOC_CTX *out_mem_ctx,
950 const struct spnego_negTokenTarg *ta =
951 &spnego_in->negTokenTarg;
952 DATA_BLOB mech_list_mic = data_blob_null;
954 struct spnego_data spnego_out;
958 if (!spnego_state->sub_sec_ready) {
960 * We're not yet ready to deal with signatures.
962 goto client_response;
965 if (spnego_state->done_mic_check) {
967 * We already checked the mic,
968 * either the in last round here
969 * in gensec_spnego_client_negTokenTarg_finish()
970 * or during this round in
971 * gensec_spnego_client_negTokenTarg_start().
973 * Both cases we're sure we don't have to
974 * call gensec_sign_packet().
976 goto client_response;
979 if (spnego_state->may_skip_mic_check) {
981 * This can only be set during
982 * the last round here in
983 * gensec_spnego_client_negTokenTarg_finish()
984 * below. And during this round
985 * we already passed the checks in
986 * gensec_spnego_client_negTokenTarg_start().
988 * So we need to skip to deal with
989 * any signatures now.
991 goto client_response;
994 if (!spnego_state->done_mic_check) {
995 bool have_sign = true;
996 bool new_spnego = false;
998 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
999 GENSEC_FEATURE_SIGN);
1000 if (spnego_state->simulate_w2k) {
1003 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1004 GENSEC_FEATURE_NEW_SPNEGO);
1006 switch (ta->negResult) {
1007 case SPNEGO_ACCEPT_COMPLETED:
1008 case SPNEGO_NONE_RESULT:
1009 if (spnego_state->num_targs == 1) {
1011 * the first exchange doesn't require
1019 case SPNEGO_ACCEPT_INCOMPLETE:
1020 if (ta->mechListMIC.length > 0) {
1025 if (spnego_state->downgraded) {
1027 * A downgrade should be protected if
1034 * The caller may just asked for
1035 * GENSEC_FEATURE_SESSION_KEY, this
1036 * is only reflected in the want_features.
1039 * gensec_have_features(GENSEC_FEATURE_SIGN)
1042 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
1045 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
1049 * Here we're sure our preferred mech was
1050 * selected by the server and our caller doesn't
1051 * need GENSEC_FEATURE_SIGN nor
1052 * GENSEC_FEATURE_SEAL support.
1054 * In this case we don't require
1055 * a mechListMIC from the server.
1057 * This works around bugs in the Azure
1058 * and Apple spnego implementations.
1061 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1063 spnego_state->may_skip_mic_check = true;
1066 case SPNEGO_REQUEST_MIC:
1067 if (ta->mechListMIC.length > 0) {
1075 if (spnego_state->mic_requested) {
1081 if (have_sign && new_spnego) {
1082 spnego_state->needs_mic_check = true;
1083 spnego_state->needs_mic_sign = true;
1087 if (ta->mechListMIC.length > 0) {
1088 status = gensec_check_packet(spnego_state->sub_sec_security,
1089 spnego_state->mech_types.data,
1090 spnego_state->mech_types.length,
1091 spnego_state->mech_types.data,
1092 spnego_state->mech_types.length,
1094 if (!NT_STATUS_IS_OK(status)) {
1095 DBG_WARNING("failed to verify mechListMIC: %s\n",
1099 spnego_state->needs_mic_check = false;
1100 spnego_state->done_mic_check = true;
1103 if (spnego_state->needs_mic_sign) {
1104 status = gensec_sign_packet(spnego_state->sub_sec_security,
1106 spnego_state->mech_types.data,
1107 spnego_state->mech_types.length,
1108 spnego_state->mech_types.data,
1109 spnego_state->mech_types.length,
1111 if (!NT_STATUS_IS_OK(status)) {
1112 DBG_WARNING("failed to sign mechListMIC: %s\n",
1116 spnego_state->needs_mic_sign = false;
1120 if (sub_out.length == 0 && mech_list_mic.length == 0) {
1121 *out = data_blob_null;
1123 if (!spnego_state->sub_sec_ready) {
1124 /* somethings wrong here... */
1125 DBG_ERR("gensec_update not ready without output\n");
1126 return NT_STATUS_INTERNAL_ERROR;
1129 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
1130 /* unless of course it did not accept */
1131 DBG_WARNING("gensec_update ok but not accepted\n");
1132 return NT_STATUS_INVALID_PARAMETER;
1135 if (!spnego_state->needs_mic_check) {
1136 spnego_state->state_position = SPNEGO_DONE;
1137 return NT_STATUS_OK;
1142 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1143 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1144 spnego_out.negTokenTarg.supportedMech = NULL;
1145 spnego_out.negTokenTarg.responseToken = sub_out;
1146 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1148 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1149 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
1150 return NT_STATUS_INVALID_PARAMETER;
1153 spnego_state->num_targs++;
1155 /* set next state */
1156 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1157 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1159 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1162 static const struct spnego_neg_ops gensec_spnego_client_negTokenTarg_ops = {
1163 .name = "client_negTokenTarg",
1164 .start_fn = gensec_spnego_client_negTokenTarg_start,
1165 .step_fn = gensec_spnego_client_negTokenTarg_step,
1166 .finish_fn = gensec_spnego_client_negTokenTarg_finish,
1169 static NTSTATUS gensec_spnego_client_negTokenTarg(struct gensec_security *gensec_security,
1170 struct spnego_state *spnego_state,
1171 struct tevent_context *ev,
1172 struct spnego_data *spnego_in,
1173 TALLOC_CTX *out_mem_ctx,
1176 return gensec_spnego_neg_loop(gensec_security, spnego_state,
1177 &gensec_spnego_client_negTokenTarg_ops,
1178 ev, spnego_in, out_mem_ctx, out);
1180 /** create a server negTokenTarg
1182 * This is the case, where the client is the first one who sends data
1185 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
1186 TALLOC_CTX *out_mem_ctx,
1188 const DATA_BLOB unwrapped_out,
1189 DATA_BLOB mech_list_mic,
1192 struct spnego_data spnego_out;
1195 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1196 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1197 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1198 spnego_out.negTokenTarg.supportedMech = NULL;
1200 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1201 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1202 if (spnego_state->mic_requested) {
1203 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
1204 spnego_state->mic_requested = false;
1206 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1208 spnego_state->state_position = SPNEGO_SERVER_TARG;
1209 } else if (NT_STATUS_IS_OK(nt_status)) {
1210 if (unwrapped_out.data) {
1211 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1213 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1214 spnego_state->state_position = SPNEGO_DONE;
1217 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1218 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1219 return NT_STATUS_INVALID_PARAMETER;
1222 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1223 spnego_state->num_targs++;
1228 static NTSTATUS gensec_spnego_server_negTokenInit_start(
1229 struct gensec_security *gensec_security,
1230 struct spnego_state *spnego_state,
1231 struct spnego_neg_state *n,
1232 struct spnego_data *spnego_in,
1233 TALLOC_CTX *in_mem_ctx,
1239 n->mech_types = spnego_in->negTokenInit.mechTypes;
1240 if (n->mech_types == NULL) {
1241 return NT_STATUS_INVALID_PARAMETER;
1245 n->all_sec = gensec_security_by_oid_list(gensec_security,
1248 if (n->all_sec == NULL) {
1249 DBG_WARNING("gensec_security_by_oid_list() failed\n");
1250 return NT_STATUS_INVALID_PARAMETER;
1253 ok = spnego_write_mech_types(spnego_state,
1255 &spnego_state->mech_types);
1257 DBG_ERR("Failed to write mechTypes\n");
1258 return NT_STATUS_NO_MEMORY;
1261 return n->ops->step_fn(gensec_security, spnego_state, n,
1262 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
1265 static NTSTATUS gensec_spnego_server_negTokenInit_step(
1266 struct gensec_security *gensec_security,
1267 struct spnego_state *spnego_state,
1268 struct spnego_neg_state *n,
1269 struct spnego_data *spnego_in,
1270 NTSTATUS last_status,
1271 TALLOC_CTX *in_mem_ctx,
1274 if (!NT_STATUS_IS_OK(last_status)) {
1275 const struct gensec_security_ops_wrapper *cur_sec =
1276 &n->all_sec[n->all_idx];
1277 const char *next_mech = n->mech_types[n->mech_idx+1];
1278 const struct gensec_security_ops_wrapper *next_sec = NULL;
1279 const char *next = NULL;
1280 int dbg_level = DBGLVL_WARNING;
1281 bool allow_fallback = false;
1282 NTSTATUS status = last_status;
1285 for (i = 0; next_mech != NULL && n->all_sec[i].op != NULL; i++) {
1286 if (strcmp(next_mech, n->all_sec[i].oid) != 0) {
1290 next_sec = &n->all_sec[i];
1294 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1295 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
1297 allow_fallback = true;
1300 if (allow_fallback && next_sec != NULL) {
1301 next = next_sec->op->name;
1302 dbg_level = DBGLVL_NOTICE;
1305 DBG_PREFIX(dbg_level, (
1306 "%s: parsing NEG_TOKEN_INIT content failed "
1307 "(next[%s]): %s\n", cur_sec->op->name,
1308 next, nt_errstr(status)));
1312 * A hard error without a possible fallback.
1318 * Pretend we never started it
1320 gensec_spnego_update_sub_abort(spnego_state);
1323 * And try the next one, based on the clients
1330 * we always reset all_idx here, as the negotiation is
1331 * done via mech_idx!
1335 for (; n->mech_types[n->mech_idx] != NULL; n->mech_idx++) {
1336 const char *cur_mech = n->mech_types[n->mech_idx];
1337 const struct gensec_security_ops_wrapper *cur_sec = NULL;
1339 DATA_BLOB sub_in = data_blob_null;
1342 for (i = 0; n->all_sec[i].op != NULL; i++) {
1343 if (strcmp(cur_mech, n->all_sec[i].oid) != 0) {
1347 cur_sec = &n->all_sec[i];
1352 if (cur_sec == NULL) {
1356 status = gensec_subcontext_start(spnego_state,
1358 &spnego_state->sub_sec_security);
1359 if (!NT_STATUS_IS_OK(status)) {
1363 /* select the sub context */
1364 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
1366 if (!NT_STATUS_IS_OK(status)) {
1368 * Pretend we never started it
1370 gensec_spnego_update_sub_abort(spnego_state);
1374 if (n->mech_idx == 0) {
1376 * We can use the optimistic token.
1378 sub_in = spnego_in->negTokenInit.mechToken;
1381 * Indicate the downgrade and request a
1384 spnego_state->downgraded = true;
1385 spnego_state->mic_requested = true;
1389 * Note that 'cur_sec' is temporary memory, but
1390 * cur_sec->oid points to a const string in the
1391 * backends gensec_security_ops structure.
1393 spnego_state->neg_oid = cur_sec->oid;
1395 /* we need some content from the mech */
1397 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1400 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1401 return NT_STATUS_INVALID_PARAMETER;
1404 static NTSTATUS gensec_spnego_server_negTokenInit_finish(
1405 struct gensec_security *gensec_security,
1406 struct spnego_state *spnego_state,
1407 struct spnego_neg_state *n,
1408 struct spnego_data *spnego_in,
1409 NTSTATUS sub_status,
1410 const DATA_BLOB sub_out,
1411 TALLOC_CTX *out_mem_ctx,
1414 DATA_BLOB mech_list_mic = data_blob_null;
1416 if (spnego_state->simulate_w2k) {
1418 * Windows 2000 returns the unwrapped token
1419 * also in the mech_list_mic field.
1421 * In order to verify our client code,
1422 * we need a way to have a server with this
1425 mech_list_mic = sub_out;
1428 return gensec_spnego_server_response(spnego_state,
1436 static const struct spnego_neg_ops gensec_spnego_server_negTokenInit_ops = {
1437 .name = "server_negTokenInit",
1438 .start_fn = gensec_spnego_server_negTokenInit_start,
1439 .step_fn = gensec_spnego_server_negTokenInit_step,
1440 .finish_fn = gensec_spnego_server_negTokenInit_finish,
1443 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
1444 struct spnego_state *spnego_state,
1445 struct tevent_context *ev,
1446 struct spnego_data *spnego_in,
1447 TALLOC_CTX *out_mem_ctx,
1450 return gensec_spnego_neg_loop(gensec_security, spnego_state,
1451 &gensec_spnego_server_negTokenInit_ops,
1452 ev, spnego_in, out_mem_ctx, out);
1455 static NTSTATUS gensec_spnego_server_negTokenTarg_start(
1456 struct gensec_security *gensec_security,
1457 struct spnego_state *spnego_state,
1458 struct spnego_neg_state *n,
1459 struct spnego_data *spnego_in,
1460 TALLOC_CTX *in_mem_ctx,
1463 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1466 spnego_state->num_targs++;
1468 if (spnego_state->sub_sec_security == NULL) {
1469 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1470 return NT_STATUS_INVALID_PARAMETER;
1473 if (spnego_state->needs_mic_check) {
1474 if (ta->responseToken.length != 0) {
1475 DBG_WARNING("non empty response token not expected\n");
1476 return NT_STATUS_INVALID_PARAMETER;
1479 status = gensec_check_packet(spnego_state->sub_sec_security,
1480 spnego_state->mech_types.data,
1481 spnego_state->mech_types.length,
1482 spnego_state->mech_types.data,
1483 spnego_state->mech_types.length,
1485 if (!NT_STATUS_IS_OK(status)) {
1486 DBG_WARNING("failed to verify mechListMIC: %s\n",
1491 spnego_state->needs_mic_check = false;
1492 spnego_state->done_mic_check = true;
1493 return NT_STATUS_OK;
1496 if (!spnego_state->sub_sec_ready) {
1497 *in_next = ta->responseToken;
1498 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1501 return NT_STATUS_OK;
1504 static NTSTATUS gensec_spnego_server_negTokenTarg_step(
1505 struct gensec_security *gensec_security,
1506 struct spnego_state *spnego_state,
1507 struct spnego_neg_state *n,
1508 struct spnego_data *spnego_in,
1509 NTSTATUS last_status,
1510 TALLOC_CTX *in_mem_ctx,
1513 if (GENSEC_UPDATE_IS_NTERROR(last_status)) {
1514 DBG_NOTICE("SPNEGO(%s) login failed: %s\n",
1515 spnego_state->sub_sec_security->ops->name,
1516 nt_errstr(last_status));
1521 * This should never be reached!
1522 * The step function is only called on errors!
1524 smb_panic(__location__);
1525 return NT_STATUS_INTERNAL_ERROR;
1528 static NTSTATUS gensec_spnego_server_negTokenTarg_finish(
1529 struct gensec_security *gensec_security,
1530 struct spnego_state *spnego_state,
1531 struct spnego_neg_state *n,
1532 struct spnego_data *spnego_in,
1533 NTSTATUS sub_status,
1534 const DATA_BLOB sub_out,
1535 TALLOC_CTX *out_mem_ctx,
1538 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1539 DATA_BLOB mech_list_mic = data_blob_null;
1541 bool have_sign = true;
1542 bool new_spnego = false;
1544 status = sub_status;
1546 if (!spnego_state->sub_sec_ready) {
1548 * We're not yet ready to deal with signatures.
1550 goto server_response;
1553 if (spnego_state->done_mic_check) {
1555 * We already checked the mic,
1556 * either the in last round here
1557 * in gensec_spnego_server_negTokenTarg_finish()
1558 * or during this round in
1559 * gensec_spnego_server_negTokenTarg_start().
1561 * Both cases we're sure we don't have to
1562 * call gensec_sign_packet().
1564 goto server_response;
1567 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1568 GENSEC_FEATURE_SIGN);
1569 if (spnego_state->simulate_w2k) {
1572 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1573 GENSEC_FEATURE_NEW_SPNEGO);
1574 if (ta->mechListMIC.length > 0) {
1578 if (have_sign && new_spnego) {
1579 spnego_state->needs_mic_check = true;
1580 spnego_state->needs_mic_sign = true;
1583 if (have_sign && ta->mechListMIC.length > 0) {
1584 status = gensec_check_packet(spnego_state->sub_sec_security,
1585 spnego_state->mech_types.data,
1586 spnego_state->mech_types.length,
1587 spnego_state->mech_types.data,
1588 spnego_state->mech_types.length,
1590 if (!NT_STATUS_IS_OK(status)) {
1591 DBG_WARNING("failed to verify mechListMIC: %s\n",
1596 spnego_state->needs_mic_check = false;
1597 spnego_state->done_mic_check = true;
1600 if (spnego_state->needs_mic_sign) {
1601 status = gensec_sign_packet(spnego_state->sub_sec_security,
1603 spnego_state->mech_types.data,
1604 spnego_state->mech_types.length,
1605 spnego_state->mech_types.data,
1606 spnego_state->mech_types.length,
1608 if (!NT_STATUS_IS_OK(status)) {
1609 DBG_WARNING("failed to sign mechListMIC: %s\n",
1613 spnego_state->needs_mic_sign = false;
1616 if (spnego_state->needs_mic_check) {
1617 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1621 return gensec_spnego_server_response(spnego_state,
1629 static const struct spnego_neg_ops gensec_spnego_server_negTokenTarg_ops = {
1630 .name = "server_negTokenTarg",
1631 .start_fn = gensec_spnego_server_negTokenTarg_start,
1632 .step_fn = gensec_spnego_server_negTokenTarg_step,
1633 .finish_fn = gensec_spnego_server_negTokenTarg_finish,
1636 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1637 struct spnego_state *spnego_state,
1638 struct tevent_context *ev,
1639 struct spnego_data *spnego_in,
1640 TALLOC_CTX *out_mem_ctx,
1643 return gensec_spnego_neg_loop(gensec_security, spnego_state,
1644 &gensec_spnego_server_negTokenTarg_ops,
1645 ev, spnego_in, out_mem_ctx, out);
1648 struct gensec_spnego_update_state {
1649 struct tevent_context *ev;
1650 struct gensec_security *gensec;
1651 struct spnego_state *spnego;
1654 struct spnego_data _spnego_in;
1655 struct spnego_data *spnego_in;
1668 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1669 enum tevent_req_state req_state)
1671 struct gensec_spnego_update_state *state =
1672 tevent_req_data(req,
1673 struct gensec_spnego_update_state);
1675 switch (req_state) {
1676 case TEVENT_REQ_USER_ERROR:
1677 case TEVENT_REQ_TIMED_OUT:
1678 case TEVENT_REQ_NO_MEMORY:
1680 * A fatal error, further updates are not allowed.
1682 state->spnego->state_position = SPNEGO_DONE;
1689 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1690 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1691 DATA_BLOB *full_in);
1692 static void gensec_spnego_update_pre(struct tevent_req *req);
1693 static void gensec_spnego_update_done(struct tevent_req *subreq);
1694 static void gensec_spnego_update_post(struct tevent_req *req);
1695 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1696 TALLOC_CTX *out_mem_ctx,
1699 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1700 struct tevent_context *ev,
1701 struct gensec_security *gensec_security,
1704 struct spnego_state *spnego_state =
1705 talloc_get_type_abort(gensec_security->private_data,
1706 struct spnego_state);
1707 struct tevent_req *req = NULL;
1708 struct gensec_spnego_update_state *state = NULL;
1712 req = tevent_req_create(mem_ctx, &state,
1713 struct gensec_spnego_update_state);
1718 state->gensec = gensec_security;
1719 state->spnego = spnego_state;
1720 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1722 if (spnego_state->out_frag.length > 0) {
1723 if (in.length > 0) {
1724 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1725 return tevent_req_post(req, ev);
1728 status = gensec_spnego_update_out(gensec_security,
1729 state, &state->out);
1730 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1731 tevent_req_nterror(req, status);
1732 return tevent_req_post(req, ev);
1735 state->status = status;
1736 tevent_req_done(req);
1737 return tevent_req_post(req, ev);
1740 status = gensec_spnego_update_in(gensec_security, in,
1741 state, &state->full_in);
1742 state->status = status;
1743 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1744 tevent_req_done(req);
1745 return tevent_req_post(req, ev);
1747 if (tevent_req_nterror(req, status)) {
1748 return tevent_req_post(req, ev);
1751 /* Check if we got a valid SPNEGO blob... */
1753 switch (spnego_state->state_position) {
1754 case SPNEGO_FALLBACK:
1757 case SPNEGO_CLIENT_TARG:
1758 case SPNEGO_SERVER_TARG:
1759 if (state->full_in.length == 0) {
1760 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1761 return tevent_req_post(req, ev);
1765 case SPNEGO_CLIENT_START:
1766 case SPNEGO_SERVER_START:
1768 if (state->full_in.length == 0) {
1769 /* create_negTokenInit later */
1773 len = spnego_read_data(state,
1775 &state->_spnego_in);
1777 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1778 DEBUG(1, ("Invalid SPNEGO request:\n"));
1779 dump_data(1, state->full_in.data,
1780 state->full_in.length);
1781 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1782 return tevent_req_post(req, ev);
1786 * This is the 'fallback' case, where we don't get
1787 * SPNEGO, and have to try all the other options (and
1788 * hope they all have a magic string they check)
1790 status = gensec_spnego_server_try_fallback(gensec_security,
1794 if (tevent_req_nterror(req, status)) {
1795 return tevent_req_post(req, ev);
1799 * We'll continue with SPNEGO_FALLBACK below...
1803 state->spnego_in = &state->_spnego_in;
1805 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1806 if (state->spnego_in->type != spnego_state->expected_packet) {
1807 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1808 state->spnego_in->type,
1809 spnego_state->expected_packet));
1810 dump_data(1, state->full_in.data,
1811 state->full_in.length);
1812 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1813 return tevent_req_post(req, ev);
1819 smb_panic(__location__);
1823 gensec_spnego_update_pre(req);
1824 if (!tevent_req_is_in_progress(req)) {
1825 return tevent_req_post(req, ev);
1828 if (state->sub.needed) {
1829 struct tevent_req *subreq = NULL;
1832 * We may need one more roundtrip...
1834 subreq = gensec_update_send(state, state->ev,
1835 spnego_state->sub_sec_security,
1837 if (tevent_req_nomem(subreq, req)) {
1838 return tevent_req_post(req, ev);
1840 tevent_req_set_callback(subreq,
1841 gensec_spnego_update_done,
1843 state->sub.needed = false;
1847 gensec_spnego_update_post(req);
1848 if (!tevent_req_is_in_progress(req)) {
1849 return tevent_req_post(req, ev);
1855 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1856 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1859 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1863 *full_in = data_blob_null;
1865 switch (spnego_state->state_position) {
1866 case SPNEGO_FALLBACK:
1868 spnego_state->in_needed = 0;
1869 return NT_STATUS_OK;
1871 case SPNEGO_CLIENT_START:
1872 case SPNEGO_CLIENT_TARG:
1873 case SPNEGO_SERVER_START:
1874 case SPNEGO_SERVER_TARG:
1879 return NT_STATUS_INVALID_PARAMETER;
1882 if (spnego_state->in_needed == 0) {
1887 * try to work out the size of the full
1888 * input token, it might be fragmented
1890 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1891 if ((ret != 0) && (ret != EAGAIN)) {
1892 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1895 if ((ret == 0) || (ret == EAGAIN)) {
1896 spnego_state->in_needed = size;
1899 * If it is not an asn1 message
1900 * just call the next layer.
1902 spnego_state->in_needed = in.length;
1906 if (spnego_state->in_needed > UINT16_MAX) {
1908 * limit the incoming message to 0xFFFF
1909 * to avoid DoS attacks.
1911 return NT_STATUS_INVALID_BUFFER_SIZE;
1914 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1916 * If we reach this, we know we got at least
1917 * part of an asn1 message, getting 0 means
1918 * the remote peer wants us to spin.
1920 return NT_STATUS_INVALID_PARAMETER;
1923 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1924 if (in.length > expected) {
1926 * we got more than expected
1928 return NT_STATUS_INVALID_PARAMETER;
1931 if (in.length == spnego_state->in_needed) {
1933 * if the in.length contains the full blob
1936 * Note: this implies spnego_state->in_frag.length == 0,
1937 * but we do not need to check this explicitly
1938 * because we already know that we did not get
1939 * more than expected.
1942 spnego_state->in_needed = 0;
1943 return NT_STATUS_OK;
1946 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1947 in.data, in.length);
1949 return NT_STATUS_NO_MEMORY;
1952 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1953 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1956 *full_in = spnego_state->in_frag;
1957 talloc_steal(mem_ctx, full_in->data);
1958 spnego_state->in_frag = data_blob_null;
1959 spnego_state->in_needed = 0;
1960 return NT_STATUS_OK;
1963 static void gensec_spnego_update_pre(struct tevent_req *req)
1965 struct gensec_spnego_update_state *state =
1966 tevent_req_data(req,
1967 struct gensec_spnego_update_state);
1968 struct gensec_security *gensec_security = state->gensec;
1969 struct spnego_state *spnego_state = state->spnego;
1970 struct tevent_context *ev = state->ev;
1973 state->sub.needed = false;
1974 state->sub.in = data_blob_null;
1975 state->sub.status = NT_STATUS_INTERNAL_ERROR;
1976 state->sub.out = data_blob_null;
1978 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1979 state->sub.in = state->full_in;
1980 state->full_in = data_blob_null;
1981 state->sub.needed = true;
1985 switch (spnego_state->state_position) {
1986 case SPNEGO_CLIENT_START:
1987 if (state->spnego_in == NULL) {
1988 /* client to produce negTokenInit */
1989 status = gensec_spnego_create_negTokenInit(gensec_security,
1990 spnego_state, state, ev,
1991 &spnego_state->out_frag);
1992 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1993 tevent_req_nterror(req, status);
1999 status = gensec_spnego_client_negTokenInit(gensec_security,
2001 state->spnego_in, state,
2002 &spnego_state->out_frag);
2005 case SPNEGO_CLIENT_TARG:
2006 status = gensec_spnego_client_negTokenTarg(gensec_security,
2008 state->spnego_in, state,
2009 &spnego_state->out_frag);
2010 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2011 tevent_req_nterror(req, status);
2016 case SPNEGO_SERVER_START:
2017 if (state->spnego_in == NULL) {
2018 /* server to produce negTokenInit */
2019 status = gensec_spnego_create_negTokenInit(gensec_security,
2020 spnego_state, state, ev,
2021 &spnego_state->out_frag);
2022 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2023 tevent_req_nterror(req, status);
2029 status = gensec_spnego_server_negTokenInit(gensec_security,
2031 state->spnego_in, state,
2032 &spnego_state->out_frag);
2033 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2034 tevent_req_nterror(req, status);
2039 case SPNEGO_SERVER_TARG:
2040 status = gensec_spnego_server_negTokenTarg(gensec_security,
2042 state->spnego_in, state,
2043 &spnego_state->out_frag);
2044 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2045 tevent_req_nterror(req, status);
2051 smb_panic(__location__);
2055 spnego_state->out_status = status;
2058 static void gensec_spnego_update_done(struct tevent_req *subreq)
2060 struct tevent_req *req =
2061 tevent_req_callback_data(subreq,
2063 struct gensec_spnego_update_state *state =
2064 tevent_req_data(req,
2065 struct gensec_spnego_update_state);
2066 struct spnego_state *spnego_state = state->spnego;
2068 state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
2069 TALLOC_FREE(subreq);
2070 if (NT_STATUS_IS_OK(state->sub.status)) {
2071 spnego_state->sub_sec_ready = true;
2074 gensec_spnego_update_post(req);
2077 static void gensec_spnego_update_post(struct tevent_req *req)
2079 struct gensec_spnego_update_state *state =
2080 tevent_req_data(req,
2081 struct gensec_spnego_update_state);
2082 struct spnego_state *spnego_state = state->spnego;
2085 state->sub.in = data_blob_null;
2086 state->sub.needed = false;
2088 if (spnego_state->state_position == SPNEGO_FALLBACK) {
2089 status = state->sub.status;
2090 spnego_state->out_frag = state->sub.out;
2091 talloc_steal(spnego_state, spnego_state->out_frag.data);
2092 state->sub.out = data_blob_null;
2097 * For now just handle the sync processing done
2098 * in gensec_spnego_update_pre()
2100 status = spnego_state->out_status;
2102 if (NT_STATUS_IS_OK(status)) {
2103 bool reset_full = true;
2105 reset_full = !spnego_state->done_mic_check;
2107 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
2109 if (tevent_req_nterror(req, status)) {
2115 spnego_state->out_status = status;
2117 status = gensec_spnego_update_out(state->gensec,
2118 state, &state->out);
2119 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2120 tevent_req_nterror(req, status);
2124 state->status = status;
2125 tevent_req_done(req);
2129 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
2130 TALLOC_CTX *out_mem_ctx,
2133 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
2134 DATA_BLOB out = data_blob_null;
2137 *_out = data_blob_null;
2139 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
2141 * Fast path, we can deliver everything
2144 *_out = spnego_state->out_frag;
2145 if (spnego_state->out_frag.length > 0) {
2146 talloc_steal(out_mem_ctx, _out->data);
2147 spnego_state->out_frag = data_blob_null;
2150 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
2151 return spnego_state->out_status;
2155 * We're completely done, further updates are not allowed.
2157 spnego_state->state_position = SPNEGO_DONE;
2158 return gensec_child_ready(gensec_security,
2159 spnego_state->sub_sec_security);
2162 out = spnego_state->out_frag;
2165 * copy the remaining bytes
2167 spnego_state->out_frag = data_blob_talloc(spnego_state,
2168 out.data + spnego_state->out_max_length,
2169 out.length - spnego_state->out_max_length);
2170 if (spnego_state->out_frag.data == NULL) {
2171 return NT_STATUS_NO_MEMORY;
2175 * truncate the buffer
2177 ok = data_blob_realloc(spnego_state, &out,
2178 spnego_state->out_max_length);
2180 return NT_STATUS_NO_MEMORY;
2183 talloc_steal(out_mem_ctx, out.data);
2185 return NT_STATUS_MORE_PROCESSING_REQUIRED;
2188 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
2189 TALLOC_CTX *out_mem_ctx,
2192 struct gensec_spnego_update_state *state =
2193 tevent_req_data(req,
2194 struct gensec_spnego_update_state);
2197 *out = data_blob_null;
2199 if (tevent_req_is_nterror(req, &status)) {
2200 tevent_req_received(req);
2205 talloc_steal(out_mem_ctx, state->out.data);
2206 status = state->status;
2207 tevent_req_received(req);
2211 static const char *gensec_spnego_oids[] = {
2216 static const struct gensec_security_ops gensec_spnego_security_ops = {
2218 .sasl_name = "GSS-SPNEGO",
2219 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
2220 .oid = gensec_spnego_oids,
2221 .client_start = gensec_spnego_client_start,
2222 .server_start = gensec_spnego_server_start,
2223 .update_send = gensec_spnego_update_send,
2224 .update_recv = gensec_spnego_update_recv,
2225 .seal_packet = gensec_child_seal_packet,
2226 .sign_packet = gensec_child_sign_packet,
2227 .sig_size = gensec_child_sig_size,
2228 .max_wrapped_size = gensec_child_max_wrapped_size,
2229 .max_input_size = gensec_child_max_input_size,
2230 .check_packet = gensec_child_check_packet,
2231 .unseal_packet = gensec_child_unseal_packet,
2232 .wrap = gensec_child_wrap,
2233 .unwrap = gensec_child_unwrap,
2234 .session_key = gensec_child_session_key,
2235 .session_info = gensec_child_session_info,
2236 .want_feature = gensec_child_want_feature,
2237 .have_feature = gensec_child_have_feature,
2238 .expire_time = gensec_child_expire_time,
2239 .final_auth_type = gensec_child_final_auth_type,
2241 .priority = GENSEC_SPNEGO
2244 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
2247 ret = gensec_register(ctx, &gensec_spnego_security_ops);
2248 if (!NT_STATUS_IS_OK(ret)) {
2249 DEBUG(0,("Failed to register '%s' gensec backend!\n",
2250 gensec_spnego_security_ops.name));