2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2008
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "lib/util/tevent_ntstatus.h"
28 #include "../libcli/auth/spnego.h"
29 #include "librpc/gen_ndr/ndr_dcerpc.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "auth/gensec/gensec_internal.h"
33 #include "param/param.h"
34 #include "lib/util/asn1.h"
35 #include "lib/util/base64.h"
39 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx);
41 enum spnego_state_position {
51 enum spnego_message_type expected_packet;
52 enum spnego_state_position state_position;
53 struct gensec_security *sub_sec_security;
54 bool no_response_expected;
64 bool may_skip_mic_check;
70 * The following is used to implement
71 * the update token fragmentation
75 size_t out_max_length;
81 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
83 struct spnego_state *spnego_state;
85 spnego_state = talloc_zero(gensec_security, struct spnego_state);
87 return NT_STATUS_NO_MEMORY;
90 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
91 spnego_state->state_position = SPNEGO_CLIENT_START;
92 spnego_state->sub_sec_security = NULL;
93 spnego_state->no_response_expected = false;
94 spnego_state->mech_types = data_blob_null;
95 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
96 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
98 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
99 "spnego", "simulate_w2k", false);
101 gensec_security->private_data = spnego_state;
105 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
107 struct spnego_state *spnego_state;
109 spnego_state = talloc_zero(gensec_security, struct spnego_state);
111 return NT_STATUS_NO_MEMORY;
114 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
115 spnego_state->state_position = SPNEGO_SERVER_START;
116 spnego_state->sub_sec_security = NULL;
117 spnego_state->no_response_expected = false;
118 spnego_state->mech_types = data_blob_null;
119 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
120 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
122 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
123 "spnego", "simulate_w2k", false);
125 gensec_security->private_data = spnego_state;
129 /** Fallback to another GENSEC mechanism, based on magic strings
131 * This is the 'fallback' case, where we don't get SPNEGO, and have to
132 * try all the other options (and hope they all have a magic string
136 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
137 struct spnego_state *spnego_state,
138 struct tevent_context *ev,
139 TALLOC_CTX *out_mem_ctx,
140 const DATA_BLOB in, DATA_BLOB *out)
143 const struct gensec_security_ops **all_ops;
145 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
147 for (i=0; all_ops && all_ops[i]; i++) {
151 if (gensec_security != NULL &&
152 !gensec_security_ops_enabled(all_ops[i], gensec_security))
155 if (!all_ops[i]->oid) {
160 for (j=0; all_ops[i]->oid[j]; j++) {
161 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
169 if (!all_ops[i]->magic) {
173 nt_status = all_ops[i]->magic(gensec_security, &in);
174 if (!NT_STATUS_IS_OK(nt_status)) {
178 spnego_state->state_position = SPNEGO_FALLBACK;
180 nt_status = gensec_subcontext_start(spnego_state,
182 &spnego_state->sub_sec_security);
184 if (!NT_STATUS_IS_OK(nt_status)) {
187 /* select the sub context */
188 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
190 if (!NT_STATUS_IS_OK(nt_status)) {
193 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
194 out_mem_ctx, ev, in, out);
197 DEBUG(1, ("Failed to parse SPNEGO request\n"));
198 return NT_STATUS_INVALID_PARAMETER;
202 Parse the netTokenInit, either from the client, to the server, or
203 from the server to the client.
206 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
207 struct spnego_state *spnego_state,
208 TALLOC_CTX *out_mem_ctx,
209 struct tevent_context *ev,
210 const char * const *mechType,
211 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
214 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
217 const struct gensec_security_ops_wrapper *all_sec
218 = gensec_security_by_oid_list(gensec_security,
223 ok = spnego_write_mech_types(spnego_state,
225 &spnego_state->mech_types);
227 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
228 return NT_STATUS_NO_MEMORY;
231 if (spnego_state->state_position == SPNEGO_SERVER_START) {
233 for (j=0; mechType && mechType[j]; j++) {
234 for (i=0; all_sec && all_sec[i].op; i++) {
235 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
239 nt_status = gensec_subcontext_start(spnego_state,
241 &spnego_state->sub_sec_security);
242 if (!NT_STATUS_IS_OK(nt_status)) {
245 /* select the sub context */
246 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
248 if (!NT_STATUS_IS_OK(nt_status)) {
249 talloc_free(spnego_state->sub_sec_security);
250 spnego_state->sub_sec_security = NULL;
255 /* no optimistic token */
256 spnego_state->neg_oid = all_sec[i].oid;
257 *unwrapped_out = data_blob_null;
258 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
260 * Indicate the downgrade and request a
263 spnego_state->downgraded = true;
264 spnego_state->mic_requested = true;
268 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
273 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
274 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
275 /* Pretend we never started it (lets the first run find some incompatible demand) */
277 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
278 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
279 talloc_free(spnego_state->sub_sec_security);
280 spnego_state->sub_sec_security = NULL;
284 spnego_state->neg_oid = all_sec[i].oid;
287 if (spnego_state->sub_sec_security) {
292 if (!spnego_state->sub_sec_security) {
293 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
294 return NT_STATUS_INVALID_PARAMETER;
298 /* Having tried any optimistic token from the client (if we
299 * were the server), if we didn't get anywhere, walk our list
300 * in our preference order */
302 if (!spnego_state->sub_sec_security) {
303 for (i=0; all_sec && all_sec[i].op; i++) {
304 nt_status = gensec_subcontext_start(spnego_state,
306 &spnego_state->sub_sec_security);
307 if (!NT_STATUS_IS_OK(nt_status)) {
310 /* select the sub context */
311 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
313 if (!NT_STATUS_IS_OK(nt_status)) {
314 talloc_free(spnego_state->sub_sec_security);
315 spnego_state->sub_sec_security = NULL;
319 spnego_state->neg_oid = all_sec[i].oid;
321 /* only get the helping start blob for the first OID */
322 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
328 /* it is likely that a NULL input token will
329 * not be liked by most server mechs, but if
330 * we are in the client, we want the first
331 * update packet to be able to abort the use
333 if (spnego_state->state_position != SPNEGO_SERVER_START) {
334 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
335 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
336 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
337 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
338 const char *next = NULL;
339 const char *principal = NULL;
340 int dbg_level = DBGLVL_WARNING;
342 if (all_sec[i+1].op != NULL) {
343 next = all_sec[i+1].op->name;
344 dbg_level = DBGLVL_NOTICE;
347 if (gensec_security->target.principal != NULL) {
348 principal = gensec_security->target.principal;
349 } else if (gensec_security->target.service != NULL &&
350 gensec_security->target.hostname != NULL)
352 principal = talloc_asprintf(spnego_state->sub_sec_security,
354 gensec_security->target.service,
355 gensec_security->target.hostname);
357 principal = gensec_security->target.hostname;
360 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
361 spnego_state->sub_sec_security->ops->name,
363 next, nt_errstr(nt_status)));
365 /* Pretend we never started it (lets the first run find some incompatible demand) */
366 talloc_free(spnego_state->sub_sec_security);
367 spnego_state->sub_sec_security = NULL;
376 if (spnego_state->sub_sec_security) {
377 /* it is likely that a NULL input token will
378 * not be liked by most server mechs, but this
379 * does the right thing in the CIFS client.
380 * just push us along the merry-go-round
381 * again, and hope for better luck next
384 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
385 *unwrapped_out = data_blob_null;
386 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
389 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
390 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
391 && !NT_STATUS_IS_OK(nt_status)) {
392 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
393 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
394 talloc_free(spnego_state->sub_sec_security);
395 spnego_state->sub_sec_security = NULL;
397 /* We started the mech correctly, and the
398 * input from the other side was valid.
399 * Return the error (say bad password, invalid
404 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
407 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
408 /* we could re-negotiate here, but it would only work
409 * if the client or server lied about what it could
410 * support the first time. Lets keep this code to
416 /** create a negTokenInit
418 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
420 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
421 struct spnego_state *spnego_state,
422 TALLOC_CTX *out_mem_ctx,
423 struct tevent_context *ev,
424 const DATA_BLOB in, DATA_BLOB *out)
427 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
428 const char **mechTypes = NULL;
429 DATA_BLOB unwrapped_out = data_blob_null;
430 const struct gensec_security_ops_wrapper *all_sec;
432 mechTypes = gensec_security_oids(gensec_security,
433 out_mem_ctx, GENSEC_OID_SPNEGO);
435 all_sec = gensec_security_by_oid_list(gensec_security,
439 for (i=0; all_sec && all_sec[i].op; i++) {
440 struct spnego_data spnego_out;
441 const char **send_mech_types;
444 nt_status = gensec_subcontext_start(spnego_state,
446 &spnego_state->sub_sec_security);
447 if (!NT_STATUS_IS_OK(nt_status)) {
450 /* select the sub context */
451 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
453 if (!NT_STATUS_IS_OK(nt_status)) {
454 talloc_free(spnego_state->sub_sec_security);
455 spnego_state->sub_sec_security = NULL;
459 /* In the client, try and produce the first (optimistic) packet */
460 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
461 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
467 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
468 && !NT_STATUS_IS_OK(nt_status)) {
469 const char *next = NULL;
470 const char *principal = NULL;
471 int dbg_level = DBGLVL_WARNING;
473 if (all_sec[i+1].op != NULL) {
474 next = all_sec[i+1].op->name;
475 dbg_level = DBGLVL_NOTICE;
478 if (gensec_security->target.principal != NULL) {
479 principal = gensec_security->target.principal;
480 } else if (gensec_security->target.service != NULL &&
481 gensec_security->target.hostname != NULL)
483 principal = talloc_asprintf(spnego_state->sub_sec_security,
485 gensec_security->target.service,
486 gensec_security->target.hostname);
488 principal = gensec_security->target.hostname;
491 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
492 spnego_state->sub_sec_security->ops->name,
494 next, nt_errstr(nt_status)));
495 talloc_free(spnego_state->sub_sec_security);
496 spnego_state->sub_sec_security = NULL;
497 /* Pretend we never started it (lets the first run find some incompatible demand) */
503 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
505 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
508 ok = spnego_write_mech_types(spnego_state,
510 &spnego_state->mech_types);
512 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
513 return NT_STATUS_NO_MEMORY;
516 /* List the remaining mechs as options */
517 spnego_out.negTokenInit.mechTypes = send_mech_types;
518 spnego_out.negTokenInit.reqFlags = data_blob_null;
519 spnego_out.negTokenInit.reqFlagsPadding = 0;
521 if (spnego_state->state_position == SPNEGO_SERVER_START) {
522 spnego_out.negTokenInit.mechListMIC
523 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
525 spnego_out.negTokenInit.mechListMIC = data_blob_null;
528 spnego_out.negTokenInit.mechToken = unwrapped_out;
530 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
531 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
532 return NT_STATUS_INVALID_PARAMETER;
536 spnego_state->neg_oid = all_sec[i].oid;
538 if (NT_STATUS_IS_OK(nt_status)) {
539 spnego_state->no_response_expected = true;
542 return NT_STATUS_MORE_PROCESSING_REQUIRED;
544 talloc_free(spnego_state->sub_sec_security);
545 spnego_state->sub_sec_security = NULL;
547 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
552 /** create a server negTokenTarg
554 * This is the case, where the client is the first one who sends data
557 static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_state,
558 TALLOC_CTX *out_mem_ctx,
560 const DATA_BLOB unwrapped_out,
561 DATA_BLOB mech_list_mic,
564 struct spnego_data spnego_out;
567 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
568 spnego_out.negTokenTarg.responseToken = unwrapped_out;
569 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
570 spnego_out.negTokenTarg.supportedMech = NULL;
572 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
573 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
574 if (spnego_state->mic_requested) {
575 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
576 spnego_state->mic_requested = false;
578 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
580 spnego_state->state_position = SPNEGO_SERVER_TARG;
581 } else if (NT_STATUS_IS_OK(nt_status)) {
582 if (unwrapped_out.data) {
583 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
585 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
586 spnego_state->state_position = SPNEGO_DONE;
588 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
589 spnego_out.negTokenTarg.mechListMIC = data_blob_null;
590 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
591 spnego_state->state_position = SPNEGO_DONE;
594 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
595 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
596 return NT_STATUS_INVALID_PARAMETER;
599 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
600 spnego_state->num_targs++;
606 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
607 struct tevent_context *ev,
608 const DATA_BLOB in, DATA_BLOB *out)
610 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
611 DATA_BLOB mech_list_mic = data_blob_null;
612 DATA_BLOB unwrapped_out = data_blob_null;
613 struct spnego_data spnego_out;
614 struct spnego_data spnego;
618 *out = data_blob_null;
621 out_mem_ctx = spnego_state;
624 /* and switch into the state machine */
626 switch (spnego_state->state_position) {
627 case SPNEGO_FALLBACK:
628 return gensec_update_ev(spnego_state->sub_sec_security,
629 out_mem_ctx, ev, in, out);
630 case SPNEGO_SERVER_START:
635 len = spnego_read_data(gensec_security, in, &spnego);
637 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
638 ev, out_mem_ctx, in, out);
640 /* client sent NegTargetInit, we send NegTokenTarg */
642 /* OK, so it's real SPNEGO, check the packet's the one we expect */
643 if (spnego.type != spnego_state->expected_packet) {
644 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
645 spnego_state->expected_packet));
646 dump_data(1, in.data, in.length);
647 spnego_free_data(&spnego);
648 return NT_STATUS_INVALID_PARAMETER;
651 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
655 spnego.negTokenInit.mechTypes,
656 spnego.negTokenInit.mechToken,
659 if (spnego_state->simulate_w2k) {
661 * Windows 2000 returns the unwrapped token
662 * also in the mech_list_mic field.
664 * In order to verify our client code,
665 * we need a way to have a server with this
668 mech_list_mic = unwrapped_out;
671 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
678 spnego_free_data(&spnego);
682 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
683 out_mem_ctx, ev, in, out);
684 spnego_state->state_position = SPNEGO_SERVER_START;
685 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
690 case SPNEGO_CLIENT_START:
692 /* The server offers a list of mechanisms */
694 const char *my_mechs[] = {NULL, NULL};
695 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
699 /* client to produce negTokenInit */
700 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
701 out_mem_ctx, ev, in, out);
702 spnego_state->state_position = SPNEGO_CLIENT_TARG;
703 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
707 len = spnego_read_data(gensec_security, in, &spnego);
710 DEBUG(1, ("Invalid SPNEGO request:\n"));
711 dump_data(1, in.data, in.length);
712 return NT_STATUS_INVALID_PARAMETER;
715 /* OK, so it's real SPNEGO, check the packet's the one we expect */
716 if (spnego.type != spnego_state->expected_packet) {
717 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
718 spnego_state->expected_packet));
719 dump_data(1, in.data, in.length);
720 spnego_free_data(&spnego);
721 return NT_STATUS_INVALID_PARAMETER;
724 if (spnego.negTokenInit.targetPrincipal
725 && strcmp(spnego.negTokenInit.targetPrincipal, ADS_IGNORE_PRINCIPAL) != 0) {
726 DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
727 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
728 gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
732 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
736 spnego.negTokenInit.mechTypes,
737 spnego.negTokenInit.mechToken,
740 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
741 spnego_free_data(&spnego);
745 my_mechs[0] = spnego_state->neg_oid;
747 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
748 spnego_out.negTokenInit.mechTypes = my_mechs;
749 spnego_out.negTokenInit.reqFlags = data_blob_null;
750 spnego_out.negTokenInit.reqFlagsPadding = 0;
751 spnego_out.negTokenInit.mechListMIC = data_blob_null;
752 spnego_out.negTokenInit.mechToken = unwrapped_out;
754 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
755 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
756 return NT_STATUS_INVALID_PARAMETER;
759 ok = spnego_write_mech_types(spnego_state,
761 &spnego_state->mech_types);
763 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
764 return NT_STATUS_NO_MEMORY;
768 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
769 spnego_state->state_position = SPNEGO_CLIENT_TARG;
771 if (NT_STATUS_IS_OK(nt_status)) {
772 spnego_state->no_response_expected = true;
775 spnego_free_data(&spnego);
776 return NT_STATUS_MORE_PROCESSING_REQUIRED;
778 case SPNEGO_SERVER_TARG:
781 bool have_sign = true;
782 bool new_spnego = false;
785 return NT_STATUS_INVALID_PARAMETER;
788 len = spnego_read_data(gensec_security, in, &spnego);
791 DEBUG(1, ("Invalid SPNEGO request:\n"));
792 dump_data(1, in.data, in.length);
793 return NT_STATUS_INVALID_PARAMETER;
796 /* OK, so it's real SPNEGO, check the packet's the one we expect */
797 if (spnego.type != spnego_state->expected_packet) {
798 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
799 spnego_state->expected_packet));
800 dump_data(1, in.data, in.length);
801 spnego_free_data(&spnego);
802 return NT_STATUS_INVALID_PARAMETER;
805 spnego_state->num_targs++;
807 if (!spnego_state->sub_sec_security) {
808 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
809 spnego_free_data(&spnego);
810 return NT_STATUS_INVALID_PARAMETER;
813 if (spnego_state->needs_mic_check) {
814 if (spnego.negTokenTarg.responseToken.length != 0) {
815 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
816 spnego_free_data(&spnego);
817 return NT_STATUS_INVALID_PARAMETER;
820 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
821 spnego_state->mech_types.data,
822 spnego_state->mech_types.length,
823 spnego_state->mech_types.data,
824 spnego_state->mech_types.length,
825 &spnego.negTokenTarg.mechListMIC);
826 if (NT_STATUS_IS_OK(nt_status)) {
827 spnego_state->needs_mic_check = false;
828 spnego_state->done_mic_check = true;
830 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
831 nt_errstr(nt_status)));
833 goto server_response;
836 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
838 spnego.negTokenTarg.responseToken,
840 if (!NT_STATUS_IS_OK(nt_status)) {
841 goto server_response;
844 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
845 GENSEC_FEATURE_SIGN);
846 if (spnego_state->simulate_w2k) {
849 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
850 GENSEC_FEATURE_NEW_SPNEGO);
851 if (spnego.negTokenTarg.mechListMIC.length > 0) {
855 if (have_sign && new_spnego) {
856 spnego_state->needs_mic_check = true;
857 spnego_state->needs_mic_sign = true;
860 if (have_sign && spnego.negTokenTarg.mechListMIC.length > 0) {
861 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
862 spnego_state->mech_types.data,
863 spnego_state->mech_types.length,
864 spnego_state->mech_types.data,
865 spnego_state->mech_types.length,
866 &spnego.negTokenTarg.mechListMIC);
867 if (!NT_STATUS_IS_OK(nt_status)) {
868 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
869 nt_errstr(nt_status)));
870 goto server_response;
873 spnego_state->needs_mic_check = false;
874 spnego_state->done_mic_check = true;
877 if (spnego_state->needs_mic_sign) {
878 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
880 spnego_state->mech_types.data,
881 spnego_state->mech_types.length,
882 spnego_state->mech_types.data,
883 spnego_state->mech_types.length,
885 if (!NT_STATUS_IS_OK(nt_status)) {
886 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
887 nt_errstr(nt_status)));
888 goto server_response;
890 spnego_state->needs_mic_sign = false;
893 if (spnego_state->needs_mic_check) {
894 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
898 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
905 spnego_free_data(&spnego);
909 case SPNEGO_CLIENT_TARG:
911 NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
914 return NT_STATUS_INVALID_PARAMETER;
917 len = spnego_read_data(gensec_security, in, &spnego);
920 DEBUG(1, ("Invalid SPNEGO request:\n"));
921 dump_data(1, in.data, in.length);
922 return NT_STATUS_INVALID_PARAMETER;
925 /* OK, so it's real SPNEGO, check the packet's the one we expect */
926 if (spnego.type != spnego_state->expected_packet) {
927 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
928 spnego_state->expected_packet));
929 dump_data(1, in.data, in.length);
930 spnego_free_data(&spnego);
931 return NT_STATUS_INVALID_PARAMETER;
934 spnego_state->num_targs++;
936 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
937 spnego_free_data(&spnego);
938 return NT_STATUS_LOGON_FAILURE;
941 if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
942 spnego_state->mic_requested = true;
945 /* Server didn't like our choice of mech, and chose something else */
946 if (((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
947 (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC)) &&
948 spnego.negTokenTarg.supportedMech &&
949 strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
950 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
951 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid),
952 gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech)));
953 spnego_state->downgraded = true;
954 spnego_state->no_response_expected = false;
955 talloc_free(spnego_state->sub_sec_security);
956 nt_status = gensec_subcontext_start(spnego_state,
958 &spnego_state->sub_sec_security);
959 if (!NT_STATUS_IS_OK(nt_status)) {
960 spnego_free_data(&spnego);
963 /* select the sub context */
964 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
965 spnego.negTokenTarg.supportedMech);
966 if (!NT_STATUS_IS_OK(nt_status)) {
967 spnego_free_data(&spnego);
971 spnego_state->neg_oid = talloc_strdup(spnego_state,
972 spnego.negTokenTarg.supportedMech);
973 if (spnego_state->neg_oid == NULL) {
974 spnego_free_data(&spnego);
975 return NT_STATUS_NO_MEMORY;
979 if (spnego.negTokenTarg.mechListMIC.length > 0) {
980 DATA_BLOB *m = &spnego.negTokenTarg.mechListMIC;
981 const DATA_BLOB *r = &spnego.negTokenTarg.responseToken;
984 * Windows 2000 has a bug, it repeats the
985 * responseToken in the mechListMIC field.
987 if (m->length == r->length) {
990 cmp = memcmp(m->data, r->data, m->length);
997 if (spnego.negTokenTarg.mechListMIC.length > 0) {
998 if (spnego_state->no_response_expected) {
999 spnego_state->needs_mic_check = true;
1003 if (spnego_state->needs_mic_check) {
1004 if (spnego.negTokenTarg.responseToken.length != 0) {
1005 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1006 spnego_free_data(&spnego);
1007 return NT_STATUS_INVALID_PARAMETER;
1010 if (spnego.negTokenTarg.mechListMIC.length == 0
1011 && spnego_state->may_skip_mic_check) {
1013 * In this case we don't require
1014 * a mechListMIC from the server.
1016 * This works around bugs in the Azure
1017 * and Apple spnego implementations.
1020 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1022 spnego_state->needs_mic_check = false;
1023 nt_status = NT_STATUS_OK;
1024 goto client_response;
1027 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1028 spnego_state->mech_types.data,
1029 spnego_state->mech_types.length,
1030 spnego_state->mech_types.data,
1031 spnego_state->mech_types.length,
1032 &spnego.negTokenTarg.mechListMIC);
1033 if (!NT_STATUS_IS_OK(nt_status)) {
1034 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1035 nt_errstr(nt_status)));
1036 spnego_free_data(&spnego);
1039 spnego_state->needs_mic_check = false;
1040 spnego_state->done_mic_check = true;
1041 goto client_response;
1044 if (!spnego_state->no_response_expected) {
1045 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1047 spnego.negTokenTarg.responseToken,
1049 if (!NT_STATUS_IS_OK(nt_status)) {
1050 goto client_response;
1053 spnego_state->no_response_expected = true;
1055 nt_status = NT_STATUS_OK;
1058 if (spnego_state->no_response_expected &&
1059 !spnego_state->done_mic_check)
1061 bool have_sign = true;
1062 bool new_spnego = false;
1064 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1065 GENSEC_FEATURE_SIGN);
1066 if (spnego_state->simulate_w2k) {
1069 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1070 GENSEC_FEATURE_NEW_SPNEGO);
1072 switch (spnego.negTokenTarg.negResult) {
1073 case SPNEGO_ACCEPT_COMPLETED:
1074 case SPNEGO_NONE_RESULT:
1075 if (spnego_state->num_targs == 1) {
1077 * the first exchange doesn't require
1085 case SPNEGO_ACCEPT_INCOMPLETE:
1086 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1091 if (spnego_state->downgraded) {
1093 * A downgrade should be protected if
1100 * The caller may just asked for
1101 * GENSEC_FEATURE_SESSION_KEY, this
1102 * is only reflected in the want_features.
1105 * gensec_have_features(GENSEC_FEATURE_SIGN)
1108 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
1111 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
1115 * Here we're sure our preferred mech was
1116 * selected by the server and our caller doesn't
1117 * need GENSEC_FEATURE_SIGN nor
1118 * GENSEC_FEATURE_SEAL support.
1120 * In this case we don't require
1121 * a mechListMIC from the server.
1123 * This works around bugs in the Azure
1124 * and Apple spnego implementations.
1127 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1129 spnego_state->may_skip_mic_check = true;
1132 case SPNEGO_REQUEST_MIC:
1133 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1141 if (spnego_state->mic_requested) {
1147 if (have_sign && new_spnego) {
1148 spnego_state->needs_mic_check = true;
1149 spnego_state->needs_mic_sign = true;
1153 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1154 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1155 spnego_state->mech_types.data,
1156 spnego_state->mech_types.length,
1157 spnego_state->mech_types.data,
1158 spnego_state->mech_types.length,
1159 &spnego.negTokenTarg.mechListMIC);
1160 if (!NT_STATUS_IS_OK(nt_status)) {
1161 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1162 nt_errstr(nt_status)));
1163 spnego_free_data(&spnego);
1166 spnego_state->needs_mic_check = false;
1167 spnego_state->done_mic_check = true;
1170 if (spnego_state->needs_mic_sign) {
1171 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1173 spnego_state->mech_types.data,
1174 spnego_state->mech_types.length,
1175 spnego_state->mech_types.data,
1176 spnego_state->mech_types.length,
1178 if (!NT_STATUS_IS_OK(nt_status)) {
1179 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1180 nt_errstr(nt_status)));
1181 spnego_free_data(&spnego);
1184 spnego_state->needs_mic_sign = false;
1187 if (spnego_state->needs_mic_check) {
1188 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1192 spnego_free_data(&spnego);
1194 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1195 && !NT_STATUS_IS_OK(nt_status)) {
1196 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1197 spnego_state->sub_sec_security->ops->name,
1198 nt_errstr(nt_status)));
1202 if (unwrapped_out.length || mech_list_mic.length) {
1204 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1205 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1206 spnego_out.negTokenTarg.supportedMech = NULL;
1207 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1208 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1210 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1211 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1212 return NT_STATUS_INVALID_PARAMETER;
1215 spnego_state->num_targs++;
1216 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1217 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1220 /* all done - server has accepted, and we agree */
1221 *out = data_blob_null;
1223 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1224 /* unless of course it did not accept */
1225 DEBUG(1,("gensec_update ok but not accepted\n"));
1226 nt_status = NT_STATUS_INVALID_PARAMETER;
1229 spnego_state->state_position = SPNEGO_DONE;
1235 /* We should not be called after we are 'done' */
1236 return NT_STATUS_INVALID_PARAMETER;
1238 return NT_STATUS_INVALID_PARAMETER;
1241 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1242 TALLOC_CTX *out_mem_ctx,
1245 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1246 DATA_BLOB out = data_blob_null;
1249 *_out = data_blob_null;
1251 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1253 * Fast path, we can deliver everything
1256 *_out = spnego_state->out_frag;
1257 if (spnego_state->out_frag.length > 0) {
1258 talloc_steal(out_mem_ctx, _out->data);
1259 spnego_state->out_frag = data_blob_null;
1262 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1263 return spnego_state->out_status;
1267 * We're completely done, further updates are not allowed.
1269 spnego_state->state_position = SPNEGO_DONE;
1270 return gensec_child_ready(gensec_security,
1271 spnego_state->sub_sec_security);
1274 out = spnego_state->out_frag;
1277 * copy the remaining bytes
1279 spnego_state->out_frag = data_blob_talloc(spnego_state,
1280 out.data + spnego_state->out_max_length,
1281 out.length - spnego_state->out_max_length);
1282 if (spnego_state->out_frag.data == NULL) {
1283 return NT_STATUS_NO_MEMORY;
1287 * truncate the buffer
1289 ok = data_blob_realloc(spnego_state, &out,
1290 spnego_state->out_max_length);
1292 return NT_STATUS_NO_MEMORY;
1295 talloc_steal(out_mem_ctx, out.data);
1297 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1300 struct gensec_spnego_update_state {
1301 struct gensec_security *gensec;
1302 struct spnego_state *spnego;
1308 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1309 enum tevent_req_state req_state)
1311 struct gensec_spnego_update_state *state =
1312 tevent_req_data(req,
1313 struct gensec_spnego_update_state);
1315 switch (req_state) {
1316 case TEVENT_REQ_USER_ERROR:
1317 case TEVENT_REQ_TIMED_OUT:
1318 case TEVENT_REQ_NO_MEMORY:
1320 * A fatal error, further updates are not allowed.
1322 state->spnego->state_position = SPNEGO_DONE;
1329 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1330 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1331 DATA_BLOB *full_in);
1333 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1334 struct tevent_context *ev,
1335 struct gensec_security *gensec_security,
1338 struct spnego_state *spnego_state =
1339 talloc_get_type_abort(gensec_security->private_data,
1340 struct spnego_state);
1341 struct tevent_req *req = NULL;
1342 struct gensec_spnego_update_state *state = NULL;
1345 req = tevent_req_create(mem_ctx, &state,
1346 struct gensec_spnego_update_state);
1350 state->gensec = gensec_security;
1351 state->spnego = spnego_state;
1352 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1354 if (spnego_state->out_frag.length > 0) {
1355 if (in.length > 0) {
1356 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1357 return tevent_req_post(req, ev);
1360 status = gensec_spnego_update_out(gensec_security,
1361 state, &state->out);
1362 state->status = status;
1363 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1364 tevent_req_done(req);
1365 return tevent_req_post(req, ev);
1367 if (tevent_req_nterror(req, status)) {
1368 return tevent_req_post(req, ev);
1371 tevent_req_done(req);
1372 return tevent_req_post(req, ev);
1375 status = gensec_spnego_update_in(gensec_security, in,
1376 state, &state->full_in);
1377 state->status = status;
1378 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1379 tevent_req_done(req);
1380 return tevent_req_post(req, ev);
1382 if (tevent_req_nterror(req, status)) {
1383 return tevent_req_post(req, ev);
1386 status = gensec_spnego_update(gensec_security,
1389 &spnego_state->out_frag);
1390 if (NT_STATUS_IS_OK(status)) {
1391 bool reset_full = true;
1393 reset_full = !spnego_state->done_mic_check;
1395 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1398 if (!NT_STATUS_IS_OK(status) &&
1399 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1400 tevent_req_nterror(req, status);
1401 return tevent_req_post(req, ev);
1404 spnego_state->out_status = status;
1406 status = gensec_spnego_update_out(gensec_security,
1407 state, &state->out);
1408 state->status = status;
1409 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1410 tevent_req_done(req);
1411 return tevent_req_post(req, ev);
1413 if (tevent_req_nterror(req, status)) {
1414 return tevent_req_post(req, ev);
1417 tevent_req_done(req);
1418 return tevent_req_post(req, ev);
1421 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1422 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1425 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1429 *full_in = data_blob_null;
1431 if (spnego_state->in_needed == 0) {
1436 * try to work out the size of the full
1437 * input token, it might be fragmented
1439 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1440 if ((ret != 0) && (ret != EAGAIN)) {
1441 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1444 if ((ret == 0) || (ret == EAGAIN)) {
1445 spnego_state->in_needed = size;
1448 * If it is not an asn1 message
1449 * just call the next layer.
1451 spnego_state->in_needed = in.length;
1455 if (spnego_state->in_needed > UINT16_MAX) {
1457 * limit the incoming message to 0xFFFF
1458 * to avoid DoS attacks.
1460 return NT_STATUS_INVALID_BUFFER_SIZE;
1463 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1465 * If we reach this, we know we got at least
1466 * part of an asn1 message, getting 0 means
1467 * the remote peer wants us to spin.
1469 return NT_STATUS_INVALID_PARAMETER;
1472 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1473 if (in.length > expected) {
1475 * we got more than expected
1477 return NT_STATUS_INVALID_PARAMETER;
1480 if (in.length == spnego_state->in_needed) {
1482 * if the in.length contains the full blob
1485 * Note: this implies spnego_state->in_frag.length == 0,
1486 * but we do not need to check this explicitly
1487 * because we already know that we did not get
1488 * more than expected.
1491 spnego_state->in_needed = 0;
1492 return NT_STATUS_OK;
1495 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1496 in.data, in.length);
1498 return NT_STATUS_NO_MEMORY;
1501 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1502 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1505 *full_in = spnego_state->in_frag;
1506 talloc_steal(mem_ctx, full_in->data);
1507 spnego_state->in_frag = data_blob_null;
1508 spnego_state->in_needed = 0;
1509 return NT_STATUS_OK;
1512 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1513 TALLOC_CTX *out_mem_ctx,
1516 struct gensec_spnego_update_state *state =
1517 tevent_req_data(req,
1518 struct gensec_spnego_update_state);
1521 *out = data_blob_null;
1523 if (tevent_req_is_nterror(req, &status)) {
1524 tevent_req_received(req);
1529 talloc_steal(out_mem_ctx, state->out.data);
1530 status = state->status;
1531 tevent_req_received(req);
1535 static const char *gensec_spnego_oids[] = {
1540 static const struct gensec_security_ops gensec_spnego_security_ops = {
1542 .sasl_name = "GSS-SPNEGO",
1543 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1544 .oid = gensec_spnego_oids,
1545 .client_start = gensec_spnego_client_start,
1546 .server_start = gensec_spnego_server_start,
1547 .update_send = gensec_spnego_update_send,
1548 .update_recv = gensec_spnego_update_recv,
1549 .seal_packet = gensec_child_seal_packet,
1550 .sign_packet = gensec_child_sign_packet,
1551 .sig_size = gensec_child_sig_size,
1552 .max_wrapped_size = gensec_child_max_wrapped_size,
1553 .max_input_size = gensec_child_max_input_size,
1554 .check_packet = gensec_child_check_packet,
1555 .unseal_packet = gensec_child_unseal_packet,
1556 .wrap = gensec_child_wrap,
1557 .unwrap = gensec_child_unwrap,
1558 .session_key = gensec_child_session_key,
1559 .session_info = gensec_child_session_info,
1560 .want_feature = gensec_child_want_feature,
1561 .have_feature = gensec_child_have_feature,
1562 .expire_time = gensec_child_expire_time,
1563 .final_auth_type = gensec_child_final_auth_type,
1565 .priority = GENSEC_SPNEGO
1568 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1571 ret = gensec_register(ctx, &gensec_spnego_security_ops);
1572 if (!NT_STATUS_IS_OK(ret)) {
1573 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1574 gensec_spnego_security_ops.name));