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 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;
221 const char **send_mech_types = NULL;
222 struct spnego_data spnego_out;
225 mechTypes = gensec_security_oids(gensec_security,
226 out_mem_ctx, GENSEC_OID_SPNEGO);
227 if (mechTypes == NULL) {
228 DBG_WARNING("gensec_security_oids() failed\n");
229 return NT_STATUS_NO_MEMORY;
232 all_sec = gensec_security_by_oid_list(gensec_security,
236 if (all_sec == NULL) {
237 DBG_WARNING("gensec_security_by_oid_list() failed\n");
238 return NT_STATUS_NO_MEMORY;
241 for (i=0; all_sec && all_sec[i].op; i++) {
242 const char *next = NULL;
243 const char *principal = NULL;
244 int dbg_level = DBGLVL_WARNING;
246 status = gensec_subcontext_start(spnego_state,
248 &spnego_state->sub_sec_security);
249 if (!NT_STATUS_IS_OK(status)) {
252 /* select the sub context */
253 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
255 if (!NT_STATUS_IS_OK(status)) {
256 gensec_spnego_update_sub_abort(spnego_state);
260 if (spnego_state->state_position != SPNEGO_CLIENT_START) {
262 * The server doesn't generate an optimistic token.
267 /* In the client, try and produce the first (optimistic) packet */
268 status = gensec_update_ev(spnego_state->sub_sec_security,
273 if (NT_STATUS_IS_OK(status)) {
274 spnego_state->sub_sec_ready = true;
277 if (!GENSEC_UPDATE_IS_NTERROR(status)) {
281 if (all_sec[i+1].op != NULL) {
282 next = all_sec[i+1].op->name;
283 dbg_level = DBGLVL_NOTICE;
286 if (gensec_security->target.principal != NULL) {
287 principal = gensec_security->target.principal;
288 } else if (gensec_security->target.service != NULL &&
289 gensec_security->target.hostname != NULL)
291 principal = talloc_asprintf(spnego_state->sub_sec_security,
293 gensec_security->target.service,
294 gensec_security->target.hostname);
296 principal = gensec_security->target.hostname;
299 DBG_PREFIX(dbg_level, (
300 "%s: creating NEG_TOKEN_INIT for %s failed "
302 spnego_state->sub_sec_security->ops->name,
303 principal, next, nt_errstr(status)));
306 * Pretend we never started it
308 gensec_spnego_update_sub_abort(spnego_state);
311 DBG_WARNING("Failed to setup SPNEGO negTokenInit request: %s\n",
316 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
318 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
320 if (send_mech_types == NULL) {
321 DBG_WARNING("gensec_security_oids_from_ops_wrapped() failed\n");
322 return NT_STATUS_NO_MEMORY;
325 ok = spnego_write_mech_types(spnego_state,
327 &spnego_state->mech_types);
329 DBG_ERR("Failed to write mechTypes\n");
330 return NT_STATUS_NO_MEMORY;
333 /* List the remaining mechs as options */
334 spnego_out.negTokenInit.mechTypes = send_mech_types;
335 spnego_out.negTokenInit.reqFlags = data_blob_null;
336 spnego_out.negTokenInit.reqFlagsPadding = 0;
338 if (spnego_state->state_position == SPNEGO_SERVER_START) {
339 spnego_out.negTokenInit.mechListMIC
340 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
342 spnego_out.negTokenInit.mechListMIC = data_blob_null;
345 spnego_out.negTokenInit.mechToken = unwrapped_out;
347 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
348 DBG_ERR("Failed to write NEG_TOKEN_INIT\n");
349 return NT_STATUS_INVALID_PARAMETER;
353 spnego_state->neg_oid = all_sec[i].oid;
355 if (spnego_state->state_position == SPNEGO_SERVER_START) {
356 spnego_state->state_position = SPNEGO_SERVER_START;
357 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
359 spnego_state->state_position = SPNEGO_CLIENT_TARG;
360 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
363 return NT_STATUS_MORE_PROCESSING_REQUIRED;
366 static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security,
367 struct spnego_state *spnego_state,
368 struct tevent_context *ev,
369 struct spnego_data *spnego_in,
370 TALLOC_CTX *out_mem_ctx,
373 TALLOC_CTX *frame = talloc_stackframe();
374 DATA_BLOB sub_out = data_blob_null;
375 const char *tp = NULL;
376 const char * const *mech_types = NULL;
378 const struct gensec_security_ops_wrapper *all_sec = NULL;
379 struct spnego_data spnego_out;
380 const char *my_mechs[] = {NULL, NULL};
384 *out = data_blob_null;
386 /* The server offers a list of mechanisms */
388 tp = spnego_in->negTokenInit.targetPrincipal;
389 if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
390 DBG_INFO("Server claims it's principal name is %s\n", tp);
391 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
392 gensec_set_target_principal(gensec_security, tp);
396 mech_types = spnego_in->negTokenInit.mechTypes;
397 if (mech_types == NULL) {
399 return NT_STATUS_INVALID_PARAMETER;
402 all_sec = gensec_security_by_oid_list(gensec_security,
405 if (all_sec == NULL) {
406 DBG_WARNING("gensec_security_by_oid_list() failed\n");
408 return NT_STATUS_INVALID_PARAMETER;
411 for (; all_sec[all_idx].op; all_idx++) {
412 const struct gensec_security_ops_wrapper *cur_sec =
414 const char *next = NULL;
415 const char *principal = NULL;
416 int dbg_level = DBGLVL_WARNING;
417 bool allow_fallback = false;
419 status = gensec_subcontext_start(spnego_state,
421 &spnego_state->sub_sec_security);
422 if (!NT_STATUS_IS_OK(status)) {
427 /* select the sub context */
428 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
430 if (!NT_STATUS_IS_OK(status)) {
432 * Pretend we never started it.
434 gensec_spnego_update_sub_abort(spnego_state);
438 spnego_state->neg_oid = cur_sec->oid;
441 * As client we don't use an optimistic token from the server.
443 status = gensec_update_ev(spnego_state->sub_sec_security,
444 frame, ev, data_blob_null, &sub_out);
445 if (NT_STATUS_IS_OK(status)) {
446 spnego_state->sub_sec_ready = true;
449 if (!GENSEC_UPDATE_IS_NTERROR(status)) {
450 /* OK or MORE_PROCESSING_REQUIRED */
455 * it is likely that a NULL input token will
456 * not be liked by most server mechs, but if
457 * we are in the client, we want the first
458 * update packet to be able to abort the use
461 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
462 NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) ||
463 NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
464 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO))
466 allow_fallback = true;
469 if (allow_fallback && cur_sec[1].op != NULL) {
470 next = cur_sec[1].op->name;
471 dbg_level = DBGLVL_NOTICE;
474 if (gensec_security->target.principal != NULL) {
475 principal = gensec_security->target.principal;
476 } else if (gensec_security->target.service != NULL &&
477 gensec_security->target.hostname != NULL)
479 principal = talloc_asprintf(spnego_state->sub_sec_security,
481 gensec_security->target.service,
482 gensec_security->target.hostname);
484 principal = gensec_security->target.hostname;
487 DBG_PREFIX(dbg_level, (
488 "%s: creating NEG_TOKEN_INIT "
489 "for %s failed (next[%s]): %s\n",
490 spnego_state->sub_sec_security->ops->name,
491 principal, next, nt_errstr(status)));
495 * A hard error without a possible fallback.
502 * Pretend we never started it.
504 gensec_spnego_update_sub_abort(spnego_state);
507 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
509 return NT_STATUS_INVALID_PARAMETER;
512 my_mechs[0] = spnego_state->neg_oid;
514 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
515 spnego_out.negTokenInit.mechTypes = my_mechs;
516 spnego_out.negTokenInit.reqFlags = data_blob_null;
517 spnego_out.negTokenInit.reqFlagsPadding = 0;
518 spnego_out.negTokenInit.mechListMIC = data_blob_null;
519 spnego_out.negTokenInit.mechToken = sub_out;
521 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
522 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
524 return NT_STATUS_INVALID_PARAMETER;
527 ok = spnego_write_mech_types(spnego_state,
529 &spnego_state->mech_types);
531 DBG_ERR("failed to write mechTypes\n");
533 return NT_STATUS_NO_MEMORY;
537 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
538 spnego_state->state_position = SPNEGO_CLIENT_TARG;
541 return NT_STATUS_MORE_PROCESSING_REQUIRED;
544 static NTSTATUS gensec_spnego_client_negTokenTarg(struct gensec_security *gensec_security,
545 struct spnego_state *spnego_state,
546 struct tevent_context *ev,
547 struct spnego_data *spnego_in,
548 TALLOC_CTX *out_mem_ctx,
551 struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
552 DATA_BLOB sub_in = ta->responseToken;
553 DATA_BLOB mech_list_mic = data_blob_null;
554 DATA_BLOB sub_out = data_blob_null;
555 struct spnego_data spnego_out;
558 *out = data_blob_null;
560 spnego_state->num_targs++;
562 if (ta->negResult == SPNEGO_REJECT) {
563 return NT_STATUS_LOGON_FAILURE;
566 if (ta->negResult == SPNEGO_REQUEST_MIC) {
567 spnego_state->mic_requested = true;
570 if (ta->mechListMIC.length > 0) {
571 DATA_BLOB *m = &ta->mechListMIC;
572 const DATA_BLOB *r = &ta->responseToken;
575 * Windows 2000 has a bug, it repeats the
576 * responseToken in the mechListMIC field.
578 if (m->length == r->length) {
581 cmp = memcmp(m->data, r->data, m->length);
588 /* Server didn't like our choice of mech, and chose something else */
589 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
590 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
591 ta->supportedMech != NULL &&
592 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0)
594 const char *client_mech = NULL;
595 const char *client_oid = NULL;
596 const char *server_mech = NULL;
597 const char *server_oid = NULL;
599 client_mech = gensec_get_name_by_oid(gensec_security,
600 spnego_state->neg_oid);
601 client_oid = spnego_state->neg_oid;
602 server_mech = gensec_get_name_by_oid(gensec_security,
604 server_oid = ta->supportedMech;
606 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
607 "server wants: %s[%s]\n",
608 client_mech, client_oid, server_mech, server_oid);
610 spnego_state->downgraded = true;
611 gensec_spnego_update_sub_abort(spnego_state);
613 status = gensec_subcontext_start(spnego_state,
615 &spnego_state->sub_sec_security);
616 if (!NT_STATUS_IS_OK(status)) {
620 /* select the sub context */
621 status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
623 if (!NT_STATUS_IS_OK(status)) {
627 spnego_state->neg_oid = talloc_strdup(spnego_state,
629 if (spnego_state->neg_oid == NULL) {
630 return NT_STATUS_NO_MEMORY;
634 if (ta->mechListMIC.length > 0) {
635 if (spnego_state->sub_sec_ready) {
636 spnego_state->needs_mic_check = true;
640 if (spnego_state->needs_mic_check) {
641 if (ta->responseToken.length != 0) {
642 DBG_WARNING("non empty response token not expected\n");
643 return NT_STATUS_INVALID_PARAMETER;
646 if (ta->mechListMIC.length == 0
647 && spnego_state->may_skip_mic_check) {
649 * In this case we don't require
650 * a mechListMIC from the server.
652 * This works around bugs in the Azure
653 * and Apple spnego implementations.
656 * https://bugzilla.samba.org/show_bug.cgi?id=11994
658 spnego_state->needs_mic_check = false;
659 status = NT_STATUS_OK;
660 goto client_response;
663 status = gensec_check_packet(spnego_state->sub_sec_security,
664 spnego_state->mech_types.data,
665 spnego_state->mech_types.length,
666 spnego_state->mech_types.data,
667 spnego_state->mech_types.length,
669 if (!NT_STATUS_IS_OK(status)) {
670 DBG_WARNING("failed to verify mechListMIC: %s\n",
674 spnego_state->needs_mic_check = false;
675 spnego_state->done_mic_check = true;
676 goto client_response;
679 if (!spnego_state->sub_sec_ready) {
680 status = gensec_update_ev(spnego_state->sub_sec_security,
684 if (NT_STATUS_IS_OK(status)) {
685 spnego_state->sub_sec_ready = true;
687 if (!NT_STATUS_IS_OK(status)) {
688 goto client_response;
691 status = NT_STATUS_OK;
694 if (!spnego_state->done_mic_check) {
695 bool have_sign = true;
696 bool new_spnego = false;
698 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
699 GENSEC_FEATURE_SIGN);
700 if (spnego_state->simulate_w2k) {
703 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
704 GENSEC_FEATURE_NEW_SPNEGO);
706 switch (ta->negResult) {
707 case SPNEGO_ACCEPT_COMPLETED:
708 case SPNEGO_NONE_RESULT:
709 if (spnego_state->num_targs == 1) {
711 * the first exchange doesn't require
719 case SPNEGO_ACCEPT_INCOMPLETE:
720 if (ta->mechListMIC.length > 0) {
725 if (spnego_state->downgraded) {
727 * A downgrade should be protected if
734 * The caller may just asked for
735 * GENSEC_FEATURE_SESSION_KEY, this
736 * is only reflected in the want_features.
739 * gensec_have_features(GENSEC_FEATURE_SIGN)
742 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
745 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
749 * Here we're sure our preferred mech was
750 * selected by the server and our caller doesn't
751 * need GENSEC_FEATURE_SIGN nor
752 * GENSEC_FEATURE_SEAL support.
754 * In this case we don't require
755 * a mechListMIC from the server.
757 * This works around bugs in the Azure
758 * and Apple spnego implementations.
761 * https://bugzilla.samba.org/show_bug.cgi?id=11994
763 spnego_state->may_skip_mic_check = true;
766 case SPNEGO_REQUEST_MIC:
767 if (ta->mechListMIC.length > 0) {
775 if (spnego_state->mic_requested) {
781 if (have_sign && new_spnego) {
782 spnego_state->needs_mic_check = true;
783 spnego_state->needs_mic_sign = true;
787 if (ta->mechListMIC.length > 0) {
788 status = gensec_check_packet(spnego_state->sub_sec_security,
789 spnego_state->mech_types.data,
790 spnego_state->mech_types.length,
791 spnego_state->mech_types.data,
792 spnego_state->mech_types.length,
794 if (!NT_STATUS_IS_OK(status)) {
795 DBG_WARNING("failed to verify mechListMIC: %s\n",
799 spnego_state->needs_mic_check = false;
800 spnego_state->done_mic_check = true;
803 if (spnego_state->needs_mic_sign) {
804 status = gensec_sign_packet(spnego_state->sub_sec_security,
806 spnego_state->mech_types.data,
807 spnego_state->mech_types.length,
808 spnego_state->mech_types.data,
809 spnego_state->mech_types.length,
811 if (!NT_STATUS_IS_OK(status)) {
812 DBG_WARNING("failed to sign mechListMIC: %s\n",
816 spnego_state->needs_mic_sign = false;
819 if (spnego_state->needs_mic_check) {
820 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
824 if (GENSEC_UPDATE_IS_NTERROR(status)) {
825 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
826 spnego_state->sub_sec_security->ops->name,
831 if (sub_out.length || mech_list_mic.length) {
833 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
834 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
835 spnego_out.negTokenTarg.supportedMech = NULL;
836 spnego_out.negTokenTarg.responseToken = sub_out;
837 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
839 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
840 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
841 return NT_STATUS_INVALID_PARAMETER;
844 spnego_state->num_targs++;
845 spnego_state->state_position = SPNEGO_CLIENT_TARG;
846 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
849 /* all done - server has accepted, and we agree */
850 *out = data_blob_null;
852 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
853 /* unless of course it did not accept */
854 DBG_WARNING("gensec_update ok but not accepted\n");
855 status = NT_STATUS_INVALID_PARAMETER;
858 spnego_state->state_position = SPNEGO_DONE;
864 /** create a server negTokenTarg
866 * This is the case, where the client is the first one who sends data
869 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
870 TALLOC_CTX *out_mem_ctx,
872 const DATA_BLOB unwrapped_out,
873 DATA_BLOB mech_list_mic,
876 struct spnego_data spnego_out;
879 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
880 spnego_out.negTokenTarg.responseToken = unwrapped_out;
881 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
882 spnego_out.negTokenTarg.supportedMech = NULL;
884 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
885 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
886 if (spnego_state->mic_requested) {
887 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
888 spnego_state->mic_requested = false;
890 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
892 spnego_state->state_position = SPNEGO_SERVER_TARG;
893 } else if (NT_STATUS_IS_OK(nt_status)) {
894 if (unwrapped_out.data) {
895 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
897 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
898 spnego_state->state_position = SPNEGO_DONE;
900 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
901 spnego_out.negTokenTarg.mechListMIC = data_blob_null;
902 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
903 spnego_state->state_position = SPNEGO_DONE;
906 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
907 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
908 return NT_STATUS_INVALID_PARAMETER;
911 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
912 spnego_state->num_targs++;
917 static NTSTATUS gensec_spnego_server_negTokenInit(struct gensec_security *gensec_security,
918 struct spnego_state *spnego_state,
919 struct tevent_context *ev,
920 struct spnego_data *spnego_in,
921 TALLOC_CTX *out_mem_ctx,
924 TALLOC_CTX *frame = talloc_stackframe();
925 DATA_BLOB sub_out = data_blob_null;
926 DATA_BLOB mech_list_mic = data_blob_null;
927 const char * const *mech_types = NULL;
929 const struct gensec_security_ops_wrapper *all_sec = NULL;
934 mech_types = spnego_in->negTokenInit.mechTypes;
935 if (mech_types == NULL) {
937 return NT_STATUS_INVALID_PARAMETER;
940 all_sec = gensec_security_by_oid_list(gensec_security, frame,
941 mech_types, GENSEC_OID_SPNEGO);
942 if (all_sec == NULL) {
943 DBG_WARNING("gensec_security_by_oid_list() failed\n");
945 return NT_STATUS_INVALID_PARAMETER;
948 ok = spnego_write_mech_types(spnego_state, mech_types,
949 &spnego_state->mech_types);
951 DBG_ERR("Failed to write mechTypes\n");
953 return NT_STATUS_NO_MEMORY;
957 * First try the preferred mechs from the client.
959 for (; mech_types[mech_idx]; mech_idx++) {
960 const char *cur_mech = mech_types[mech_idx];
961 const struct gensec_security_ops_wrapper *cur_sec = NULL;
962 DATA_BLOB sub_in = data_blob_null;
964 for (all_idx = 0; all_sec[all_idx].op; all_idx++) {
965 if (strcmp(cur_mech, all_sec[all_idx].oid) == 0) {
966 cur_sec = &all_sec[all_idx];
971 if (cur_sec == NULL) {
975 status = gensec_subcontext_start(spnego_state,
977 &spnego_state->sub_sec_security);
978 if (!NT_STATUS_IS_OK(status)) {
983 /* select the sub context */
984 status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
986 if (!NT_STATUS_IS_OK(status)) {
988 * Pretend we never started it
990 gensec_spnego_update_sub_abort(spnego_state);
996 * Indicate the downgrade and request a
999 spnego_state->downgraded = true;
1000 spnego_state->mic_requested = true;
1001 /* no optimistic token */
1002 spnego_state->neg_oid = cur_sec->oid;
1003 sub_out = data_blob_null;
1004 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1009 * Try the optimistic token from the client
1011 sub_in = spnego_in->negTokenInit.mechToken;
1012 status = gensec_update_ev(spnego_state->sub_sec_security,
1013 frame, ev, sub_in, &sub_out);
1014 if (NT_STATUS_IS_OK(status)) {
1015 spnego_state->sub_sec_ready = true;
1017 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
1018 NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
1020 DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1021 cur_sec->op->name, nt_errstr(status));
1024 * Pretend we never started it
1026 gensec_spnego_update_sub_abort(spnego_state);
1030 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1031 DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1032 cur_sec->op->name, nt_errstr(status));
1036 spnego_state->neg_oid = cur_sec->oid;
1037 goto reply; /* OK or MORE PROCESSING */
1040 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1041 status = NT_STATUS_INVALID_PARAMETER;
1044 if (spnego_state->simulate_w2k) {
1046 * Windows 2000 returns the unwrapped token
1047 * also in the mech_list_mic field.
1049 * In order to verify our client code,
1050 * we need a way to have a server with this
1053 mech_list_mic = sub_out;
1056 status = gensec_spnego_server_response(spnego_state,
1066 static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
1067 struct spnego_state *spnego_state,
1068 struct tevent_context *ev,
1069 struct spnego_data *spnego_in,
1070 TALLOC_CTX *out_mem_ctx,
1073 const struct spnego_negTokenTarg *ta = &spnego_in->negTokenTarg;
1074 DATA_BLOB sub_in = ta->responseToken;
1075 DATA_BLOB mech_list_mic = data_blob_null;
1076 DATA_BLOB sub_out = data_blob_null;
1078 bool have_sign = true;
1079 bool new_spnego = false;
1081 spnego_state->num_targs++;
1083 if (spnego_state->sub_sec_security == NULL) {
1084 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1085 return NT_STATUS_INVALID_PARAMETER;
1088 if (spnego_state->needs_mic_check) {
1089 if (ta->responseToken.length != 0) {
1090 DBG_WARNING("non empty response token not expected\n");
1091 return NT_STATUS_INVALID_PARAMETER;
1094 status = gensec_check_packet(spnego_state->sub_sec_security,
1095 spnego_state->mech_types.data,
1096 spnego_state->mech_types.length,
1097 spnego_state->mech_types.data,
1098 spnego_state->mech_types.length,
1100 if (!NT_STATUS_IS_OK(status)) {
1101 DBG_WARNING("failed to verify mechListMIC: %s\n",
1103 goto server_response;
1106 spnego_state->needs_mic_check = false;
1107 spnego_state->done_mic_check = true;
1108 goto server_response;
1111 if (!spnego_state->sub_sec_ready) {
1112 status = gensec_update_ev(spnego_state->sub_sec_security,
1115 if (NT_STATUS_IS_OK(status)) {
1116 spnego_state->sub_sec_ready = true;
1118 if (!NT_STATUS_IS_OK(status)) {
1119 goto server_response;
1122 status = NT_STATUS_OK;
1125 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1126 GENSEC_FEATURE_SIGN);
1127 if (spnego_state->simulate_w2k) {
1130 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1131 GENSEC_FEATURE_NEW_SPNEGO);
1132 if (ta->mechListMIC.length > 0) {
1136 if (have_sign && new_spnego) {
1137 spnego_state->needs_mic_check = true;
1138 spnego_state->needs_mic_sign = true;
1141 if (have_sign && ta->mechListMIC.length > 0) {
1142 status = gensec_check_packet(spnego_state->sub_sec_security,
1143 spnego_state->mech_types.data,
1144 spnego_state->mech_types.length,
1145 spnego_state->mech_types.data,
1146 spnego_state->mech_types.length,
1148 if (!NT_STATUS_IS_OK(status)) {
1149 DBG_WARNING("failed to verify mechListMIC: %s\n",
1151 goto server_response;
1154 spnego_state->needs_mic_check = false;
1155 spnego_state->done_mic_check = true;
1158 if (spnego_state->needs_mic_sign) {
1159 status = gensec_sign_packet(spnego_state->sub_sec_security,
1161 spnego_state->mech_types.data,
1162 spnego_state->mech_types.length,
1163 spnego_state->mech_types.data,
1164 spnego_state->mech_types.length,
1166 if (!NT_STATUS_IS_OK(status)) {
1167 DBG_WARNING("failed to sign mechListMIC: %s\n",
1171 spnego_state->needs_mic_sign = false;
1174 if (spnego_state->needs_mic_check) {
1175 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1179 return gensec_spnego_server_response(spnego_state,
1187 struct gensec_spnego_update_state {
1188 struct tevent_context *ev;
1189 struct gensec_security *gensec;
1190 struct spnego_state *spnego;
1193 struct spnego_data _spnego_in;
1194 struct spnego_data *spnego_in;
1207 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1208 enum tevent_req_state req_state)
1210 struct gensec_spnego_update_state *state =
1211 tevent_req_data(req,
1212 struct gensec_spnego_update_state);
1214 switch (req_state) {
1215 case TEVENT_REQ_USER_ERROR:
1216 case TEVENT_REQ_TIMED_OUT:
1217 case TEVENT_REQ_NO_MEMORY:
1219 * A fatal error, further updates are not allowed.
1221 state->spnego->state_position = SPNEGO_DONE;
1228 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1229 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1230 DATA_BLOB *full_in);
1231 static void gensec_spnego_update_pre(struct tevent_req *req);
1232 static void gensec_spnego_update_done(struct tevent_req *subreq);
1233 static void gensec_spnego_update_post(struct tevent_req *req);
1234 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1235 TALLOC_CTX *out_mem_ctx,
1238 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1239 struct tevent_context *ev,
1240 struct gensec_security *gensec_security,
1243 struct spnego_state *spnego_state =
1244 talloc_get_type_abort(gensec_security->private_data,
1245 struct spnego_state);
1246 struct tevent_req *req = NULL;
1247 struct gensec_spnego_update_state *state = NULL;
1251 req = tevent_req_create(mem_ctx, &state,
1252 struct gensec_spnego_update_state);
1257 state->gensec = gensec_security;
1258 state->spnego = spnego_state;
1259 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1261 if (spnego_state->out_frag.length > 0) {
1262 if (in.length > 0) {
1263 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1264 return tevent_req_post(req, ev);
1267 status = gensec_spnego_update_out(gensec_security,
1268 state, &state->out);
1269 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1270 tevent_req_nterror(req, status);
1271 return tevent_req_post(req, ev);
1274 state->status = status;
1275 tevent_req_done(req);
1276 return tevent_req_post(req, ev);
1279 status = gensec_spnego_update_in(gensec_security, in,
1280 state, &state->full_in);
1281 state->status = status;
1282 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1283 tevent_req_done(req);
1284 return tevent_req_post(req, ev);
1286 if (tevent_req_nterror(req, status)) {
1287 return tevent_req_post(req, ev);
1290 /* Check if we got a valid SPNEGO blob... */
1292 switch (spnego_state->state_position) {
1293 case SPNEGO_FALLBACK:
1296 case SPNEGO_CLIENT_TARG:
1297 case SPNEGO_SERVER_TARG:
1298 if (state->full_in.length == 0) {
1299 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1300 return tevent_req_post(req, ev);
1304 case SPNEGO_CLIENT_START:
1305 case SPNEGO_SERVER_START:
1307 if (state->full_in.length == 0) {
1308 /* create_negTokenInit later */
1312 len = spnego_read_data(state,
1314 &state->_spnego_in);
1316 if (spnego_state->state_position != SPNEGO_SERVER_START) {
1317 DEBUG(1, ("Invalid SPNEGO request:\n"));
1318 dump_data(1, state->full_in.data,
1319 state->full_in.length);
1320 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1321 return tevent_req_post(req, ev);
1325 * This is the 'fallback' case, where we don't get
1326 * SPNEGO, and have to try all the other options (and
1327 * hope they all have a magic string they check)
1329 status = gensec_spnego_server_try_fallback(gensec_security,
1333 if (tevent_req_nterror(req, status)) {
1334 return tevent_req_post(req, ev);
1338 * We'll continue with SPNEGO_FALLBACK below...
1342 state->spnego_in = &state->_spnego_in;
1344 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1345 if (state->spnego_in->type != spnego_state->expected_packet) {
1346 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1347 state->spnego_in->type,
1348 spnego_state->expected_packet));
1349 dump_data(1, state->full_in.data,
1350 state->full_in.length);
1351 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1352 return tevent_req_post(req, ev);
1358 smb_panic(__location__);
1362 gensec_spnego_update_pre(req);
1363 if (!tevent_req_is_in_progress(req)) {
1364 return tevent_req_post(req, ev);
1367 if (state->sub.needed) {
1368 struct tevent_req *subreq = NULL;
1371 * We may need one more roundtrip...
1373 subreq = gensec_update_send(state, state->ev,
1374 spnego_state->sub_sec_security,
1376 if (tevent_req_nomem(subreq, req)) {
1377 return tevent_req_post(req, ev);
1379 tevent_req_set_callback(subreq,
1380 gensec_spnego_update_done,
1382 state->sub.needed = false;
1386 gensec_spnego_update_post(req);
1387 if (!tevent_req_is_in_progress(req)) {
1388 return tevent_req_post(req, ev);
1394 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1395 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1398 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1402 *full_in = data_blob_null;
1404 switch (spnego_state->state_position) {
1405 case SPNEGO_FALLBACK:
1407 spnego_state->in_needed = 0;
1408 return NT_STATUS_OK;
1410 case SPNEGO_CLIENT_START:
1411 case SPNEGO_CLIENT_TARG:
1412 case SPNEGO_SERVER_START:
1413 case SPNEGO_SERVER_TARG:
1418 return NT_STATUS_INVALID_PARAMETER;
1421 if (spnego_state->in_needed == 0) {
1426 * try to work out the size of the full
1427 * input token, it might be fragmented
1429 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1430 if ((ret != 0) && (ret != EAGAIN)) {
1431 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1434 if ((ret == 0) || (ret == EAGAIN)) {
1435 spnego_state->in_needed = size;
1438 * If it is not an asn1 message
1439 * just call the next layer.
1441 spnego_state->in_needed = in.length;
1445 if (spnego_state->in_needed > UINT16_MAX) {
1447 * limit the incoming message to 0xFFFF
1448 * to avoid DoS attacks.
1450 return NT_STATUS_INVALID_BUFFER_SIZE;
1453 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1455 * If we reach this, we know we got at least
1456 * part of an asn1 message, getting 0 means
1457 * the remote peer wants us to spin.
1459 return NT_STATUS_INVALID_PARAMETER;
1462 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1463 if (in.length > expected) {
1465 * we got more than expected
1467 return NT_STATUS_INVALID_PARAMETER;
1470 if (in.length == spnego_state->in_needed) {
1472 * if the in.length contains the full blob
1475 * Note: this implies spnego_state->in_frag.length == 0,
1476 * but we do not need to check this explicitly
1477 * because we already know that we did not get
1478 * more than expected.
1481 spnego_state->in_needed = 0;
1482 return NT_STATUS_OK;
1485 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1486 in.data, in.length);
1488 return NT_STATUS_NO_MEMORY;
1491 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1492 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1495 *full_in = spnego_state->in_frag;
1496 talloc_steal(mem_ctx, full_in->data);
1497 spnego_state->in_frag = data_blob_null;
1498 spnego_state->in_needed = 0;
1499 return NT_STATUS_OK;
1502 static void gensec_spnego_update_pre(struct tevent_req *req)
1504 struct gensec_spnego_update_state *state =
1505 tevent_req_data(req,
1506 struct gensec_spnego_update_state);
1507 struct gensec_security *gensec_security = state->gensec;
1508 struct spnego_state *spnego_state = state->spnego;
1509 struct tevent_context *ev = state->ev;
1512 state->sub.needed = false;
1513 state->sub.in = data_blob_null;
1514 state->sub.status = NT_STATUS_INTERNAL_ERROR;
1515 state->sub.out = data_blob_null;
1517 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1518 state->sub.in = state->full_in;
1519 state->full_in = data_blob_null;
1520 state->sub.needed = true;
1524 switch (spnego_state->state_position) {
1525 case SPNEGO_CLIENT_START:
1526 if (state->spnego_in == NULL) {
1527 /* client to produce negTokenInit */
1528 status = gensec_spnego_create_negTokenInit(gensec_security,
1529 spnego_state, state, ev,
1530 &spnego_state->out_frag);
1531 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1532 tevent_req_nterror(req, status);
1538 status = gensec_spnego_client_negTokenInit(gensec_security,
1540 state->spnego_in, state,
1541 &spnego_state->out_frag);
1544 case SPNEGO_CLIENT_TARG:
1545 status = gensec_spnego_client_negTokenTarg(gensec_security,
1547 state->spnego_in, state,
1548 &spnego_state->out_frag);
1549 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1550 tevent_req_nterror(req, status);
1555 case SPNEGO_SERVER_START:
1556 if (state->spnego_in == NULL) {
1557 /* server to produce negTokenInit */
1558 status = gensec_spnego_create_negTokenInit(gensec_security,
1559 spnego_state, state, ev,
1560 &spnego_state->out_frag);
1561 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1562 tevent_req_nterror(req, status);
1568 status = gensec_spnego_server_negTokenInit(gensec_security,
1570 state->spnego_in, state,
1571 &spnego_state->out_frag);
1572 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1573 tevent_req_nterror(req, status);
1578 case SPNEGO_SERVER_TARG:
1579 status = gensec_spnego_server_negTokenTarg(gensec_security,
1581 state->spnego_in, state,
1582 &spnego_state->out_frag);
1583 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1584 tevent_req_nterror(req, status);
1590 smb_panic(__location__);
1594 spnego_state->out_status = status;
1597 static void gensec_spnego_update_done(struct tevent_req *subreq)
1599 struct tevent_req *req =
1600 tevent_req_callback_data(subreq,
1602 struct gensec_spnego_update_state *state =
1603 tevent_req_data(req,
1604 struct gensec_spnego_update_state);
1605 struct spnego_state *spnego_state = state->spnego;
1607 state->sub.status = gensec_update_recv(subreq, state, &state->sub.out);
1608 TALLOC_FREE(subreq);
1609 if (NT_STATUS_IS_OK(state->sub.status)) {
1610 spnego_state->sub_sec_ready = true;
1613 gensec_spnego_update_post(req);
1616 static void gensec_spnego_update_post(struct tevent_req *req)
1618 struct gensec_spnego_update_state *state =
1619 tevent_req_data(req,
1620 struct gensec_spnego_update_state);
1621 struct spnego_state *spnego_state = state->spnego;
1624 state->sub.in = data_blob_null;
1625 state->sub.needed = false;
1627 if (spnego_state->state_position == SPNEGO_FALLBACK) {
1628 status = state->sub.status;
1629 spnego_state->out_frag = state->sub.out;
1630 talloc_steal(spnego_state, spnego_state->out_frag.data);
1631 state->sub.out = data_blob_null;
1636 * For now just handle the sync processing done
1637 * in gensec_spnego_update_pre()
1639 status = spnego_state->out_status;
1641 if (NT_STATUS_IS_OK(status)) {
1642 bool reset_full = true;
1644 reset_full = !spnego_state->done_mic_check;
1646 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1648 if (tevent_req_nterror(req, status)) {
1654 spnego_state->out_status = status;
1656 status = gensec_spnego_update_out(state->gensec,
1657 state, &state->out);
1658 if (GENSEC_UPDATE_IS_NTERROR(status)) {
1659 tevent_req_nterror(req, status);
1663 state->status = status;
1664 tevent_req_done(req);
1668 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1669 TALLOC_CTX *out_mem_ctx,
1672 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1673 DATA_BLOB out = data_blob_null;
1676 *_out = data_blob_null;
1678 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1680 * Fast path, we can deliver everything
1683 *_out = spnego_state->out_frag;
1684 if (spnego_state->out_frag.length > 0) {
1685 talloc_steal(out_mem_ctx, _out->data);
1686 spnego_state->out_frag = data_blob_null;
1689 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1690 return spnego_state->out_status;
1694 * We're completely done, further updates are not allowed.
1696 spnego_state->state_position = SPNEGO_DONE;
1697 return gensec_child_ready(gensec_security,
1698 spnego_state->sub_sec_security);
1701 out = spnego_state->out_frag;
1704 * copy the remaining bytes
1706 spnego_state->out_frag = data_blob_talloc(spnego_state,
1707 out.data + spnego_state->out_max_length,
1708 out.length - spnego_state->out_max_length);
1709 if (spnego_state->out_frag.data == NULL) {
1710 return NT_STATUS_NO_MEMORY;
1714 * truncate the buffer
1716 ok = data_blob_realloc(spnego_state, &out,
1717 spnego_state->out_max_length);
1719 return NT_STATUS_NO_MEMORY;
1722 talloc_steal(out_mem_ctx, out.data);
1724 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1727 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1728 TALLOC_CTX *out_mem_ctx,
1731 struct gensec_spnego_update_state *state =
1732 tevent_req_data(req,
1733 struct gensec_spnego_update_state);
1736 *out = data_blob_null;
1738 if (tevent_req_is_nterror(req, &status)) {
1739 tevent_req_received(req);
1744 talloc_steal(out_mem_ctx, state->out.data);
1745 status = state->status;
1746 tevent_req_received(req);
1750 static const char *gensec_spnego_oids[] = {
1755 static const struct gensec_security_ops gensec_spnego_security_ops = {
1757 .sasl_name = "GSS-SPNEGO",
1758 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1759 .oid = gensec_spnego_oids,
1760 .client_start = gensec_spnego_client_start,
1761 .server_start = gensec_spnego_server_start,
1762 .update_send = gensec_spnego_update_send,
1763 .update_recv = gensec_spnego_update_recv,
1764 .seal_packet = gensec_child_seal_packet,
1765 .sign_packet = gensec_child_sign_packet,
1766 .sig_size = gensec_child_sig_size,
1767 .max_wrapped_size = gensec_child_max_wrapped_size,
1768 .max_input_size = gensec_child_max_input_size,
1769 .check_packet = gensec_child_check_packet,
1770 .unseal_packet = gensec_child_unseal_packet,
1771 .wrap = gensec_child_wrap,
1772 .unwrap = gensec_child_unwrap,
1773 .session_key = gensec_child_session_key,
1774 .session_info = gensec_child_session_info,
1775 .want_feature = gensec_child_want_feature,
1776 .have_feature = gensec_child_have_feature,
1777 .expire_time = gensec_child_expire_time,
1778 .final_auth_type = gensec_child_final_auth_type,
1780 .priority = GENSEC_SPNEGO
1783 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1786 ret = gensec_register(ctx, &gensec_spnego_security_ops);
1787 if (!NT_STATUS_IS_OK(ret)) {
1788 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1789 gensec_spnego_security_ops.name));