s3 rpc_client: Fix Asan stack use after scope
[Samba.git] / auth / gensec / spnego.c
blob0b3fbdce7acd1b44a85f1109d4e2961e0d5668e0
1 /*
2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2008
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include <tevent.h>
27 #include "lib/util/tevent_ntstatus.h"
28 #include "../libcli/auth/spnego.h"
29 #include "librpc/gen_ndr/ndr_dcerpc.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "auth/gensec/gensec_internal.h"
33 #include "param/param.h"
34 #include "lib/util/asn1.h"
35 #include "lib/util/base64.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_AUTH
40 #undef strcasecmp
42 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
44 enum spnego_state_position {
45 SPNEGO_SERVER_START,
46 SPNEGO_CLIENT_START,
47 SPNEGO_SERVER_TARG,
48 SPNEGO_CLIENT_TARG,
49 SPNEGO_FALLBACK,
50 SPNEGO_DONE
53 struct spnego_state;
54 struct spnego_neg_ops;
55 struct spnego_neg_state;
57 struct spnego_neg_state {
58 const struct spnego_neg_ops *ops;
59 const struct gensec_security_ops_wrapper *all_sec;
60 size_t all_idx;
61 const char * const *mech_types;
62 size_t mech_idx;
65 struct spnego_neg_ops {
66 const char *name;
68 * The start hook does the initial processing on the incoming paket and
69 * may starts the first possible subcontext. It indicates that
70 * gensec_update() is required on the subcontext by returning
71 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
72 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
73 * caller should treat 'in_next' as const and don't attempt to free the
74 * content. NT_STATUS_OK indicates the finish hook should be invoked
75 * directly withing the need of gensec_update() on the subcontext.
76 * Every other error indicates an error that's returned to the caller.
78 NTSTATUS (*start_fn)(struct gensec_security *gensec_security,
79 struct spnego_state *spnego_state,
80 struct spnego_neg_state *n,
81 struct spnego_data *spnego_in,
82 TALLOC_CTX *in_mem_ctx,
83 DATA_BLOB *in_next);
85 * The step hook processes the result of a failed gensec_update() and
86 * can decide to ignore a failure and continue the negotiation by
87 * setting up the next possible subcontext. It indicates that
88 * gensec_update() is required on the subcontext by returning
89 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
90 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
91 * caller should treat 'in_next' as const and don't attempt to free the
92 * content. NT_STATUS_OK indicates the finish hook should be invoked
93 * directly withing the need of gensec_update() on the subcontext.
94 * Every other error indicates an error that's returned to the caller.
96 NTSTATUS (*step_fn)(struct gensec_security *gensec_security,
97 struct spnego_state *spnego_state,
98 struct spnego_neg_state *n,
99 struct spnego_data *spnego_in,
100 NTSTATUS last_status,
101 TALLOC_CTX *in_mem_ctx,
102 DATA_BLOB *in_next);
104 * The finish hook processes the result of a successful gensec_update()
105 * (NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED). It forms the
106 * response pdu that will be returned from the toplevel gensec_update()
107 * together with NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED. It
108 * may also alter the state machine to prepare receiving the next pdu
109 * from the peer.
111 NTSTATUS (*finish_fn)(struct gensec_security *gensec_security,
112 struct spnego_state *spnego_state,
113 struct spnego_neg_state *n,
114 struct spnego_data *spnego_in,
115 NTSTATUS sub_status,
116 const DATA_BLOB sub_out,
117 TALLOC_CTX *out_mem_ctx,
118 DATA_BLOB *out);
121 struct spnego_state {
122 enum spnego_message_type expected_packet;
123 enum spnego_state_position state_position;
124 struct gensec_security *sub_sec_security;
125 bool sub_sec_ready;
127 const char *neg_oid;
129 DATA_BLOB mech_types;
130 size_t num_targs;
131 bool downgraded;
132 bool mic_requested;
133 bool needs_mic_sign;
134 bool needs_mic_check;
135 bool may_skip_mic_check;
136 bool done_mic_check;
138 bool simulate_w2k;
141 * The following is used to implement
142 * the update token fragmentation
144 size_t in_needed;
145 DATA_BLOB in_frag;
146 size_t out_max_length;
147 DATA_BLOB out_frag;
148 NTSTATUS out_status;
151 static struct spnego_neg_state *gensec_spnego_neg_state(TALLOC_CTX *mem_ctx,
152 const struct spnego_neg_ops *ops)
154 struct spnego_neg_state *n = NULL;
156 n = talloc_zero(mem_ctx, struct spnego_neg_state);
157 if (n == NULL) {
158 return NULL;
160 n->ops = ops;
162 return n;
165 static void gensec_spnego_reset_sub_sec(struct spnego_state *spnego_state)
167 spnego_state->sub_sec_ready = false;
168 TALLOC_FREE(spnego_state->sub_sec_security);
171 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
173 struct spnego_state *spnego_state;
175 spnego_state = talloc_zero(gensec_security, struct spnego_state);
176 if (!spnego_state) {
177 return NT_STATUS_NO_MEMORY;
180 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
181 spnego_state->state_position = SPNEGO_CLIENT_START;
182 spnego_state->sub_sec_security = NULL;
183 spnego_state->sub_sec_ready = false;
184 spnego_state->mech_types = data_blob_null;
185 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
186 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
188 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
189 "spnego", "simulate_w2k", false);
191 gensec_security->private_data = spnego_state;
192 return NT_STATUS_OK;
195 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
197 struct spnego_state *spnego_state;
199 spnego_state = talloc_zero(gensec_security, struct spnego_state);
200 if (!spnego_state) {
201 return NT_STATUS_NO_MEMORY;
204 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
205 spnego_state->state_position = SPNEGO_SERVER_START;
206 spnego_state->sub_sec_security = NULL;
207 spnego_state->sub_sec_ready = false;
208 spnego_state->mech_types = data_blob_null;
209 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
210 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
212 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
213 "spnego", "simulate_w2k", false);
215 gensec_security->private_data = spnego_state;
216 return NT_STATUS_OK;
219 /** Fallback to another GENSEC mechanism, based on magic strings
221 * This is the 'fallback' case, where we don't get SPNEGO, and have to
222 * try all the other options (and hope they all have a magic string
223 * they check)
226 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
227 struct spnego_state *spnego_state,
228 TALLOC_CTX *mem_ctx,
229 const DATA_BLOB in)
231 int i,j;
232 const struct gensec_security_ops **all_ops;
234 all_ops = gensec_security_mechs(gensec_security, mem_ctx);
236 for (i=0; all_ops && all_ops[i]; i++) {
237 bool is_spnego;
238 NTSTATUS nt_status;
240 if (gensec_security != NULL &&
241 !gensec_security_ops_enabled(all_ops[i], gensec_security))
243 continue;
246 if (!all_ops[i]->oid) {
247 continue;
250 is_spnego = false;
251 for (j=0; all_ops[i]->oid[j]; j++) {
252 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
253 is_spnego = true;
256 if (is_spnego) {
257 continue;
260 if (!all_ops[i]->magic) {
261 continue;
264 nt_status = all_ops[i]->magic(gensec_security, &in);
265 if (!NT_STATUS_IS_OK(nt_status)) {
266 continue;
269 spnego_state->state_position = SPNEGO_FALLBACK;
271 nt_status = gensec_subcontext_start(spnego_state,
272 gensec_security,
273 &spnego_state->sub_sec_security);
275 if (!NT_STATUS_IS_OK(nt_status)) {
276 return nt_status;
278 /* select the sub context */
279 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
280 all_ops[i]);
281 if (!NT_STATUS_IS_OK(nt_status)) {
282 return nt_status;
285 return NT_STATUS_OK;
287 DEBUG(1, ("Failed to parse SPNEGO request\n"));
288 return NT_STATUS_INVALID_PARAMETER;
291 static NTSTATUS gensec_spnego_create_negTokenInit_start(
292 struct gensec_security *gensec_security,
293 struct spnego_state *spnego_state,
294 struct spnego_neg_state *n,
295 struct spnego_data *spnego_in,
296 TALLOC_CTX *in_mem_ctx,
297 DATA_BLOB *in_next)
299 n->mech_idx = 0;
300 n->mech_types = gensec_security_oids(gensec_security, n,
301 GENSEC_OID_SPNEGO);
302 if (n->mech_types == NULL) {
303 DBG_WARNING("gensec_security_oids() failed\n");
304 return NT_STATUS_NO_MEMORY;
307 n->all_idx = 0;
308 n->all_sec = gensec_security_by_oid_list(gensec_security,
309 n, n->mech_types,
310 GENSEC_OID_SPNEGO);
311 if (n->all_sec == NULL) {
312 DBG_WARNING("gensec_security_by_oid_list() failed\n");
313 return NT_STATUS_NO_MEMORY;
316 return n->ops->step_fn(gensec_security, spnego_state, n,
317 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
320 static NTSTATUS gensec_spnego_create_negTokenInit_step(
321 struct gensec_security *gensec_security,
322 struct spnego_state *spnego_state,
323 struct spnego_neg_state *n,
324 struct spnego_data *spnego_in,
325 NTSTATUS last_status,
326 TALLOC_CTX *in_mem_ctx,
327 DATA_BLOB *in_next)
329 if (!NT_STATUS_IS_OK(last_status)) {
330 const struct gensec_security_ops_wrapper *cur_sec =
331 &n->all_sec[n->all_idx];
332 const struct gensec_security_ops_wrapper *next_sec = NULL;
333 const char *next = NULL;
334 const char *principal = NULL;
335 int dbg_level = DBGLVL_WARNING;
336 NTSTATUS status = last_status;
338 if (cur_sec[1].op != NULL) {
339 next_sec = &cur_sec[1];
342 if (next_sec != NULL) {
343 next = next_sec->op->name;
344 dbg_level = DBGLVL_NOTICE;
347 if (gensec_security->target.principal != NULL) {
348 principal = gensec_security->target.principal;
349 } else if (gensec_security->target.service != NULL &&
350 gensec_security->target.hostname != NULL)
352 principal = talloc_asprintf(spnego_state->sub_sec_security,
353 "%s/%s",
354 gensec_security->target.service,
355 gensec_security->target.hostname);
356 } else {
357 principal = gensec_security->target.hostname;
360 DBG_PREFIX(dbg_level, (
361 "%s: creating NEG_TOKEN_INIT for %s failed "
362 "(next[%s]): %s\n", cur_sec->op->name,
363 principal, next, nt_errstr(status)));
365 if (next == NULL) {
367 * A hard error without a possible fallback.
369 return status;
373 * Pretend we never started it
375 gensec_spnego_reset_sub_sec(spnego_state);
378 * And try the next one...
380 n->all_idx += 1;
383 for (; n->all_sec[n->all_idx].op != NULL; n->all_idx++) {
384 const struct gensec_security_ops_wrapper *cur_sec =
385 &n->all_sec[n->all_idx];
386 NTSTATUS status;
388 status = gensec_subcontext_start(spnego_state,
389 gensec_security,
390 &spnego_state->sub_sec_security);
391 if (!NT_STATUS_IS_OK(status)) {
392 return status;
395 /* select the sub context */
396 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
397 cur_sec->op);
398 if (!NT_STATUS_IS_OK(status)) {
399 gensec_spnego_reset_sub_sec(spnego_state);
400 continue;
403 /* In the client, try and produce the first (optimistic) packet */
404 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
405 *in_next = data_blob_null;
406 return NT_STATUS_MORE_PROCESSING_REQUIRED;
409 *in_next = data_blob_null;
410 return NT_STATUS_OK;
413 DBG_WARNING("Failed to setup SPNEGO negTokenInit request\n");
414 return NT_STATUS_INVALID_PARAMETER;
417 static NTSTATUS gensec_spnego_create_negTokenInit_finish(
418 struct gensec_security *gensec_security,
419 struct spnego_state *spnego_state,
420 struct spnego_neg_state *n,
421 struct spnego_data *spnego_in,
422 NTSTATUS sub_status,
423 const DATA_BLOB sub_out,
424 TALLOC_CTX *out_mem_ctx,
425 DATA_BLOB *out)
427 const struct gensec_security_ops_wrapper *cur_sec =
428 &n->all_sec[n->all_idx];
429 struct spnego_data spnego_out;
430 bool ok;
432 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
434 n->mech_types = gensec_security_oids_from_ops_wrapped(n, cur_sec);
435 if (n->mech_types == NULL) {
436 DBG_WARNING("gensec_security_oids_from_ops_wrapped() failed\n");
437 return NT_STATUS_NO_MEMORY;
440 ok = spnego_write_mech_types(spnego_state,
441 n->mech_types,
442 &spnego_state->mech_types);
443 if (!ok) {
444 DBG_ERR("Failed to write mechTypes\n");
445 return NT_STATUS_NO_MEMORY;
448 /* List the remaining mechs as options */
449 spnego_out.negTokenInit.mechTypes = n->mech_types;
450 spnego_out.negTokenInit.reqFlags = data_blob_null;
451 spnego_out.negTokenInit.reqFlagsPadding = 0;
453 if (spnego_state->state_position == SPNEGO_SERVER_START) {
454 spnego_out.negTokenInit.mechListMIC
455 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
456 } else {
457 spnego_out.negTokenInit.mechListMIC = data_blob_null;
460 spnego_out.negTokenInit.mechToken = sub_out;
462 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
463 DBG_ERR("Failed to write NEG_TOKEN_INIT\n");
464 return NT_STATUS_INVALID_PARAMETER;
468 * Note that 'cur_sec' is temporary memory, but
469 * cur_sec->oid points to a const string in the
470 * backends gensec_security_ops structure.
472 spnego_state->neg_oid = cur_sec->oid;
474 /* set next state */
475 if (spnego_state->state_position == SPNEGO_SERVER_START) {
476 spnego_state->state_position = SPNEGO_SERVER_START;
477 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
478 } else {
479 spnego_state->state_position = SPNEGO_CLIENT_TARG;
480 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
483 return NT_STATUS_MORE_PROCESSING_REQUIRED;
486 static const struct spnego_neg_ops gensec_spnego_create_negTokenInit_ops = {
487 .name = "create_negTokenInit",
488 .start_fn = gensec_spnego_create_negTokenInit_start,
489 .step_fn = gensec_spnego_create_negTokenInit_step,
490 .finish_fn = gensec_spnego_create_negTokenInit_finish,
493 static NTSTATUS gensec_spnego_client_negTokenInit_start(
494 struct gensec_security *gensec_security,
495 struct spnego_state *spnego_state,
496 struct spnego_neg_state *n,
497 struct spnego_data *spnego_in,
498 TALLOC_CTX *in_mem_ctx,
499 DATA_BLOB *in_next)
501 const char *tp = NULL;
503 /* The server offers a list of mechanisms */
505 tp = spnego_in->negTokenInit.targetPrincipal;
506 if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
507 DBG_INFO("Server claims it's principal name is %s\n", tp);
508 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
509 gensec_set_target_principal(gensec_security, tp);
513 n->mech_idx = 0;
514 n->mech_types = spnego_in->negTokenInit.mechTypes;
515 if (n->mech_types == NULL) {
516 return NT_STATUS_INVALID_PARAMETER;
519 n->all_idx = 0;
520 n->all_sec = gensec_security_by_oid_list(gensec_security,
521 n, n->mech_types,
522 GENSEC_OID_SPNEGO);
523 if (n->all_sec == NULL) {
524 DBG_WARNING("gensec_security_by_oid_list() failed\n");
525 return NT_STATUS_INVALID_PARAMETER;
528 return n->ops->step_fn(gensec_security, spnego_state, n,
529 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
532 static NTSTATUS gensec_spnego_client_negTokenInit_step(
533 struct gensec_security *gensec_security,
534 struct spnego_state *spnego_state,
535 struct spnego_neg_state *n,
536 struct spnego_data *spnego_in,
537 NTSTATUS last_status,
538 TALLOC_CTX *in_mem_ctx,
539 DATA_BLOB *in_next)
541 if (!NT_STATUS_IS_OK(last_status)) {
542 const struct gensec_security_ops_wrapper *cur_sec =
543 &n->all_sec[n->all_idx];
544 const struct gensec_security_ops_wrapper *next_sec = NULL;
545 const char *next = NULL;
546 const char *principal = NULL;
547 int dbg_level = DBGLVL_WARNING;
548 bool allow_fallback = false;
549 NTSTATUS status = last_status;
551 if (cur_sec[1].op != NULL) {
552 next_sec = &cur_sec[1];
556 * it is likely that a NULL input token will
557 * not be liked by most server mechs, but if
558 * we are in the client, we want the first
559 * update packet to be able to abort the use
560 * of this mech
562 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
563 NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) ||
564 NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
565 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
567 allow_fallback = true;
570 if (allow_fallback && next_sec != NULL) {
571 next = next_sec->op->name;
572 dbg_level = DBGLVL_NOTICE;
575 if (gensec_security->target.principal != NULL) {
576 principal = gensec_security->target.principal;
577 } else if (gensec_security->target.service != NULL &&
578 gensec_security->target.hostname != NULL)
580 principal = talloc_asprintf(spnego_state->sub_sec_security,
581 "%s/%s",
582 gensec_security->target.service,
583 gensec_security->target.hostname);
584 } else {
585 principal = gensec_security->target.hostname;
588 DBG_PREFIX(dbg_level, (
589 "%s: creating NEG_TOKEN_INIT for %s failed "
590 "(next[%s]): %s\n", cur_sec->op->name,
591 principal, next, nt_errstr(status)));
593 if (next == NULL) {
595 * A hard error without a possible fallback.
597 return status;
601 * Pretend we never started it.
603 gensec_spnego_reset_sub_sec(spnego_state);
606 * And try the next one...
608 n->all_idx += 1;
611 for (; n->all_sec[n->all_idx].op != NULL; n->all_idx++) {
612 const struct gensec_security_ops_wrapper *cur_sec =
613 &n->all_sec[n->all_idx];
614 NTSTATUS status;
616 status = gensec_subcontext_start(spnego_state,
617 gensec_security,
618 &spnego_state->sub_sec_security);
619 if (!NT_STATUS_IS_OK(status)) {
620 return status;
623 /* select the sub context */
624 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
625 cur_sec->op);
626 if (!NT_STATUS_IS_OK(status)) {
627 gensec_spnego_reset_sub_sec(spnego_state);
628 continue;
632 * Note that 'cur_sec' is temporary memory, but
633 * cur_sec->oid points to a const string in the
634 * backends gensec_security_ops structure.
636 spnego_state->neg_oid = cur_sec->oid;
639 * As client we don't use an optimistic token from the server.
640 * But try to produce one for the server.
642 *in_next = data_blob_null;
643 return NT_STATUS_MORE_PROCESSING_REQUIRED;
646 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
647 return NT_STATUS_INVALID_PARAMETER;
650 static NTSTATUS gensec_spnego_client_negTokenInit_finish(
651 struct gensec_security *gensec_security,
652 struct spnego_state *spnego_state,
653 struct spnego_neg_state *n,
654 struct spnego_data *spnego_in,
655 NTSTATUS sub_status,
656 const DATA_BLOB sub_out,
657 TALLOC_CTX *out_mem_ctx,
658 DATA_BLOB *out)
660 struct spnego_data spnego_out;
661 const char *my_mechs[] = {NULL, NULL};
662 bool ok;
664 my_mechs[0] = spnego_state->neg_oid;
665 /* compose reply */
666 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
667 spnego_out.negTokenInit.mechTypes = my_mechs;
668 spnego_out.negTokenInit.reqFlags = data_blob_null;
669 spnego_out.negTokenInit.reqFlagsPadding = 0;
670 spnego_out.negTokenInit.mechListMIC = data_blob_null;
671 spnego_out.negTokenInit.mechToken = sub_out;
673 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
674 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
675 return NT_STATUS_INVALID_PARAMETER;
678 ok = spnego_write_mech_types(spnego_state,
679 my_mechs,
680 &spnego_state->mech_types);
681 if (!ok) {
682 DBG_ERR("failed to write mechTypes\n");
683 return NT_STATUS_NO_MEMORY;
686 /* set next state */
687 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
688 spnego_state->state_position = SPNEGO_CLIENT_TARG;
690 return NT_STATUS_MORE_PROCESSING_REQUIRED;
693 static const struct spnego_neg_ops gensec_spnego_client_negTokenInit_ops = {
694 .name = "client_negTokenInit",
695 .start_fn = gensec_spnego_client_negTokenInit_start,
696 .step_fn = gensec_spnego_client_negTokenInit_step,
697 .finish_fn = gensec_spnego_client_negTokenInit_finish,
700 static NTSTATUS gensec_spnego_client_negTokenTarg_start(
701 struct gensec_security *gensec_security,
702 struct spnego_state *spnego_state,
703 struct spnego_neg_state *n,
704 struct spnego_data *spnego_in,
705 TALLOC_CTX *in_mem_ctx,
706 DATA_BLOB *in_next)
708 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
709 NTSTATUS status;
711 spnego_state->num_targs++;
713 if (ta->negResult == SPNEGO_REJECT) {
714 return NT_STATUS_LOGON_FAILURE;
717 if (ta->negResult == SPNEGO_REQUEST_MIC) {
718 spnego_state->mic_requested = true;
721 if (ta->mechListMIC.length > 0) {
722 DATA_BLOB *m = &ta->mechListMIC;
723 const DATA_BLOB *r = &ta->responseToken;
726 * Windows 2000 has a bug, it repeats the
727 * responseToken in the mechListMIC field.
729 if (m->length == r->length) {
730 int cmp;
732 cmp = memcmp(m->data, r->data, m->length);
733 if (cmp == 0) {
734 data_blob_free(m);
739 /* Server didn't like our choice of mech, and chose something else */
740 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
741 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
742 ta->supportedMech != NULL &&
743 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
745 const char *client_mech = NULL;
746 const char *client_oid = NULL;
747 const char *server_mech = NULL;
748 const char *server_oid = NULL;
750 client_mech = gensec_get_name_by_oid(gensec_security,
751 spnego_state->neg_oid);
752 client_oid = spnego_state->neg_oid;
753 server_mech = gensec_get_name_by_oid(gensec_security,
754 ta->supportedMech);
755 server_oid = ta->supportedMech;
757 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
758 "server wants: %s[%s]\n",
759 client_mech, client_oid, server_mech, server_oid);
761 spnego_state->downgraded = true;
762 gensec_spnego_reset_sub_sec(spnego_state);
764 status = gensec_subcontext_start(spnego_state,
765 gensec_security,
766 &spnego_state->sub_sec_security);
767 if (!NT_STATUS_IS_OK(status)) {
768 return status;
771 /* select the sub context */
772 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
773 ta->supportedMech);
774 if (!NT_STATUS_IS_OK(status)) {
775 return status;
778 spnego_state->neg_oid = talloc_strdup(spnego_state,
779 ta->supportedMech);
780 if (spnego_state->neg_oid == NULL) {
781 return NT_STATUS_NO_MEMORY;
785 if (ta->mechListMIC.length > 0) {
786 if (spnego_state->sub_sec_ready) {
787 spnego_state->needs_mic_check = true;
791 if (spnego_state->needs_mic_check) {
792 if (ta->responseToken.length != 0) {
793 DBG_WARNING("non empty response token not expected\n");
794 return NT_STATUS_INVALID_PARAMETER;
797 if (ta->mechListMIC.length == 0
798 && spnego_state->may_skip_mic_check) {
800 * In this case we don't require
801 * a mechListMIC from the server.
803 * This works around bugs in the Azure
804 * and Apple spnego implementations.
806 * See
807 * https://bugzilla.samba.org/show_bug.cgi?id=11994
809 spnego_state->needs_mic_check = false;
810 return NT_STATUS_OK;
813 status = gensec_check_packet(spnego_state->sub_sec_security,
814 spnego_state->mech_types.data,
815 spnego_state->mech_types.length,
816 spnego_state->mech_types.data,
817 spnego_state->mech_types.length,
818 &ta->mechListMIC);
819 if (!NT_STATUS_IS_OK(status)) {
820 DBG_WARNING("failed to verify mechListMIC: %s\n",
821 nt_errstr(status));
822 return status;
824 spnego_state->needs_mic_check = false;
825 spnego_state->done_mic_check = true;
826 return NT_STATUS_OK;
829 if (!spnego_state->sub_sec_ready) {
830 *in_next = ta->responseToken;
831 return NT_STATUS_MORE_PROCESSING_REQUIRED;
834 return NT_STATUS_OK;
837 static NTSTATUS gensec_spnego_client_negTokenTarg_step(
838 struct gensec_security *gensec_security,
839 struct spnego_state *spnego_state,
840 struct spnego_neg_state *n,
841 struct spnego_data *spnego_in,
842 NTSTATUS last_status,
843 TALLOC_CTX *in_mem_ctx,
844 DATA_BLOB *in_next)
846 if (GENSEC_UPDATE_IS_NTERROR(last_status)) {
847 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
848 spnego_state->sub_sec_security->ops->name,
849 nt_errstr(last_status));
850 return last_status;
854 * This should never be reached!
855 * The step function is only called on errors!
857 smb_panic(__location__);
858 return NT_STATUS_INTERNAL_ERROR;
861 static NTSTATUS gensec_spnego_client_negTokenTarg_finish(
862 struct gensec_security *gensec_security,
863 struct spnego_state *spnego_state,
864 struct spnego_neg_state *n,
865 struct spnego_data *spnego_in,
866 NTSTATUS sub_status,
867 const DATA_BLOB sub_out,
868 TALLOC_CTX *out_mem_ctx,
869 DATA_BLOB *out)
871 const struct spnego_negTokenTarg *ta =
872 &spnego_in->negTokenTarg;
873 DATA_BLOB mech_list_mic = data_blob_null;
874 NTSTATUS status;
875 struct spnego_data spnego_out;
877 status = sub_status;
879 if (!spnego_state->sub_sec_ready) {
881 * We're not yet ready to deal with signatures.
883 goto client_response;
886 if (spnego_state->done_mic_check) {
888 * We already checked the mic,
889 * either the in last round here
890 * in gensec_spnego_client_negTokenTarg_finish()
891 * or during this round in
892 * gensec_spnego_client_negTokenTarg_start().
894 * Both cases we're sure we don't have to
895 * call gensec_sign_packet().
897 goto client_response;
900 if (spnego_state->may_skip_mic_check) {
902 * This can only be set during
903 * the last round here in
904 * gensec_spnego_client_negTokenTarg_finish()
905 * below. And during this round
906 * we already passed the checks in
907 * gensec_spnego_client_negTokenTarg_start().
909 * So we need to skip to deal with
910 * any signatures now.
912 goto client_response;
915 if (!spnego_state->done_mic_check) {
916 bool have_sign = true;
917 bool new_spnego = false;
919 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
920 GENSEC_FEATURE_SIGN);
921 if (spnego_state->simulate_w2k) {
922 have_sign = false;
924 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
925 GENSEC_FEATURE_NEW_SPNEGO);
927 switch (ta->negResult) {
928 case SPNEGO_ACCEPT_COMPLETED:
929 case SPNEGO_NONE_RESULT:
930 if (spnego_state->num_targs == 1) {
932 * the first exchange doesn't require
933 * verification
935 new_spnego = false;
938 break;
940 case SPNEGO_ACCEPT_INCOMPLETE:
941 if (ta->mechListMIC.length > 0) {
942 new_spnego = true;
943 break;
946 if (spnego_state->downgraded) {
948 * A downgrade should be protected if
949 * supported
951 break;
955 * The caller may just asked for
956 * GENSEC_FEATURE_SESSION_KEY, this
957 * is only reflected in the want_features.
959 * As it will imply
960 * gensec_have_features(GENSEC_FEATURE_SIGN)
961 * to return true.
963 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
964 break;
966 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
967 break;
970 * Here we're sure our preferred mech was
971 * selected by the server and our caller doesn't
972 * need GENSEC_FEATURE_SIGN nor
973 * GENSEC_FEATURE_SEAL support.
975 * In this case we don't require
976 * a mechListMIC from the server.
978 * This works around bugs in the Azure
979 * and Apple spnego implementations.
981 * See
982 * https://bugzilla.samba.org/show_bug.cgi?id=11994
984 spnego_state->may_skip_mic_check = true;
985 break;
987 case SPNEGO_REQUEST_MIC:
988 if (ta->mechListMIC.length > 0) {
989 new_spnego = true;
991 break;
992 default:
993 break;
996 if (spnego_state->mic_requested) {
997 if (have_sign) {
998 new_spnego = true;
1002 if (have_sign && new_spnego) {
1003 spnego_state->needs_mic_check = true;
1004 spnego_state->needs_mic_sign = true;
1008 if (ta->mechListMIC.length > 0) {
1009 status = gensec_check_packet(spnego_state->sub_sec_security,
1010 spnego_state->mech_types.data,
1011 spnego_state->mech_types.length,
1012 spnego_state->mech_types.data,
1013 spnego_state->mech_types.length,
1014 &ta->mechListMIC);
1015 if (!NT_STATUS_IS_OK(status)) {
1016 DBG_WARNING("failed to verify mechListMIC: %s\n",
1017 nt_errstr(status));
1018 return status;
1020 spnego_state->needs_mic_check = false;
1021 spnego_state->done_mic_check = true;
1024 if (spnego_state->needs_mic_sign) {
1025 status = gensec_sign_packet(spnego_state->sub_sec_security,
1027 spnego_state->mech_types.data,
1028 spnego_state->mech_types.length,
1029 spnego_state->mech_types.data,
1030 spnego_state->mech_types.length,
1031 &mech_list_mic);
1032 if (!NT_STATUS_IS_OK(status)) {
1033 DBG_WARNING("failed to sign mechListMIC: %s\n",
1034 nt_errstr(status));
1035 return status;
1037 spnego_state->needs_mic_sign = false;
1040 client_response:
1041 if (sub_out.length == 0 && mech_list_mic.length == 0) {
1042 *out = data_blob_null;
1044 if (!spnego_state->sub_sec_ready) {
1045 /* somethings wrong here... */
1046 DBG_ERR("gensec_update not ready without output\n");
1047 return NT_STATUS_INTERNAL_ERROR;
1050 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
1051 /* unless of course it did not accept */
1052 DBG_WARNING("gensec_update ok but not accepted\n");
1053 return NT_STATUS_INVALID_PARAMETER;
1056 if (!spnego_state->needs_mic_check) {
1057 spnego_state->state_position = SPNEGO_DONE;
1058 return NT_STATUS_OK;
1062 /* compose reply */
1063 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1064 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1065 spnego_out.negTokenTarg.supportedMech = NULL;
1066 spnego_out.negTokenTarg.responseToken = sub_out;
1067 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1069 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1070 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
1071 return NT_STATUS_INVALID_PARAMETER;
1074 spnego_state->num_targs++;
1076 /* set next state */
1077 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1078 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1080 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1083 static const struct spnego_neg_ops gensec_spnego_client_negTokenTarg_ops = {
1084 .name = "client_negTokenTarg",
1085 .start_fn = gensec_spnego_client_negTokenTarg_start,
1086 .step_fn = gensec_spnego_client_negTokenTarg_step,
1087 .finish_fn = gensec_spnego_client_negTokenTarg_finish,
1090 /** create a server negTokenTarg
1092 * This is the case, where the client is the first one who sends data
1095 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
1096 TALLOC_CTX *out_mem_ctx,
1097 NTSTATUS nt_status,
1098 const DATA_BLOB unwrapped_out,
1099 DATA_BLOB mech_list_mic,
1100 DATA_BLOB *out)
1102 struct spnego_data spnego_out;
1104 /* compose reply */
1105 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1106 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1107 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1108 spnego_out.negTokenTarg.supportedMech = NULL;
1110 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1111 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1112 if (spnego_state->mic_requested) {
1113 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
1114 spnego_state->mic_requested = false;
1115 } else {
1116 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
1118 spnego_state->state_position = SPNEGO_SERVER_TARG;
1119 } else if (NT_STATUS_IS_OK(nt_status)) {
1120 if (unwrapped_out.data) {
1121 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
1123 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
1124 spnego_state->state_position = SPNEGO_DONE;
1127 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1128 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1129 return NT_STATUS_INVALID_PARAMETER;
1132 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
1133 spnego_state->num_targs++;
1135 return nt_status;
1138 static NTSTATUS gensec_spnego_server_negTokenInit_start(
1139 struct gensec_security *gensec_security,
1140 struct spnego_state *spnego_state,
1141 struct spnego_neg_state *n,
1142 struct spnego_data *spnego_in,
1143 TALLOC_CTX *in_mem_ctx,
1144 DATA_BLOB *in_next)
1146 bool ok;
1148 n->mech_idx = 0;
1149 n->mech_types = spnego_in->negTokenInit.mechTypes;
1150 if (n->mech_types == NULL) {
1151 return NT_STATUS_INVALID_PARAMETER;
1154 n->all_idx = 0;
1155 n->all_sec = gensec_security_by_oid_list(gensec_security,
1156 n, n->mech_types,
1157 GENSEC_OID_SPNEGO);
1158 if (n->all_sec == NULL) {
1159 DBG_WARNING("gensec_security_by_oid_list() failed\n");
1160 return NT_STATUS_INVALID_PARAMETER;
1163 ok = spnego_write_mech_types(spnego_state,
1164 n->mech_types,
1165 &spnego_state->mech_types);
1166 if (!ok) {
1167 DBG_ERR("Failed to write mechTypes\n");
1168 return NT_STATUS_NO_MEMORY;
1171 return n->ops->step_fn(gensec_security, spnego_state, n,
1172 spnego_in, NT_STATUS_OK, in_mem_ctx, in_next);
1175 static NTSTATUS gensec_spnego_server_negTokenInit_step(
1176 struct gensec_security *gensec_security,
1177 struct spnego_state *spnego_state,
1178 struct spnego_neg_state *n,
1179 struct spnego_data *spnego_in,
1180 NTSTATUS last_status,
1181 TALLOC_CTX *in_mem_ctx,
1182 DATA_BLOB *in_next)
1184 if (!NT_STATUS_IS_OK(last_status)) {
1185 const struct gensec_security_ops_wrapper *cur_sec =
1186 &n->all_sec[n->all_idx];
1187 const char *next_mech = n->mech_types[n->mech_idx+1];
1188 const struct gensec_security_ops_wrapper *next_sec = NULL;
1189 const char *next = NULL;
1190 int dbg_level = DBGLVL_WARNING;
1191 bool allow_fallback = false;
1192 NTSTATUS status = last_status;
1193 size_t i;
1195 for (i = 0; next_mech != NULL && n->all_sec[i].op != NULL; i++) {
1196 if (strcmp(next_mech, n->all_sec[i].oid) != 0) {
1197 continue;
1200 next_sec = &n->all_sec[i];
1201 break;
1204 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1205 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
1207 allow_fallback = true;
1210 if (allow_fallback && next_sec != NULL) {
1211 next = next_sec->op->name;
1212 dbg_level = DBGLVL_NOTICE;
1215 DBG_PREFIX(dbg_level, (
1216 "%s: parsing NEG_TOKEN_INIT content failed "
1217 "(next[%s]): %s\n", cur_sec->op->name,
1218 next, nt_errstr(status)));
1220 if (next == NULL) {
1222 * A hard error without a possible fallback.
1224 return status;
1228 * Pretend we never started it
1230 gensec_spnego_reset_sub_sec(spnego_state);
1233 * And try the next one, based on the clients
1234 * mech type list...
1236 n->mech_idx += 1;
1240 * we always reset all_idx here, as the negotiation is
1241 * done via mech_idx!
1243 n->all_idx = 0;
1245 for (; n->mech_types[n->mech_idx] != NULL; n->mech_idx++) {
1246 const char *cur_mech = n->mech_types[n->mech_idx];
1247 const struct gensec_security_ops_wrapper *cur_sec = NULL;
1248 NTSTATUS status;
1249 DATA_BLOB sub_in = data_blob_null;
1250 size_t i;
1252 for (i = 0; n->all_sec[i].op != NULL; i++) {
1253 if (strcmp(cur_mech, n->all_sec[i].oid) != 0) {
1254 continue;
1257 cur_sec = &n->all_sec[i];
1258 n->all_idx = i;
1259 break;
1262 if (cur_sec == NULL) {
1263 continue;
1266 status = gensec_subcontext_start(spnego_state,
1267 gensec_security,
1268 &spnego_state->sub_sec_security);
1269 if (!NT_STATUS_IS_OK(status)) {
1270 return status;
1273 /* select the sub context */
1274 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
1275 cur_sec->op);
1276 if (!NT_STATUS_IS_OK(status)) {
1278 * Pretend we never started it
1280 gensec_spnego_reset_sub_sec(spnego_state);
1281 continue;
1284 if (n->mech_idx == 0) {
1286 * We can use the optimistic token.
1288 sub_in = spnego_in->negTokenInit.mechToken;
1289 } else {
1291 * Indicate the downgrade and request a
1292 * mic.
1294 spnego_state->downgraded = true;
1295 spnego_state->mic_requested = true;
1299 * Note that 'cur_sec' is temporary memory, but
1300 * cur_sec->oid points to a const string in the
1301 * backends gensec_security_ops structure.
1303 spnego_state->neg_oid = cur_sec->oid;
1305 /* we need some content from the mech */
1306 *in_next = sub_in;
1307 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1310 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1311 return NT_STATUS_INVALID_PARAMETER;
1314 static NTSTATUS gensec_spnego_server_negTokenInit_finish(
1315 struct gensec_security *gensec_security,
1316 struct spnego_state *spnego_state,
1317 struct spnego_neg_state *n,
1318 struct spnego_data *spnego_in,
1319 NTSTATUS sub_status,
1320 const DATA_BLOB sub_out,
1321 TALLOC_CTX *out_mem_ctx,
1322 DATA_BLOB *out)
1324 DATA_BLOB mech_list_mic = data_blob_null;
1326 if (spnego_state->simulate_w2k) {
1328 * Windows 2000 returns the unwrapped token
1329 * also in the mech_list_mic field.
1331 * In order to verify our client code,
1332 * we need a way to have a server with this
1333 * broken behaviour
1335 mech_list_mic = sub_out;
1338 return gensec_spnego_server_response(spnego_state,
1339 out_mem_ctx,
1340 sub_status,
1341 sub_out,
1342 mech_list_mic,
1343 out);
1346 static const struct spnego_neg_ops gensec_spnego_server_negTokenInit_ops = {
1347 .name = "server_negTokenInit",
1348 .start_fn = gensec_spnego_server_negTokenInit_start,
1349 .step_fn = gensec_spnego_server_negTokenInit_step,
1350 .finish_fn = gensec_spnego_server_negTokenInit_finish,
1353 static NTSTATUS gensec_spnego_server_negTokenTarg_start(
1354 struct gensec_security *gensec_security,
1355 struct spnego_state *spnego_state,
1356 struct spnego_neg_state *n,
1357 struct spnego_data *spnego_in,
1358 TALLOC_CTX *in_mem_ctx,
1359 DATA_BLOB *in_next)
1361 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1362 NTSTATUS status;
1364 spnego_state->num_targs++;
1366 if (spnego_state->sub_sec_security == NULL) {
1367 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1368 return NT_STATUS_INVALID_PARAMETER;
1371 if (spnego_state->needs_mic_check) {
1372 if (ta->responseToken.length != 0) {
1373 DBG_WARNING("non empty response token not expected\n");
1374 return NT_STATUS_INVALID_PARAMETER;
1377 status = gensec_check_packet(spnego_state->sub_sec_security,
1378 spnego_state->mech_types.data,
1379 spnego_state->mech_types.length,
1380 spnego_state->mech_types.data,
1381 spnego_state->mech_types.length,
1382 &ta->mechListMIC);
1383 if (!NT_STATUS_IS_OK(status)) {
1384 DBG_WARNING("failed to verify mechListMIC: %s\n",
1385 nt_errstr(status));
1386 return status;
1389 spnego_state->needs_mic_check = false;
1390 spnego_state->done_mic_check = true;
1391 return NT_STATUS_OK;
1394 if (!spnego_state->sub_sec_ready) {
1395 *in_next = ta->responseToken;
1396 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1399 return NT_STATUS_OK;
1402 static NTSTATUS gensec_spnego_server_negTokenTarg_step(
1403 struct gensec_security *gensec_security,
1404 struct spnego_state *spnego_state,
1405 struct spnego_neg_state *n,
1406 struct spnego_data *spnego_in,
1407 NTSTATUS last_status,
1408 TALLOC_CTX *in_mem_ctx,
1409 DATA_BLOB *in_next)
1411 if (GENSEC_UPDATE_IS_NTERROR(last_status)) {
1412 DBG_NOTICE("SPNEGO(%s) login failed: %s\n",
1413 spnego_state->sub_sec_security->ops->name,
1414 nt_errstr(last_status));
1415 return last_status;
1419 * This should never be reached!
1420 * The step function is only called on errors!
1422 smb_panic(__location__);
1423 return NT_STATUS_INTERNAL_ERROR;
1426 static NTSTATUS gensec_spnego_server_negTokenTarg_finish(
1427 struct gensec_security *gensec_security,
1428 struct spnego_state *spnego_state,
1429 struct spnego_neg_state *n,
1430 struct spnego_data *spnego_in,
1431 NTSTATUS sub_status,
1432 const DATA_BLOB sub_out,
1433 TALLOC_CTX *out_mem_ctx,
1434 DATA_BLOB *out)
1436 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1437 DATA_BLOB mech_list_mic = data_blob_null;
1438 NTSTATUS status;
1439 bool have_sign = true;
1440 bool new_spnego = false;
1442 status = sub_status;
1444 if (!spnego_state->sub_sec_ready) {
1446 * We're not yet ready to deal with signatures.
1448 goto server_response;
1451 if (spnego_state->done_mic_check) {
1453 * We already checked the mic,
1454 * either the in last round here
1455 * in gensec_spnego_server_negTokenTarg_finish()
1456 * or during this round in
1457 * gensec_spnego_server_negTokenTarg_start().
1459 * Both cases we're sure we don't have to
1460 * call gensec_sign_packet().
1462 goto server_response;
1465 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1466 GENSEC_FEATURE_SIGN);
1467 if (spnego_state->simulate_w2k) {
1468 have_sign = false;
1470 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1471 GENSEC_FEATURE_NEW_SPNEGO);
1472 if (ta->mechListMIC.length > 0) {
1473 new_spnego = true;
1476 if (have_sign && new_spnego) {
1477 spnego_state->needs_mic_check = true;
1478 spnego_state->needs_mic_sign = true;
1481 if (have_sign && ta->mechListMIC.length > 0) {
1482 status = gensec_check_packet(spnego_state->sub_sec_security,
1483 spnego_state->mech_types.data,
1484 spnego_state->mech_types.length,
1485 spnego_state->mech_types.data,
1486 spnego_state->mech_types.length,
1487 &ta->mechListMIC);
1488 if (!NT_STATUS_IS_OK(status)) {
1489 DBG_WARNING("failed to verify mechListMIC: %s\n",
1490 nt_errstr(status));
1491 return status;
1494 spnego_state->needs_mic_check = false;
1495 spnego_state->done_mic_check = true;
1498 if (spnego_state->needs_mic_sign) {
1499 status = gensec_sign_packet(spnego_state->sub_sec_security,
1501 spnego_state->mech_types.data,
1502 spnego_state->mech_types.length,
1503 spnego_state->mech_types.data,
1504 spnego_state->mech_types.length,
1505 &mech_list_mic);
1506 if (!NT_STATUS_IS_OK(status)) {
1507 DBG_WARNING("failed to sign mechListMIC: %s\n",
1508 nt_errstr(status));
1509 return status;
1511 spnego_state->needs_mic_sign = false;
1514 if (spnego_state->needs_mic_check) {
1515 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1518 server_response:
1519 return gensec_spnego_server_response(spnego_state,
1520 out_mem_ctx,
1521 status,
1522 sub_out,
1523 mech_list_mic,
1524 out);
1527 static const struct spnego_neg_ops gensec_spnego_server_negTokenTarg_ops = {
1528 .name = "server_negTokenTarg",
1529 .start_fn = gensec_spnego_server_negTokenTarg_start,
1530 .step_fn = gensec_spnego_server_negTokenTarg_step,
1531 .finish_fn = gensec_spnego_server_negTokenTarg_finish,
1534 struct gensec_spnego_update_state {
1535 struct tevent_context *ev;
1536 struct gensec_security *gensec;
1537 struct spnego_state *spnego;
1539 DATA_BLOB full_in;
1540 struct spnego_data _spnego_in;
1541 struct spnego_data *spnego_in;
1543 struct {
1544 bool needed;
1545 DATA_BLOB in;
1546 NTSTATUS status;
1547 DATA_BLOB out;
1548 } sub;
1550 struct spnego_neg_state *n;
1552 NTSTATUS status;
1553 DATA_BLOB out;
1556 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1557 enum tevent_req_state req_state)
1559 struct gensec_spnego_update_state *state =
1560 tevent_req_data(req,
1561 struct gensec_spnego_update_state);
1563 switch (req_state) {
1564 case TEVENT_REQ_USER_ERROR:
1565 case TEVENT_REQ_TIMED_OUT:
1566 case TEVENT_REQ_NO_MEMORY:
1568 * A fatal error, further updates are not allowed.
1570 state->spnego->state_position = SPNEGO_DONE;
1571 break;
1572 default:
1573 break;
1577 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1578 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1579 DATA_BLOB *full_in);
1580 static void gensec_spnego_update_pre(struct tevent_req *req);
1581 static void gensec_spnego_update_done(struct tevent_req *subreq);
1582 static void gensec_spnego_update_post(struct tevent_req *req);
1583 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1584 TALLOC_CTX *out_mem_ctx,
1585 DATA_BLOB *_out);
1587 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1588 struct tevent_context *ev,
1589 struct gensec_security *gensec_security,
1590 const DATA_BLOB in)
1592 struct spnego_state *spnego_state =
1593 talloc_get_type_abort(gensec_security->private_data,
1594 struct spnego_state);
1595 struct tevent_req *req = NULL;
1596 struct gensec_spnego_update_state *state = NULL;
1597 NTSTATUS status;
1598 ssize_t len;
1600 req = tevent_req_create(mem_ctx, &state,
1601 struct gensec_spnego_update_state);
1602 if (req == NULL) {
1603 return NULL;
1605 state->ev = ev;
1606 state->gensec = gensec_security;
1607 state->spnego = spnego_state;
1608 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1610 if (spnego_state->out_frag.length > 0) {
1611 if (in.length > 0) {
1612 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1613 return tevent_req_post(req, ev);
1616 status = gensec_spnego_update_out(gensec_security,
1617 state, &state->out);
1618 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1619 tevent_req_nterror(req, status);
1620 return tevent_req_post(req, ev);
1623 state->status = status;
1624 tevent_req_done(req);
1625 return tevent_req_post(req, ev);
1628 status = gensec_spnego_update_in(gensec_security, in,
1629 state, &state->full_in);
1630 state->status = status;
1631 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1632 tevent_req_done(req);
1633 return tevent_req_post(req, ev);
1635 if (tevent_req_nterror(req, status)) {
1636 return tevent_req_post(req, ev);
1639 /* Check if we got a valid SPNEGO blob... */
1641 switch (spnego_state->state_position) {
1642 case SPNEGO_FALLBACK:
1643 break;
1645 case SPNEGO_CLIENT_TARG:
1646 case SPNEGO_SERVER_TARG:
1647 if (state->full_in.length == 0) {
1648 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1649 return tevent_req_post(req, ev);
1652 FALL_THROUGH;
1653 case SPNEGO_CLIENT_START:
1654 case SPNEGO_SERVER_START:
1656 if (state->full_in.length == 0) {
1657 /* create_negTokenInit later */
1658 break;
1661 len = spnego_read_data(state,
1662 state->full_in,
1663 &state->_spnego_in);
1664 if (len == -1) {
1665 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1666 DEBUG(1, ("Invalid SPNEGO request:\n"));
1667 dump_data(1, state->full_in.data,
1668 state->full_in.length);
1669 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1670 return tevent_req_post(req, ev);
1674 * This is the 'fallback' case, where we don't get
1675 * SPNEGO, and have to try all the other options (and
1676 * hope they all have a magic string they check)
1678 status = gensec_spnego_server_try_fallback(gensec_security,
1679 spnego_state,
1680 state,
1681 state->full_in);
1682 if (tevent_req_nterror(req, status)) {
1683 return tevent_req_post(req, ev);
1687 * We'll continue with SPNEGO_FALLBACK below...
1689 break;
1691 state->spnego_in = &state->_spnego_in;
1693 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1694 if (state->spnego_in->type != spnego_state->expected_packet) {
1695 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1696 state->spnego_in->type,
1697 spnego_state->expected_packet));
1698 dump_data(1, state->full_in.data,
1699 state->full_in.length);
1700 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1701 return tevent_req_post(req, ev);
1704 break;
1706 default:
1707 smb_panic(__location__);
1708 return NULL;
1711 gensec_spnego_update_pre(req);
1712 if (!tevent_req_is_in_progress(req)) {
1713 return tevent_req_post(req, ev);
1716 if (state->sub.needed) {
1717 struct tevent_req *subreq = NULL;
1720 * We may need one more roundtrip...
1722 subreq = gensec_update_send(state, state->ev,
1723 spnego_state->sub_sec_security,
1724 state->sub.in);
1725 if (tevent_req_nomem(subreq, req)) {
1726 return tevent_req_post(req, ev);
1728 tevent_req_set_callback(subreq,
1729 gensec_spnego_update_done,
1730 req);
1731 state->sub.needed = false;
1732 return req;
1735 gensec_spnego_update_post(req);
1736 if (!tevent_req_is_in_progress(req)) {
1737 return tevent_req_post(req, ev);
1740 return req;
1743 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1744 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1745 DATA_BLOB *full_in)
1747 struct spnego_state *spnego_state =
1748 talloc_get_type_abort(gensec_security->private_data,
1749 struct spnego_state);
1750 size_t expected;
1751 bool ok;
1753 *full_in = data_blob_null;
1755 switch (spnego_state->state_position) {
1756 case SPNEGO_FALLBACK:
1757 *full_in = in;
1758 spnego_state->in_needed = 0;
1759 return NT_STATUS_OK;
1761 case SPNEGO_CLIENT_START:
1762 case SPNEGO_CLIENT_TARG:
1763 case SPNEGO_SERVER_START:
1764 case SPNEGO_SERVER_TARG:
1765 break;
1767 case SPNEGO_DONE:
1768 default:
1769 return NT_STATUS_INVALID_PARAMETER;
1772 if (spnego_state->in_needed == 0) {
1773 size_t size = 0;
1774 int ret;
1777 * try to work out the size of the full
1778 * input token, it might be fragmented
1780 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1781 if ((ret != 0) && (ret != EAGAIN)) {
1782 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1785 if ((ret == 0) || (ret == EAGAIN)) {
1786 spnego_state->in_needed = size;
1787 } else {
1789 * If it is not an asn1 message
1790 * just call the next layer.
1792 spnego_state->in_needed = in.length;
1796 if (spnego_state->in_needed > UINT16_MAX) {
1798 * limit the incoming message to 0xFFFF
1799 * to avoid DoS attacks.
1801 return NT_STATUS_INVALID_BUFFER_SIZE;
1804 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1806 * If we reach this, we know we got at least
1807 * part of an asn1 message, getting 0 means
1808 * the remote peer wants us to spin.
1810 return NT_STATUS_INVALID_PARAMETER;
1813 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1814 if (in.length > expected) {
1816 * we got more than expected
1818 return NT_STATUS_INVALID_PARAMETER;
1821 if (in.length == spnego_state->in_needed) {
1823 * if the in.length contains the full blob
1824 * we are done.
1826 * Note: this implies spnego_state->in_frag.length == 0,
1827 * but we do not need to check this explicitly
1828 * because we already know that we did not get
1829 * more than expected.
1831 *full_in = in;
1832 spnego_state->in_needed = 0;
1833 return NT_STATUS_OK;
1836 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1837 in.data, in.length);
1838 if (!ok) {
1839 return NT_STATUS_NO_MEMORY;
1842 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1843 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1846 *full_in = spnego_state->in_frag;
1847 talloc_steal(mem_ctx, full_in->data);
1848 spnego_state->in_frag = data_blob_null;
1849 spnego_state->in_needed = 0;
1850 return NT_STATUS_OK;
1853 static void gensec_spnego_update_pre(struct tevent_req *req)
1855 struct gensec_spnego_update_state *state =
1856 tevent_req_data(req,
1857 struct gensec_spnego_update_state);
1858 struct spnego_state *spnego_state = state->spnego;
1859 const struct spnego_neg_ops *ops = NULL;
1860 NTSTATUS status;
1862 state->sub.needed = false;
1863 state->sub.in = data_blob_null;
1864 state->sub.status = NT_STATUS_INTERNAL_ERROR;
1865 state->sub.out = data_blob_null;
1867 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1868 state->sub.in = state->full_in;
1869 state->full_in = data_blob_null;
1870 state->sub.needed = true;
1871 return;
1874 switch (spnego_state->state_position) {
1875 case SPNEGO_CLIENT_START:
1876 if (state->spnego_in == NULL) {
1877 /* client to produce negTokenInit */
1878 ops = &gensec_spnego_create_negTokenInit_ops;
1879 break;
1882 ops = &gensec_spnego_client_negTokenInit_ops;
1883 break;
1885 case SPNEGO_CLIENT_TARG:
1886 ops = &gensec_spnego_client_negTokenTarg_ops;
1887 break;
1889 case SPNEGO_SERVER_START:
1890 if (state->spnego_in == NULL) {
1891 /* server to produce negTokenInit */
1892 ops = &gensec_spnego_create_negTokenInit_ops;
1893 break;
1896 ops = &gensec_spnego_server_negTokenInit_ops;
1897 break;
1899 case SPNEGO_SERVER_TARG:
1900 ops = &gensec_spnego_server_negTokenTarg_ops;
1901 break;
1903 default:
1904 smb_panic(__location__);
1905 return;
1908 state->n = gensec_spnego_neg_state(state, ops);
1909 if (tevent_req_nomem(state->n, req)) {
1910 return;
1913 status = ops->start_fn(state->gensec, spnego_state, state->n,
1914 state->spnego_in, state, &state->sub.in);
1915 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1916 tevent_req_nterror(req, status);
1917 return;
1920 if (NT_STATUS_IS_OK(status)) {
1922 * Call finish_fn() with an empty
1923 * blob and NT_STATUS_OK.
1925 state->sub.status = NT_STATUS_OK;
1926 } else {
1928 * MORE_PROCESSING_REQUIRED =>
1929 * we need to call gensec_update_send().
1931 state->sub.needed = true;
1935 static void gensec_spnego_update_done(struct tevent_req *subreq)
1937 struct tevent_req *req =
1938 tevent_req_callback_data(subreq,
1939 struct tevent_req);
1940 struct gensec_spnego_update_state *state =
1941 tevent_req_data(req,
1942 struct gensec_spnego_update_state);
1943 struct spnego_state *spnego_state = state->spnego;
1945 state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1946 TALLOC_FREE(subreq);
1947 if (NT_STATUS_IS_OK(state->sub.status)) {
1948 spnego_state->sub_sec_ready = true;
1951 gensec_spnego_update_post(req);
1954 static void gensec_spnego_update_post(struct tevent_req *req)
1956 struct gensec_spnego_update_state *state =
1957 tevent_req_data(req,
1958 struct gensec_spnego_update_state);
1959 struct spnego_state *spnego_state = state->spnego;
1960 const struct spnego_neg_ops *ops = NULL;
1961 NTSTATUS status;
1963 state->sub.in = data_blob_null;
1964 state->sub.needed = false;
1966 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1967 status = state->sub.status;
1968 spnego_state->out_frag = state->sub.out;
1969 talloc_steal(spnego_state, spnego_state->out_frag.data);
1970 state->sub.out = data_blob_null;
1971 goto respond;
1974 ops = state->n->ops;
1976 if (GENSEC_UPDATE_IS_NTERROR(state->sub.status)) {
1980 * gensec_update_recv() returned an error,
1981 * let's see if the step_fn() want to
1982 * handle it and negotiate something else.
1985 status = ops->step_fn(state->gensec,
1986 spnego_state,
1987 state->n,
1988 state->spnego_in,
1989 state->sub.status,
1990 state,
1991 &state->sub.in);
1992 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1993 tevent_req_nterror(req, status);
1994 return;
1997 state->sub.out = data_blob_null;
1998 state->sub.status = NT_STATUS_INTERNAL_ERROR;
2000 if (NT_STATUS_IS_OK(status)) {
2002 * Call finish_fn() with an empty
2003 * blob and NT_STATUS_OK.
2005 state->sub.status = NT_STATUS_OK;
2006 } else {
2008 * MORE_PROCESSING_REQUIRED...
2010 state->sub.needed = true;
2014 if (state->sub.needed) {
2015 struct tevent_req *subreq = NULL;
2018 * We may need one more roundtrip...
2020 subreq = gensec_update_send(state, state->ev,
2021 spnego_state->sub_sec_security,
2022 state->sub.in);
2023 if (tevent_req_nomem(subreq, req)) {
2024 return;
2026 tevent_req_set_callback(subreq,
2027 gensec_spnego_update_done,
2028 req);
2029 state->sub.needed = false;
2030 return;
2033 status = ops->finish_fn(state->gensec,
2034 spnego_state,
2035 state->n,
2036 state->spnego_in,
2037 state->sub.status,
2038 state->sub.out,
2039 spnego_state,
2040 &spnego_state->out_frag);
2041 TALLOC_FREE(state->n);
2042 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2043 tevent_req_nterror(req, status);
2044 return;
2047 if (NT_STATUS_IS_OK(status)) {
2048 bool reset_full = true;
2050 reset_full = !spnego_state->done_mic_check;
2052 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
2053 reset_full);
2054 if (tevent_req_nterror(req, status)) {
2055 return;
2059 respond:
2060 spnego_state->out_status = status;
2062 status = gensec_spnego_update_out(state->gensec,
2063 state, &state->out);
2064 if (GENSEC_UPDATE_IS_NTERROR(status)) {
2065 tevent_req_nterror(req, status);
2066 return;
2069 state->status = status;
2070 tevent_req_done(req);
2071 return;
2074 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
2075 TALLOC_CTX *out_mem_ctx,
2076 DATA_BLOB *_out)
2078 struct spnego_state *spnego_state =
2079 talloc_get_type_abort(gensec_security->private_data,
2080 struct spnego_state);
2081 DATA_BLOB out = data_blob_null;
2082 bool ok;
2084 *_out = data_blob_null;
2086 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
2088 * Fast path, we can deliver everything
2091 *_out = spnego_state->out_frag;
2092 if (spnego_state->out_frag.length > 0) {
2093 talloc_steal(out_mem_ctx, _out->data);
2094 spnego_state->out_frag = data_blob_null;
2097 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
2098 return spnego_state->out_status;
2102 * We're completely done, further updates are not allowed.
2104 spnego_state->state_position = SPNEGO_DONE;
2105 return gensec_child_ready(gensec_security,
2106 spnego_state->sub_sec_security);
2109 out = spnego_state->out_frag;
2112 * copy the remaining bytes
2114 spnego_state->out_frag = data_blob_talloc(spnego_state,
2115 out.data + spnego_state->out_max_length,
2116 out.length - spnego_state->out_max_length);
2117 if (spnego_state->out_frag.data == NULL) {
2118 return NT_STATUS_NO_MEMORY;
2122 * truncate the buffer
2124 ok = data_blob_realloc(spnego_state, &out,
2125 spnego_state->out_max_length);
2126 if (!ok) {
2127 return NT_STATUS_NO_MEMORY;
2130 talloc_steal(out_mem_ctx, out.data);
2131 *_out = out;
2132 return NT_STATUS_MORE_PROCESSING_REQUIRED;
2135 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
2136 TALLOC_CTX *out_mem_ctx,
2137 DATA_BLOB *out)
2139 struct gensec_spnego_update_state *state =
2140 tevent_req_data(req,
2141 struct gensec_spnego_update_state);
2142 NTSTATUS status;
2144 *out = data_blob_null;
2146 if (tevent_req_is_nterror(req, &status)) {
2147 tevent_req_received(req);
2148 return status;
2151 *out = state->out;
2152 talloc_steal(out_mem_ctx, state->out.data);
2153 status = state->status;
2154 tevent_req_received(req);
2155 return status;
2158 static const char *gensec_spnego_oids[] = {
2159 GENSEC_OID_SPNEGO,
2160 NULL
2163 static const struct gensec_security_ops gensec_spnego_security_ops = {
2164 .name = "spnego",
2165 .sasl_name = "GSS-SPNEGO",
2166 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
2167 .oid = gensec_spnego_oids,
2168 .client_start = gensec_spnego_client_start,
2169 .server_start = gensec_spnego_server_start,
2170 .update_send = gensec_spnego_update_send,
2171 .update_recv = gensec_spnego_update_recv,
2172 .seal_packet = gensec_child_seal_packet,
2173 .sign_packet = gensec_child_sign_packet,
2174 .sig_size = gensec_child_sig_size,
2175 .max_wrapped_size = gensec_child_max_wrapped_size,
2176 .max_input_size = gensec_child_max_input_size,
2177 .check_packet = gensec_child_check_packet,
2178 .unseal_packet = gensec_child_unseal_packet,
2179 .wrap = gensec_child_wrap,
2180 .unwrap = gensec_child_unwrap,
2181 .session_key = gensec_child_session_key,
2182 .session_info = gensec_child_session_info,
2183 .want_feature = gensec_child_want_feature,
2184 .have_feature = gensec_child_have_feature,
2185 .expire_time = gensec_child_expire_time,
2186 .final_auth_type = gensec_child_final_auth_type,
2187 .enabled = true,
2188 .priority = GENSEC_SPNEGO,
2189 .glue = true,
2192 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
2194 NTSTATUS ret;
2195 ret = gensec_register(ctx, &gensec_spnego_security_ops);
2196 if (!NT_STATUS_IS_OK(ret)) {
2197 DEBUG(0,("Failed to register '%s' gensec backend!\n",
2198 gensec_spnego_security_ops.name));
2199 return ret;
2202 return ret;