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/>.
26 #include "../libcli/auth/spnego.h"
27 #include "librpc/gen_ndr/ndr_dcerpc.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/gensec/gensec_internal.h"
31 #include "param/param.h"
32 #include "lib/util/asn1.h"
36 _PUBLIC_ NTSTATUS gensec_spnego_init(void);
38 enum spnego_state_position {
48 enum spnego_message_type expected_packet;
49 enum spnego_state_position state_position;
50 struct gensec_security *sub_sec_security;
51 bool no_response_expected;
58 * The following is used to implement
59 * the update token fragmentation
63 size_t out_max_length;
69 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
71 struct spnego_state *spnego_state;
73 spnego_state = talloc_zero(gensec_security, struct spnego_state);
75 return NT_STATUS_NO_MEMORY;
78 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
79 spnego_state->state_position = SPNEGO_CLIENT_START;
80 spnego_state->sub_sec_security = NULL;
81 spnego_state->no_response_expected = false;
82 spnego_state->mech_types = data_blob(NULL, 0);
83 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
84 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
86 gensec_security->private_data = spnego_state;
90 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
92 struct spnego_state *spnego_state;
94 spnego_state = talloc_zero(gensec_security, struct spnego_state);
96 return NT_STATUS_NO_MEMORY;
99 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
100 spnego_state->state_position = SPNEGO_SERVER_START;
101 spnego_state->sub_sec_security = NULL;
102 spnego_state->no_response_expected = false;
103 spnego_state->mech_types = data_blob(NULL, 0);
104 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
105 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
107 gensec_security->private_data = spnego_state;
112 wrappers for the spnego_*() functions
114 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
115 uint8_t *data, size_t length,
116 const uint8_t *whole_pdu, size_t pdu_length,
117 const DATA_BLOB *sig)
119 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
121 if (spnego_state->state_position != SPNEGO_DONE
122 && spnego_state->state_position != SPNEGO_FALLBACK) {
123 return NT_STATUS_INVALID_PARAMETER;
126 return gensec_unseal_packet(spnego_state->sub_sec_security,
128 whole_pdu, pdu_length,
132 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
133 const uint8_t *data, size_t length,
134 const uint8_t *whole_pdu, size_t pdu_length,
135 const DATA_BLOB *sig)
137 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
139 if (spnego_state->state_position != SPNEGO_DONE
140 && spnego_state->state_position != SPNEGO_FALLBACK) {
141 return NT_STATUS_INVALID_PARAMETER;
144 return gensec_check_packet(spnego_state->sub_sec_security,
146 whole_pdu, pdu_length,
150 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
152 uint8_t *data, size_t length,
153 const uint8_t *whole_pdu, size_t pdu_length,
156 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
158 if (spnego_state->state_position != SPNEGO_DONE
159 && spnego_state->state_position != SPNEGO_FALLBACK) {
160 return NT_STATUS_INVALID_PARAMETER;
163 return gensec_seal_packet(spnego_state->sub_sec_security,
166 whole_pdu, pdu_length,
170 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
172 const uint8_t *data, size_t length,
173 const uint8_t *whole_pdu, size_t pdu_length,
176 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
178 if (spnego_state->state_position != SPNEGO_DONE
179 && spnego_state->state_position != SPNEGO_FALLBACK) {
180 return NT_STATUS_INVALID_PARAMETER;
183 return gensec_sign_packet(spnego_state->sub_sec_security,
186 whole_pdu, pdu_length,
190 static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security,
195 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
197 if (spnego_state->state_position != SPNEGO_DONE
198 && spnego_state->state_position != SPNEGO_FALLBACK) {
199 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
200 return NT_STATUS_INVALID_PARAMETER;
203 return gensec_wrap(spnego_state->sub_sec_security,
207 static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security,
212 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
214 if (spnego_state->state_position != SPNEGO_DONE
215 && spnego_state->state_position != SPNEGO_FALLBACK) {
216 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
217 return NT_STATUS_INVALID_PARAMETER;
220 return gensec_unwrap(spnego_state->sub_sec_security,
224 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size)
226 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
228 if (spnego_state->state_position != SPNEGO_DONE
229 && spnego_state->state_position != SPNEGO_FALLBACK) {
233 return gensec_sig_size(spnego_state->sub_sec_security, data_size);
236 static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
238 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
240 if (spnego_state->state_position != SPNEGO_DONE
241 && spnego_state->state_position != SPNEGO_FALLBACK) {
245 return gensec_max_input_size(spnego_state->sub_sec_security);
248 static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
250 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
252 if (spnego_state->state_position != SPNEGO_DONE
253 && spnego_state->state_position != SPNEGO_FALLBACK) {
257 return gensec_max_wrapped_size(spnego_state->sub_sec_security);
260 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
262 DATA_BLOB *session_key)
264 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
265 if (!spnego_state->sub_sec_security) {
266 return NT_STATUS_INVALID_PARAMETER;
269 return gensec_session_key(spnego_state->sub_sec_security,
274 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
276 struct auth_session_info **session_info)
278 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
279 if (!spnego_state->sub_sec_security) {
280 return NT_STATUS_INVALID_PARAMETER;
283 return gensec_session_info(spnego_state->sub_sec_security,
288 /** Fallback to another GENSEC mechanism, based on magic strings
290 * This is the 'fallback' case, where we don't get SPNEGO, and have to
291 * try all the other options (and hope they all have a magic string
295 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
296 struct spnego_state *spnego_state,
297 struct tevent_context *ev,
298 TALLOC_CTX *out_mem_ctx,
299 const DATA_BLOB in, DATA_BLOB *out)
302 const struct gensec_security_ops **all_ops;
304 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
306 for (i=0; all_ops && all_ops[i]; i++) {
310 if (gensec_security != NULL &&
311 !gensec_security_ops_enabled(all_ops[i], gensec_security))
314 if (!all_ops[i]->oid) {
319 for (j=0; all_ops[i]->oid[j]; j++) {
320 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
328 if (!all_ops[i]->magic) {
332 nt_status = all_ops[i]->magic(gensec_security, &in);
333 if (!NT_STATUS_IS_OK(nt_status)) {
337 spnego_state->state_position = SPNEGO_FALLBACK;
339 nt_status = gensec_subcontext_start(spnego_state,
341 &spnego_state->sub_sec_security);
343 if (!NT_STATUS_IS_OK(nt_status)) {
346 /* select the sub context */
347 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
349 if (!NT_STATUS_IS_OK(nt_status)) {
352 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
353 ev, out_mem_ctx, in, out);
356 DEBUG(1, ("Failed to parse SPNEGO request\n"));
357 return NT_STATUS_INVALID_PARAMETER;
361 Parse the netTokenInit, either from the client, to the server, or
362 from the server to the client.
365 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
366 struct spnego_state *spnego_state,
367 TALLOC_CTX *out_mem_ctx,
368 struct tevent_context *ev,
369 const char * const *mechType,
370 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
373 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
374 DATA_BLOB null_data_blob = data_blob(NULL,0);
377 const struct gensec_security_ops_wrapper *all_sec
378 = gensec_security_by_oid_list(gensec_security,
383 ok = spnego_write_mech_types(spnego_state,
385 &spnego_state->mech_types);
387 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
388 return NT_STATUS_NO_MEMORY;
391 if (spnego_state->state_position == SPNEGO_SERVER_START) {
393 for (j=0; mechType && mechType[j]; j++) {
394 for (i=0; all_sec && all_sec[i].op; i++) {
395 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
399 nt_status = gensec_subcontext_start(spnego_state,
401 &spnego_state->sub_sec_security);
402 if (!NT_STATUS_IS_OK(nt_status)) {
405 /* select the sub context */
406 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
408 if (!NT_STATUS_IS_OK(nt_status)) {
409 talloc_free(spnego_state->sub_sec_security);
410 spnego_state->sub_sec_security = NULL;
415 /* no optimistic token */
416 spnego_state->neg_oid = all_sec[i].oid;
417 *unwrapped_out = data_blob_null;
418 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
422 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
427 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
428 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
429 /* Pretend we never started it (lets the first run find some incompatible demand) */
431 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
432 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
433 talloc_free(spnego_state->sub_sec_security);
434 spnego_state->sub_sec_security = NULL;
438 spnego_state->neg_oid = all_sec[i].oid;
441 if (spnego_state->sub_sec_security) {
446 if (!spnego_state->sub_sec_security) {
447 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
448 return NT_STATUS_INVALID_PARAMETER;
452 /* Having tried any optimistic token from the client (if we
453 * were the server), if we didn't get anywhere, walk our list
454 * in our preference order */
456 if (!spnego_state->sub_sec_security) {
457 for (i=0; all_sec && all_sec[i].op; i++) {
458 nt_status = gensec_subcontext_start(spnego_state,
460 &spnego_state->sub_sec_security);
461 if (!NT_STATUS_IS_OK(nt_status)) {
464 /* select the sub context */
465 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
467 if (!NT_STATUS_IS_OK(nt_status)) {
468 talloc_free(spnego_state->sub_sec_security);
469 spnego_state->sub_sec_security = NULL;
473 spnego_state->neg_oid = all_sec[i].oid;
475 /* only get the helping start blob for the first OID */
476 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
482 /* it is likely that a NULL input token will
483 * not be liked by most server mechs, but if
484 * we are in the client, we want the first
485 * update packet to be able to abort the use
487 if (spnego_state->state_position != SPNEGO_SERVER_START) {
488 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
489 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
490 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
491 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
492 /* Pretend we never started it (lets the first run find some incompatible demand) */
494 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
495 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
496 talloc_free(spnego_state->sub_sec_security);
497 spnego_state->sub_sec_security = NULL;
506 if (spnego_state->sub_sec_security) {
507 /* it is likely that a NULL input token will
508 * not be liked by most server mechs, but this
509 * does the right thing in the CIFS client.
510 * just push us along the merry-go-round
511 * again, and hope for better luck next
514 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
515 *unwrapped_out = data_blob(NULL, 0);
516 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
519 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
520 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
521 && !NT_STATUS_IS_OK(nt_status)) {
522 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
523 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
524 talloc_free(spnego_state->sub_sec_security);
525 spnego_state->sub_sec_security = NULL;
527 /* We started the mech correctly, and the
528 * input from the other side was valid.
529 * Return the error (say bad password, invalid
534 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
537 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
538 /* we could re-negotiate here, but it would only work
539 * if the client or server lied about what it could
540 * support the first time. Lets keep this code to
546 /** create a negTokenInit
548 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
550 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
551 struct spnego_state *spnego_state,
552 TALLOC_CTX *out_mem_ctx,
553 struct tevent_context *ev,
554 const DATA_BLOB in, DATA_BLOB *out)
557 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
558 DATA_BLOB null_data_blob = data_blob(NULL,0);
559 const char **mechTypes = NULL;
560 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
561 const struct gensec_security_ops_wrapper *all_sec;
563 mechTypes = gensec_security_oids(gensec_security,
564 out_mem_ctx, GENSEC_OID_SPNEGO);
566 all_sec = gensec_security_by_oid_list(gensec_security,
570 for (i=0; all_sec && all_sec[i].op; i++) {
571 struct spnego_data spnego_out;
572 const char **send_mech_types;
575 nt_status = gensec_subcontext_start(spnego_state,
577 &spnego_state->sub_sec_security);
578 if (!NT_STATUS_IS_OK(nt_status)) {
581 /* select the sub context */
582 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
584 if (!NT_STATUS_IS_OK(nt_status)) {
585 talloc_free(spnego_state->sub_sec_security);
586 spnego_state->sub_sec_security = NULL;
590 /* In the client, try and produce the first (optimistic) packet */
591 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
592 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
598 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
599 && !NT_STATUS_IS_OK(nt_status)) {
600 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
601 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
602 talloc_free(spnego_state->sub_sec_security);
603 spnego_state->sub_sec_security = NULL;
604 /* Pretend we never started it (lets the first run find some incompatible demand) */
610 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
612 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
615 ok = spnego_write_mech_types(spnego_state,
617 &spnego_state->mech_types);
619 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
620 return NT_STATUS_NO_MEMORY;
623 /* List the remaining mechs as options */
624 spnego_out.negTokenInit.mechTypes = send_mech_types;
625 spnego_out.negTokenInit.reqFlags = null_data_blob;
626 spnego_out.negTokenInit.reqFlagsPadding = 0;
628 if (spnego_state->state_position == SPNEGO_SERVER_START) {
629 spnego_out.negTokenInit.mechListMIC
630 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
632 spnego_out.negTokenInit.mechListMIC = null_data_blob;
635 spnego_out.negTokenInit.mechToken = unwrapped_out;
637 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
638 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
639 return NT_STATUS_INVALID_PARAMETER;
643 spnego_state->neg_oid = all_sec[i].oid;
645 if (NT_STATUS_IS_OK(nt_status)) {
646 spnego_state->no_response_expected = true;
649 return NT_STATUS_MORE_PROCESSING_REQUIRED;
651 talloc_free(spnego_state->sub_sec_security);
652 spnego_state->sub_sec_security = NULL;
654 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
659 /** create a server negTokenTarg
661 * This is the case, where the client is the first one who sends data
664 static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_state,
665 TALLOC_CTX *out_mem_ctx,
667 const DATA_BLOB unwrapped_out,
668 DATA_BLOB mech_list_mic,
671 struct spnego_data spnego_out;
672 DATA_BLOB null_data_blob = data_blob(NULL, 0);
675 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
676 spnego_out.negTokenTarg.responseToken = unwrapped_out;
677 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
678 spnego_out.negTokenTarg.supportedMech = NULL;
680 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
681 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
682 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
683 spnego_state->state_position = SPNEGO_SERVER_TARG;
684 } else if (NT_STATUS_IS_OK(nt_status)) {
685 if (unwrapped_out.data) {
686 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
688 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
689 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
690 spnego_state->state_position = SPNEGO_DONE;
692 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
693 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
694 spnego_state->state_position = SPNEGO_DONE;
697 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
698 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
699 return NT_STATUS_INVALID_PARAMETER;
702 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
708 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
709 struct tevent_context *ev,
710 const DATA_BLOB in, DATA_BLOB *out)
712 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
713 DATA_BLOB null_data_blob = data_blob(NULL, 0);
714 DATA_BLOB mech_list_mic = data_blob(NULL, 0);
715 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
716 struct spnego_data spnego_out;
717 struct spnego_data spnego;
721 *out = data_blob(NULL, 0);
724 out_mem_ctx = spnego_state;
727 /* and switch into the state machine */
729 switch (spnego_state->state_position) {
730 case SPNEGO_FALLBACK:
731 return gensec_update_ev(spnego_state->sub_sec_security, ev,
732 out_mem_ctx, in, out);
733 case SPNEGO_SERVER_START:
738 len = spnego_read_data(gensec_security, in, &spnego);
740 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
741 ev, out_mem_ctx, in, out);
743 /* client sent NegTargetInit, we send NegTokenTarg */
745 /* OK, so it's real SPNEGO, check the packet's the one we expect */
746 if (spnego.type != spnego_state->expected_packet) {
747 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
748 spnego_state->expected_packet));
749 dump_data(1, in.data, in.length);
750 spnego_free_data(&spnego);
751 return NT_STATUS_INVALID_PARAMETER;
754 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
758 spnego.negTokenInit.mechTypes,
759 spnego.negTokenInit.mechToken,
762 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
769 spnego_free_data(&spnego);
773 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
774 out_mem_ctx, ev, in, out);
775 spnego_state->state_position = SPNEGO_SERVER_START;
776 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
781 case SPNEGO_CLIENT_START:
783 /* The server offers a list of mechanisms */
785 const char *my_mechs[] = {NULL, NULL};
786 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
790 /* client to produce negTokenInit */
791 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
792 out_mem_ctx, ev, in, out);
793 spnego_state->state_position = SPNEGO_CLIENT_TARG;
794 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
798 len = spnego_read_data(gensec_security, in, &spnego);
801 DEBUG(1, ("Invalid SPNEGO request:\n"));
802 dump_data(1, in.data, in.length);
803 return NT_STATUS_INVALID_PARAMETER;
806 /* OK, so it's real SPNEGO, check the packet's the one we expect */
807 if (spnego.type != spnego_state->expected_packet) {
808 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
809 spnego_state->expected_packet));
810 dump_data(1, in.data, in.length);
811 spnego_free_data(&spnego);
812 return NT_STATUS_INVALID_PARAMETER;
815 if (spnego.negTokenInit.targetPrincipal
816 && strcmp(spnego.negTokenInit.targetPrincipal, ADS_IGNORE_PRINCIPAL) != 0) {
817 DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
818 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
819 gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
823 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
827 spnego.negTokenInit.mechTypes,
828 spnego.negTokenInit.mechToken,
831 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
832 spnego_free_data(&spnego);
836 my_mechs[0] = spnego_state->neg_oid;
838 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
839 spnego_out.negTokenInit.mechTypes = my_mechs;
840 spnego_out.negTokenInit.reqFlags = null_data_blob;
841 spnego_out.negTokenInit.reqFlagsPadding = 0;
842 spnego_out.negTokenInit.mechListMIC = null_data_blob;
843 spnego_out.negTokenInit.mechToken = unwrapped_out;
845 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
846 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
847 return NT_STATUS_INVALID_PARAMETER;
850 ok = spnego_write_mech_types(spnego_state,
852 &spnego_state->mech_types);
854 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
855 return NT_STATUS_NO_MEMORY;
859 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
860 spnego_state->state_position = SPNEGO_CLIENT_TARG;
862 if (NT_STATUS_IS_OK(nt_status)) {
863 spnego_state->no_response_expected = true;
866 spnego_free_data(&spnego);
867 return NT_STATUS_MORE_PROCESSING_REQUIRED;
869 case SPNEGO_SERVER_TARG:
872 bool new_spnego = false;
875 return NT_STATUS_INVALID_PARAMETER;
878 len = spnego_read_data(gensec_security, in, &spnego);
881 DEBUG(1, ("Invalid SPNEGO request:\n"));
882 dump_data(1, in.data, in.length);
883 return NT_STATUS_INVALID_PARAMETER;
886 /* OK, so it's real SPNEGO, check the packet's the one we expect */
887 if (spnego.type != spnego_state->expected_packet) {
888 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
889 spnego_state->expected_packet));
890 dump_data(1, in.data, in.length);
891 spnego_free_data(&spnego);
892 return NT_STATUS_INVALID_PARAMETER;
895 if (!spnego_state->sub_sec_security) {
896 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
897 spnego_free_data(&spnego);
898 return NT_STATUS_INVALID_PARAMETER;
901 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
903 spnego.negTokenTarg.responseToken,
905 if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
907 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
908 spnego_state->mech_types.data,
909 spnego_state->mech_types.length,
910 spnego_state->mech_types.data,
911 spnego_state->mech_types.length,
912 &spnego.negTokenTarg.mechListMIC);
913 if (!NT_STATUS_IS_OK(nt_status)) {
914 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
915 nt_errstr(nt_status)));
918 if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
919 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
921 spnego_state->mech_types.data,
922 spnego_state->mech_types.length,
923 spnego_state->mech_types.data,
924 spnego_state->mech_types.length,
926 if (!NT_STATUS_IS_OK(nt_status)) {
927 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
928 nt_errstr(nt_status)));
932 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
939 spnego_free_data(&spnego);
943 case SPNEGO_CLIENT_TARG:
947 return NT_STATUS_INVALID_PARAMETER;
950 len = spnego_read_data(gensec_security, in, &spnego);
953 DEBUG(1, ("Invalid SPNEGO request:\n"));
954 dump_data(1, in.data, in.length);
955 return NT_STATUS_INVALID_PARAMETER;
958 /* OK, so it's real SPNEGO, check the packet's the one we expect */
959 if (spnego.type != spnego_state->expected_packet) {
960 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
961 spnego_state->expected_packet));
962 dump_data(1, in.data, in.length);
963 spnego_free_data(&spnego);
964 return NT_STATUS_INVALID_PARAMETER;
967 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
968 spnego_free_data(&spnego);
969 return NT_STATUS_LOGON_FAILURE;
972 /* Server didn't like our choice of mech, and chose something else */
973 if (((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
974 (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC)) &&
975 spnego.negTokenTarg.supportedMech &&
976 strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
977 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
978 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid),
979 gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech)));
981 spnego_state->no_response_expected = false;
982 talloc_free(spnego_state->sub_sec_security);
983 nt_status = gensec_subcontext_start(spnego_state,
985 &spnego_state->sub_sec_security);
986 if (!NT_STATUS_IS_OK(nt_status)) {
987 spnego_free_data(&spnego);
990 /* select the sub context */
991 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
992 spnego.negTokenTarg.supportedMech);
993 if (!NT_STATUS_IS_OK(nt_status)) {
994 spnego_free_data(&spnego);
998 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1000 spnego.negTokenTarg.responseToken,
1002 spnego_state->neg_oid = talloc_strdup(spnego_state, spnego.negTokenTarg.supportedMech);
1003 } else if (spnego_state->no_response_expected) {
1004 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1005 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
1006 nt_status = NT_STATUS_INVALID_PARAMETER;
1007 } else if (spnego.negTokenTarg.responseToken.length) {
1008 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
1009 nt_status = NT_STATUS_INVALID_PARAMETER;
1011 nt_status = NT_STATUS_OK;
1013 if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
1014 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1015 spnego_state->mech_types.data,
1016 spnego_state->mech_types.length,
1017 spnego_state->mech_types.data,
1018 spnego_state->mech_types.length,
1019 &spnego.negTokenTarg.mechListMIC);
1020 if (!NT_STATUS_IS_OK(nt_status)) {
1021 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1022 nt_errstr(nt_status)));
1026 bool new_spnego = false;
1028 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1030 spnego.negTokenTarg.responseToken,
1033 if (NT_STATUS_IS_OK(nt_status)
1034 && spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1035 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1036 GENSEC_FEATURE_NEW_SPNEGO);
1038 if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
1039 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1041 spnego_state->mech_types.data,
1042 spnego_state->mech_types.length,
1043 spnego_state->mech_types.data,
1044 spnego_state->mech_types.length,
1046 if (!NT_STATUS_IS_OK(nt_status)) {
1047 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1048 nt_errstr(nt_status)));
1051 if (NT_STATUS_IS_OK(nt_status)) {
1052 spnego_state->no_response_expected = true;
1056 spnego_free_data(&spnego);
1058 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1059 && !NT_STATUS_IS_OK(nt_status)) {
1060 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1061 spnego_state->sub_sec_security->ops->name,
1062 nt_errstr(nt_status)));
1066 if (unwrapped_out.length || mech_list_mic.length) {
1068 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1069 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1070 spnego_out.negTokenTarg.supportedMech = NULL;
1071 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1072 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1074 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1075 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1076 return NT_STATUS_INVALID_PARAMETER;
1079 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1080 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1083 /* all done - server has accepted, and we agree */
1084 *out = null_data_blob;
1086 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1087 /* unless of course it did not accept */
1088 DEBUG(1,("gensec_update ok but not accepted\n"));
1089 nt_status = NT_STATUS_INVALID_PARAMETER;
1092 spnego_state->state_position = SPNEGO_DONE;
1098 /* We should not be called after we are 'done' */
1099 return NT_STATUS_INVALID_PARAMETER;
1101 return NT_STATUS_INVALID_PARAMETER;
1104 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1105 const DATA_BLOB in, DATA_BLOB *full_in)
1107 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1111 *full_in = data_blob_null;
1113 if (spnego_state->in_needed == 0) {
1118 * try to work out the size of the full
1119 * input token, it might be fragmented
1121 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1122 if ((ret != 0) && (ret != EAGAIN)) {
1123 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1126 if ((ret == 0) || (ret == EAGAIN)) {
1127 spnego_state->in_needed = size;
1130 * If it is not an asn1 message
1131 * just call the next layer.
1133 spnego_state->in_needed = in.length;
1137 if (spnego_state->in_needed > UINT16_MAX) {
1139 * limit the incoming message to 0xFFFF
1140 * to avoid DoS attacks.
1142 return NT_STATUS_INVALID_BUFFER_SIZE;
1145 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1147 * If we reach this, we know we got at least
1148 * part of an asn1 message, getting 0 means
1149 * the remote peer wants us to spin.
1151 return NT_STATUS_INVALID_PARAMETER;
1154 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1155 if (in.length > expected) {
1157 * we got more than expected
1159 return NT_STATUS_INVALID_PARAMETER;
1162 if (in.length == spnego_state->in_needed) {
1164 * if the in.length contains the full blob
1167 * Note: this implies spnego_state->in_frag.length == 0,
1168 * but we do not need to check this explicitly
1169 * because we already know that we did not get
1170 * more than expected.
1173 return NT_STATUS_OK;
1176 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1177 in.data, in.length);
1179 return NT_STATUS_NO_MEMORY;
1182 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1183 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1186 *full_in = spnego_state->in_frag;
1187 return NT_STATUS_OK;
1190 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1191 TALLOC_CTX *out_mem_ctx,
1194 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1195 DATA_BLOB out = data_blob_null;
1198 *_out = data_blob_null;
1200 if (spnego_state->out_frag.length == 0) {
1201 return spnego_state->out_status;
1205 * There is still more data to be delivered
1206 * to the remote peer.
1209 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1211 * Fast path, we can deliver everything
1214 *_out = spnego_state->out_frag;
1215 talloc_steal(out_mem_ctx, _out->data);
1216 spnego_state->out_frag = data_blob_null;
1217 return spnego_state->out_status;
1220 out = spnego_state->out_frag;
1223 * copy the remaining bytes
1225 spnego_state->out_frag = data_blob_talloc(spnego_state,
1226 out.data + spnego_state->out_max_length,
1227 out.length - spnego_state->out_max_length);
1228 if (spnego_state->out_frag.data == NULL) {
1229 return NT_STATUS_NO_MEMORY;
1233 * truncate the buffer
1235 ok = data_blob_realloc(spnego_state, &out,
1236 spnego_state->out_max_length);
1238 return NT_STATUS_NO_MEMORY;
1241 talloc_steal(out_mem_ctx, out.data);
1243 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1246 static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security,
1247 TALLOC_CTX *out_mem_ctx,
1248 struct tevent_context *ev,
1249 const DATA_BLOB in, DATA_BLOB *out)
1251 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1252 DATA_BLOB full_in = data_blob_null;
1255 *out = data_blob_null;
1257 if (spnego_state->out_frag.length > 0) {
1258 if (in.length > 0) {
1259 return NT_STATUS_INVALID_PARAMETER;
1262 return gensec_spnego_update_out(gensec_security,
1267 status = gensec_spnego_update_in(gensec_security,
1269 if (!NT_STATUS_IS_OK(status)) {
1273 status = gensec_spnego_update(gensec_security,
1276 &spnego_state->out_frag);
1277 data_blob_free(&spnego_state->in_frag);
1278 spnego_state->in_needed = 0;
1279 if (NT_STATUS_IS_OK(status)) {
1280 gensec_security->child_security = spnego_state->sub_sec_security;
1282 if (!NT_STATUS_IS_OK(status) &&
1283 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1287 spnego_state->out_status = status;
1289 return gensec_spnego_update_out(gensec_security,
1294 static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
1297 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1299 if (!spnego_state || !spnego_state->sub_sec_security) {
1300 gensec_security->want_features |= feature;
1304 gensec_want_feature(spnego_state->sub_sec_security,
1308 static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
1311 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1312 if (!spnego_state->sub_sec_security) {
1316 return gensec_have_feature(spnego_state->sub_sec_security,
1320 static NTTIME gensec_spnego_expire_time(struct gensec_security *gensec_security)
1322 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1324 if (!spnego_state->sub_sec_security) {
1325 return GENSEC_EXPIRE_TIME_INFINITY;
1328 return gensec_expire_time(spnego_state->sub_sec_security);
1331 static const char *gensec_spnego_oids[] = {
1336 static const struct gensec_security_ops gensec_spnego_security_ops = {
1338 .sasl_name = "GSS-SPNEGO",
1339 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1340 .oid = gensec_spnego_oids,
1341 .client_start = gensec_spnego_client_start,
1342 .server_start = gensec_spnego_server_start,
1343 .update = gensec_spnego_update_wrapper,
1344 .seal_packet = gensec_spnego_seal_packet,
1345 .sign_packet = gensec_spnego_sign_packet,
1346 .sig_size = gensec_spnego_sig_size,
1347 .max_wrapped_size = gensec_spnego_max_wrapped_size,
1348 .max_input_size = gensec_spnego_max_input_size,
1349 .check_packet = gensec_spnego_check_packet,
1350 .unseal_packet = gensec_spnego_unseal_packet,
1351 .wrap = gensec_spnego_wrap,
1352 .unwrap = gensec_spnego_unwrap,
1353 .session_key = gensec_spnego_session_key,
1354 .session_info = gensec_spnego_session_info,
1355 .want_feature = gensec_spnego_want_feature,
1356 .have_feature = gensec_spnego_have_feature,
1357 .expire_time = gensec_spnego_expire_time,
1359 .priority = GENSEC_SPNEGO
1362 _PUBLIC_ NTSTATUS gensec_spnego_init(void)
1365 ret = gensec_register(&gensec_spnego_security_ops);
1366 if (!NT_STATUS_IS_OK(ret)) {
1367 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1368 gensec_spnego_security_ops.name));