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;
64 bool may_skip_mic_check;
70 * The following is used to implement
71 * the update token fragmentation
75 size_t out_max_length;
80 static void gensec_spnego_update_sub_abort(struct spnego_state *spnego_state)
82 spnego_state->sub_sec_ready = false;
83 TALLOC_FREE(spnego_state->sub_sec_security);
86 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
88 struct spnego_state *spnego_state;
90 spnego_state = talloc_zero(gensec_security, struct spnego_state);
92 return NT_STATUS_NO_MEMORY;
95 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
96 spnego_state->state_position = SPNEGO_CLIENT_START;
97 spnego_state->sub_sec_security = NULL;
98 spnego_state->sub_sec_ready = false;
99 spnego_state->mech_types = data_blob_null;
100 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
101 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
103 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
104 "spnego", "simulate_w2k", false);
106 gensec_security->private_data = spnego_state;
110 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
112 struct spnego_state *spnego_state;
114 spnego_state = talloc_zero(gensec_security, struct spnego_state);
116 return NT_STATUS_NO_MEMORY;
119 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
120 spnego_state->state_position = SPNEGO_SERVER_START;
121 spnego_state->sub_sec_security = NULL;
122 spnego_state->sub_sec_ready = false;
123 spnego_state->mech_types = data_blob_null;
124 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
125 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
127 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
128 "spnego", "simulate_w2k", false);
130 gensec_security->private_data = spnego_state;
134 /** Fallback to another GENSEC mechanism, based on magic strings
136 * This is the 'fallback' case, where we don't get SPNEGO, and have to
137 * try all the other options (and hope they all have a magic string
141 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
142 struct spnego_state *spnego_state,
147 const struct gensec_security_ops **all_ops;
149 all_ops = gensec_security_mechs(gensec_security, mem_ctx);
151 for (i=0; all_ops && all_ops[i]; i++) {
155 if (gensec_security != NULL &&
156 !gensec_security_ops_enabled(all_ops[i], gensec_security))
161 if (!all_ops[i]->oid) {
166 for (j=0; all_ops[i]->oid[j]; j++) {
167 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
175 if (!all_ops[i]->magic) {
179 nt_status = all_ops[i]->magic(gensec_security, &in);
180 if (!NT_STATUS_IS_OK(nt_status)) {
184 spnego_state->state_position = SPNEGO_FALLBACK;
186 nt_status = gensec_subcontext_start(spnego_state,
188 &spnego_state->sub_sec_security);
190 if (!NT_STATUS_IS_OK(nt_status)) {
193 /* select the sub context */
194 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
196 if (!NT_STATUS_IS_OK(nt_status)) {
202 DEBUG(1, ("Failed to parse SPNEGO request\n"));
203 return NT_STATUS_INVALID_PARAMETER;
206 /** create a negTokenInit
208 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
210 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
211 struct spnego_state *spnego_state,
212 TALLOC_CTX *out_mem_ctx,
213 struct tevent_context *ev,
217 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
218 const char **mechTypes = NULL;
219 DATA_BLOB unwrapped_out = data_blob_null;
220 const struct gensec_security_ops_wrapper *all_sec;
222 mechTypes = gensec_security_oids(gensec_security,
223 out_mem_ctx, GENSEC_OID_SPNEGO);
225 all_sec = gensec_security_by_oid_list(gensec_security,
229 for (i=0; all_sec && all_sec[i].op; i++) {
230 struct spnego_data spnego_out;
231 const char **send_mech_types;
234 nt_status = gensec_subcontext_start(spnego_state,
236 &spnego_state->sub_sec_security);
237 if (!NT_STATUS_IS_OK(nt_status)) {
240 /* select the sub context */
241 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
243 if (!NT_STATUS_IS_OK(nt_status)) {
244 gensec_spnego_update_sub_abort(spnego_state);
248 /* In the client, try and produce the first (optimistic) packet */
249 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
250 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
255 if (NT_STATUS_IS_OK(nt_status)) {
256 spnego_state->sub_sec_ready = true;
259 if (GENSEC_UPDATE_IS_NTERROR(nt_status)) {
260 const char *next = NULL;
261 const char *principal = NULL;
262 int dbg_level = DBGLVL_WARNING;
264 if (all_sec[i+1].op != NULL) {
265 next = all_sec[i+1].op->name;
266 dbg_level = DBGLVL_NOTICE;
269 if (gensec_security->target.principal != NULL) {
270 principal = gensec_security->target.principal;
271 } else if (gensec_security->target.service != NULL &&
272 gensec_security->target.hostname != NULL)
274 principal = talloc_asprintf(spnego_state->sub_sec_security,
276 gensec_security->target.service,
277 gensec_security->target.hostname);
279 principal = gensec_security->target.hostname;
282 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
283 spnego_state->sub_sec_security->ops->name,
285 next, nt_errstr(nt_status)));
288 * Pretend we never started it
290 gensec_spnego_update_sub_abort(spnego_state);
295 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
297 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
300 ok = spnego_write_mech_types(spnego_state,
302 &spnego_state->mech_types);
304 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
305 return NT_STATUS_NO_MEMORY;
308 /* List the remaining mechs as options */
309 spnego_out.negTokenInit.mechTypes = send_mech_types;
310 spnego_out.negTokenInit.reqFlags = data_blob_null;
311 spnego_out.negTokenInit.reqFlagsPadding = 0;
313 if (spnego_state->state_position == SPNEGO_SERVER_START) {
314 spnego_out.negTokenInit.mechListMIC
315 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
317 spnego_out.negTokenInit.mechListMIC = data_blob_null;
320 spnego_out.negTokenInit.mechToken = unwrapped_out;
322 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
323 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
324 return NT_STATUS_INVALID_PARAMETER;
328 spnego_state->neg_oid = all_sec[i].oid;
330 if (spnego_state->state_position == SPNEGO_SERVER_START) {
331 spnego_state->state_position = SPNEGO_SERVER_START;
332 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
334 spnego_state->state_position = SPNEGO_CLIENT_TARG;
335 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
338 return NT_STATUS_MORE_PROCESSING_REQUIRED;
340 gensec_spnego_update_sub_abort(spnego_state);
342 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
346 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security,
347 struct spnego_state *spnego_state,
348 struct tevent_context *ev,
349 struct spnego_data *spnego_in,
350 TALLOC_CTX *out_mem_ctx,
353 TALLOC_CTX *frame = talloc_stackframe();
354 DATA_BLOB sub_out = data_blob_null;
355 const char *tp = NULL;
356 const char * const *mech_types = NULL;
358 const struct gensec_security_ops_wrapper *all_sec = NULL;
359 struct spnego_data spnego_out;
360 const char *my_mechs[] = {NULL, NULL};
364 *out = data_blob_null;
366 /* The server offers a list of mechanisms */
368 tp = spnego_in->negTokenInit.targetPrincipal;
369 if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
370 DBG_INFO("Server claims it's principal name is %s\n", tp);
371 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
372 gensec_set_target_principal(gensec_security, tp);
376 mech_types = spnego_in->negTokenInit.mechTypes;
377 if (mech_types == NULL) {
379 return NT_STATUS_INVALID_PARAMETER;
382 all_sec = gensec_security_by_oid_list(gensec_security,
385 if (all_sec == NULL) {
386 DBG_WARNING("gensec_security_by_oid_list() failed\n");
388 return NT_STATUS_INVALID_PARAMETER;
391 for (; all_sec[all_idx].op; all_idx++) {
392 const struct gensec_security_ops_wrapper *cur_sec =
394 const char *next = NULL;
395 const char *principal = NULL;
396 int dbg_level = DBGLVL_WARNING;
397 bool allow_fallback = false;
399 status = gensec_subcontext_start(spnego_state,
401 &spnego_state->sub_sec_security);
402 if (!NT_STATUS_IS_OK(status)) {
407 /* select the sub context */
408 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
410 if (!NT_STATUS_IS_OK(status)) {
412 * Pretend we never started it.
414 gensec_spnego_update_sub_abort(spnego_state);
418 spnego_state->neg_oid = cur_sec->oid;
421 * As client we don't use an optimistic token from the server.
423 status = gensec_update_ev(spnego_state->sub_sec_security,
424 frame, ev, data_blob_null, &sub_out);
425 if (NT_STATUS_IS_OK(status)) {
426 spnego_state->sub_sec_ready = true;
429 if (!GENSEC_UPDATE_IS_NTERROR(status)) {
430 /* OK or MORE_PROCESSING_REQUIRED */
435 * it is likely that a NULL input token will
436 * not be liked by most server mechs, but if
437 * we are in the client, we want the first
438 * update packet to be able to abort the use
441 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
442 NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) ||
443 NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
444 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
446 allow_fallback = true;
449 if (allow_fallback && cur_sec[1].op != NULL) {
450 next = cur_sec[1].op->name;
451 dbg_level = DBGLVL_NOTICE;
454 if (gensec_security->target.principal != NULL) {
455 principal = gensec_security->target.principal;
456 } else if (gensec_security->target.service != NULL &&
457 gensec_security->target.hostname != NULL)
459 principal = talloc_asprintf(spnego_state->sub_sec_security,
461 gensec_security->target.service,
462 gensec_security->target.hostname);
464 principal = gensec_security->target.hostname;
467 DBG_PREFIX(dbg_level, (
468 "%s: creating NEG_TOKEN_INIT "
469 "for %s failed (next[%s]): %s\n",
470 spnego_state->sub_sec_security->ops->name,
471 principal, next, nt_errstr(status)));
473 if (allow_fallback && next != NULL) {
475 * Pretend we never started it.
477 gensec_spnego_update_sub_abort(spnego_state);
488 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
490 return NT_STATUS_INVALID_PARAMETER;
493 my_mechs[0] = spnego_state->neg_oid;
495 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
496 spnego_out.negTokenInit.mechTypes = my_mechs;
497 spnego_out.negTokenInit.reqFlags = data_blob_null;
498 spnego_out.negTokenInit.reqFlagsPadding = 0;
499 spnego_out.negTokenInit.mechListMIC = data_blob_null;
500 spnego_out.negTokenInit.mechToken = sub_out;
502 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
503 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
505 return NT_STATUS_INVALID_PARAMETER;
508 ok = spnego_write_mech_types(spnego_state,
510 &spnego_state->mech_types);
512 DBG_ERR("failed to write mechTypes\n");
514 return NT_STATUS_NO_MEMORY;
518 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
519 spnego_state->state_position = SPNEGO_CLIENT_TARG;
522 return NT_STATUS_MORE_PROCESSING_REQUIRED;
525 static NTSTATUS gensec_spnego_client_negTokenTarg(struct gensec_security *gensec_security,
526 struct spnego_state *spnego_state,
527 struct tevent_context *ev,
528 struct spnego_data *spnego_in,
529 TALLOC_CTX *out_mem_ctx,
532 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
533 DATA_BLOB sub_in = ta->responseToken;
534 DATA_BLOB mech_list_mic = data_blob_null;
535 DATA_BLOB sub_out = data_blob_null;
536 struct spnego_data spnego_out;
539 *out = data_blob_null;
541 spnego_state->num_targs++;
543 if (ta->negResult == SPNEGO_REJECT) {
544 return NT_STATUS_LOGON_FAILURE;
547 if (ta->negResult == SPNEGO_REQUEST_MIC) {
548 spnego_state->mic_requested = true;
551 if (ta->mechListMIC.length > 0) {
552 DATA_BLOB *m = &ta->mechListMIC;
553 const DATA_BLOB *r = &ta->responseToken;
556 * Windows 2000 has a bug, it repeats the
557 * responseToken in the mechListMIC field.
559 if (m->length == r->length) {
562 cmp = memcmp(m->data, r->data, m->length);
569 /* Server didn't like our choice of mech, and chose something else */
570 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
571 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
572 ta->supportedMech != NULL &&
573 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
575 const char *client_mech = NULL;
576 const char *client_oid = NULL;
577 const char *server_mech = NULL;
578 const char *server_oid = NULL;
580 client_mech = gensec_get_name_by_oid(gensec_security,
581 spnego_state->neg_oid);
582 client_oid = spnego_state->neg_oid;
583 server_mech = gensec_get_name_by_oid(gensec_security,
585 server_oid = ta->supportedMech;
587 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
588 "server wants: %s[%s]\n",
589 client_mech, client_oid, server_mech, server_oid);
591 spnego_state->downgraded = true;
592 gensec_spnego_update_sub_abort(spnego_state);
594 status = gensec_subcontext_start(spnego_state,
596 &spnego_state->sub_sec_security);
597 if (!NT_STATUS_IS_OK(status)) {
601 /* select the sub context */
602 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
604 if (!NT_STATUS_IS_OK(status)) {
608 spnego_state->neg_oid = talloc_strdup(spnego_state,
610 if (spnego_state->neg_oid == NULL) {
611 return NT_STATUS_NO_MEMORY;
615 if (ta->mechListMIC.length > 0) {
616 if (spnego_state->sub_sec_ready) {
617 spnego_state->needs_mic_check = true;
621 if (spnego_state->needs_mic_check) {
622 if (ta->responseToken.length != 0) {
623 DBG_WARNING("non empty response token not expected\n");
624 return NT_STATUS_INVALID_PARAMETER;
627 if (ta->mechListMIC.length == 0
628 && spnego_state->may_skip_mic_check) {
630 * In this case we don't require
631 * a mechListMIC from the server.
633 * This works around bugs in the Azure
634 * and Apple spnego implementations.
637 * https://bugzilla.samba.org/show_bug.cgi?id=11994
639 spnego_state->needs_mic_check = false;
640 status = NT_STATUS_OK;
641 goto client_response;
644 status = gensec_check_packet(spnego_state->sub_sec_security,
645 spnego_state->mech_types.data,
646 spnego_state->mech_types.length,
647 spnego_state->mech_types.data,
648 spnego_state->mech_types.length,
650 if (!NT_STATUS_IS_OK(status)) {
651 DBG_WARNING("failed to verify mechListMIC: %s\n",
655 spnego_state->needs_mic_check = false;
656 spnego_state->done_mic_check = true;
657 goto client_response;
660 if (!spnego_state->sub_sec_ready) {
661 status = gensec_update_ev(spnego_state->sub_sec_security,
665 if (NT_STATUS_IS_OK(status)) {
666 spnego_state->sub_sec_ready = true;
668 if (!NT_STATUS_IS_OK(status)) {
669 goto client_response;
672 status = NT_STATUS_OK;
675 if (!spnego_state->done_mic_check) {
676 bool have_sign = true;
677 bool new_spnego = false;
679 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
680 GENSEC_FEATURE_SIGN);
681 if (spnego_state->simulate_w2k) {
684 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
685 GENSEC_FEATURE_NEW_SPNEGO);
687 switch (ta->negResult) {
688 case SPNEGO_ACCEPT_COMPLETED:
689 case SPNEGO_NONE_RESULT:
690 if (spnego_state->num_targs == 1) {
692 * the first exchange doesn't require
700 case SPNEGO_ACCEPT_INCOMPLETE:
701 if (ta->mechListMIC.length > 0) {
706 if (spnego_state->downgraded) {
708 * A downgrade should be protected if
715 * The caller may just asked for
716 * GENSEC_FEATURE_SESSION_KEY, this
717 * is only reflected in the want_features.
720 * gensec_have_features(GENSEC_FEATURE_SIGN)
723 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
726 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
730 * Here we're sure our preferred mech was
731 * selected by the server and our caller doesn't
732 * need GENSEC_FEATURE_SIGN nor
733 * GENSEC_FEATURE_SEAL support.
735 * In this case we don't require
736 * a mechListMIC from the server.
738 * This works around bugs in the Azure
739 * and Apple spnego implementations.
742 * https://bugzilla.samba.org/show_bug.cgi?id=11994
744 spnego_state->may_skip_mic_check = true;
747 case SPNEGO_REQUEST_MIC:
748 if (ta->mechListMIC.length > 0) {
756 if (spnego_state->mic_requested) {
762 if (have_sign && new_spnego) {
763 spnego_state->needs_mic_check = true;
764 spnego_state->needs_mic_sign = true;
768 if (ta->mechListMIC.length > 0) {
769 status = gensec_check_packet(spnego_state->sub_sec_security,
770 spnego_state->mech_types.data,
771 spnego_state->mech_types.length,
772 spnego_state->mech_types.data,
773 spnego_state->mech_types.length,
775 if (!NT_STATUS_IS_OK(status)) {
776 DBG_WARNING("failed to verify mechListMIC: %s\n",
780 spnego_state->needs_mic_check = false;
781 spnego_state->done_mic_check = true;
784 if (spnego_state->needs_mic_sign) {
785 status = gensec_sign_packet(spnego_state->sub_sec_security,
787 spnego_state->mech_types.data,
788 spnego_state->mech_types.length,
789 spnego_state->mech_types.data,
790 spnego_state->mech_types.length,
792 if (!NT_STATUS_IS_OK(status)) {
793 DBG_WARNING("failed to sign mechListMIC: %s\n",
797 spnego_state->needs_mic_sign = false;
800 if (spnego_state->needs_mic_check) {
801 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
805 if (GENSEC_UPDATE_IS_NTERROR(status)) {
806 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
807 spnego_state->sub_sec_security->ops->name,
812 if (sub_out.length || mech_list_mic.length) {
814 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
815 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
816 spnego_out.negTokenTarg.supportedMech = NULL;
817 spnego_out.negTokenTarg.responseToken = sub_out;
818 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
820 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
821 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
822 return NT_STATUS_INVALID_PARAMETER;
825 spnego_state->num_targs++;
826 spnego_state->state_position = SPNEGO_CLIENT_TARG;
827 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
830 /* all done - server has accepted, and we agree */
831 *out = data_blob_null;
833 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
834 /* unless of course it did not accept */
835 DBG_WARNING("gensec_update ok but not accepted\n");
836 status = NT_STATUS_INVALID_PARAMETER;
839 spnego_state->state_position = SPNEGO_DONE;
845 /** create a server negTokenTarg
847 * This is the case, where the client is the first one who sends data
850 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
851 TALLOC_CTX *out_mem_ctx,
853 const DATA_BLOB unwrapped_out,
854 DATA_BLOB mech_list_mic,
857 struct spnego_data spnego_out;
860 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
861 spnego_out.negTokenTarg.responseToken = unwrapped_out;
862 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
863 spnego_out.negTokenTarg.supportedMech = NULL;
865 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
866 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
867 if (spnego_state->mic_requested) {
868 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
869 spnego_state->mic_requested = false;
871 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
873 spnego_state->state_position = SPNEGO_SERVER_TARG;
874 } else if (NT_STATUS_IS_OK(nt_status)) {
875 if (unwrapped_out.data) {
876 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
878 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
879 spnego_state->state_position = SPNEGO_DONE;
881 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
882 spnego_out.negTokenTarg.mechListMIC = data_blob_null;
883 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
884 spnego_state->state_position = SPNEGO_DONE;
887 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
888 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
889 return NT_STATUS_INVALID_PARAMETER;
892 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
893 spnego_state->num_targs++;
898 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
899 struct spnego_state *spnego_state,
900 struct tevent_context *ev,
901 struct spnego_data *spnego_in,
902 TALLOC_CTX *out_mem_ctx,
905 TALLOC_CTX *frame = talloc_stackframe();
906 DATA_BLOB sub_out = data_blob_null;
907 DATA_BLOB mech_list_mic = data_blob_null;
908 const char * const *mech_types = NULL;
910 const struct gensec_security_ops_wrapper *all_sec = NULL;
915 mech_types = spnego_in->negTokenInit.mechTypes;
916 if (mech_types == NULL) {
918 return NT_STATUS_INVALID_PARAMETER;
921 all_sec = gensec_security_by_oid_list(gensec_security, frame,
922 mech_types, GENSEC_OID_SPNEGO);
923 if (all_sec == NULL) {
924 DBG_WARNING("gensec_security_by_oid_list() failed\n");
926 return NT_STATUS_INVALID_PARAMETER;
929 ok = spnego_write_mech_types(spnego_state, mech_types,
930 &spnego_state->mech_types);
932 DBG_ERR("Failed to write mechTypes\n");
934 return NT_STATUS_NO_MEMORY;
938 * First try the preferred mechs from the client.
940 for (; mech_types[mech_idx]; mech_idx++) {
941 const char *cur_mech = mech_types[mech_idx];
942 const struct gensec_security_ops_wrapper *cur_sec = NULL;
943 DATA_BLOB sub_in = data_blob_null;
945 for (all_idx = 0; all_sec[all_idx].op; all_idx++) {
946 if (strcmp(cur_mech, all_sec[all_idx].oid) == 0) {
947 cur_sec = &all_sec[all_idx];
952 if (cur_sec == NULL) {
956 status = gensec_subcontext_start(spnego_state,
958 &spnego_state->sub_sec_security);
959 if (!NT_STATUS_IS_OK(status)) {
964 /* select the sub context */
965 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
967 if (!NT_STATUS_IS_OK(status)) {
969 * Pretend we never started it
971 gensec_spnego_update_sub_abort(spnego_state);
977 * Indicate the downgrade and request a
980 spnego_state->downgraded = true;
981 spnego_state->mic_requested = true;
982 /* no optimistic token */
983 spnego_state->neg_oid = cur_sec->oid;
984 sub_out = data_blob_null;
985 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
990 * Try the optimistic token from the client
992 sub_in = spnego_in->negTokenInit.mechToken;
993 status = gensec_update_ev(spnego_state->sub_sec_security,
994 frame, ev, sub_in, &sub_out);
995 if (NT_STATUS_IS_OK(status)) {
996 spnego_state->sub_sec_ready = true;
998 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
999 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1001 DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1002 cur_sec->op->name, nt_errstr(status));
1005 * Pretend we never started it
1007 gensec_spnego_update_sub_abort(spnego_state);
1011 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1012 DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1013 cur_sec->op->name, nt_errstr(status));
1017 spnego_state->neg_oid = cur_sec->oid;
1018 goto reply; /* OK or MORE PROCESSING */
1021 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1022 status = NT_STATUS_INVALID_PARAMETER;
1025 if (spnego_state->simulate_w2k) {
1027 * Windows 2000 returns the unwrapped token
1028 * also in the mech_list_mic field.
1030 * In order to verify our client code,
1031 * we need a way to have a server with this
1034 mech_list_mic = sub_out;
1037 status = gensec_spnego_server_response(spnego_state,
1047 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1048 struct spnego_state *spnego_state,
1049 struct tevent_context *ev,
1050 struct spnego_data *spnego_in,
1051 TALLOC_CTX *out_mem_ctx,
1054 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1055 DATA_BLOB sub_in = ta->responseToken;
1056 DATA_BLOB mech_list_mic = data_blob_null;
1057 DATA_BLOB sub_out = data_blob_null;
1059 bool have_sign = true;
1060 bool new_spnego = false;
1062 spnego_state->num_targs++;
1064 if (spnego_state->sub_sec_security == NULL) {
1065 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1066 return NT_STATUS_INVALID_PARAMETER;
1069 if (spnego_state->needs_mic_check) {
1070 if (ta->responseToken.length != 0) {
1071 DBG_WARNING("non empty response token not expected\n");
1072 return NT_STATUS_INVALID_PARAMETER;
1075 status = gensec_check_packet(spnego_state->sub_sec_security,
1076 spnego_state->mech_types.data,
1077 spnego_state->mech_types.length,
1078 spnego_state->mech_types.data,
1079 spnego_state->mech_types.length,
1081 if (!NT_STATUS_IS_OK(status)) {
1082 DBG_WARNING("failed to verify mechListMIC: %s\n",
1084 goto server_response;
1087 spnego_state->needs_mic_check = false;
1088 spnego_state->done_mic_check = true;
1089 goto server_response;
1092 if (!spnego_state->sub_sec_ready) {
1093 status = gensec_update_ev(spnego_state->sub_sec_security,
1096 if (NT_STATUS_IS_OK(status)) {
1097 spnego_state->sub_sec_ready = true;
1099 if (!NT_STATUS_IS_OK(status)) {
1100 goto server_response;
1103 status = NT_STATUS_OK;
1106 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1107 GENSEC_FEATURE_SIGN);
1108 if (spnego_state->simulate_w2k) {
1111 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1112 GENSEC_FEATURE_NEW_SPNEGO);
1113 if (ta->mechListMIC.length > 0) {
1117 if (have_sign && new_spnego) {
1118 spnego_state->needs_mic_check = true;
1119 spnego_state->needs_mic_sign = true;
1122 if (have_sign && ta->mechListMIC.length > 0) {
1123 status = gensec_check_packet(spnego_state->sub_sec_security,
1124 spnego_state->mech_types.data,
1125 spnego_state->mech_types.length,
1126 spnego_state->mech_types.data,
1127 spnego_state->mech_types.length,
1129 if (!NT_STATUS_IS_OK(status)) {
1130 DBG_WARNING("failed to verify mechListMIC: %s\n",
1132 goto server_response;
1135 spnego_state->needs_mic_check = false;
1136 spnego_state->done_mic_check = true;
1139 if (spnego_state->needs_mic_sign) {
1140 status = gensec_sign_packet(spnego_state->sub_sec_security,
1142 spnego_state->mech_types.data,
1143 spnego_state->mech_types.length,
1144 spnego_state->mech_types.data,
1145 spnego_state->mech_types.length,
1147 if (!NT_STATUS_IS_OK(status)) {
1148 DBG_WARNING("failed to sign mechListMIC: %s\n",
1152 spnego_state->needs_mic_sign = false;
1155 if (spnego_state->needs_mic_check) {
1156 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1160 return gensec_spnego_server_response(spnego_state,
1168 struct gensec_spnego_update_state {
1169 struct tevent_context *ev;
1170 struct gensec_security *gensec;
1171 struct spnego_state *spnego;
1173 struct spnego_data _spnego_in;
1174 struct spnego_data *spnego_in;
1179 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1180 enum tevent_req_state req_state)
1182 struct gensec_spnego_update_state *state =
1183 tevent_req_data(req,
1184 struct gensec_spnego_update_state);
1186 switch (req_state) {
1187 case TEVENT_REQ_USER_ERROR:
1188 case TEVENT_REQ_TIMED_OUT:
1189 case TEVENT_REQ_NO_MEMORY:
1191 * A fatal error, further updates are not allowed.
1193 state->spnego->state_position = SPNEGO_DONE;
1200 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1201 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1202 DATA_BLOB *full_in);
1203 static void gensec_spnego_update_pre(struct tevent_req *req);
1204 static void gensec_spnego_update_post(struct tevent_req *req);
1205 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1206 TALLOC_CTX *out_mem_ctx,
1209 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1210 struct tevent_context *ev,
1211 struct gensec_security *gensec_security,
1214 struct spnego_state *spnego_state =
1215 talloc_get_type_abort(gensec_security->private_data,
1216 struct spnego_state);
1217 struct tevent_req *req = NULL;
1218 struct gensec_spnego_update_state *state = NULL;
1222 req = tevent_req_create(mem_ctx, &state,
1223 struct gensec_spnego_update_state);
1228 state->gensec = gensec_security;
1229 state->spnego = spnego_state;
1230 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1232 if (spnego_state->out_frag.length > 0) {
1233 if (in.length > 0) {
1234 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1235 return tevent_req_post(req, ev);
1238 status = gensec_spnego_update_out(gensec_security,
1239 state, &state->out);
1240 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1241 tevent_req_nterror(req, status);
1242 return tevent_req_post(req, ev);
1245 state->status = status;
1246 tevent_req_done(req);
1247 return tevent_req_post(req, ev);
1250 status = gensec_spnego_update_in(gensec_security, in,
1251 state, &state->full_in);
1252 state->status = status;
1253 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1254 tevent_req_done(req);
1255 return tevent_req_post(req, ev);
1257 if (tevent_req_nterror(req, status)) {
1258 return tevent_req_post(req, ev);
1261 /* Check if we got a valid SPNEGO blob... */
1263 switch (spnego_state->state_position) {
1264 case SPNEGO_FALLBACK:
1267 case SPNEGO_CLIENT_TARG:
1268 case SPNEGO_SERVER_TARG:
1269 if (state->full_in.length == 0) {
1270 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1271 return tevent_req_post(req, ev);
1275 case SPNEGO_CLIENT_START:
1276 case SPNEGO_SERVER_START:
1278 if (state->full_in.length == 0) {
1279 /* create_negTokenInit later */
1283 len = spnego_read_data(state,
1285 &state->_spnego_in);
1287 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1288 DEBUG(1, ("Invalid SPNEGO request:\n"));
1289 dump_data(1, state->full_in.data,
1290 state->full_in.length);
1291 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1292 return tevent_req_post(req, ev);
1296 * This is the 'fallback' case, where we don't get
1297 * SPNEGO, and have to try all the other options (and
1298 * hope they all have a magic string they check)
1300 status = gensec_spnego_server_try_fallback(gensec_security,
1304 if (tevent_req_nterror(req, status)) {
1305 return tevent_req_post(req, ev);
1309 * We'll continue with SPNEGO_FALLBACK below...
1313 state->spnego_in = &state->_spnego_in;
1315 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1316 if (state->spnego_in->type != spnego_state->expected_packet) {
1317 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1318 state->spnego_in->type,
1319 spnego_state->expected_packet));
1320 dump_data(1, state->full_in.data,
1321 state->full_in.length);
1322 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1323 return tevent_req_post(req, ev);
1329 smb_panic(__location__);
1333 gensec_spnego_update_pre(req);
1334 if (!tevent_req_is_in_progress(req)) {
1335 return tevent_req_post(req, ev);
1339 * TODO: prepare async processing here in future.
1342 gensec_spnego_update_post(req);
1343 if (!tevent_req_is_in_progress(req)) {
1344 return tevent_req_post(req, ev);
1350 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1351 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1354 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1358 *full_in = data_blob_null;
1360 switch (spnego_state->state_position) {
1361 case SPNEGO_FALLBACK:
1363 spnego_state->in_needed = 0;
1364 return NT_STATUS_OK;
1366 case SPNEGO_CLIENT_START:
1367 case SPNEGO_CLIENT_TARG:
1368 case SPNEGO_SERVER_START:
1369 case SPNEGO_SERVER_TARG:
1374 return NT_STATUS_INVALID_PARAMETER;
1377 if (spnego_state->in_needed == 0) {
1382 * try to work out the size of the full
1383 * input token, it might be fragmented
1385 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1386 if ((ret != 0) && (ret != EAGAIN)) {
1387 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1390 if ((ret == 0) || (ret == EAGAIN)) {
1391 spnego_state->in_needed = size;
1394 * If it is not an asn1 message
1395 * just call the next layer.
1397 spnego_state->in_needed = in.length;
1401 if (spnego_state->in_needed > UINT16_MAX) {
1403 * limit the incoming message to 0xFFFF
1404 * to avoid DoS attacks.
1406 return NT_STATUS_INVALID_BUFFER_SIZE;
1409 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1411 * If we reach this, we know we got at least
1412 * part of an asn1 message, getting 0 means
1413 * the remote peer wants us to spin.
1415 return NT_STATUS_INVALID_PARAMETER;
1418 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1419 if (in.length > expected) {
1421 * we got more than expected
1423 return NT_STATUS_INVALID_PARAMETER;
1426 if (in.length == spnego_state->in_needed) {
1428 * if the in.length contains the full blob
1431 * Note: this implies spnego_state->in_frag.length == 0,
1432 * but we do not need to check this explicitly
1433 * because we already know that we did not get
1434 * more than expected.
1437 spnego_state->in_needed = 0;
1438 return NT_STATUS_OK;
1441 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1442 in.data, in.length);
1444 return NT_STATUS_NO_MEMORY;
1447 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1448 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1451 *full_in = spnego_state->in_frag;
1452 talloc_steal(mem_ctx, full_in->data);
1453 spnego_state->in_frag = data_blob_null;
1454 spnego_state->in_needed = 0;
1455 return NT_STATUS_OK;
1458 static void gensec_spnego_update_pre(struct tevent_req *req)
1460 struct gensec_spnego_update_state *state =
1461 tevent_req_data(req,
1462 struct gensec_spnego_update_state);
1463 struct gensec_security *gensec_security = state->gensec;
1464 struct spnego_state *spnego_state = state->spnego;
1465 struct tevent_context *ev = state->ev;
1468 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1469 status = gensec_update_ev(spnego_state->sub_sec_security,
1472 &spnego_state->out_frag);
1474 * We don't check status here.
1476 spnego_state->out_status = status;
1480 switch (spnego_state->state_position) {
1481 case SPNEGO_CLIENT_START:
1482 if (state->spnego_in == NULL) {
1483 /* client to produce negTokenInit */
1484 status = gensec_spnego_create_negTokenInit(gensec_security,
1485 spnego_state, state, ev,
1486 &spnego_state->out_frag);
1487 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1488 tevent_req_nterror(req, status);
1494 status = gensec_spnego_client_negTokenInit(gensec_security,
1496 state->spnego_in, state,
1497 &spnego_state->out_frag);
1500 case SPNEGO_CLIENT_TARG:
1501 status = gensec_spnego_client_negTokenTarg(gensec_security,
1503 state->spnego_in, state,
1504 &spnego_state->out_frag);
1505 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1506 tevent_req_nterror(req, status);
1511 case SPNEGO_SERVER_START:
1512 if (state->spnego_in == NULL) {
1513 /* server to produce negTokenInit */
1514 status = gensec_spnego_create_negTokenInit(gensec_security,
1515 spnego_state, state, ev,
1516 &spnego_state->out_frag);
1517 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1518 tevent_req_nterror(req, status);
1524 status = gensec_spnego_server_negTokenInit(gensec_security,
1526 state->spnego_in, state,
1527 &spnego_state->out_frag);
1528 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1529 tevent_req_nterror(req, status);
1534 case SPNEGO_SERVER_TARG:
1535 status = gensec_spnego_server_negTokenTarg(gensec_security,
1537 state->spnego_in, state,
1538 &spnego_state->out_frag);
1539 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1540 tevent_req_nterror(req, status);
1546 smb_panic(__location__);
1550 spnego_state->out_status = status;
1553 static void gensec_spnego_update_post(struct tevent_req *req)
1555 struct gensec_spnego_update_state *state =
1556 tevent_req_data(req,
1557 struct gensec_spnego_update_state);
1558 struct spnego_state *spnego_state = state->spnego;
1561 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1562 status = spnego_state->out_status;
1567 * For now just handle the sync processing done
1568 * in gensec_spnego_update_pre()
1570 status = spnego_state->out_status;
1572 if (NT_STATUS_IS_OK(status)) {
1573 bool reset_full = true;
1575 reset_full = !spnego_state->done_mic_check;
1577 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1579 if (tevent_req_nterror(req, status)) {
1585 spnego_state->out_status = status;
1587 status = gensec_spnego_update_out(state->gensec,
1588 state, &state->out);
1589 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1590 tevent_req_nterror(req, status);
1594 state->status = status;
1595 tevent_req_done(req);
1599 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1600 TALLOC_CTX *out_mem_ctx,
1603 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1604 DATA_BLOB out = data_blob_null;
1607 *_out = data_blob_null;
1609 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1611 * Fast path, we can deliver everything
1614 *_out = spnego_state->out_frag;
1615 if (spnego_state->out_frag.length > 0) {
1616 talloc_steal(out_mem_ctx, _out->data);
1617 spnego_state->out_frag = data_blob_null;
1620 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1621 return spnego_state->out_status;
1625 * We're completely done, further updates are not allowed.
1627 spnego_state->state_position = SPNEGO_DONE;
1628 return gensec_child_ready(gensec_security,
1629 spnego_state->sub_sec_security);
1632 out = spnego_state->out_frag;
1635 * copy the remaining bytes
1637 spnego_state->out_frag = data_blob_talloc(spnego_state,
1638 out.data + spnego_state->out_max_length,
1639 out.length - spnego_state->out_max_length);
1640 if (spnego_state->out_frag.data == NULL) {
1641 return NT_STATUS_NO_MEMORY;
1645 * truncate the buffer
1647 ok = data_blob_realloc(spnego_state, &out,
1648 spnego_state->out_max_length);
1650 return NT_STATUS_NO_MEMORY;
1653 talloc_steal(out_mem_ctx, out.data);
1655 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1658 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1659 TALLOC_CTX *out_mem_ctx,
1662 struct gensec_spnego_update_state *state =
1663 tevent_req_data(req,
1664 struct gensec_spnego_update_state);
1667 *out = data_blob_null;
1669 if (tevent_req_is_nterror(req, &status)) {
1670 tevent_req_received(req);
1675 talloc_steal(out_mem_ctx, state->out.data);
1676 status = state->status;
1677 tevent_req_received(req);
1681 static const char *gensec_spnego_oids[] = {
1686 static const struct gensec_security_ops gensec_spnego_security_ops = {
1688 .sasl_name = "GSS-SPNEGO",
1689 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1690 .oid = gensec_spnego_oids,
1691 .client_start = gensec_spnego_client_start,
1692 .server_start = gensec_spnego_server_start,
1693 .update_send = gensec_spnego_update_send,
1694 .update_recv = gensec_spnego_update_recv,
1695 .seal_packet = gensec_child_seal_packet,
1696 .sign_packet = gensec_child_sign_packet,
1697 .sig_size = gensec_child_sig_size,
1698 .max_wrapped_size = gensec_child_max_wrapped_size,
1699 .max_input_size = gensec_child_max_input_size,
1700 .check_packet = gensec_child_check_packet,
1701 .unseal_packet = gensec_child_unseal_packet,
1702 .wrap = gensec_child_wrap,
1703 .unwrap = gensec_child_unwrap,
1704 .session_key = gensec_child_session_key,
1705 .session_info = gensec_child_session_info,
1706 .want_feature = gensec_child_want_feature,
1707 .have_feature = gensec_child_have_feature,
1708 .expire_time = gensec_child_expire_time,
1709 .final_auth_type = gensec_child_final_auth_type,
1711 .priority = GENSEC_SPNEGO
1714 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1717 ret = gensec_register(ctx, &gensec_spnego_security_ops);
1718 if (!NT_STATUS_IS_OK(ret)) {
1719 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1720 gensec_spnego_security_ops.name));