auth/spnego: split gensec_spnego_client_negTokenTarg() into subfunctions
[Samba.git] / auth / gensec / spnego.c
blobc4a3455f723a40c7fd05deffd34103ab5785516c
1 /*
2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2008
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include <tevent.h>
27 #include "lib/util/tevent_ntstatus.h"
28 #include "../libcli/auth/spnego.h"
29 #include "librpc/gen_ndr/ndr_dcerpc.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "auth/gensec/gensec_internal.h"
33 #include "param/param.h"
34 #include "lib/util/asn1.h"
35 #include "lib/util/base64.h"
37 #undef strcasecmp
39 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
41 enum spnego_state_position {
42 SPNEGO_SERVER_START,
43 SPNEGO_CLIENT_START,
44 SPNEGO_SERVER_TARG,
45 SPNEGO_CLIENT_TARG,
46 SPNEGO_FALLBACK,
47 SPNEGO_DONE
50 struct spnego_state;
51 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;
57 size_t all_idx;
58 const char * const *mech_types;
59 size_t mech_idx;
62 struct spnego_neg_ops {
63 const char *name;
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,
80 DATA_BLOB *in_next);
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,
97 NTSTATUS last_status,
98 TALLOC_CTX *in_mem_ctx,
99 DATA_BLOB *in_next);
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
106 * from the peer.
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,
112 NTSTATUS sub_status,
113 const DATA_BLOB sub_out,
114 TALLOC_CTX *out_mem_ctx,
115 DATA_BLOB *out);
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;
122 bool sub_sec_ready;
124 const char *neg_oid;
126 DATA_BLOB mech_types;
127 size_t num_targs;
128 bool downgraded;
129 bool mic_requested;
130 bool needs_mic_sign;
131 bool needs_mic_check;
132 bool may_skip_mic_check;
133 bool done_mic_check;
135 bool simulate_w2k;
138 * The following is used to implement
139 * the update token fragmentation
141 size_t in_needed;
142 DATA_BLOB in_frag;
143 size_t out_max_length;
144 DATA_BLOB out_frag;
145 NTSTATUS out_status;
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);
154 if (n == NULL) {
155 return NULL;
157 n->ops = ops;
159 return n;
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,
168 DATA_BLOB *out)
170 struct spnego_neg_state *n = NULL;
171 NTSTATUS status;
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);
178 if (n == NULL) {
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)) {
185 TALLOC_FREE(n);
186 return 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)) {
197 break;
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)) {
204 TALLOC_FREE(n);
205 return status;
209 status = n->ops->finish_fn(gensec_security, spnego_state, n,
210 spnego_in, status, sub_out,
211 out_mem_ctx, out);
212 TALLOC_FREE(n);
213 return status;
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);
227 if (!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;
243 return NT_STATUS_OK;
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);
251 if (!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;
267 return NT_STATUS_OK;
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
274 * they check)
277 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
278 struct spnego_state *spnego_state,
279 TALLOC_CTX *mem_ctx,
280 const DATA_BLOB in)
282 int i,j;
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++) {
288 bool is_spnego;
289 NTSTATUS nt_status;
291 if (gensec_security != NULL &&
292 !gensec_security_ops_enabled(all_ops[i], gensec_security))
294 continue;
297 if (!all_ops[i]->oid) {
298 continue;
301 is_spnego = false;
302 for (j=0; all_ops[i]->oid[j]; j++) {
303 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
304 is_spnego = true;
307 if (is_spnego) {
308 continue;
311 if (!all_ops[i]->magic) {
312 continue;
315 nt_status = all_ops[i]->magic(gensec_security, &in);
316 if (!NT_STATUS_IS_OK(nt_status)) {
317 continue;
320 spnego_state->state_position = SPNEGO_FALLBACK;
322 nt_status = gensec_subcontext_start(spnego_state,
323 gensec_security,
324 &spnego_state->sub_sec_security);
326 if (!NT_STATUS_IS_OK(nt_status)) {
327 return nt_status;
329 /* select the sub context */
330 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
331 all_ops[i]);
332 if (!NT_STATUS_IS_OK(nt_status)) {
333 return nt_status;
336 return NT_STATUS_OK;
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,
348 DATA_BLOB *in_next)
350 n->mech_idx = 0;
351 n->mech_types = gensec_security_oids(gensec_security, n,
352 GENSEC_OID_SPNEGO);
353 if (n->mech_types == NULL) {
354 DBG_WARNING("gensec_security_oids() failed\n");
355 return NT_STATUS_NO_MEMORY;
358 n->all_idx = 0;
359 n->all_sec = gensec_security_by_oid_list(gensec_security,
360 n, n->mech_types,
361 GENSEC_OID_SPNEGO);
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,
378 DATA_BLOB *in_next)
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,
404 "%s/%s",
405 gensec_security->target.service,
406 gensec_security->target.hostname);
407 } else {
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)));
416 if (next == NULL) {
418 * A hard error without a possible fallback.
420 return status;
424 * Pretend we never started it
426 gensec_spnego_update_sub_abort(spnego_state);
429 * And try the next one...
431 n->all_idx += 1;
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];
437 NTSTATUS status;
439 status = gensec_subcontext_start(spnego_state,
440 gensec_security,
441 &spnego_state->sub_sec_security);
442 if (!NT_STATUS_IS_OK(status)) {
443 return status;
446 /* select the sub context */
447 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
448 cur_sec->op);
449 if (!NT_STATUS_IS_OK(status)) {
450 gensec_spnego_update_sub_abort(spnego_state);
451 continue;
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;
461 return NT_STATUS_OK;
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,
473 NTSTATUS sub_status,
474 const DATA_BLOB sub_out,
475 TALLOC_CTX *out_mem_ctx,
476 DATA_BLOB *out)
478 const struct gensec_security_ops_wrapper *cur_sec =
479 &n->all_sec[n->all_idx];
480 struct spnego_data spnego_out;
481 bool ok;
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,
492 n->mech_types,
493 &spnego_state->mech_types);
494 if (!ok) {
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);
507 } else {
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;
525 /* set next state */
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;
529 } else {
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,
552 DATA_BLOB *out)
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,
566 DATA_BLOB *in_next)
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);
580 n->mech_idx = 0;
581 n->mech_types = spnego_in->negTokenInit.mechTypes;
582 if (n->mech_types == NULL) {
583 return NT_STATUS_INVALID_PARAMETER;
586 n->all_idx = 0;
587 n->all_sec = gensec_security_by_oid_list(gensec_security,
588 n, n->mech_types,
589 GENSEC_OID_SPNEGO);
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,
606 DATA_BLOB *in_next)
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
627 * of this mech
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,
648 "%s/%s",
649 gensec_security->target.service,
650 gensec_security->target.hostname);
651 } else {
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)));
660 if (next == NULL) {
662 * A hard error without a possible fallback.
664 return status;
668 * Pretend we never started it.
670 gensec_spnego_update_sub_abort(spnego_state);
673 * And try the next one...
675 n->all_idx += 1;
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];
681 NTSTATUS status;
683 status = gensec_subcontext_start(spnego_state,
684 gensec_security,
685 &spnego_state->sub_sec_security);
686 if (!NT_STATUS_IS_OK(status)) {
687 return status;
690 /* select the sub context */
691 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
692 cur_sec->op);
693 if (!NT_STATUS_IS_OK(status)) {
694 gensec_spnego_update_sub_abort(spnego_state);
695 continue;
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,
722 NTSTATUS sub_status,
723 const DATA_BLOB sub_out,
724 TALLOC_CTX *out_mem_ctx,
725 DATA_BLOB *out)
727 struct spnego_data spnego_out;
728 const char *my_mechs[] = {NULL, NULL};
729 bool ok;
731 my_mechs[0] = spnego_state->neg_oid;
732 /* compose reply */
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,
746 my_mechs,
747 &spnego_state->mech_types);
748 if (!ok) {
749 DBG_ERR("failed to write mechTypes\n");
750 return NT_STATUS_NO_MEMORY;
753 /* set next state */
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,
772 DATA_BLOB *out)
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,
785 DATA_BLOB *in_next)
787 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
788 NTSTATUS status;
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) {
809 int cmp;
811 cmp = memcmp(m->data, r->data, m->length);
812 if (cmp == 0) {
813 data_blob_free(m);
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,
833 ta->supportedMech);
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,
844 gensec_security,
845 &spnego_state->sub_sec_security);
846 if (!NT_STATUS_IS_OK(status)) {
847 return status;
850 /* select the sub context */
851 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
852 ta->supportedMech);
853 if (!NT_STATUS_IS_OK(status)) {
854 return status;
857 spnego_state->neg_oid = talloc_strdup(spnego_state,
858 ta->supportedMech);
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.
885 * See
886 * https://bugzilla.samba.org/show_bug.cgi?id=11994
888 spnego_state->needs_mic_check = false;
889 return NT_STATUS_OK;
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,
897 &ta->mechListMIC);
898 if (!NT_STATUS_IS_OK(status)) {
899 DBG_WARNING("failed to verify mechListMIC: %s\n",
900 nt_errstr(status));
901 return status;
903 spnego_state->needs_mic_check = false;
904 spnego_state->done_mic_check = true;
905 return NT_STATUS_OK;
908 if (!spnego_state->sub_sec_ready) {
909 *in_next = ta->responseToken;
910 return NT_STATUS_MORE_PROCESSING_REQUIRED;
913 return NT_STATUS_OK;
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,
923 DATA_BLOB *in_next)
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));
929 return 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,
945 NTSTATUS sub_status,
946 const DATA_BLOB sub_out,
947 TALLOC_CTX *out_mem_ctx,
948 DATA_BLOB *out)
950 const struct spnego_negTokenTarg *ta =
951 &spnego_in->negTokenTarg;
952 DATA_BLOB mech_list_mic = data_blob_null;
953 NTSTATUS status;
954 struct spnego_data spnego_out;
956 status = sub_status;
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) {
1001 have_sign = false;
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
1012 * verification
1014 new_spnego = false;
1017 break;
1019 case SPNEGO_ACCEPT_INCOMPLETE:
1020 if (ta->mechListMIC.length > 0) {
1021 new_spnego = true;
1022 break;
1025 if (spnego_state->downgraded) {
1027 * A downgrade should be protected if
1028 * supported
1030 break;
1034 * The caller may just asked for
1035 * GENSEC_FEATURE_SESSION_KEY, this
1036 * is only reflected in the want_features.
1038 * As it will imply
1039 * gensec_have_features(GENSEC_FEATURE_SIGN)
1040 * to return true.
1042 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
1043 break;
1045 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
1046 break;
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.
1060 * See
1061 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1063 spnego_state->may_skip_mic_check = true;
1064 break;
1066 case SPNEGO_REQUEST_MIC:
1067 if (ta->mechListMIC.length > 0) {
1068 new_spnego = true;
1070 break;
1071 default:
1072 break;
1075 if (spnego_state->mic_requested) {
1076 if (have_sign) {
1077 new_spnego = true;
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,
1093 &ta->mechListMIC);
1094 if (!NT_STATUS_IS_OK(status)) {
1095 DBG_WARNING("failed to verify mechListMIC: %s\n",
1096 nt_errstr(status));
1097 return status;
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,
1110 &mech_list_mic);
1111 if (!NT_STATUS_IS_OK(status)) {
1112 DBG_WARNING("failed to sign mechListMIC: %s\n",
1113 nt_errstr(status));
1114 return status;
1116 spnego_state->needs_mic_sign = false;
1119 client_response:
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;
1141 /* compose reply */
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,
1174 DATA_BLOB *out)
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,
1187 NTSTATUS nt_status,
1188 const DATA_BLOB unwrapped_out,
1189 DATA_BLOB mech_list_mic,
1190 DATA_BLOB *out)
1192 struct spnego_data spnego_out;
1194 /* compose reply */
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;
1205 } else {
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++;
1225 return nt_status;
1228 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
1229 struct spnego_state *spnego_state,
1230 struct tevent_context *ev,
1231 struct spnego_data *spnego_in,
1232 TALLOC_CTX *out_mem_ctx,
1233 DATA_BLOB *out)
1235 TALLOC_CTX *frame = talloc_stackframe();
1236 DATA_BLOB sub_out = data_blob_null;
1237 DATA_BLOB mech_list_mic = data_blob_null;
1238 const char * const *mech_types = NULL;
1239 size_t all_idx = 0;
1240 const struct gensec_security_ops_wrapper *all_sec = NULL;
1241 size_t mech_idx = 0;
1242 NTSTATUS status;
1243 bool ok;
1245 mech_types = spnego_in->negTokenInit.mechTypes;
1246 if (mech_types == NULL) {
1247 TALLOC_FREE(frame);
1248 return NT_STATUS_INVALID_PARAMETER;
1251 all_sec = gensec_security_by_oid_list(gensec_security, frame,
1252 mech_types, GENSEC_OID_SPNEGO);
1253 if (all_sec == NULL) {
1254 DBG_WARNING("gensec_security_by_oid_list() failed\n");
1255 TALLOC_FREE(frame);
1256 return NT_STATUS_INVALID_PARAMETER;
1259 ok = spnego_write_mech_types(spnego_state, mech_types,
1260 &spnego_state->mech_types);
1261 if (!ok) {
1262 DBG_ERR("Failed to write mechTypes\n");
1263 TALLOC_FREE(frame);
1264 return NT_STATUS_NO_MEMORY;
1268 * First try the preferred mechs from the client.
1270 for (; mech_types[mech_idx]; mech_idx++) {
1271 const char *cur_mech = mech_types[mech_idx];
1272 const struct gensec_security_ops_wrapper *cur_sec = NULL;
1273 DATA_BLOB sub_in = data_blob_null;
1275 for (all_idx = 0; all_sec[all_idx].op; all_idx++) {
1276 if (strcmp(cur_mech, all_sec[all_idx].oid) == 0) {
1277 cur_sec = &all_sec[all_idx];
1278 break;
1282 if (cur_sec == NULL) {
1283 continue;
1286 status = gensec_subcontext_start(spnego_state,
1287 gensec_security,
1288 &spnego_state->sub_sec_security);
1289 if (!NT_STATUS_IS_OK(status)) {
1290 TALLOC_FREE(frame);
1291 return status;
1294 /* select the sub context */
1295 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
1296 cur_sec->op);
1297 if (!NT_STATUS_IS_OK(status)) {
1299 * Pretend we never started it
1301 gensec_spnego_update_sub_abort(spnego_state);
1302 continue;
1305 if (mech_idx > 0) {
1307 * Indicate the downgrade and request a
1308 * mic.
1310 spnego_state->downgraded = true;
1311 spnego_state->mic_requested = true;
1312 /* no optimistic token */
1313 spnego_state->neg_oid = cur_sec->oid;
1314 sub_out = data_blob_null;
1315 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1316 goto reply;
1320 * Try the optimistic token from the client
1322 sub_in = spnego_in->negTokenInit.mechToken;
1323 status = gensec_update_ev(spnego_state->sub_sec_security,
1324 frame, ev, sub_in, &sub_out);
1325 if (NT_STATUS_IS_OK(status)) {
1326 spnego_state->sub_sec_ready = true;
1328 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1329 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1331 DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1332 cur_sec->op->name, nt_errstr(status));
1335 * Pretend we never started it
1337 gensec_spnego_update_sub_abort(spnego_state);
1338 continue;
1341 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1342 DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1343 cur_sec->op->name, nt_errstr(status));
1344 TALLOC_FREE(frame);
1345 return status;
1348 spnego_state->neg_oid = cur_sec->oid;
1349 goto reply; /* OK or MORE PROCESSING */
1352 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1353 TALLOC_FREE(frame);
1354 return NT_STATUS_INVALID_PARAMETER;
1356 reply:
1357 if (spnego_state->simulate_w2k) {
1359 * Windows 2000 returns the unwrapped token
1360 * also in the mech_list_mic field.
1362 * In order to verify our client code,
1363 * we need a way to have a server with this
1364 * broken behaviour
1366 mech_list_mic = sub_out;
1369 status = gensec_spnego_server_response(spnego_state,
1370 out_mem_ctx,
1371 status,
1372 sub_out,
1373 mech_list_mic,
1374 out);
1375 TALLOC_FREE(frame);
1376 return status;
1379 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1380 struct spnego_state *spnego_state,
1381 struct tevent_context *ev,
1382 struct spnego_data *spnego_in,
1383 TALLOC_CTX *out_mem_ctx,
1384 DATA_BLOB *out)
1386 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1387 DATA_BLOB sub_in = ta->responseToken;
1388 DATA_BLOB mech_list_mic = data_blob_null;
1389 DATA_BLOB sub_out = data_blob_null;
1390 NTSTATUS status;
1391 bool have_sign = true;
1392 bool new_spnego = false;
1394 spnego_state->num_targs++;
1396 if (spnego_state->sub_sec_security == NULL) {
1397 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1398 return NT_STATUS_INVALID_PARAMETER;
1401 if (spnego_state->needs_mic_check) {
1402 if (ta->responseToken.length != 0) {
1403 DBG_WARNING("non empty response token not expected\n");
1404 return NT_STATUS_INVALID_PARAMETER;
1407 status = gensec_check_packet(spnego_state->sub_sec_security,
1408 spnego_state->mech_types.data,
1409 spnego_state->mech_types.length,
1410 spnego_state->mech_types.data,
1411 spnego_state->mech_types.length,
1412 &ta->mechListMIC);
1413 if (!NT_STATUS_IS_OK(status)) {
1414 DBG_WARNING("failed to verify mechListMIC: %s\n",
1415 nt_errstr(status));
1416 return status;
1419 spnego_state->needs_mic_check = false;
1420 spnego_state->done_mic_check = true;
1421 goto server_response;
1424 if (!spnego_state->sub_sec_ready) {
1425 status = gensec_update_ev(spnego_state->sub_sec_security,
1426 out_mem_ctx, ev,
1427 sub_in, &sub_out);
1428 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1429 DEBUG(2, ("SPNEGO login failed: %s\n",
1430 nt_errstr(status)));
1431 return status;
1433 if (NT_STATUS_IS_OK(status)) {
1434 spnego_state->sub_sec_ready = true;
1436 if (!NT_STATUS_IS_OK(status)) {
1437 goto server_response;
1439 } else {
1440 status = NT_STATUS_OK;
1443 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1444 GENSEC_FEATURE_SIGN);
1445 if (spnego_state->simulate_w2k) {
1446 have_sign = false;
1448 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1449 GENSEC_FEATURE_NEW_SPNEGO);
1450 if (ta->mechListMIC.length > 0) {
1451 new_spnego = true;
1454 if (have_sign && new_spnego) {
1455 spnego_state->needs_mic_check = true;
1456 spnego_state->needs_mic_sign = true;
1459 if (have_sign && ta->mechListMIC.length > 0) {
1460 status = gensec_check_packet(spnego_state->sub_sec_security,
1461 spnego_state->mech_types.data,
1462 spnego_state->mech_types.length,
1463 spnego_state->mech_types.data,
1464 spnego_state->mech_types.length,
1465 &ta->mechListMIC);
1466 if (!NT_STATUS_IS_OK(status)) {
1467 DBG_WARNING("failed to verify mechListMIC: %s\n",
1468 nt_errstr(status));
1469 return status;
1472 spnego_state->needs_mic_check = false;
1473 spnego_state->done_mic_check = true;
1476 if (spnego_state->needs_mic_sign) {
1477 status = gensec_sign_packet(spnego_state->sub_sec_security,
1478 out_mem_ctx,
1479 spnego_state->mech_types.data,
1480 spnego_state->mech_types.length,
1481 spnego_state->mech_types.data,
1482 spnego_state->mech_types.length,
1483 &mech_list_mic);
1484 if (!NT_STATUS_IS_OK(status)) {
1485 DBG_WARNING("failed to sign mechListMIC: %s\n",
1486 nt_errstr(status));
1487 return status;
1489 spnego_state->needs_mic_sign = false;
1492 if (spnego_state->needs_mic_check) {
1493 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1496 server_response:
1497 return gensec_spnego_server_response(spnego_state,
1498 out_mem_ctx,
1499 status,
1500 sub_out,
1501 mech_list_mic,
1502 out);
1505 struct gensec_spnego_update_state {
1506 struct tevent_context *ev;
1507 struct gensec_security *gensec;
1508 struct spnego_state *spnego;
1510 DATA_BLOB full_in;
1511 struct spnego_data _spnego_in;
1512 struct spnego_data *spnego_in;
1514 struct {
1515 bool needed;
1516 DATA_BLOB in;
1517 NTSTATUS status;
1518 DATA_BLOB out;
1519 } sub;
1521 NTSTATUS status;
1522 DATA_BLOB out;
1525 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1526 enum tevent_req_state req_state)
1528 struct gensec_spnego_update_state *state =
1529 tevent_req_data(req,
1530 struct gensec_spnego_update_state);
1532 switch (req_state) {
1533 case TEVENT_REQ_USER_ERROR:
1534 case TEVENT_REQ_TIMED_OUT:
1535 case TEVENT_REQ_NO_MEMORY:
1537 * A fatal error, further updates are not allowed.
1539 state->spnego->state_position = SPNEGO_DONE;
1540 break;
1541 default:
1542 break;
1546 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1547 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1548 DATA_BLOB *full_in);
1549 static void gensec_spnego_update_pre(struct tevent_req *req);
1550 static void gensec_spnego_update_done(struct tevent_req *subreq);
1551 static void gensec_spnego_update_post(struct tevent_req *req);
1552 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1553 TALLOC_CTX *out_mem_ctx,
1554 DATA_BLOB *_out);
1556 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1557 struct tevent_context *ev,
1558 struct gensec_security *gensec_security,
1559 const DATA_BLOB in)
1561 struct spnego_state *spnego_state =
1562 talloc_get_type_abort(gensec_security->private_data,
1563 struct spnego_state);
1564 struct tevent_req *req = NULL;
1565 struct gensec_spnego_update_state *state = NULL;
1566 NTSTATUS status;
1567 ssize_t len;
1569 req = tevent_req_create(mem_ctx, &state,
1570 struct gensec_spnego_update_state);
1571 if (req == NULL) {
1572 return NULL;
1574 state->ev = ev;
1575 state->gensec = gensec_security;
1576 state->spnego = spnego_state;
1577 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1579 if (spnego_state->out_frag.length > 0) {
1580 if (in.length > 0) {
1581 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1582 return tevent_req_post(req, ev);
1585 status = gensec_spnego_update_out(gensec_security,
1586 state, &state->out);
1587 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1588 tevent_req_nterror(req, status);
1589 return tevent_req_post(req, ev);
1592 state->status = status;
1593 tevent_req_done(req);
1594 return tevent_req_post(req, ev);
1597 status = gensec_spnego_update_in(gensec_security, in,
1598 state, &state->full_in);
1599 state->status = status;
1600 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1601 tevent_req_done(req);
1602 return tevent_req_post(req, ev);
1604 if (tevent_req_nterror(req, status)) {
1605 return tevent_req_post(req, ev);
1608 /* Check if we got a valid SPNEGO blob... */
1610 switch (spnego_state->state_position) {
1611 case SPNEGO_FALLBACK:
1612 break;
1614 case SPNEGO_CLIENT_TARG:
1615 case SPNEGO_SERVER_TARG:
1616 if (state->full_in.length == 0) {
1617 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1618 return tevent_req_post(req, ev);
1621 /* fall through */
1622 case SPNEGO_CLIENT_START:
1623 case SPNEGO_SERVER_START:
1625 if (state->full_in.length == 0) {
1626 /* create_negTokenInit later */
1627 break;
1630 len = spnego_read_data(state,
1631 state->full_in,
1632 &state->_spnego_in);
1633 if (len == -1) {
1634 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1635 DEBUG(1, ("Invalid SPNEGO request:\n"));
1636 dump_data(1, state->full_in.data,
1637 state->full_in.length);
1638 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1639 return tevent_req_post(req, ev);
1643 * This is the 'fallback' case, where we don't get
1644 * SPNEGO, and have to try all the other options (and
1645 * hope they all have a magic string they check)
1647 status = gensec_spnego_server_try_fallback(gensec_security,
1648 spnego_state,
1649 state,
1650 state->full_in);
1651 if (tevent_req_nterror(req, status)) {
1652 return tevent_req_post(req, ev);
1656 * We'll continue with SPNEGO_FALLBACK below...
1658 break;
1660 state->spnego_in = &state->_spnego_in;
1662 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1663 if (state->spnego_in->type != spnego_state->expected_packet) {
1664 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1665 state->spnego_in->type,
1666 spnego_state->expected_packet));
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);
1673 break;
1675 default:
1676 smb_panic(__location__);
1677 return NULL;
1680 gensec_spnego_update_pre(req);
1681 if (!tevent_req_is_in_progress(req)) {
1682 return tevent_req_post(req, ev);
1685 if (state->sub.needed) {
1686 struct tevent_req *subreq = NULL;
1689 * We may need one more roundtrip...
1691 subreq = gensec_update_send(state, state->ev,
1692 spnego_state->sub_sec_security,
1693 state->sub.in);
1694 if (tevent_req_nomem(subreq, req)) {
1695 return tevent_req_post(req, ev);
1697 tevent_req_set_callback(subreq,
1698 gensec_spnego_update_done,
1699 req);
1700 state->sub.needed = false;
1701 return req;
1704 gensec_spnego_update_post(req);
1705 if (!tevent_req_is_in_progress(req)) {
1706 return tevent_req_post(req, ev);
1709 return req;
1712 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1713 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1714 DATA_BLOB *full_in)
1716 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1717 size_t expected;
1718 bool ok;
1720 *full_in = data_blob_null;
1722 switch (spnego_state->state_position) {
1723 case SPNEGO_FALLBACK:
1724 *full_in = in;
1725 spnego_state->in_needed = 0;
1726 return NT_STATUS_OK;
1728 case SPNEGO_CLIENT_START:
1729 case SPNEGO_CLIENT_TARG:
1730 case SPNEGO_SERVER_START:
1731 case SPNEGO_SERVER_TARG:
1732 break;
1734 case SPNEGO_DONE:
1735 default:
1736 return NT_STATUS_INVALID_PARAMETER;
1739 if (spnego_state->in_needed == 0) {
1740 size_t size = 0;
1741 int ret;
1744 * try to work out the size of the full
1745 * input token, it might be fragmented
1747 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1748 if ((ret != 0) && (ret != EAGAIN)) {
1749 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1752 if ((ret == 0) || (ret == EAGAIN)) {
1753 spnego_state->in_needed = size;
1754 } else {
1756 * If it is not an asn1 message
1757 * just call the next layer.
1759 spnego_state->in_needed = in.length;
1763 if (spnego_state->in_needed > UINT16_MAX) {
1765 * limit the incoming message to 0xFFFF
1766 * to avoid DoS attacks.
1768 return NT_STATUS_INVALID_BUFFER_SIZE;
1771 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1773 * If we reach this, we know we got at least
1774 * part of an asn1 message, getting 0 means
1775 * the remote peer wants us to spin.
1777 return NT_STATUS_INVALID_PARAMETER;
1780 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1781 if (in.length > expected) {
1783 * we got more than expected
1785 return NT_STATUS_INVALID_PARAMETER;
1788 if (in.length == spnego_state->in_needed) {
1790 * if the in.length contains the full blob
1791 * we are done.
1793 * Note: this implies spnego_state->in_frag.length == 0,
1794 * but we do not need to check this explicitly
1795 * because we already know that we did not get
1796 * more than expected.
1798 *full_in = in;
1799 spnego_state->in_needed = 0;
1800 return NT_STATUS_OK;
1803 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1804 in.data, in.length);
1805 if (!ok) {
1806 return NT_STATUS_NO_MEMORY;
1809 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1810 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1813 *full_in = spnego_state->in_frag;
1814 talloc_steal(mem_ctx, full_in->data);
1815 spnego_state->in_frag = data_blob_null;
1816 spnego_state->in_needed = 0;
1817 return NT_STATUS_OK;
1820 static void gensec_spnego_update_pre(struct tevent_req *req)
1822 struct gensec_spnego_update_state *state =
1823 tevent_req_data(req,
1824 struct gensec_spnego_update_state);
1825 struct gensec_security *gensec_security = state->gensec;
1826 struct spnego_state *spnego_state = state->spnego;
1827 struct tevent_context *ev = state->ev;
1828 NTSTATUS status;
1830 state->sub.needed = false;
1831 state->sub.in = data_blob_null;
1832 state->sub.status = NT_STATUS_INTERNAL_ERROR;
1833 state->sub.out = data_blob_null;
1835 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1836 state->sub.in = state->full_in;
1837 state->full_in = data_blob_null;
1838 state->sub.needed = true;
1839 return;
1842 switch (spnego_state->state_position) {
1843 case SPNEGO_CLIENT_START:
1844 if (state->spnego_in == NULL) {
1845 /* client to produce negTokenInit */
1846 status = gensec_spnego_create_negTokenInit(gensec_security,
1847 spnego_state, state, ev,
1848 &spnego_state->out_frag);
1849 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1850 tevent_req_nterror(req, status);
1851 return;
1853 break;
1856 status = gensec_spnego_client_negTokenInit(gensec_security,
1857 spnego_state, ev,
1858 state->spnego_in, state,
1859 &spnego_state->out_frag);
1860 break;
1862 case SPNEGO_CLIENT_TARG:
1863 status = gensec_spnego_client_negTokenTarg(gensec_security,
1864 spnego_state, ev,
1865 state->spnego_in, state,
1866 &spnego_state->out_frag);
1867 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1868 tevent_req_nterror(req, status);
1869 return;
1871 break;
1873 case SPNEGO_SERVER_START:
1874 if (state->spnego_in == NULL) {
1875 /* server to produce negTokenInit */
1876 status = gensec_spnego_create_negTokenInit(gensec_security,
1877 spnego_state, state, ev,
1878 &spnego_state->out_frag);
1879 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1880 tevent_req_nterror(req, status);
1881 return;
1883 break;
1886 status = gensec_spnego_server_negTokenInit(gensec_security,
1887 spnego_state, ev,
1888 state->spnego_in, state,
1889 &spnego_state->out_frag);
1890 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1891 tevent_req_nterror(req, status);
1892 return;
1894 break;
1896 case SPNEGO_SERVER_TARG:
1897 status = gensec_spnego_server_negTokenTarg(gensec_security,
1898 spnego_state, ev,
1899 state->spnego_in, state,
1900 &spnego_state->out_frag);
1901 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1902 tevent_req_nterror(req, status);
1903 return;
1905 break;
1907 default:
1908 smb_panic(__location__);
1909 return;
1912 spnego_state->out_status = status;
1915 static void gensec_spnego_update_done(struct tevent_req *subreq)
1917 struct tevent_req *req =
1918 tevent_req_callback_data(subreq,
1919 struct tevent_req);
1920 struct gensec_spnego_update_state *state =
1921 tevent_req_data(req,
1922 struct gensec_spnego_update_state);
1923 struct spnego_state *spnego_state = state->spnego;
1925 state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1926 TALLOC_FREE(subreq);
1927 if (NT_STATUS_IS_OK(state->sub.status)) {
1928 spnego_state->sub_sec_ready = true;
1931 gensec_spnego_update_post(req);
1934 static void gensec_spnego_update_post(struct tevent_req *req)
1936 struct gensec_spnego_update_state *state =
1937 tevent_req_data(req,
1938 struct gensec_spnego_update_state);
1939 struct spnego_state *spnego_state = state->spnego;
1940 NTSTATUS status;
1942 state->sub.in = data_blob_null;
1943 state->sub.needed = false;
1945 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1946 status = state->sub.status;
1947 spnego_state->out_frag = state->sub.out;
1948 talloc_steal(spnego_state, spnego_state->out_frag.data);
1949 state->sub.out = data_blob_null;
1950 goto respond;
1954 * For now just handle the sync processing done
1955 * in gensec_spnego_update_pre()
1957 status = spnego_state->out_status;
1959 if (NT_STATUS_IS_OK(status)) {
1960 bool reset_full = true;
1962 reset_full = !spnego_state->done_mic_check;
1964 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1965 reset_full);
1966 if (tevent_req_nterror(req, status)) {
1967 return;
1971 respond:
1972 spnego_state->out_status = status;
1974 status = gensec_spnego_update_out(state->gensec,
1975 state, &state->out);
1976 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1977 tevent_req_nterror(req, status);
1978 return;
1981 state->status = status;
1982 tevent_req_done(req);
1983 return;
1986 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1987 TALLOC_CTX *out_mem_ctx,
1988 DATA_BLOB *_out)
1990 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1991 DATA_BLOB out = data_blob_null;
1992 bool ok;
1994 *_out = data_blob_null;
1996 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1998 * Fast path, we can deliver everything
2001 *_out = spnego_state->out_frag;
2002 if (spnego_state->out_frag.length > 0) {
2003 talloc_steal(out_mem_ctx, _out->data);
2004 spnego_state->out_frag = data_blob_null;
2007 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
2008 return spnego_state->out_status;
2012 * We're completely done, further updates are not allowed.
2014 spnego_state->state_position = SPNEGO_DONE;
2015 return gensec_child_ready(gensec_security,
2016 spnego_state->sub_sec_security);
2019 out = spnego_state->out_frag;
2022 * copy the remaining bytes
2024 spnego_state->out_frag = data_blob_talloc(spnego_state,
2025 out.data + spnego_state->out_max_length,
2026 out.length - spnego_state->out_max_length);
2027 if (spnego_state->out_frag.data == NULL) {
2028 return NT_STATUS_NO_MEMORY;
2032 * truncate the buffer
2034 ok = data_blob_realloc(spnego_state, &out,
2035 spnego_state->out_max_length);
2036 if (!ok) {
2037 return NT_STATUS_NO_MEMORY;
2040 talloc_steal(out_mem_ctx, out.data);
2041 *_out = out;
2042 return NT_STATUS_MORE_PROCESSING_REQUIRED;
2045 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
2046 TALLOC_CTX *out_mem_ctx,
2047 DATA_BLOB *out)
2049 struct gensec_spnego_update_state *state =
2050 tevent_req_data(req,
2051 struct gensec_spnego_update_state);
2052 NTSTATUS status;
2054 *out = data_blob_null;
2056 if (tevent_req_is_nterror(req, &status)) {
2057 tevent_req_received(req);
2058 return status;
2061 *out = state->out;
2062 talloc_steal(out_mem_ctx, state->out.data);
2063 status = state->status;
2064 tevent_req_received(req);
2065 return status;
2068 static const char *gensec_spnego_oids[] = {
2069 GENSEC_OID_SPNEGO,
2070 NULL
2073 static const struct gensec_security_ops gensec_spnego_security_ops = {
2074 .name = "spnego",
2075 .sasl_name = "GSS-SPNEGO",
2076 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
2077 .oid = gensec_spnego_oids,
2078 .client_start = gensec_spnego_client_start,
2079 .server_start = gensec_spnego_server_start,
2080 .update_send = gensec_spnego_update_send,
2081 .update_recv = gensec_spnego_update_recv,
2082 .seal_packet = gensec_child_seal_packet,
2083 .sign_packet = gensec_child_sign_packet,
2084 .sig_size = gensec_child_sig_size,
2085 .max_wrapped_size = gensec_child_max_wrapped_size,
2086 .max_input_size = gensec_child_max_input_size,
2087 .check_packet = gensec_child_check_packet,
2088 .unseal_packet = gensec_child_unseal_packet,
2089 .wrap = gensec_child_wrap,
2090 .unwrap = gensec_child_unwrap,
2091 .session_key = gensec_child_session_key,
2092 .session_info = gensec_child_session_info,
2093 .want_feature = gensec_child_want_feature,
2094 .have_feature = gensec_child_have_feature,
2095 .expire_time = gensec_child_expire_time,
2096 .final_auth_type = gensec_child_final_auth_type,
2097 .enabled = true,
2098 .priority = GENSEC_SPNEGO
2101 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
2103 NTSTATUS ret;
2104 ret = gensec_register(ctx, &gensec_spnego_security_ops);
2105 if (!NT_STATUS_IS_OK(ret)) {
2106 DEBUG(0,("Failed to register '%s' gensec backend!\n",
2107 gensec_spnego_security_ops.name));
2108 return ret;
2111 return ret;