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,
143 struct tevent_context *ev,
144 TALLOC_CTX *out_mem_ctx,
145 const DATA_BLOB in, DATA_BLOB *out)
148 const struct gensec_security_ops **all_ops;
150 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
152 for (i=0; all_ops && all_ops[i]; i++) {
156 if (gensec_security != NULL &&
157 !gensec_security_ops_enabled(all_ops[i], gensec_security))
160 if (!all_ops[i]->oid) {
165 for (j=0; all_ops[i]->oid[j]; j++) {
166 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
174 if (!all_ops[i]->magic) {
178 nt_status = all_ops[i]->magic(gensec_security, &in);
179 if (!NT_STATUS_IS_OK(nt_status)) {
183 spnego_state->state_position = SPNEGO_FALLBACK;
185 nt_status = gensec_subcontext_start(spnego_state,
187 &spnego_state->sub_sec_security);
189 if (!NT_STATUS_IS_OK(nt_status)) {
192 /* select the sub context */
193 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
195 if (!NT_STATUS_IS_OK(nt_status)) {
198 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
199 out_mem_ctx, ev, in, out);
202 DEBUG(1, ("Failed to parse SPNEGO request\n"));
203 return NT_STATUS_INVALID_PARAMETER;
207 Parse the netTokenInit, either from the client, to the server, or
208 from the server to the client.
211 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
212 struct spnego_state *spnego_state,
213 TALLOC_CTX *out_mem_ctx,
214 struct tevent_context *ev,
215 const char * const *mechType,
216 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
219 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
222 const struct gensec_security_ops_wrapper *all_sec
223 = gensec_security_by_oid_list(gensec_security,
228 ok = spnego_write_mech_types(spnego_state,
230 &spnego_state->mech_types);
232 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
233 return NT_STATUS_NO_MEMORY;
236 if (spnego_state->state_position == SPNEGO_SERVER_START) {
238 for (j=0; mechType && mechType[j]; j++) {
239 for (i=0; all_sec && all_sec[i].op; i++) {
240 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
244 nt_status = gensec_subcontext_start(spnego_state,
246 &spnego_state->sub_sec_security);
247 if (!NT_STATUS_IS_OK(nt_status)) {
250 /* select the sub context */
251 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
253 if (!NT_STATUS_IS_OK(nt_status)) {
255 * Pretend we never started it
257 gensec_spnego_update_sub_abort(spnego_state);
262 /* no optimistic token */
263 spnego_state->neg_oid = all_sec[i].oid;
264 *unwrapped_out = data_blob_null;
265 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
267 * Indicate the downgrade and request a
270 spnego_state->downgraded = true;
271 spnego_state->mic_requested = true;
275 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
280 if (NT_STATUS_IS_OK(nt_status)) {
281 spnego_state->sub_sec_ready = true;
283 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
284 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
286 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
287 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
290 * Pretend we never started it
292 gensec_spnego_update_sub_abort(spnego_state);
296 spnego_state->neg_oid = all_sec[i].oid;
299 if (spnego_state->sub_sec_security) {
304 if (!spnego_state->sub_sec_security) {
305 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
306 return NT_STATUS_INVALID_PARAMETER;
310 /* Having tried any optimistic token from the client (if we
311 * were the server), if we didn't get anywhere, walk our list
312 * in our preference order */
314 if (!spnego_state->sub_sec_security) {
315 for (i=0; all_sec && all_sec[i].op; i++) {
316 nt_status = gensec_subcontext_start(spnego_state,
318 &spnego_state->sub_sec_security);
319 if (!NT_STATUS_IS_OK(nt_status)) {
322 /* select the sub context */
323 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
325 if (!NT_STATUS_IS_OK(nt_status)) {
327 * Pretend we never started it.
329 gensec_spnego_update_sub_abort(spnego_state);
333 spnego_state->neg_oid = all_sec[i].oid;
335 /* only get the helping start blob for the first OID */
336 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
341 if (NT_STATUS_IS_OK(nt_status)) {
342 spnego_state->sub_sec_ready = true;
345 /* it is likely that a NULL input token will
346 * not be liked by most server mechs, but if
347 * we are in the client, we want the first
348 * update packet to be able to abort the use
350 if (spnego_state->state_position != SPNEGO_SERVER_START) {
351 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
352 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
353 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
354 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
355 const char *next = NULL;
356 const char *principal = NULL;
357 int dbg_level = DBGLVL_WARNING;
359 if (all_sec[i+1].op != NULL) {
360 next = all_sec[i+1].op->name;
361 dbg_level = DBGLVL_NOTICE;
364 if (gensec_security->target.principal != NULL) {
365 principal = gensec_security->target.principal;
366 } else if (gensec_security->target.service != NULL &&
367 gensec_security->target.hostname != NULL)
369 principal = talloc_asprintf(spnego_state->sub_sec_security,
371 gensec_security->target.service,
372 gensec_security->target.hostname);
374 principal = gensec_security->target.hostname;
377 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
378 spnego_state->sub_sec_security->ops->name,
380 next, nt_errstr(nt_status)));
383 * Pretend we never started it.
385 gensec_spnego_update_sub_abort(spnego_state);
394 if (spnego_state->sub_sec_security) {
395 /* it is likely that a NULL input token will
396 * not be liked by most server mechs, but this
397 * does the right thing in the CIFS client.
398 * just push us along the merry-go-round
399 * again, and hope for better luck next
402 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
403 *unwrapped_out = data_blob_null;
404 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
407 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
408 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
409 && !NT_STATUS_IS_OK(nt_status)) {
410 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
411 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
413 /* We started the mech correctly, and the
414 * input from the other side was valid.
415 * Return the error (say bad password, invalid
417 gensec_spnego_update_sub_abort(spnego_state);
421 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
424 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
425 /* we could re-negotiate here, but it would only work
426 * if the client or server lied about what it could
427 * support the first time. Lets keep this code to
433 /** create a negTokenInit
435 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
437 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
438 struct spnego_state *spnego_state,
439 TALLOC_CTX *out_mem_ctx,
440 struct tevent_context *ev,
444 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
445 const char **mechTypes = NULL;
446 DATA_BLOB unwrapped_out = data_blob_null;
447 const struct gensec_security_ops_wrapper *all_sec;
449 mechTypes = gensec_security_oids(gensec_security,
450 out_mem_ctx, GENSEC_OID_SPNEGO);
452 all_sec = gensec_security_by_oid_list(gensec_security,
456 for (i=0; all_sec && all_sec[i].op; i++) {
457 struct spnego_data spnego_out;
458 const char **send_mech_types;
461 nt_status = gensec_subcontext_start(spnego_state,
463 &spnego_state->sub_sec_security);
464 if (!NT_STATUS_IS_OK(nt_status)) {
467 /* select the sub context */
468 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
470 if (!NT_STATUS_IS_OK(nt_status)) {
471 gensec_spnego_update_sub_abort(spnego_state);
475 /* In the client, try and produce the first (optimistic) packet */
476 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
477 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
482 if (NT_STATUS_IS_OK(nt_status)) {
483 spnego_state->sub_sec_ready = true;
486 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
487 && !NT_STATUS_IS_OK(nt_status)) {
488 const char *next = NULL;
489 const char *principal = NULL;
490 int dbg_level = DBGLVL_WARNING;
492 if (all_sec[i+1].op != NULL) {
493 next = all_sec[i+1].op->name;
494 dbg_level = DBGLVL_NOTICE;
497 if (gensec_security->target.principal != NULL) {
498 principal = gensec_security->target.principal;
499 } else if (gensec_security->target.service != NULL &&
500 gensec_security->target.hostname != NULL)
502 principal = talloc_asprintf(spnego_state->sub_sec_security,
504 gensec_security->target.service,
505 gensec_security->target.hostname);
507 principal = gensec_security->target.hostname;
510 DEBUG(dbg_level, ("SPNEGO(%s) creating NEG_TOKEN_INIT for %s failed (next[%s]): %s\n",
511 spnego_state->sub_sec_security->ops->name,
513 next, nt_errstr(nt_status)));
516 * Pretend we never started it
518 gensec_spnego_update_sub_abort(spnego_state);
523 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
525 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
528 ok = spnego_write_mech_types(spnego_state,
530 &spnego_state->mech_types);
532 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
533 return NT_STATUS_NO_MEMORY;
536 /* List the remaining mechs as options */
537 spnego_out.negTokenInit.mechTypes = send_mech_types;
538 spnego_out.negTokenInit.reqFlags = data_blob_null;
539 spnego_out.negTokenInit.reqFlagsPadding = 0;
541 if (spnego_state->state_position == SPNEGO_SERVER_START) {
542 spnego_out.negTokenInit.mechListMIC
543 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
545 spnego_out.negTokenInit.mechListMIC = data_blob_null;
548 spnego_out.negTokenInit.mechToken = unwrapped_out;
550 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
551 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
552 return NT_STATUS_INVALID_PARAMETER;
556 spnego_state->neg_oid = all_sec[i].oid;
558 if (spnego_state->state_position == SPNEGO_SERVER_START) {
559 spnego_state->state_position = SPNEGO_SERVER_START;
560 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
562 spnego_state->state_position = SPNEGO_CLIENT_TARG;
563 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
566 return NT_STATUS_MORE_PROCESSING_REQUIRED;
568 gensec_spnego_update_sub_abort(spnego_state);
570 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
575 /** create a server negTokenTarg
577 * This is the case, where the client is the first one who sends data
580 static NTSTATUS gensec_spnego_server_response(struct spnego_state *spnego_state,
581 TALLOC_CTX *out_mem_ctx,
583 const DATA_BLOB unwrapped_out,
584 DATA_BLOB mech_list_mic,
587 struct spnego_data spnego_out;
590 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
591 spnego_out.negTokenTarg.responseToken = unwrapped_out;
592 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
593 spnego_out.negTokenTarg.supportedMech = NULL;
595 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
596 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
597 if (spnego_state->mic_requested) {
598 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
599 spnego_state->mic_requested = false;
601 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
603 spnego_state->state_position = SPNEGO_SERVER_TARG;
604 } else if (NT_STATUS_IS_OK(nt_status)) {
605 if (unwrapped_out.data) {
606 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
608 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
609 spnego_state->state_position = SPNEGO_DONE;
611 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
612 spnego_out.negTokenTarg.mechListMIC = data_blob_null;
613 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
614 spnego_state->state_position = SPNEGO_DONE;
617 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
618 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
619 return NT_STATUS_INVALID_PARAMETER;
622 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
623 spnego_state->num_targs++;
628 static NTSTATUS gensec_spnego_update_client(struct gensec_security *gensec_security,
629 TALLOC_CTX *out_mem_ctx,
630 struct tevent_context *ev,
631 const DATA_BLOB in, DATA_BLOB *out)
633 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
634 DATA_BLOB mech_list_mic = data_blob_null;
635 DATA_BLOB unwrapped_out = data_blob_null;
636 struct spnego_data spnego_out;
637 struct spnego_data spnego;
640 *out = data_blob_null;
642 /* and switch into the state machine */
644 switch (spnego_state->state_position) {
645 case SPNEGO_CLIENT_START:
647 /* The server offers a list of mechanisms */
649 const char *my_mechs[] = {NULL, NULL};
650 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
652 const char *tp = NULL;
655 /* client to produce negTokenInit */
656 return gensec_spnego_create_negTokenInit(gensec_security,
662 len = spnego_read_data(gensec_security, in, &spnego);
665 DEBUG(1, ("Invalid SPNEGO request:\n"));
666 dump_data(1, in.data, in.length);
667 return NT_STATUS_INVALID_PARAMETER;
670 /* OK, so it's real SPNEGO, check the packet's the one we expect */
671 if (spnego.type != spnego_state->expected_packet) {
672 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
673 spnego_state->expected_packet));
674 dump_data(1, in.data, in.length);
675 spnego_free_data(&spnego);
676 return NT_STATUS_INVALID_PARAMETER;
679 tp = spnego.negTokenInit.targetPrincipal;
680 if (tp != NULL && strcmp(tp, ADS_IGNORE_PRINCIPAL) != 0) {
681 DEBUG(5, ("Server claims it's principal name is %s\n", tp));
682 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
683 gensec_set_target_principal(gensec_security, tp);
687 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
691 spnego.negTokenInit.mechTypes,
692 spnego.negTokenInit.mechToken,
695 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
696 spnego_free_data(&spnego);
700 my_mechs[0] = spnego_state->neg_oid;
702 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
703 spnego_out.negTokenInit.mechTypes = my_mechs;
704 spnego_out.negTokenInit.reqFlags = data_blob_null;
705 spnego_out.negTokenInit.reqFlagsPadding = 0;
706 spnego_out.negTokenInit.mechListMIC = data_blob_null;
707 spnego_out.negTokenInit.mechToken = unwrapped_out;
709 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
710 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
711 return NT_STATUS_INVALID_PARAMETER;
714 ok = spnego_write_mech_types(spnego_state,
716 &spnego_state->mech_types);
718 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
719 return NT_STATUS_NO_MEMORY;
723 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
724 spnego_state->state_position = SPNEGO_CLIENT_TARG;
726 spnego_free_data(&spnego);
727 return NT_STATUS_MORE_PROCESSING_REQUIRED;
730 case SPNEGO_CLIENT_TARG:
732 NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
733 const struct spnego_negTokenTarg *ta = NULL;
736 return NT_STATUS_INVALID_PARAMETER;
739 len = spnego_read_data(gensec_security, in, &spnego);
742 DEBUG(1, ("Invalid SPNEGO request:\n"));
743 dump_data(1, in.data, in.length);
744 return NT_STATUS_INVALID_PARAMETER;
747 /* OK, so it's real SPNEGO, check the packet's the one we expect */
748 if (spnego.type != spnego_state->expected_packet) {
749 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
750 spnego_state->expected_packet));
751 dump_data(1, in.data, in.length);
752 spnego_free_data(&spnego);
753 return NT_STATUS_INVALID_PARAMETER;
755 ta = &spnego.negTokenTarg;
757 spnego_state->num_targs++;
759 if (ta->negResult == SPNEGO_REJECT) {
760 return NT_STATUS_LOGON_FAILURE;
763 if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
764 spnego_state->mic_requested = true;
767 /* Server didn't like our choice of mech, and chose something else */
768 if (((ta->negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
769 (ta->negResult == SPNEGO_REQUEST_MIC)) &&
770 ta->supportedMech != NULL&&
771 strcmp(ta->supportedMech, spnego_state->neg_oid) != 0) {
772 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
773 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid),
774 gensec_get_name_by_oid(gensec_security, ta->supportedMech)));
775 spnego_state->downgraded = true;
776 gensec_spnego_update_sub_abort(spnego_state);
777 nt_status = gensec_subcontext_start(spnego_state,
779 &spnego_state->sub_sec_security);
780 if (!NT_STATUS_IS_OK(nt_status)) {
781 spnego_free_data(&spnego);
784 /* select the sub context */
785 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
787 if (!NT_STATUS_IS_OK(nt_status)) {
788 spnego_free_data(&spnego);
792 spnego_state->neg_oid = talloc_strdup(spnego_state,
794 if (spnego_state->neg_oid == NULL) {
795 spnego_free_data(&spnego);
796 return NT_STATUS_NO_MEMORY;
800 if (spnego.negTokenTarg.mechListMIC.length > 0) {
801 DATA_BLOB *m = &spnego.negTokenTarg.mechListMIC;
802 const DATA_BLOB *r = &spnego.negTokenTarg.responseToken;
805 * Windows 2000 has a bug, it repeats the
806 * responseToken in the mechListMIC field.
808 if (m->length == r->length) {
811 cmp = memcmp(m->data, r->data, m->length);
818 if (spnego.negTokenTarg.mechListMIC.length > 0) {
819 if (spnego_state->sub_sec_ready) {
820 spnego_state->needs_mic_check = true;
824 if (spnego_state->needs_mic_check) {
825 if (spnego.negTokenTarg.responseToken.length != 0) {
826 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
827 spnego_free_data(&spnego);
828 return NT_STATUS_INVALID_PARAMETER;
831 if (spnego.negTokenTarg.mechListMIC.length == 0
832 && spnego_state->may_skip_mic_check) {
834 * In this case we don't require
835 * a mechListMIC from the server.
837 * This works around bugs in the Azure
838 * and Apple spnego implementations.
841 * https://bugzilla.samba.org/show_bug.cgi?id=11994
843 spnego_state->needs_mic_check = false;
844 nt_status = NT_STATUS_OK;
845 goto client_response;
848 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
849 spnego_state->mech_types.data,
850 spnego_state->mech_types.length,
851 spnego_state->mech_types.data,
852 spnego_state->mech_types.length,
853 &spnego.negTokenTarg.mechListMIC);
854 if (!NT_STATUS_IS_OK(nt_status)) {
855 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
856 nt_errstr(nt_status)));
857 spnego_free_data(&spnego);
860 spnego_state->needs_mic_check = false;
861 spnego_state->done_mic_check = true;
862 goto client_response;
865 if (!spnego_state->sub_sec_ready) {
866 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
868 spnego.negTokenTarg.responseToken,
870 if (NT_STATUS_IS_OK(nt_status)) {
871 spnego_state->sub_sec_ready = true;
873 if (!NT_STATUS_IS_OK(nt_status)) {
874 goto client_response;
877 nt_status = NT_STATUS_OK;
880 if (!spnego_state->done_mic_check) {
881 bool have_sign = true;
882 bool new_spnego = false;
884 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
885 GENSEC_FEATURE_SIGN);
886 if (spnego_state->simulate_w2k) {
889 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
890 GENSEC_FEATURE_NEW_SPNEGO);
892 switch (spnego.negTokenTarg.negResult) {
893 case SPNEGO_ACCEPT_COMPLETED:
894 case SPNEGO_NONE_RESULT:
895 if (spnego_state->num_targs == 1) {
897 * the first exchange doesn't require
905 case SPNEGO_ACCEPT_INCOMPLETE:
906 if (spnego.negTokenTarg.mechListMIC.length > 0) {
911 if (spnego_state->downgraded) {
913 * A downgrade should be protected if
920 * The caller may just asked for
921 * GENSEC_FEATURE_SESSION_KEY, this
922 * is only reflected in the want_features.
925 * gensec_have_features(GENSEC_FEATURE_SIGN)
928 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
931 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
935 * Here we're sure our preferred mech was
936 * selected by the server and our caller doesn't
937 * need GENSEC_FEATURE_SIGN nor
938 * GENSEC_FEATURE_SEAL support.
940 * In this case we don't require
941 * a mechListMIC from the server.
943 * This works around bugs in the Azure
944 * and Apple spnego implementations.
947 * https://bugzilla.samba.org/show_bug.cgi?id=11994
949 spnego_state->may_skip_mic_check = true;
952 case SPNEGO_REQUEST_MIC:
953 if (spnego.negTokenTarg.mechListMIC.length > 0) {
961 if (spnego_state->mic_requested) {
967 if (have_sign && new_spnego) {
968 spnego_state->needs_mic_check = true;
969 spnego_state->needs_mic_sign = true;
973 if (spnego.negTokenTarg.mechListMIC.length > 0) {
974 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
975 spnego_state->mech_types.data,
976 spnego_state->mech_types.length,
977 spnego_state->mech_types.data,
978 spnego_state->mech_types.length,
979 &spnego.negTokenTarg.mechListMIC);
980 if (!NT_STATUS_IS_OK(nt_status)) {
981 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
982 nt_errstr(nt_status)));
983 spnego_free_data(&spnego);
986 spnego_state->needs_mic_check = false;
987 spnego_state->done_mic_check = true;
990 if (spnego_state->needs_mic_sign) {
991 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
993 spnego_state->mech_types.data,
994 spnego_state->mech_types.length,
995 spnego_state->mech_types.data,
996 spnego_state->mech_types.length,
998 if (!NT_STATUS_IS_OK(nt_status)) {
999 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1000 nt_errstr(nt_status)));
1001 spnego_free_data(&spnego);
1004 spnego_state->needs_mic_sign = false;
1007 if (spnego_state->needs_mic_check) {
1008 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1012 spnego_free_data(&spnego);
1014 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1015 && !NT_STATUS_IS_OK(nt_status)) {
1016 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1017 spnego_state->sub_sec_security->ops->name,
1018 nt_errstr(nt_status)));
1022 if (unwrapped_out.length || mech_list_mic.length) {
1024 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1025 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1026 spnego_out.negTokenTarg.supportedMech = NULL;
1027 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1028 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1030 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1031 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1032 return NT_STATUS_INVALID_PARAMETER;
1035 spnego_state->num_targs++;
1036 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1037 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1040 /* all done - server has accepted, and we agree */
1041 *out = data_blob_null;
1043 if (ta->negResult != SPNEGO_ACCEPT_COMPLETED) {
1044 /* unless of course it did not accept */
1045 DEBUG(1,("gensec_update ok but not accepted\n"));
1046 nt_status = NT_STATUS_INVALID_PARAMETER;
1049 spnego_state->state_position = SPNEGO_DONE;
1059 smb_panic(__location__);
1060 return NT_STATUS_INTERNAL_ERROR;
1063 static NTSTATUS gensec_spnego_update_server(struct gensec_security *gensec_security,
1064 TALLOC_CTX *out_mem_ctx,
1065 struct tevent_context *ev,
1066 const DATA_BLOB in, DATA_BLOB *out)
1068 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1069 DATA_BLOB mech_list_mic = data_blob_null;
1070 DATA_BLOB unwrapped_out = data_blob_null;
1071 struct spnego_data spnego;
1074 /* and switch into the state machine */
1076 switch (spnego_state->state_position) {
1077 case SPNEGO_SERVER_START:
1081 if (in.length == 0) {
1082 return gensec_spnego_create_negTokenInit(gensec_security,
1088 len = spnego_read_data(gensec_security, in, &spnego);
1090 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
1091 ev, out_mem_ctx, in, out);
1093 /* client sent NegTargetInit, we send NegTokenTarg */
1095 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1096 if (spnego.type != spnego_state->expected_packet) {
1097 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
1098 spnego_state->expected_packet));
1099 dump_data(1, in.data, in.length);
1100 spnego_free_data(&spnego);
1101 return NT_STATUS_INVALID_PARAMETER;
1104 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
1108 spnego.negTokenInit.mechTypes,
1109 spnego.negTokenInit.mechToken,
1112 if (spnego_state->simulate_w2k) {
1114 * Windows 2000 returns the unwrapped token
1115 * also in the mech_list_mic field.
1117 * In order to verify our client code,
1118 * we need a way to have a server with this
1121 mech_list_mic = unwrapped_out;
1124 nt_status = gensec_spnego_server_response(spnego_state,
1131 spnego_free_data(&spnego);
1136 case SPNEGO_SERVER_TARG:
1139 bool have_sign = true;
1140 bool new_spnego = false;
1143 return NT_STATUS_INVALID_PARAMETER;
1146 len = spnego_read_data(gensec_security, in, &spnego);
1149 DEBUG(1, ("Invalid SPNEGO request:\n"));
1150 dump_data(1, in.data, in.length);
1151 return NT_STATUS_INVALID_PARAMETER;
1154 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1155 if (spnego.type != spnego_state->expected_packet) {
1156 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
1157 spnego_state->expected_packet));
1158 dump_data(1, in.data, in.length);
1159 spnego_free_data(&spnego);
1160 return NT_STATUS_INVALID_PARAMETER;
1163 spnego_state->num_targs++;
1165 if (!spnego_state->sub_sec_security) {
1166 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1167 spnego_free_data(&spnego);
1168 return NT_STATUS_INVALID_PARAMETER;
1171 if (spnego_state->needs_mic_check) {
1172 if (spnego.negTokenTarg.responseToken.length != 0) {
1173 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1174 spnego_free_data(&spnego);
1175 return NT_STATUS_INVALID_PARAMETER;
1178 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1179 spnego_state->mech_types.data,
1180 spnego_state->mech_types.length,
1181 spnego_state->mech_types.data,
1182 spnego_state->mech_types.length,
1183 &spnego.negTokenTarg.mechListMIC);
1184 if (NT_STATUS_IS_OK(nt_status)) {
1185 spnego_state->needs_mic_check = false;
1186 spnego_state->done_mic_check = true;
1188 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1189 nt_errstr(nt_status)));
1191 goto server_response;
1194 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1196 spnego.negTokenTarg.responseToken,
1198 if (NT_STATUS_IS_OK(nt_status)) {
1199 spnego_state->sub_sec_ready = true;
1201 if (!NT_STATUS_IS_OK(nt_status)) {
1202 goto server_response;
1205 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1206 GENSEC_FEATURE_SIGN);
1207 if (spnego_state->simulate_w2k) {
1210 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1211 GENSEC_FEATURE_NEW_SPNEGO);
1212 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1216 if (have_sign && new_spnego) {
1217 spnego_state->needs_mic_check = true;
1218 spnego_state->needs_mic_sign = true;
1221 if (have_sign && spnego.negTokenTarg.mechListMIC.length > 0) {
1222 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1223 spnego_state->mech_types.data,
1224 spnego_state->mech_types.length,
1225 spnego_state->mech_types.data,
1226 spnego_state->mech_types.length,
1227 &spnego.negTokenTarg.mechListMIC);
1228 if (!NT_STATUS_IS_OK(nt_status)) {
1229 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1230 nt_errstr(nt_status)));
1231 goto server_response;
1234 spnego_state->needs_mic_check = false;
1235 spnego_state->done_mic_check = true;
1238 if (spnego_state->needs_mic_sign) {
1239 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1241 spnego_state->mech_types.data,
1242 spnego_state->mech_types.length,
1243 spnego_state->mech_types.data,
1244 spnego_state->mech_types.length,
1246 if (!NT_STATUS_IS_OK(nt_status)) {
1247 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1248 nt_errstr(nt_status)));
1249 goto server_response;
1251 spnego_state->needs_mic_sign = false;
1254 if (spnego_state->needs_mic_check) {
1255 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1259 nt_status = gensec_spnego_server_response(spnego_state,
1266 spnego_free_data(&spnego);
1275 smb_panic(__location__);
1276 return NT_STATUS_INTERNAL_ERROR;
1279 struct gensec_spnego_update_state {
1280 struct gensec_security *gensec;
1281 struct spnego_state *spnego;
1287 static void gensec_spnego_update_cleanup(struct tevent_req *req,
1288 enum tevent_req_state req_state)
1290 struct gensec_spnego_update_state *state =
1291 tevent_req_data(req,
1292 struct gensec_spnego_update_state);
1294 switch (req_state) {
1295 case TEVENT_REQ_USER_ERROR:
1296 case TEVENT_REQ_TIMED_OUT:
1297 case TEVENT_REQ_NO_MEMORY:
1299 * A fatal error, further updates are not allowed.
1301 state->spnego->state_position = SPNEGO_DONE;
1308 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1309 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1310 DATA_BLOB *full_in);
1311 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1312 TALLOC_CTX *out_mem_ctx,
1315 static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
1316 struct tevent_context *ev,
1317 struct gensec_security *gensec_security,
1320 struct spnego_state *spnego_state =
1321 talloc_get_type_abort(gensec_security->private_data,
1322 struct spnego_state);
1323 struct tevent_req *req = NULL;
1324 struct gensec_spnego_update_state *state = NULL;
1327 req = tevent_req_create(mem_ctx, &state,
1328 struct gensec_spnego_update_state);
1332 state->gensec = gensec_security;
1333 state->spnego = spnego_state;
1334 tevent_req_set_cleanup_fn(req, gensec_spnego_update_cleanup);
1336 if (spnego_state->out_frag.length > 0) {
1337 if (in.length > 0) {
1338 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
1339 return tevent_req_post(req, ev);
1342 status = gensec_spnego_update_out(gensec_security,
1343 state, &state->out);
1344 state->status = status;
1345 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1346 tevent_req_done(req);
1347 return tevent_req_post(req, ev);
1349 if (tevent_req_nterror(req, status)) {
1350 return tevent_req_post(req, ev);
1353 tevent_req_done(req);
1354 return tevent_req_post(req, ev);
1357 status = gensec_spnego_update_in(gensec_security, in,
1358 state, &state->full_in);
1359 state->status = status;
1360 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1361 tevent_req_done(req);
1362 return tevent_req_post(req, ev);
1364 if (tevent_req_nterror(req, status)) {
1365 return tevent_req_post(req, ev);
1368 /* and switch into the state machine */
1370 switch (spnego_state->state_position) {
1371 case SPNEGO_FALLBACK:
1372 status = gensec_update_ev(spnego_state->sub_sec_security,
1375 &spnego_state->out_frag);
1378 case SPNEGO_CLIENT_START:
1379 case SPNEGO_CLIENT_TARG:
1380 status = gensec_spnego_update_client(gensec_security,
1383 &spnego_state->out_frag);
1386 case SPNEGO_SERVER_START:
1387 case SPNEGO_SERVER_TARG:
1388 status = gensec_spnego_update_server(gensec_security,
1391 &spnego_state->out_frag);
1395 smb_panic(__location__);
1399 if (NT_STATUS_IS_OK(status)) {
1400 bool reset_full = true;
1402 reset_full = !spnego_state->done_mic_check;
1404 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1407 if (!NT_STATUS_IS_OK(status) &&
1408 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1409 tevent_req_nterror(req, status);
1410 return tevent_req_post(req, ev);
1413 spnego_state->out_status = status;
1415 status = gensec_spnego_update_out(gensec_security,
1416 state, &state->out);
1417 state->status = status;
1418 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1419 tevent_req_done(req);
1420 return tevent_req_post(req, ev);
1422 if (tevent_req_nterror(req, status)) {
1423 return tevent_req_post(req, ev);
1426 tevent_req_done(req);
1427 return tevent_req_post(req, ev);
1430 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1431 const DATA_BLOB in, TALLOC_CTX *mem_ctx,
1434 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1438 *full_in = data_blob_null;
1440 switch (spnego_state->state_position) {
1441 case SPNEGO_FALLBACK:
1443 spnego_state->in_needed = 0;
1444 return NT_STATUS_OK;
1446 case SPNEGO_CLIENT_START:
1447 case SPNEGO_CLIENT_TARG:
1448 case SPNEGO_SERVER_START:
1449 case SPNEGO_SERVER_TARG:
1454 return NT_STATUS_INVALID_PARAMETER;
1457 if (spnego_state->in_needed == 0) {
1462 * try to work out the size of the full
1463 * input token, it might be fragmented
1465 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1466 if ((ret != 0) && (ret != EAGAIN)) {
1467 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1470 if ((ret == 0) || (ret == EAGAIN)) {
1471 spnego_state->in_needed = size;
1474 * If it is not an asn1 message
1475 * just call the next layer.
1477 spnego_state->in_needed = in.length;
1481 if (spnego_state->in_needed > UINT16_MAX) {
1483 * limit the incoming message to 0xFFFF
1484 * to avoid DoS attacks.
1486 return NT_STATUS_INVALID_BUFFER_SIZE;
1489 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1491 * If we reach this, we know we got at least
1492 * part of an asn1 message, getting 0 means
1493 * the remote peer wants us to spin.
1495 return NT_STATUS_INVALID_PARAMETER;
1498 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1499 if (in.length > expected) {
1501 * we got more than expected
1503 return NT_STATUS_INVALID_PARAMETER;
1506 if (in.length == spnego_state->in_needed) {
1508 * if the in.length contains the full blob
1511 * Note: this implies spnego_state->in_frag.length == 0,
1512 * but we do not need to check this explicitly
1513 * because we already know that we did not get
1514 * more than expected.
1517 spnego_state->in_needed = 0;
1518 return NT_STATUS_OK;
1521 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1522 in.data, in.length);
1524 return NT_STATUS_NO_MEMORY;
1527 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1528 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1531 *full_in = spnego_state->in_frag;
1532 talloc_steal(mem_ctx, full_in->data);
1533 spnego_state->in_frag = data_blob_null;
1534 spnego_state->in_needed = 0;
1535 return NT_STATUS_OK;
1538 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1539 TALLOC_CTX *out_mem_ctx,
1542 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1543 DATA_BLOB out = data_blob_null;
1546 *_out = data_blob_null;
1548 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1550 * Fast path, we can deliver everything
1553 *_out = spnego_state->out_frag;
1554 if (spnego_state->out_frag.length > 0) {
1555 talloc_steal(out_mem_ctx, _out->data);
1556 spnego_state->out_frag = data_blob_null;
1559 if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
1560 return spnego_state->out_status;
1564 * We're completely done, further updates are not allowed.
1566 spnego_state->state_position = SPNEGO_DONE;
1567 return gensec_child_ready(gensec_security,
1568 spnego_state->sub_sec_security);
1571 out = spnego_state->out_frag;
1574 * copy the remaining bytes
1576 spnego_state->out_frag = data_blob_talloc(spnego_state,
1577 out.data + spnego_state->out_max_length,
1578 out.length - spnego_state->out_max_length);
1579 if (spnego_state->out_frag.data == NULL) {
1580 return NT_STATUS_NO_MEMORY;
1584 * truncate the buffer
1586 ok = data_blob_realloc(spnego_state, &out,
1587 spnego_state->out_max_length);
1589 return NT_STATUS_NO_MEMORY;
1592 talloc_steal(out_mem_ctx, out.data);
1594 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1597 static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
1598 TALLOC_CTX *out_mem_ctx,
1601 struct gensec_spnego_update_state *state =
1602 tevent_req_data(req,
1603 struct gensec_spnego_update_state);
1606 *out = data_blob_null;
1608 if (tevent_req_is_nterror(req, &status)) {
1609 tevent_req_received(req);
1614 talloc_steal(out_mem_ctx, state->out.data);
1615 status = state->status;
1616 tevent_req_received(req);
1620 static const char *gensec_spnego_oids[] = {
1625 static const struct gensec_security_ops gensec_spnego_security_ops = {
1627 .sasl_name = "GSS-SPNEGO",
1628 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1629 .oid = gensec_spnego_oids,
1630 .client_start = gensec_spnego_client_start,
1631 .server_start = gensec_spnego_server_start,
1632 .update_send = gensec_spnego_update_send,
1633 .update_recv = gensec_spnego_update_recv,
1634 .seal_packet = gensec_child_seal_packet,
1635 .sign_packet = gensec_child_sign_packet,
1636 .sig_size = gensec_child_sig_size,
1637 .max_wrapped_size = gensec_child_max_wrapped_size,
1638 .max_input_size = gensec_child_max_input_size,
1639 .check_packet = gensec_child_check_packet,
1640 .unseal_packet = gensec_child_unseal_packet,
1641 .wrap = gensec_child_wrap,
1642 .unwrap = gensec_child_unwrap,
1643 .session_key = gensec_child_session_key,
1644 .session_info = gensec_child_session_info,
1645 .want_feature = gensec_child_want_feature,
1646 .have_feature = gensec_child_have_feature,
1647 .expire_time = gensec_child_expire_time,
1648 .final_auth_type = gensec_child_final_auth_type,
1650 .priority = GENSEC_SPNEGO
1653 _PUBLIC_ NTSTATUS gensec_spnego_init(TALLOC_CTX *ctx)
1656 ret = gensec_register(ctx, &gensec_spnego_security_ops);
1657 if (!NT_STATUS_IS_OK(ret)) {
1658 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1659 gensec_spnego_security_ops.name));