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 const char **mechTypes
= NULL
;
218 DATA_BLOB unwrapped_out
= data_blob_null
;
220 const struct gensec_security_ops_wrapper
*all_sec
;
221 const struct gensec_security_ops_wrapper
*cur_sec
= NULL
;
222 const char **send_mech_types
= NULL
;
223 struct spnego_data spnego_out
;
226 mechTypes
= gensec_security_oids(gensec_security
,
227 out_mem_ctx
, GENSEC_OID_SPNEGO
);
228 if (mechTypes
== NULL
) {
229 DBG_WARNING("gensec_security_oids() failed\n");
230 return NT_STATUS_NO_MEMORY
;
233 all_sec
= gensec_security_by_oid_list(gensec_security
,
237 if (all_sec
== NULL
) {
238 DBG_WARNING("gensec_security_by_oid_list() failed\n");
239 return NT_STATUS_NO_MEMORY
;
242 for (; all_sec
[all_idx
].op
!= NULL
; all_idx
++) {
243 const char *next
= NULL
;
244 const char *principal
= NULL
;
245 int dbg_level
= DBGLVL_WARNING
;
247 cur_sec
= &all_sec
[all_idx
];
249 status
= gensec_subcontext_start(spnego_state
,
251 &spnego_state
->sub_sec_security
);
252 if (!NT_STATUS_IS_OK(status
)) {
255 /* select the sub context */
256 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
258 if (!NT_STATUS_IS_OK(status
)) {
259 gensec_spnego_update_sub_abort(spnego_state
);
263 if (spnego_state
->state_position
!= SPNEGO_CLIENT_START
) {
265 * The server doesn't generate an optimistic token.
270 /* In the client, try and produce the first (optimistic) packet */
271 status
= gensec_update_ev(spnego_state
->sub_sec_security
,
276 if (NT_STATUS_IS_OK(status
)) {
277 spnego_state
->sub_sec_ready
= true;
280 if (!GENSEC_UPDATE_IS_NTERROR(status
)) {
284 if (cur_sec
[1].op
!= NULL
) {
285 next
= cur_sec
[1].op
->name
;
286 dbg_level
= DBGLVL_NOTICE
;
289 if (gensec_security
->target
.principal
!= NULL
) {
290 principal
= gensec_security
->target
.principal
;
291 } else if (gensec_security
->target
.service
!= NULL
&&
292 gensec_security
->target
.hostname
!= NULL
)
294 principal
= talloc_asprintf(spnego_state
->sub_sec_security
,
296 gensec_security
->target
.service
,
297 gensec_security
->target
.hostname
);
299 principal
= gensec_security
->target
.hostname
;
302 DBG_PREFIX(dbg_level
, (
303 "%s: creating NEG_TOKEN_INIT for %s failed "
304 "(next[%s]): %s\n", cur_sec
->op
->name
,
305 principal
, next
, nt_errstr(status
)));
309 * A hard error without a possible fallback.
315 * Pretend we never started it
317 gensec_spnego_update_sub_abort(spnego_state
);
320 DBG_WARNING("Failed to setup SPNEGO negTokenInit request\n");
321 return NT_STATUS_INVALID_PARAMETER
;
324 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
326 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
328 if (send_mech_types
== NULL
) {
329 DBG_WARNING("gensec_security_oids_from_ops_wrapped() failed\n");
330 return NT_STATUS_NO_MEMORY
;
333 ok
= spnego_write_mech_types(spnego_state
,
335 &spnego_state
->mech_types
);
337 DBG_ERR("Failed to write mechTypes\n");
338 return NT_STATUS_NO_MEMORY
;
341 /* List the remaining mechs as options */
342 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
343 spnego_out
.negTokenInit
.reqFlags
= data_blob_null
;
344 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
346 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
347 spnego_out
.negTokenInit
.mechListMIC
348 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
350 spnego_out
.negTokenInit
.mechListMIC
= data_blob_null
;
353 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
355 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
356 DBG_ERR("Failed to write NEG_TOKEN_INIT\n");
357 return NT_STATUS_INVALID_PARAMETER
;
361 * Note that 'cur_sec' is temporary memory, but
362 * cur_sec->oid points to a const string in the
363 * backends gensec_security_ops structure.
365 spnego_state
->neg_oid
= cur_sec
->oid
;
368 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
369 spnego_state
->state_position
= SPNEGO_SERVER_START
;
370 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
372 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
373 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
376 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
379 static NTSTATUS
gensec_spnego_client_negTokenInit(struct gensec_security
*gensec_security
,
380 struct spnego_state
*spnego_state
,
381 struct tevent_context
*ev
,
382 struct spnego_data
*spnego_in
,
383 TALLOC_CTX
*out_mem_ctx
,
386 TALLOC_CTX
*frame
= talloc_stackframe();
387 DATA_BLOB sub_out
= data_blob_null
;
388 const char *tp
= NULL
;
389 const char * const *mech_types
= NULL
;
391 const struct gensec_security_ops_wrapper
*all_sec
= NULL
;
392 struct spnego_data spnego_out
;
393 const char *my_mechs
[] = {NULL
, NULL
};
397 *out
= data_blob_null
;
399 /* The server offers a list of mechanisms */
401 tp
= spnego_in
->negTokenInit
.targetPrincipal
;
402 if (tp
!= NULL
&& strcmp(tp
, ADS_IGNORE_PRINCIPAL
) != 0) {
403 DBG_INFO("Server claims it's principal name is %s\n", tp
);
404 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
405 gensec_set_target_principal(gensec_security
, tp
);
409 mech_types
= spnego_in
->negTokenInit
.mechTypes
;
410 if (mech_types
== NULL
) {
412 return NT_STATUS_INVALID_PARAMETER
;
415 all_sec
= gensec_security_by_oid_list(gensec_security
,
418 if (all_sec
== NULL
) {
419 DBG_WARNING("gensec_security_by_oid_list() failed\n");
421 return NT_STATUS_INVALID_PARAMETER
;
424 for (; all_sec
[all_idx
].op
; all_idx
++) {
425 const struct gensec_security_ops_wrapper
*cur_sec
=
427 const char *next
= NULL
;
428 const char *principal
= NULL
;
429 int dbg_level
= DBGLVL_WARNING
;
430 bool allow_fallback
= false;
432 status
= gensec_subcontext_start(spnego_state
,
434 &spnego_state
->sub_sec_security
);
435 if (!NT_STATUS_IS_OK(status
)) {
440 /* select the sub context */
441 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
443 if (!NT_STATUS_IS_OK(status
)) {
445 * Pretend we never started it.
447 gensec_spnego_update_sub_abort(spnego_state
);
451 spnego_state
->neg_oid
= cur_sec
->oid
;
454 * As client we don't use an optimistic token from the server.
456 status
= gensec_update_ev(spnego_state
->sub_sec_security
,
457 frame
, ev
, data_blob_null
, &sub_out
);
458 if (NT_STATUS_IS_OK(status
)) {
459 spnego_state
->sub_sec_ready
= true;
462 if (!GENSEC_UPDATE_IS_NTERROR(status
)) {
463 /* OK or MORE_PROCESSING_REQUIRED */
468 * it is likely that a NULL input token will
469 * not be liked by most server mechs, but if
470 * we are in the client, we want the first
471 * update packet to be able to abort the use
474 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) ||
475 NT_STATUS_EQUAL(status
, NT_STATUS_NO_LOGON_SERVERS
) ||
476 NT_STATUS_EQUAL(status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
477 NT_STATUS_EQUAL(status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
))
479 allow_fallback
= true;
482 if (allow_fallback
&& cur_sec
[1].op
!= NULL
) {
483 next
= cur_sec
[1].op
->name
;
484 dbg_level
= DBGLVL_NOTICE
;
487 if (gensec_security
->target
.principal
!= NULL
) {
488 principal
= gensec_security
->target
.principal
;
489 } else if (gensec_security
->target
.service
!= NULL
&&
490 gensec_security
->target
.hostname
!= NULL
)
492 principal
= talloc_asprintf(spnego_state
->sub_sec_security
,
494 gensec_security
->target
.service
,
495 gensec_security
->target
.hostname
);
497 principal
= gensec_security
->target
.hostname
;
500 DBG_PREFIX(dbg_level
, (
501 "%s: creating NEG_TOKEN_INIT "
502 "for %s failed (next[%s]): %s\n",
503 spnego_state
->sub_sec_security
->ops
->name
,
504 principal
, next
, nt_errstr(status
)));
508 * A hard error without a possible fallback.
515 * Pretend we never started it.
517 gensec_spnego_update_sub_abort(spnego_state
);
520 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
522 return NT_STATUS_INVALID_PARAMETER
;
525 my_mechs
[0] = spnego_state
->neg_oid
;
527 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
528 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
529 spnego_out
.negTokenInit
.reqFlags
= data_blob_null
;
530 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
531 spnego_out
.negTokenInit
.mechListMIC
= data_blob_null
;
532 spnego_out
.negTokenInit
.mechToken
= sub_out
;
534 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
535 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
537 return NT_STATUS_INVALID_PARAMETER
;
540 ok
= spnego_write_mech_types(spnego_state
,
542 &spnego_state
->mech_types
);
544 DBG_ERR("failed to write mechTypes\n");
546 return NT_STATUS_NO_MEMORY
;
550 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
551 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
554 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
557 static NTSTATUS
gensec_spnego_client_negTokenTarg(struct gensec_security
*gensec_security
,
558 struct spnego_state
*spnego_state
,
559 struct tevent_context
*ev
,
560 struct spnego_data
*spnego_in
,
561 TALLOC_CTX
*out_mem_ctx
,
564 struct spnego_negTokenTarg
*ta
= &spnego_in
->negTokenTarg
;
565 DATA_BLOB sub_in
= ta
->responseToken
;
566 DATA_BLOB mech_list_mic
= data_blob_null
;
567 DATA_BLOB sub_out
= data_blob_null
;
568 struct spnego_data spnego_out
;
571 *out
= data_blob_null
;
573 spnego_state
->num_targs
++;
575 if (ta
->negResult
== SPNEGO_REJECT
) {
576 return NT_STATUS_LOGON_FAILURE
;
579 if (ta
->negResult
== SPNEGO_REQUEST_MIC
) {
580 spnego_state
->mic_requested
= true;
583 if (ta
->mechListMIC
.length
> 0) {
584 DATA_BLOB
*m
= &ta
->mechListMIC
;
585 const DATA_BLOB
*r
= &ta
->responseToken
;
588 * Windows 2000 has a bug, it repeats the
589 * responseToken in the mechListMIC field.
591 if (m
->length
== r
->length
) {
594 cmp
= memcmp(m
->data
, r
->data
, m
->length
);
601 /* Server didn't like our choice of mech, and chose something else */
602 if (((ta
->negResult
== SPNEGO_ACCEPT_INCOMPLETE
) ||
603 (ta
->negResult
== SPNEGO_REQUEST_MIC
)) &&
604 ta
->supportedMech
!= NULL
&&
605 strcmp(ta
->supportedMech
, spnego_state
->neg_oid
) != 0)
607 const char *client_mech
= NULL
;
608 const char *client_oid
= NULL
;
609 const char *server_mech
= NULL
;
610 const char *server_oid
= NULL
;
612 client_mech
= gensec_get_name_by_oid(gensec_security
,
613 spnego_state
->neg_oid
);
614 client_oid
= spnego_state
->neg_oid
;
615 server_mech
= gensec_get_name_by_oid(gensec_security
,
617 server_oid
= ta
->supportedMech
;
619 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
620 "server wants: %s[%s]\n",
621 client_mech
, client_oid
, server_mech
, server_oid
);
623 spnego_state
->downgraded
= true;
624 gensec_spnego_update_sub_abort(spnego_state
);
626 status
= gensec_subcontext_start(spnego_state
,
628 &spnego_state
->sub_sec_security
);
629 if (!NT_STATUS_IS_OK(status
)) {
633 /* select the sub context */
634 status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
636 if (!NT_STATUS_IS_OK(status
)) {
640 spnego_state
->neg_oid
= talloc_strdup(spnego_state
,
642 if (spnego_state
->neg_oid
== NULL
) {
643 return NT_STATUS_NO_MEMORY
;
647 if (ta
->mechListMIC
.length
> 0) {
648 if (spnego_state
->sub_sec_ready
) {
649 spnego_state
->needs_mic_check
= true;
653 if (spnego_state
->needs_mic_check
) {
654 if (ta
->responseToken
.length
!= 0) {
655 DBG_WARNING("non empty response token not expected\n");
656 return NT_STATUS_INVALID_PARAMETER
;
659 if (ta
->mechListMIC
.length
== 0
660 && spnego_state
->may_skip_mic_check
) {
662 * In this case we don't require
663 * a mechListMIC from the server.
665 * This works around bugs in the Azure
666 * and Apple spnego implementations.
669 * https://bugzilla.samba.org/show_bug.cgi?id=11994
671 spnego_state
->needs_mic_check
= false;
672 status
= NT_STATUS_OK
;
673 goto client_response
;
676 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
677 spnego_state
->mech_types
.data
,
678 spnego_state
->mech_types
.length
,
679 spnego_state
->mech_types
.data
,
680 spnego_state
->mech_types
.length
,
682 if (!NT_STATUS_IS_OK(status
)) {
683 DBG_WARNING("failed to verify mechListMIC: %s\n",
687 spnego_state
->needs_mic_check
= false;
688 spnego_state
->done_mic_check
= true;
689 goto client_response
;
692 if (!spnego_state
->sub_sec_ready
) {
693 status
= gensec_update_ev(spnego_state
->sub_sec_security
,
697 if (NT_STATUS_IS_OK(status
)) {
698 spnego_state
->sub_sec_ready
= true;
700 if (!NT_STATUS_IS_OK(status
)) {
701 goto client_response
;
704 status
= NT_STATUS_OK
;
707 if (!spnego_state
->done_mic_check
) {
708 bool have_sign
= true;
709 bool new_spnego
= false;
711 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
712 GENSEC_FEATURE_SIGN
);
713 if (spnego_state
->simulate_w2k
) {
716 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
717 GENSEC_FEATURE_NEW_SPNEGO
);
719 switch (ta
->negResult
) {
720 case SPNEGO_ACCEPT_COMPLETED
:
721 case SPNEGO_NONE_RESULT
:
722 if (spnego_state
->num_targs
== 1) {
724 * the first exchange doesn't require
732 case SPNEGO_ACCEPT_INCOMPLETE
:
733 if (ta
->mechListMIC
.length
> 0) {
738 if (spnego_state
->downgraded
) {
740 * A downgrade should be protected if
747 * The caller may just asked for
748 * GENSEC_FEATURE_SESSION_KEY, this
749 * is only reflected in the want_features.
752 * gensec_have_features(GENSEC_FEATURE_SIGN)
755 if (gensec_security
->want_features
& GENSEC_FEATURE_SIGN
) {
758 if (gensec_security
->want_features
& GENSEC_FEATURE_SEAL
) {
762 * Here we're sure our preferred mech was
763 * selected by the server and our caller doesn't
764 * need GENSEC_FEATURE_SIGN nor
765 * GENSEC_FEATURE_SEAL support.
767 * In this case we don't require
768 * a mechListMIC from the server.
770 * This works around bugs in the Azure
771 * and Apple spnego implementations.
774 * https://bugzilla.samba.org/show_bug.cgi?id=11994
776 spnego_state
->may_skip_mic_check
= true;
779 case SPNEGO_REQUEST_MIC
:
780 if (ta
->mechListMIC
.length
> 0) {
788 if (spnego_state
->mic_requested
) {
794 if (have_sign
&& new_spnego
) {
795 spnego_state
->needs_mic_check
= true;
796 spnego_state
->needs_mic_sign
= true;
800 if (ta
->mechListMIC
.length
> 0) {
801 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
802 spnego_state
->mech_types
.data
,
803 spnego_state
->mech_types
.length
,
804 spnego_state
->mech_types
.data
,
805 spnego_state
->mech_types
.length
,
807 if (!NT_STATUS_IS_OK(status
)) {
808 DBG_WARNING("failed to verify mechListMIC: %s\n",
812 spnego_state
->needs_mic_check
= false;
813 spnego_state
->done_mic_check
= true;
816 if (spnego_state
->needs_mic_sign
) {
817 status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
819 spnego_state
->mech_types
.data
,
820 spnego_state
->mech_types
.length
,
821 spnego_state
->mech_types
.data
,
822 spnego_state
->mech_types
.length
,
824 if (!NT_STATUS_IS_OK(status
)) {
825 DBG_WARNING("failed to sign mechListMIC: %s\n",
829 spnego_state
->needs_mic_sign
= false;
833 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
834 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
835 spnego_state
->sub_sec_security
->ops
->name
,
840 if (sub_out
.length
== 0 && mech_list_mic
.length
== 0) {
841 *out
= data_blob_null
;
843 if (!spnego_state
->sub_sec_ready
) {
844 /* somethings wrong here... */
845 DBG_ERR("gensec_update not ready without output\n");
846 return NT_STATUS_INTERNAL_ERROR
;
849 if (ta
->negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
850 /* unless of course it did not accept */
851 DBG_WARNING("gensec_update ok but not accepted\n");
852 return NT_STATUS_INVALID_PARAMETER
;
855 if (!spnego_state
->needs_mic_check
) {
856 spnego_state
->state_position
= SPNEGO_DONE
;
862 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
863 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
864 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
865 spnego_out
.negTokenTarg
.responseToken
= sub_out
;
866 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
868 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
869 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
870 return NT_STATUS_INVALID_PARAMETER
;
873 spnego_state
->num_targs
++;
876 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
877 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
879 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
882 /** create a server negTokenTarg
884 * This is the case, where the client is the first one who sends data
887 static NTSTATUS
gensec_spnego_server_response(struct spnego_state
*spnego_state
,
888 TALLOC_CTX
*out_mem_ctx
,
890 const DATA_BLOB unwrapped_out
,
891 DATA_BLOB mech_list_mic
,
894 struct spnego_data spnego_out
;
897 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
898 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
899 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
900 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
902 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
903 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
904 if (spnego_state
->mic_requested
) {
905 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REQUEST_MIC
;
906 spnego_state
->mic_requested
= false;
908 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
910 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
911 } else if (NT_STATUS_IS_OK(nt_status
)) {
912 if (unwrapped_out
.data
) {
913 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
915 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
916 spnego_state
->state_position
= SPNEGO_DONE
;
918 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
919 spnego_out
.negTokenTarg
.mechListMIC
= data_blob_null
;
920 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
921 spnego_state
->state_position
= SPNEGO_DONE
;
924 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
925 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
926 return NT_STATUS_INVALID_PARAMETER
;
929 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
930 spnego_state
->num_targs
++;
935 static NTSTATUS
gensec_spnego_server_negTokenInit(struct gensec_security
*gensec_security
,
936 struct spnego_state
*spnego_state
,
937 struct tevent_context
*ev
,
938 struct spnego_data
*spnego_in
,
939 TALLOC_CTX
*out_mem_ctx
,
942 TALLOC_CTX
*frame
= talloc_stackframe();
943 DATA_BLOB sub_out
= data_blob_null
;
944 DATA_BLOB mech_list_mic
= data_blob_null
;
945 const char * const *mech_types
= NULL
;
947 const struct gensec_security_ops_wrapper
*all_sec
= NULL
;
952 mech_types
= spnego_in
->negTokenInit
.mechTypes
;
953 if (mech_types
== NULL
) {
955 return NT_STATUS_INVALID_PARAMETER
;
958 all_sec
= gensec_security_by_oid_list(gensec_security
, frame
,
959 mech_types
, GENSEC_OID_SPNEGO
);
960 if (all_sec
== NULL
) {
961 DBG_WARNING("gensec_security_by_oid_list() failed\n");
963 return NT_STATUS_INVALID_PARAMETER
;
966 ok
= spnego_write_mech_types(spnego_state
, mech_types
,
967 &spnego_state
->mech_types
);
969 DBG_ERR("Failed to write mechTypes\n");
971 return NT_STATUS_NO_MEMORY
;
975 * First try the preferred mechs from the client.
977 for (; mech_types
[mech_idx
]; mech_idx
++) {
978 const char *cur_mech
= mech_types
[mech_idx
];
979 const struct gensec_security_ops_wrapper
*cur_sec
= NULL
;
980 DATA_BLOB sub_in
= data_blob_null
;
982 for (all_idx
= 0; all_sec
[all_idx
].op
; all_idx
++) {
983 if (strcmp(cur_mech
, all_sec
[all_idx
].oid
) == 0) {
984 cur_sec
= &all_sec
[all_idx
];
989 if (cur_sec
== NULL
) {
993 status
= gensec_subcontext_start(spnego_state
,
995 &spnego_state
->sub_sec_security
);
996 if (!NT_STATUS_IS_OK(status
)) {
1001 /* select the sub context */
1002 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
1004 if (!NT_STATUS_IS_OK(status
)) {
1006 * Pretend we never started it
1008 gensec_spnego_update_sub_abort(spnego_state
);
1014 * Indicate the downgrade and request a
1017 spnego_state
->downgraded
= true;
1018 spnego_state
->mic_requested
= true;
1019 /* no optimistic token */
1020 spnego_state
->neg_oid
= cur_sec
->oid
;
1021 sub_out
= data_blob_null
;
1022 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1027 * Try the optimistic token from the client
1029 sub_in
= spnego_in
->negTokenInit
.mechToken
;
1030 status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1031 frame
, ev
, sub_in
, &sub_out
);
1032 if (NT_STATUS_IS_OK(status
)) {
1033 spnego_state
->sub_sec_ready
= true;
1035 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) ||
1036 NT_STATUS_EQUAL(status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
1038 DBG_WARNING("%s: NEG_TOKEN_INIT failed to parse contents: %s\n",
1039 cur_sec
->op
->name
, nt_errstr(status
));
1042 * Pretend we never started it
1044 gensec_spnego_update_sub_abort(spnego_state
);
1048 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1049 DBG_WARNING("%s: NEG_TOKEN_INIT failed: %s\n",
1050 cur_sec
->op
->name
, nt_errstr(status
));
1054 spnego_state
->neg_oid
= cur_sec
->oid
;
1055 goto reply
; /* OK or MORE PROCESSING */
1058 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1059 status
= NT_STATUS_INVALID_PARAMETER
;
1062 if (spnego_state
->simulate_w2k
) {
1064 * Windows 2000 returns the unwrapped token
1065 * also in the mech_list_mic field.
1067 * In order to verify our client code,
1068 * we need a way to have a server with this
1071 mech_list_mic
= sub_out
;
1074 status
= gensec_spnego_server_response(spnego_state
,
1084 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct gensec_security
*gensec_security
,
1085 struct spnego_state
*spnego_state
,
1086 struct tevent_context
*ev
,
1087 struct spnego_data
*spnego_in
,
1088 TALLOC_CTX
*out_mem_ctx
,
1091 const struct spnego_negTokenTarg
*ta
= &spnego_in
->negTokenTarg
;
1092 DATA_BLOB sub_in
= ta
->responseToken
;
1093 DATA_BLOB mech_list_mic
= data_blob_null
;
1094 DATA_BLOB sub_out
= data_blob_null
;
1096 bool have_sign
= true;
1097 bool new_spnego
= false;
1099 spnego_state
->num_targs
++;
1101 if (spnego_state
->sub_sec_security
== NULL
) {
1102 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1103 return NT_STATUS_INVALID_PARAMETER
;
1106 if (spnego_state
->needs_mic_check
) {
1107 if (ta
->responseToken
.length
!= 0) {
1108 DBG_WARNING("non empty response token not expected\n");
1109 return NT_STATUS_INVALID_PARAMETER
;
1112 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1113 spnego_state
->mech_types
.data
,
1114 spnego_state
->mech_types
.length
,
1115 spnego_state
->mech_types
.data
,
1116 spnego_state
->mech_types
.length
,
1118 if (!NT_STATUS_IS_OK(status
)) {
1119 DBG_WARNING("failed to verify mechListMIC: %s\n",
1121 goto server_response
;
1124 spnego_state
->needs_mic_check
= false;
1125 spnego_state
->done_mic_check
= true;
1126 goto server_response
;
1129 if (!spnego_state
->sub_sec_ready
) {
1130 status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1133 if (NT_STATUS_IS_OK(status
)) {
1134 spnego_state
->sub_sec_ready
= true;
1136 if (!NT_STATUS_IS_OK(status
)) {
1137 goto server_response
;
1140 status
= NT_STATUS_OK
;
1143 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
1144 GENSEC_FEATURE_SIGN
);
1145 if (spnego_state
->simulate_w2k
) {
1148 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1149 GENSEC_FEATURE_NEW_SPNEGO
);
1150 if (ta
->mechListMIC
.length
> 0) {
1154 if (have_sign
&& new_spnego
) {
1155 spnego_state
->needs_mic_check
= true;
1156 spnego_state
->needs_mic_sign
= true;
1159 if (have_sign
&& ta
->mechListMIC
.length
> 0) {
1160 status
= gensec_check_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 verify mechListMIC: %s\n",
1169 goto server_response
;
1172 spnego_state
->needs_mic_check
= false;
1173 spnego_state
->done_mic_check
= true;
1176 if (spnego_state
->needs_mic_sign
) {
1177 status
= gensec_sign_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
,
1184 if (!NT_STATUS_IS_OK(status
)) {
1185 DBG_WARNING("failed to sign mechListMIC: %s\n",
1189 spnego_state
->needs_mic_sign
= false;
1192 if (spnego_state
->needs_mic_check
) {
1193 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1197 return gensec_spnego_server_response(spnego_state
,
1205 struct gensec_spnego_update_state
{
1206 struct tevent_context
*ev
;
1207 struct gensec_security
*gensec
;
1208 struct spnego_state
*spnego
;
1211 struct spnego_data _spnego_in
;
1212 struct spnego_data
*spnego_in
;
1225 static void gensec_spnego_update_cleanup(struct tevent_req
*req
,
1226 enum tevent_req_state req_state
)
1228 struct gensec_spnego_update_state
*state
=
1229 tevent_req_data(req
,
1230 struct gensec_spnego_update_state
);
1232 switch (req_state
) {
1233 case TEVENT_REQ_USER_ERROR
:
1234 case TEVENT_REQ_TIMED_OUT
:
1235 case TEVENT_REQ_NO_MEMORY
:
1237 * A fatal error, further updates are not allowed.
1239 state
->spnego
->state_position
= SPNEGO_DONE
;
1246 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1247 const DATA_BLOB in
, TALLOC_CTX
*mem_ctx
,
1248 DATA_BLOB
*full_in
);
1249 static void gensec_spnego_update_pre(struct tevent_req
*req
);
1250 static void gensec_spnego_update_done(struct tevent_req
*subreq
);
1251 static void gensec_spnego_update_post(struct tevent_req
*req
);
1252 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1253 TALLOC_CTX
*out_mem_ctx
,
1256 static struct tevent_req
*gensec_spnego_update_send(TALLOC_CTX
*mem_ctx
,
1257 struct tevent_context
*ev
,
1258 struct gensec_security
*gensec_security
,
1261 struct spnego_state
*spnego_state
=
1262 talloc_get_type_abort(gensec_security
->private_data
,
1263 struct spnego_state
);
1264 struct tevent_req
*req
= NULL
;
1265 struct gensec_spnego_update_state
*state
= NULL
;
1269 req
= tevent_req_create(mem_ctx
, &state
,
1270 struct gensec_spnego_update_state
);
1275 state
->gensec
= gensec_security
;
1276 state
->spnego
= spnego_state
;
1277 tevent_req_set_cleanup_fn(req
, gensec_spnego_update_cleanup
);
1279 if (spnego_state
->out_frag
.length
> 0) {
1280 if (in
.length
> 0) {
1281 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1282 return tevent_req_post(req
, ev
);
1285 status
= gensec_spnego_update_out(gensec_security
,
1286 state
, &state
->out
);
1287 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1288 tevent_req_nterror(req
, status
);
1289 return tevent_req_post(req
, ev
);
1292 state
->status
= status
;
1293 tevent_req_done(req
);
1294 return tevent_req_post(req
, ev
);
1297 status
= gensec_spnego_update_in(gensec_security
, in
,
1298 state
, &state
->full_in
);
1299 state
->status
= status
;
1300 if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1301 tevent_req_done(req
);
1302 return tevent_req_post(req
, ev
);
1304 if (tevent_req_nterror(req
, status
)) {
1305 return tevent_req_post(req
, ev
);
1308 /* Check if we got a valid SPNEGO blob... */
1310 switch (spnego_state
->state_position
) {
1311 case SPNEGO_FALLBACK
:
1314 case SPNEGO_CLIENT_TARG
:
1315 case SPNEGO_SERVER_TARG
:
1316 if (state
->full_in
.length
== 0) {
1317 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1318 return tevent_req_post(req
, ev
);
1322 case SPNEGO_CLIENT_START
:
1323 case SPNEGO_SERVER_START
:
1325 if (state
->full_in
.length
== 0) {
1326 /* create_negTokenInit later */
1330 len
= spnego_read_data(state
,
1332 &state
->_spnego_in
);
1334 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
1335 DEBUG(1, ("Invalid SPNEGO request:\n"));
1336 dump_data(1, state
->full_in
.data
,
1337 state
->full_in
.length
);
1338 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1339 return tevent_req_post(req
, ev
);
1343 * This is the 'fallback' case, where we don't get
1344 * SPNEGO, and have to try all the other options (and
1345 * hope they all have a magic string they check)
1347 status
= gensec_spnego_server_try_fallback(gensec_security
,
1351 if (tevent_req_nterror(req
, status
)) {
1352 return tevent_req_post(req
, ev
);
1356 * We'll continue with SPNEGO_FALLBACK below...
1360 state
->spnego_in
= &state
->_spnego_in
;
1362 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1363 if (state
->spnego_in
->type
!= spnego_state
->expected_packet
) {
1364 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1365 state
->spnego_in
->type
,
1366 spnego_state
->expected_packet
));
1367 dump_data(1, state
->full_in
.data
,
1368 state
->full_in
.length
);
1369 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1370 return tevent_req_post(req
, ev
);
1376 smb_panic(__location__
);
1380 gensec_spnego_update_pre(req
);
1381 if (!tevent_req_is_in_progress(req
)) {
1382 return tevent_req_post(req
, ev
);
1385 if (state
->sub
.needed
) {
1386 struct tevent_req
*subreq
= NULL
;
1389 * We may need one more roundtrip...
1391 subreq
= gensec_update_send(state
, state
->ev
,
1392 spnego_state
->sub_sec_security
,
1394 if (tevent_req_nomem(subreq
, req
)) {
1395 return tevent_req_post(req
, ev
);
1397 tevent_req_set_callback(subreq
,
1398 gensec_spnego_update_done
,
1400 state
->sub
.needed
= false;
1404 gensec_spnego_update_post(req
);
1405 if (!tevent_req_is_in_progress(req
)) {
1406 return tevent_req_post(req
, ev
);
1412 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1413 const DATA_BLOB in
, TALLOC_CTX
*mem_ctx
,
1416 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1420 *full_in
= data_blob_null
;
1422 switch (spnego_state
->state_position
) {
1423 case SPNEGO_FALLBACK
:
1425 spnego_state
->in_needed
= 0;
1426 return NT_STATUS_OK
;
1428 case SPNEGO_CLIENT_START
:
1429 case SPNEGO_CLIENT_TARG
:
1430 case SPNEGO_SERVER_START
:
1431 case SPNEGO_SERVER_TARG
:
1436 return NT_STATUS_INVALID_PARAMETER
;
1439 if (spnego_state
->in_needed
== 0) {
1444 * try to work out the size of the full
1445 * input token, it might be fragmented
1447 ret
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1448 if ((ret
!= 0) && (ret
!= EAGAIN
)) {
1449 ret
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1452 if ((ret
== 0) || (ret
== EAGAIN
)) {
1453 spnego_state
->in_needed
= size
;
1456 * If it is not an asn1 message
1457 * just call the next layer.
1459 spnego_state
->in_needed
= in
.length
;
1463 if (spnego_state
->in_needed
> UINT16_MAX
) {
1465 * limit the incoming message to 0xFFFF
1466 * to avoid DoS attacks.
1468 return NT_STATUS_INVALID_BUFFER_SIZE
;
1471 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1473 * If we reach this, we know we got at least
1474 * part of an asn1 message, getting 0 means
1475 * the remote peer wants us to spin.
1477 return NT_STATUS_INVALID_PARAMETER
;
1480 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1481 if (in
.length
> expected
) {
1483 * we got more than expected
1485 return NT_STATUS_INVALID_PARAMETER
;
1488 if (in
.length
== spnego_state
->in_needed
) {
1490 * if the in.length contains the full blob
1493 * Note: this implies spnego_state->in_frag.length == 0,
1494 * but we do not need to check this explicitly
1495 * because we already know that we did not get
1496 * more than expected.
1499 spnego_state
->in_needed
= 0;
1500 return NT_STATUS_OK
;
1503 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1504 in
.data
, in
.length
);
1506 return NT_STATUS_NO_MEMORY
;
1509 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1510 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1513 *full_in
= spnego_state
->in_frag
;
1514 talloc_steal(mem_ctx
, full_in
->data
);
1515 spnego_state
->in_frag
= data_blob_null
;
1516 spnego_state
->in_needed
= 0;
1517 return NT_STATUS_OK
;
1520 static void gensec_spnego_update_pre(struct tevent_req
*req
)
1522 struct gensec_spnego_update_state
*state
=
1523 tevent_req_data(req
,
1524 struct gensec_spnego_update_state
);
1525 struct gensec_security
*gensec_security
= state
->gensec
;
1526 struct spnego_state
*spnego_state
= state
->spnego
;
1527 struct tevent_context
*ev
= state
->ev
;
1530 state
->sub
.needed
= false;
1531 state
->sub
.in
= data_blob_null
;
1532 state
->sub
.status
= NT_STATUS_INTERNAL_ERROR
;
1533 state
->sub
.out
= data_blob_null
;
1535 if (spnego_state
->state_position
== SPNEGO_FALLBACK
) {
1536 state
->sub
.in
= state
->full_in
;
1537 state
->full_in
= data_blob_null
;
1538 state
->sub
.needed
= true;
1542 switch (spnego_state
->state_position
) {
1543 case SPNEGO_CLIENT_START
:
1544 if (state
->spnego_in
== NULL
) {
1545 /* client to produce negTokenInit */
1546 status
= gensec_spnego_create_negTokenInit(gensec_security
,
1547 spnego_state
, state
, ev
,
1548 &spnego_state
->out_frag
);
1549 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1550 tevent_req_nterror(req
, status
);
1556 status
= gensec_spnego_client_negTokenInit(gensec_security
,
1558 state
->spnego_in
, state
,
1559 &spnego_state
->out_frag
);
1562 case SPNEGO_CLIENT_TARG
:
1563 status
= gensec_spnego_client_negTokenTarg(gensec_security
,
1565 state
->spnego_in
, state
,
1566 &spnego_state
->out_frag
);
1567 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1568 tevent_req_nterror(req
, status
);
1573 case SPNEGO_SERVER_START
:
1574 if (state
->spnego_in
== NULL
) {
1575 /* server to produce negTokenInit */
1576 status
= gensec_spnego_create_negTokenInit(gensec_security
,
1577 spnego_state
, state
, ev
,
1578 &spnego_state
->out_frag
);
1579 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1580 tevent_req_nterror(req
, status
);
1586 status
= gensec_spnego_server_negTokenInit(gensec_security
,
1588 state
->spnego_in
, state
,
1589 &spnego_state
->out_frag
);
1590 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1591 tevent_req_nterror(req
, status
);
1596 case SPNEGO_SERVER_TARG
:
1597 status
= gensec_spnego_server_negTokenTarg(gensec_security
,
1599 state
->spnego_in
, state
,
1600 &spnego_state
->out_frag
);
1601 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1602 tevent_req_nterror(req
, status
);
1608 smb_panic(__location__
);
1612 spnego_state
->out_status
= status
;
1615 static void gensec_spnego_update_done(struct tevent_req
*subreq
)
1617 struct tevent_req
*req
=
1618 tevent_req_callback_data(subreq
,
1620 struct gensec_spnego_update_state
*state
=
1621 tevent_req_data(req
,
1622 struct gensec_spnego_update_state
);
1623 struct spnego_state
*spnego_state
= state
->spnego
;
1625 state
->sub
.status
= gensec_update_recv(subreq
, state
, &state
->sub
.out
);
1626 TALLOC_FREE(subreq
);
1627 if (NT_STATUS_IS_OK(state
->sub
.status
)) {
1628 spnego_state
->sub_sec_ready
= true;
1631 gensec_spnego_update_post(req
);
1634 static void gensec_spnego_update_post(struct tevent_req
*req
)
1636 struct gensec_spnego_update_state
*state
=
1637 tevent_req_data(req
,
1638 struct gensec_spnego_update_state
);
1639 struct spnego_state
*spnego_state
= state
->spnego
;
1642 state
->sub
.in
= data_blob_null
;
1643 state
->sub
.needed
= false;
1645 if (spnego_state
->state_position
== SPNEGO_FALLBACK
) {
1646 status
= state
->sub
.status
;
1647 spnego_state
->out_frag
= state
->sub
.out
;
1648 talloc_steal(spnego_state
, spnego_state
->out_frag
.data
);
1649 state
->sub
.out
= data_blob_null
;
1654 * For now just handle the sync processing done
1655 * in gensec_spnego_update_pre()
1657 status
= spnego_state
->out_status
;
1659 if (NT_STATUS_IS_OK(status
)) {
1660 bool reset_full
= true;
1662 reset_full
= !spnego_state
->done_mic_check
;
1664 status
= gensec_may_reset_crypto(spnego_state
->sub_sec_security
,
1666 if (tevent_req_nterror(req
, status
)) {
1672 spnego_state
->out_status
= status
;
1674 status
= gensec_spnego_update_out(state
->gensec
,
1675 state
, &state
->out
);
1676 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1677 tevent_req_nterror(req
, status
);
1681 state
->status
= status
;
1682 tevent_req_done(req
);
1686 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1687 TALLOC_CTX
*out_mem_ctx
,
1690 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1691 DATA_BLOB out
= data_blob_null
;
1694 *_out
= data_blob_null
;
1696 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1698 * Fast path, we can deliver everything
1701 *_out
= spnego_state
->out_frag
;
1702 if (spnego_state
->out_frag
.length
> 0) {
1703 talloc_steal(out_mem_ctx
, _out
->data
);
1704 spnego_state
->out_frag
= data_blob_null
;
1707 if (!NT_STATUS_IS_OK(spnego_state
->out_status
)) {
1708 return spnego_state
->out_status
;
1712 * We're completely done, further updates are not allowed.
1714 spnego_state
->state_position
= SPNEGO_DONE
;
1715 return gensec_child_ready(gensec_security
,
1716 spnego_state
->sub_sec_security
);
1719 out
= spnego_state
->out_frag
;
1722 * copy the remaining bytes
1724 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1725 out
.data
+ spnego_state
->out_max_length
,
1726 out
.length
- spnego_state
->out_max_length
);
1727 if (spnego_state
->out_frag
.data
== NULL
) {
1728 return NT_STATUS_NO_MEMORY
;
1732 * truncate the buffer
1734 ok
= data_blob_realloc(spnego_state
, &out
,
1735 spnego_state
->out_max_length
);
1737 return NT_STATUS_NO_MEMORY
;
1740 talloc_steal(out_mem_ctx
, out
.data
);
1742 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1745 static NTSTATUS
gensec_spnego_update_recv(struct tevent_req
*req
,
1746 TALLOC_CTX
*out_mem_ctx
,
1749 struct gensec_spnego_update_state
*state
=
1750 tevent_req_data(req
,
1751 struct gensec_spnego_update_state
);
1754 *out
= data_blob_null
;
1756 if (tevent_req_is_nterror(req
, &status
)) {
1757 tevent_req_received(req
);
1762 talloc_steal(out_mem_ctx
, state
->out
.data
);
1763 status
= state
->status
;
1764 tevent_req_received(req
);
1768 static const char *gensec_spnego_oids
[] = {
1773 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1775 .sasl_name
= "GSS-SPNEGO",
1776 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1777 .oid
= gensec_spnego_oids
,
1778 .client_start
= gensec_spnego_client_start
,
1779 .server_start
= gensec_spnego_server_start
,
1780 .update_send
= gensec_spnego_update_send
,
1781 .update_recv
= gensec_spnego_update_recv
,
1782 .seal_packet
= gensec_child_seal_packet
,
1783 .sign_packet
= gensec_child_sign_packet
,
1784 .sig_size
= gensec_child_sig_size
,
1785 .max_wrapped_size
= gensec_child_max_wrapped_size
,
1786 .max_input_size
= gensec_child_max_input_size
,
1787 .check_packet
= gensec_child_check_packet
,
1788 .unseal_packet
= gensec_child_unseal_packet
,
1789 .wrap
= gensec_child_wrap
,
1790 .unwrap
= gensec_child_unwrap
,
1791 .session_key
= gensec_child_session_key
,
1792 .session_info
= gensec_child_session_info
,
1793 .want_feature
= gensec_child_want_feature
,
1794 .have_feature
= gensec_child_have_feature
,
1795 .expire_time
= gensec_child_expire_time
,
1796 .final_auth_type
= gensec_child_final_auth_type
,
1798 .priority
= GENSEC_SPNEGO
1801 _PUBLIC_ NTSTATUS
gensec_spnego_init(TALLOC_CTX
*ctx
)
1804 ret
= gensec_register(ctx
, &gensec_spnego_security_ops
);
1805 if (!NT_STATUS_IS_OK(ret
)) {
1806 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1807 gensec_spnego_security_ops
.name
));