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"
38 #define DBGC_CLASS DBGC_AUTH
42 _PUBLIC_ NTSTATUS
gensec_spnego_init(TALLOC_CTX
*ctx
);
44 enum spnego_state_position
{
54 struct spnego_neg_ops
;
55 struct spnego_neg_state
;
57 struct spnego_neg_state
{
58 const struct spnego_neg_ops
*ops
;
59 const struct gensec_security_ops_wrapper
*all_sec
;
61 const char * const *mech_types
;
65 struct spnego_neg_ops
{
68 * The start hook does the initial processing on the incoming paket and
69 * may starts the first possible subcontext. It indicates that
70 * gensec_update() is required on the subcontext by returning
71 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
72 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
73 * caller should treat 'in_next' as const and don't attempt to free the
74 * content. NT_STATUS_OK indicates the finish hook should be invoked
75 * directly withing the need of gensec_update() on the subcontext.
76 * Every other error indicates an error that's returned to the caller.
78 NTSTATUS (*start_fn
)(struct gensec_security
*gensec_security
,
79 struct spnego_state
*spnego_state
,
80 struct spnego_neg_state
*n
,
81 struct spnego_data
*spnego_in
,
82 TALLOC_CTX
*in_mem_ctx
,
85 * The step hook processes the result of a failed gensec_update() and
86 * can decide to ignore a failure and continue the negotiation by
87 * setting up the next possible subcontext. It indicates that
88 * gensec_update() is required on the subcontext by returning
89 * NT_STATUS_MORE_PROCESSING_REQUIRED and return something useful in
90 * 'in_next'. Note that 'in_mem_ctx' is just passed as a hint, the
91 * caller should treat 'in_next' as const and don't attempt to free the
92 * content. NT_STATUS_OK indicates the finish hook should be invoked
93 * directly withing the need of gensec_update() on the subcontext.
94 * Every other error indicates an error that's returned to the caller.
96 NTSTATUS (*step_fn
)(struct gensec_security
*gensec_security
,
97 struct spnego_state
*spnego_state
,
98 struct spnego_neg_state
*n
,
99 struct spnego_data
*spnego_in
,
100 NTSTATUS last_status
,
101 TALLOC_CTX
*in_mem_ctx
,
104 * The finish hook processes the result of a successful gensec_update()
105 * (NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED). It forms the
106 * response pdu that will be returned from the toplevel gensec_update()
107 * together with NT_STATUS_OK or NT_STATUS_MORE_PROCESSING_REQUIRED. It
108 * may also alter the state machine to prepare receiving the next pdu
111 NTSTATUS (*finish_fn
)(struct gensec_security
*gensec_security
,
112 struct spnego_state
*spnego_state
,
113 struct spnego_neg_state
*n
,
114 struct spnego_data
*spnego_in
,
116 const DATA_BLOB sub_out
,
117 TALLOC_CTX
*out_mem_ctx
,
121 struct spnego_state
{
122 enum spnego_message_type expected_packet
;
123 enum spnego_state_position state_position
;
124 struct gensec_security
*sub_sec_security
;
129 DATA_BLOB mech_types
;
134 bool needs_mic_check
;
135 bool may_skip_mic_check
;
141 * The following is used to implement
142 * the update token fragmentation
146 size_t out_max_length
;
151 static struct spnego_neg_state
*gensec_spnego_neg_state(TALLOC_CTX
*mem_ctx
,
152 const struct spnego_neg_ops
*ops
)
154 struct spnego_neg_state
*n
= NULL
;
156 n
= talloc_zero(mem_ctx
, struct spnego_neg_state
);
165 static void gensec_spnego_reset_sub_sec(struct spnego_state
*spnego_state
)
167 spnego_state
->sub_sec_ready
= false;
168 TALLOC_FREE(spnego_state
->sub_sec_security
);
171 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
173 struct spnego_state
*spnego_state
;
175 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
177 return NT_STATUS_NO_MEMORY
;
180 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
181 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
182 spnego_state
->sub_sec_security
= NULL
;
183 spnego_state
->sub_sec_ready
= false;
184 spnego_state
->mech_types
= data_blob_null
;
185 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
186 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
188 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
189 "spnego", "simulate_w2k", false);
191 gensec_security
->private_data
= spnego_state
;
195 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
197 struct spnego_state
*spnego_state
;
199 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
201 return NT_STATUS_NO_MEMORY
;
204 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
205 spnego_state
->state_position
= SPNEGO_SERVER_START
;
206 spnego_state
->sub_sec_security
= NULL
;
207 spnego_state
->sub_sec_ready
= false;
208 spnego_state
->mech_types
= data_blob_null
;
209 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
210 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
212 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
213 "spnego", "simulate_w2k", false);
215 gensec_security
->private_data
= spnego_state
;
219 /** Fallback to another GENSEC mechanism, based on magic strings
221 * This is the 'fallback' case, where we don't get SPNEGO, and have to
222 * try all the other options (and hope they all have a magic string
226 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
227 struct spnego_state
*spnego_state
,
232 const struct gensec_security_ops
**all_ops
;
234 all_ops
= gensec_security_mechs(gensec_security
, mem_ctx
);
236 for (i
=0; all_ops
&& all_ops
[i
]; i
++) {
240 if (gensec_security
!= NULL
&&
241 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
246 if (!all_ops
[i
]->oid
) {
251 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
252 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
260 if (!all_ops
[i
]->magic
) {
264 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
265 if (!NT_STATUS_IS_OK(nt_status
)) {
269 spnego_state
->state_position
= SPNEGO_FALLBACK
;
271 nt_status
= gensec_subcontext_start(spnego_state
,
273 &spnego_state
->sub_sec_security
);
275 if (!NT_STATUS_IS_OK(nt_status
)) {
278 /* select the sub context */
279 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
281 if (!NT_STATUS_IS_OK(nt_status
)) {
287 DEBUG(1, ("Failed to parse SPNEGO request\n"));
288 return NT_STATUS_INVALID_PARAMETER
;
291 static NTSTATUS
gensec_spnego_create_negTokenInit_start(
292 struct gensec_security
*gensec_security
,
293 struct spnego_state
*spnego_state
,
294 struct spnego_neg_state
*n
,
295 struct spnego_data
*spnego_in
,
296 TALLOC_CTX
*in_mem_ctx
,
300 n
->mech_types
= gensec_security_oids(gensec_security
, n
,
302 if (n
->mech_types
== NULL
) {
303 DBG_WARNING("gensec_security_oids() failed\n");
304 return NT_STATUS_NO_MEMORY
;
308 n
->all_sec
= gensec_security_by_oid_list(gensec_security
,
311 if (n
->all_sec
== NULL
) {
312 DBG_WARNING("gensec_security_by_oid_list() failed\n");
313 return NT_STATUS_NO_MEMORY
;
316 return n
->ops
->step_fn(gensec_security
, spnego_state
, n
,
317 spnego_in
, NT_STATUS_OK
, in_mem_ctx
, in_next
);
320 static NTSTATUS
gensec_spnego_create_negTokenInit_step(
321 struct gensec_security
*gensec_security
,
322 struct spnego_state
*spnego_state
,
323 struct spnego_neg_state
*n
,
324 struct spnego_data
*spnego_in
,
325 NTSTATUS last_status
,
326 TALLOC_CTX
*in_mem_ctx
,
329 if (!NT_STATUS_IS_OK(last_status
)) {
330 const struct gensec_security_ops_wrapper
*cur_sec
=
331 &n
->all_sec
[n
->all_idx
];
332 const struct gensec_security_ops_wrapper
*next_sec
= NULL
;
333 const char *next
= NULL
;
334 const char *principal
= NULL
;
335 int dbg_level
= DBGLVL_WARNING
;
336 NTSTATUS status
= last_status
;
338 if (cur_sec
[1].op
!= NULL
) {
339 next_sec
= &cur_sec
[1];
342 if (next_sec
!= NULL
) {
343 next
= next_sec
->op
->name
;
344 dbg_level
= DBGLVL_NOTICE
;
347 if (gensec_security
->target
.principal
!= NULL
) {
348 principal
= gensec_security
->target
.principal
;
349 } else if (gensec_security
->target
.service
!= NULL
&&
350 gensec_security
->target
.hostname
!= NULL
)
352 principal
= talloc_asprintf(spnego_state
->sub_sec_security
,
354 gensec_security
->target
.service
,
355 gensec_security
->target
.hostname
);
357 principal
= gensec_security
->target
.hostname
;
360 DBG_PREFIX(dbg_level
, (
361 "%s: creating NEG_TOKEN_INIT for %s failed "
362 "(next[%s]): %s\n", cur_sec
->op
->name
,
363 principal
, next
, nt_errstr(status
)));
367 * A hard error without a possible fallback.
373 * Pretend we never started it
375 gensec_spnego_reset_sub_sec(spnego_state
);
378 * And try the next one...
383 for (; n
->all_sec
[n
->all_idx
].op
!= NULL
; n
->all_idx
++) {
384 const struct gensec_security_ops_wrapper
*cur_sec
=
385 &n
->all_sec
[n
->all_idx
];
388 status
= gensec_subcontext_start(spnego_state
,
390 &spnego_state
->sub_sec_security
);
391 if (!NT_STATUS_IS_OK(status
)) {
395 /* select the sub context */
396 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
398 if (!NT_STATUS_IS_OK(status
)) {
399 gensec_spnego_reset_sub_sec(spnego_state
);
403 /* In the client, try and produce the first (optimistic) packet */
404 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
405 *in_next
= data_blob_null
;
406 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
409 *in_next
= data_blob_null
;
413 DBG_WARNING("Failed to setup SPNEGO negTokenInit request\n");
414 return NT_STATUS_INVALID_PARAMETER
;
417 static NTSTATUS
gensec_spnego_create_negTokenInit_finish(
418 struct gensec_security
*gensec_security
,
419 struct spnego_state
*spnego_state
,
420 struct spnego_neg_state
*n
,
421 struct spnego_data
*spnego_in
,
423 const DATA_BLOB sub_out
,
424 TALLOC_CTX
*out_mem_ctx
,
427 const struct gensec_security_ops_wrapper
*cur_sec
=
428 &n
->all_sec
[n
->all_idx
];
429 struct spnego_data spnego_out
;
432 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
434 n
->mech_types
= gensec_security_oids_from_ops_wrapped(n
, cur_sec
);
435 if (n
->mech_types
== NULL
) {
436 DBG_WARNING("gensec_security_oids_from_ops_wrapped() failed\n");
437 return NT_STATUS_NO_MEMORY
;
440 ok
= spnego_write_mech_types(spnego_state
,
442 &spnego_state
->mech_types
);
444 DBG_ERR("Failed to write mechTypes\n");
445 return NT_STATUS_NO_MEMORY
;
448 /* List the remaining mechs as options */
449 spnego_out
.negTokenInit
.mechTypes
= n
->mech_types
;
450 spnego_out
.negTokenInit
.reqFlags
= data_blob_null
;
451 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
453 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
454 spnego_out
.negTokenInit
.mechListMIC
455 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
457 spnego_out
.negTokenInit
.mechListMIC
= data_blob_null
;
460 spnego_out
.negTokenInit
.mechToken
= sub_out
;
462 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
463 DBG_ERR("Failed to write NEG_TOKEN_INIT\n");
464 return NT_STATUS_INVALID_PARAMETER
;
468 * Note that 'cur_sec' is temporary memory, but
469 * cur_sec->oid points to a const string in the
470 * backends gensec_security_ops structure.
472 spnego_state
->neg_oid
= cur_sec
->oid
;
475 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
476 spnego_state
->state_position
= SPNEGO_SERVER_START
;
477 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
479 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
480 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
483 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
486 static const struct spnego_neg_ops gensec_spnego_create_negTokenInit_ops
= {
487 .name
= "create_negTokenInit",
488 .start_fn
= gensec_spnego_create_negTokenInit_start
,
489 .step_fn
= gensec_spnego_create_negTokenInit_step
,
490 .finish_fn
= gensec_spnego_create_negTokenInit_finish
,
493 static NTSTATUS
gensec_spnego_client_negTokenInit_start(
494 struct gensec_security
*gensec_security
,
495 struct spnego_state
*spnego_state
,
496 struct spnego_neg_state
*n
,
497 struct spnego_data
*spnego_in
,
498 TALLOC_CTX
*in_mem_ctx
,
501 const char *tp
= NULL
;
503 /* The server offers a list of mechanisms */
505 tp
= spnego_in
->negTokenInit
.targetPrincipal
;
506 if (tp
!= NULL
&& strcmp(tp
, ADS_IGNORE_PRINCIPAL
) != 0) {
507 DBG_INFO("Server claims it's principal name is %s\n", tp
);
508 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
509 gensec_set_target_principal(gensec_security
, tp
);
514 n
->mech_types
= spnego_in
->negTokenInit
.mechTypes
;
515 if (n
->mech_types
== NULL
) {
516 return NT_STATUS_INVALID_PARAMETER
;
520 n
->all_sec
= gensec_security_by_oid_list(gensec_security
,
523 if (n
->all_sec
== NULL
) {
524 DBG_WARNING("gensec_security_by_oid_list() failed\n");
525 return NT_STATUS_INVALID_PARAMETER
;
528 return n
->ops
->step_fn(gensec_security
, spnego_state
, n
,
529 spnego_in
, NT_STATUS_OK
, in_mem_ctx
, in_next
);
532 static NTSTATUS
gensec_spnego_client_negTokenInit_step(
533 struct gensec_security
*gensec_security
,
534 struct spnego_state
*spnego_state
,
535 struct spnego_neg_state
*n
,
536 struct spnego_data
*spnego_in
,
537 NTSTATUS last_status
,
538 TALLOC_CTX
*in_mem_ctx
,
541 if (!NT_STATUS_IS_OK(last_status
)) {
542 const struct gensec_security_ops_wrapper
*cur_sec
=
543 &n
->all_sec
[n
->all_idx
];
544 const struct gensec_security_ops_wrapper
*next_sec
= NULL
;
545 const char *next
= NULL
;
546 const char *principal
= NULL
;
547 int dbg_level
= DBGLVL_WARNING
;
548 bool allow_fallback
= false;
549 NTSTATUS status
= last_status
;
551 if (cur_sec
[1].op
!= NULL
) {
552 next_sec
= &cur_sec
[1];
556 * it is likely that a NULL input token will
557 * not be liked by most server mechs, but if
558 * we are in the client, we want the first
559 * update packet to be able to abort the use
562 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) ||
563 NT_STATUS_EQUAL(status
, NT_STATUS_NO_LOGON_SERVERS
) ||
564 NT_STATUS_EQUAL(status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
565 NT_STATUS_EQUAL(status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
))
567 allow_fallback
= true;
570 if (allow_fallback
&& next_sec
!= NULL
) {
571 next
= next_sec
->op
->name
;
572 dbg_level
= DBGLVL_NOTICE
;
575 if (gensec_security
->target
.principal
!= NULL
) {
576 principal
= gensec_security
->target
.principal
;
577 } else if (gensec_security
->target
.service
!= NULL
&&
578 gensec_security
->target
.hostname
!= NULL
)
580 principal
= talloc_asprintf(spnego_state
->sub_sec_security
,
582 gensec_security
->target
.service
,
583 gensec_security
->target
.hostname
);
585 principal
= gensec_security
->target
.hostname
;
588 DBG_PREFIX(dbg_level
, (
589 "%s: creating NEG_TOKEN_INIT for %s failed "
590 "(next[%s]): %s\n", cur_sec
->op
->name
,
591 principal
, next
, nt_errstr(status
)));
595 * A hard error without a possible fallback.
601 * Pretend we never started it.
603 gensec_spnego_reset_sub_sec(spnego_state
);
606 * And try the next one...
611 for (; n
->all_sec
[n
->all_idx
].op
!= NULL
; n
->all_idx
++) {
612 const struct gensec_security_ops_wrapper
*cur_sec
=
613 &n
->all_sec
[n
->all_idx
];
616 status
= gensec_subcontext_start(spnego_state
,
618 &spnego_state
->sub_sec_security
);
619 if (!NT_STATUS_IS_OK(status
)) {
623 /* select the sub context */
624 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
626 if (!NT_STATUS_IS_OK(status
)) {
627 gensec_spnego_reset_sub_sec(spnego_state
);
632 * Note that 'cur_sec' is temporary memory, but
633 * cur_sec->oid points to a const string in the
634 * backends gensec_security_ops structure.
636 spnego_state
->neg_oid
= cur_sec
->oid
;
639 * As client we don't use an optimistic token from the server.
640 * But try to produce one for the server.
642 *in_next
= data_blob_null
;
643 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
646 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
647 return NT_STATUS_INVALID_PARAMETER
;
650 static NTSTATUS
gensec_spnego_client_negTokenInit_finish(
651 struct gensec_security
*gensec_security
,
652 struct spnego_state
*spnego_state
,
653 struct spnego_neg_state
*n
,
654 struct spnego_data
*spnego_in
,
656 const DATA_BLOB sub_out
,
657 TALLOC_CTX
*out_mem_ctx
,
660 struct spnego_data spnego_out
;
661 const char *my_mechs
[] = {NULL
, NULL
};
664 my_mechs
[0] = spnego_state
->neg_oid
;
666 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
667 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
668 spnego_out
.negTokenInit
.reqFlags
= data_blob_null
;
669 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
670 spnego_out
.negTokenInit
.mechListMIC
= data_blob_null
;
671 spnego_out
.negTokenInit
.mechToken
= sub_out
;
673 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
674 DBG_ERR("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n");
675 return NT_STATUS_INVALID_PARAMETER
;
678 ok
= spnego_write_mech_types(spnego_state
,
680 &spnego_state
->mech_types
);
682 DBG_ERR("failed to write mechTypes\n");
683 return NT_STATUS_NO_MEMORY
;
687 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
688 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
690 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
693 static const struct spnego_neg_ops gensec_spnego_client_negTokenInit_ops
= {
694 .name
= "client_negTokenInit",
695 .start_fn
= gensec_spnego_client_negTokenInit_start
,
696 .step_fn
= gensec_spnego_client_negTokenInit_step
,
697 .finish_fn
= gensec_spnego_client_negTokenInit_finish
,
700 static NTSTATUS
gensec_spnego_client_negTokenTarg_start(
701 struct gensec_security
*gensec_security
,
702 struct spnego_state
*spnego_state
,
703 struct spnego_neg_state
*n
,
704 struct spnego_data
*spnego_in
,
705 TALLOC_CTX
*in_mem_ctx
,
708 struct spnego_negTokenTarg
*ta
= &spnego_in
->negTokenTarg
;
711 spnego_state
->num_targs
++;
713 if (ta
->negResult
== SPNEGO_REJECT
) {
714 return NT_STATUS_LOGON_FAILURE
;
717 if (ta
->negResult
== SPNEGO_REQUEST_MIC
) {
718 spnego_state
->mic_requested
= true;
721 if (ta
->mechListMIC
.length
> 0) {
722 DATA_BLOB
*m
= &ta
->mechListMIC
;
723 const DATA_BLOB
*r
= &ta
->responseToken
;
726 * Windows 2000 has a bug, it repeats the
727 * responseToken in the mechListMIC field.
729 if (m
->length
== r
->length
) {
732 cmp
= memcmp(m
->data
, r
->data
, m
->length
);
739 /* Server didn't like our choice of mech, and chose something else */
740 if (((ta
->negResult
== SPNEGO_ACCEPT_INCOMPLETE
) ||
741 (ta
->negResult
== SPNEGO_REQUEST_MIC
)) &&
742 ta
->supportedMech
!= NULL
&&
743 strcmp(ta
->supportedMech
, spnego_state
->neg_oid
) != 0)
745 const char *client_mech
= NULL
;
746 const char *client_oid
= NULL
;
747 const char *server_mech
= NULL
;
748 const char *server_oid
= NULL
;
750 client_mech
= gensec_get_name_by_oid(gensec_security
,
751 spnego_state
->neg_oid
);
752 client_oid
= spnego_state
->neg_oid
;
753 server_mech
= gensec_get_name_by_oid(gensec_security
,
755 server_oid
= ta
->supportedMech
;
757 DBG_NOTICE("client preferred mech (%s[%s]) not accepted, "
758 "server wants: %s[%s]\n",
759 client_mech
, client_oid
, server_mech
, server_oid
);
761 spnego_state
->downgraded
= true;
762 gensec_spnego_reset_sub_sec(spnego_state
);
764 status
= gensec_subcontext_start(spnego_state
,
766 &spnego_state
->sub_sec_security
);
767 if (!NT_STATUS_IS_OK(status
)) {
771 /* select the sub context */
772 status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
774 if (!NT_STATUS_IS_OK(status
)) {
778 spnego_state
->neg_oid
= talloc_strdup(spnego_state
,
780 if (spnego_state
->neg_oid
== NULL
) {
781 return NT_STATUS_NO_MEMORY
;
785 if (ta
->mechListMIC
.length
> 0) {
786 if (spnego_state
->sub_sec_ready
) {
787 spnego_state
->needs_mic_check
= true;
791 if (spnego_state
->needs_mic_check
) {
792 if (ta
->responseToken
.length
!= 0) {
793 DBG_WARNING("non empty response token not expected\n");
794 return NT_STATUS_INVALID_PARAMETER
;
797 if (ta
->mechListMIC
.length
== 0
798 && spnego_state
->may_skip_mic_check
) {
800 * In this case we don't require
801 * a mechListMIC from the server.
803 * This works around bugs in the Azure
804 * and Apple spnego implementations.
807 * https://bugzilla.samba.org/show_bug.cgi?id=11994
809 spnego_state
->needs_mic_check
= false;
813 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
814 spnego_state
->mech_types
.data
,
815 spnego_state
->mech_types
.length
,
816 spnego_state
->mech_types
.data
,
817 spnego_state
->mech_types
.length
,
819 if (!NT_STATUS_IS_OK(status
)) {
820 DBG_WARNING("failed to verify mechListMIC: %s\n",
824 spnego_state
->needs_mic_check
= false;
825 spnego_state
->done_mic_check
= true;
829 if (!spnego_state
->sub_sec_ready
) {
830 *in_next
= ta
->responseToken
;
831 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
837 static NTSTATUS
gensec_spnego_client_negTokenTarg_step(
838 struct gensec_security
*gensec_security
,
839 struct spnego_state
*spnego_state
,
840 struct spnego_neg_state
*n
,
841 struct spnego_data
*spnego_in
,
842 NTSTATUS last_status
,
843 TALLOC_CTX
*in_mem_ctx
,
846 if (GENSEC_UPDATE_IS_NTERROR(last_status
)) {
847 DBG_WARNING("SPNEGO(%s) login failed: %s\n",
848 spnego_state
->sub_sec_security
->ops
->name
,
849 nt_errstr(last_status
));
854 * This should never be reached!
855 * The step function is only called on errors!
857 smb_panic(__location__
);
858 return NT_STATUS_INTERNAL_ERROR
;
861 static NTSTATUS
gensec_spnego_client_negTokenTarg_finish(
862 struct gensec_security
*gensec_security
,
863 struct spnego_state
*spnego_state
,
864 struct spnego_neg_state
*n
,
865 struct spnego_data
*spnego_in
,
867 const DATA_BLOB sub_out
,
868 TALLOC_CTX
*out_mem_ctx
,
871 const struct spnego_negTokenTarg
*ta
=
872 &spnego_in
->negTokenTarg
;
873 DATA_BLOB mech_list_mic
= data_blob_null
;
875 struct spnego_data spnego_out
;
879 if (!spnego_state
->sub_sec_ready
) {
881 * We're not yet ready to deal with signatures.
883 goto client_response
;
886 if (spnego_state
->done_mic_check
) {
888 * We already checked the mic,
889 * either the in last round here
890 * in gensec_spnego_client_negTokenTarg_finish()
891 * or during this round in
892 * gensec_spnego_client_negTokenTarg_start().
894 * Both cases we're sure we don't have to
895 * call gensec_sign_packet().
897 goto client_response
;
900 if (spnego_state
->may_skip_mic_check
) {
902 * This can only be set during
903 * the last round here in
904 * gensec_spnego_client_negTokenTarg_finish()
905 * below. And during this round
906 * we already passed the checks in
907 * gensec_spnego_client_negTokenTarg_start().
909 * So we need to skip to deal with
910 * any signatures now.
912 goto client_response
;
915 if (!spnego_state
->done_mic_check
) {
916 bool have_sign
= true;
917 bool new_spnego
= false;
919 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
920 GENSEC_FEATURE_SIGN
);
921 if (spnego_state
->simulate_w2k
) {
924 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
925 GENSEC_FEATURE_NEW_SPNEGO
);
927 switch (ta
->negResult
) {
928 case SPNEGO_ACCEPT_COMPLETED
:
929 case SPNEGO_NONE_RESULT
:
930 if (spnego_state
->num_targs
== 1) {
932 * the first exchange doesn't require
940 case SPNEGO_ACCEPT_INCOMPLETE
:
941 if (ta
->mechListMIC
.length
> 0) {
946 if (spnego_state
->downgraded
) {
948 * A downgrade should be protected if
955 * The caller may just asked for
956 * GENSEC_FEATURE_SESSION_KEY, this
957 * is only reflected in the want_features.
960 * gensec_have_features(GENSEC_FEATURE_SIGN)
963 if (gensec_security
->want_features
& GENSEC_FEATURE_SIGN
) {
966 if (gensec_security
->want_features
& GENSEC_FEATURE_SEAL
) {
970 * Here we're sure our preferred mech was
971 * selected by the server and our caller doesn't
972 * need GENSEC_FEATURE_SIGN nor
973 * GENSEC_FEATURE_SEAL support.
975 * In this case we don't require
976 * a mechListMIC from the server.
978 * This works around bugs in the Azure
979 * and Apple spnego implementations.
982 * https://bugzilla.samba.org/show_bug.cgi?id=11994
984 spnego_state
->may_skip_mic_check
= true;
987 case SPNEGO_REQUEST_MIC
:
988 if (ta
->mechListMIC
.length
> 0) {
996 if (spnego_state
->mic_requested
) {
1002 if (have_sign
&& new_spnego
) {
1003 spnego_state
->needs_mic_check
= true;
1004 spnego_state
->needs_mic_sign
= true;
1008 if (ta
->mechListMIC
.length
> 0) {
1009 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1010 spnego_state
->mech_types
.data
,
1011 spnego_state
->mech_types
.length
,
1012 spnego_state
->mech_types
.data
,
1013 spnego_state
->mech_types
.length
,
1015 if (!NT_STATUS_IS_OK(status
)) {
1016 DBG_WARNING("failed to verify mechListMIC: %s\n",
1020 spnego_state
->needs_mic_check
= false;
1021 spnego_state
->done_mic_check
= true;
1024 if (spnego_state
->needs_mic_sign
) {
1025 status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1027 spnego_state
->mech_types
.data
,
1028 spnego_state
->mech_types
.length
,
1029 spnego_state
->mech_types
.data
,
1030 spnego_state
->mech_types
.length
,
1032 if (!NT_STATUS_IS_OK(status
)) {
1033 DBG_WARNING("failed to sign mechListMIC: %s\n",
1037 spnego_state
->needs_mic_sign
= false;
1041 if (sub_out
.length
== 0 && mech_list_mic
.length
== 0) {
1042 *out
= data_blob_null
;
1044 if (!spnego_state
->sub_sec_ready
) {
1045 /* somethings wrong here... */
1046 DBG_ERR("gensec_update not ready without output\n");
1047 return NT_STATUS_INTERNAL_ERROR
;
1050 if (ta
->negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1051 /* unless of course it did not accept */
1052 DBG_WARNING("gensec_update ok but not accepted\n");
1053 return NT_STATUS_INVALID_PARAMETER
;
1056 if (!spnego_state
->needs_mic_check
) {
1057 spnego_state
->state_position
= SPNEGO_DONE
;
1058 return NT_STATUS_OK
;
1063 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1064 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1065 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1066 spnego_out
.negTokenTarg
.responseToken
= sub_out
;
1067 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1069 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1070 DBG_WARNING("Failed to write NEG_TOKEN_TARG\n");
1071 return NT_STATUS_INVALID_PARAMETER
;
1074 spnego_state
->num_targs
++;
1076 /* set next state */
1077 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1078 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
1080 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1083 static const struct spnego_neg_ops gensec_spnego_client_negTokenTarg_ops
= {
1084 .name
= "client_negTokenTarg",
1085 .start_fn
= gensec_spnego_client_negTokenTarg_start
,
1086 .step_fn
= gensec_spnego_client_negTokenTarg_step
,
1087 .finish_fn
= gensec_spnego_client_negTokenTarg_finish
,
1090 /** create a server negTokenTarg
1092 * This is the case, where the client is the first one who sends data
1095 static NTSTATUS
gensec_spnego_server_response(struct spnego_state
*spnego_state
,
1096 TALLOC_CTX
*out_mem_ctx
,
1098 const DATA_BLOB unwrapped_out
,
1099 DATA_BLOB mech_list_mic
,
1102 struct spnego_data spnego_out
;
1105 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1106 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1107 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1108 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1110 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1111 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
1112 if (spnego_state
->mic_requested
) {
1113 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REQUEST_MIC
;
1114 spnego_state
->mic_requested
= false;
1116 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
1118 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
1119 } else if (NT_STATUS_IS_OK(nt_status
)) {
1120 if (unwrapped_out
.data
) {
1121 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
1123 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
1124 spnego_state
->state_position
= SPNEGO_DONE
;
1127 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1128 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1129 return NT_STATUS_INVALID_PARAMETER
;
1132 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
1133 spnego_state
->num_targs
++;
1138 static NTSTATUS
gensec_spnego_server_negTokenInit_start(
1139 struct gensec_security
*gensec_security
,
1140 struct spnego_state
*spnego_state
,
1141 struct spnego_neg_state
*n
,
1142 struct spnego_data
*spnego_in
,
1143 TALLOC_CTX
*in_mem_ctx
,
1149 n
->mech_types
= spnego_in
->negTokenInit
.mechTypes
;
1150 if (n
->mech_types
== NULL
) {
1151 return NT_STATUS_INVALID_PARAMETER
;
1155 n
->all_sec
= gensec_security_by_oid_list(gensec_security
,
1158 if (n
->all_sec
== NULL
) {
1159 DBG_WARNING("gensec_security_by_oid_list() failed\n");
1160 return NT_STATUS_INVALID_PARAMETER
;
1163 ok
= spnego_write_mech_types(spnego_state
,
1165 &spnego_state
->mech_types
);
1167 DBG_ERR("Failed to write mechTypes\n");
1168 return NT_STATUS_NO_MEMORY
;
1171 return n
->ops
->step_fn(gensec_security
, spnego_state
, n
,
1172 spnego_in
, NT_STATUS_OK
, in_mem_ctx
, in_next
);
1175 static NTSTATUS
gensec_spnego_server_negTokenInit_step(
1176 struct gensec_security
*gensec_security
,
1177 struct spnego_state
*spnego_state
,
1178 struct spnego_neg_state
*n
,
1179 struct spnego_data
*spnego_in
,
1180 NTSTATUS last_status
,
1181 TALLOC_CTX
*in_mem_ctx
,
1184 if (!NT_STATUS_IS_OK(last_status
)) {
1185 const struct gensec_security_ops_wrapper
*cur_sec
=
1186 &n
->all_sec
[n
->all_idx
];
1187 const char *next_mech
= n
->mech_types
[n
->mech_idx
+1];
1188 const struct gensec_security_ops_wrapper
*next_sec
= NULL
;
1189 const char *next
= NULL
;
1190 int dbg_level
= DBGLVL_WARNING
;
1191 bool allow_fallback
= false;
1192 NTSTATUS status
= last_status
;
1195 for (i
= 0; next_mech
!= NULL
&& n
->all_sec
[i
].op
!= NULL
; i
++) {
1196 if (strcmp(next_mech
, n
->all_sec
[i
].oid
) != 0) {
1200 next_sec
= &n
->all_sec
[i
];
1204 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) ||
1205 NT_STATUS_EQUAL(status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
))
1207 allow_fallback
= true;
1210 if (allow_fallback
&& next_sec
!= NULL
) {
1211 next
= next_sec
->op
->name
;
1212 dbg_level
= DBGLVL_NOTICE
;
1215 DBG_PREFIX(dbg_level
, (
1216 "%s: parsing NEG_TOKEN_INIT content failed "
1217 "(next[%s]): %s\n", cur_sec
->op
->name
,
1218 next
, nt_errstr(status
)));
1222 * A hard error without a possible fallback.
1228 * Pretend we never started it
1230 gensec_spnego_reset_sub_sec(spnego_state
);
1233 * And try the next one, based on the clients
1240 * we always reset all_idx here, as the negotiation is
1241 * done via mech_idx!
1245 for (; n
->mech_types
[n
->mech_idx
] != NULL
; n
->mech_idx
++) {
1246 const char *cur_mech
= n
->mech_types
[n
->mech_idx
];
1247 const struct gensec_security_ops_wrapper
*cur_sec
= NULL
;
1249 DATA_BLOB sub_in
= data_blob_null
;
1252 for (i
= 0; n
->all_sec
[i
].op
!= NULL
; i
++) {
1253 if (strcmp(cur_mech
, n
->all_sec
[i
].oid
) != 0) {
1257 cur_sec
= &n
->all_sec
[i
];
1262 if (cur_sec
== NULL
) {
1266 status
= gensec_subcontext_start(spnego_state
,
1268 &spnego_state
->sub_sec_security
);
1269 if (!NT_STATUS_IS_OK(status
)) {
1273 /* select the sub context */
1274 status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
1276 if (!NT_STATUS_IS_OK(status
)) {
1278 * Pretend we never started it
1280 gensec_spnego_reset_sub_sec(spnego_state
);
1284 if (n
->mech_idx
== 0) {
1286 * We can use the optimistic token.
1288 sub_in
= spnego_in
->negTokenInit
.mechToken
;
1291 * Indicate the downgrade and request a
1294 spnego_state
->downgraded
= true;
1295 spnego_state
->mic_requested
= true;
1299 * Note that 'cur_sec' is temporary memory, but
1300 * cur_sec->oid points to a const string in the
1301 * backends gensec_security_ops structure.
1303 spnego_state
->neg_oid
= cur_sec
->oid
;
1305 /* we need some content from the mech */
1307 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1310 DBG_WARNING("Could not find a suitable mechtype in NEG_TOKEN_INIT\n");
1311 return NT_STATUS_INVALID_PARAMETER
;
1314 static NTSTATUS
gensec_spnego_server_negTokenInit_finish(
1315 struct gensec_security
*gensec_security
,
1316 struct spnego_state
*spnego_state
,
1317 struct spnego_neg_state
*n
,
1318 struct spnego_data
*spnego_in
,
1319 NTSTATUS sub_status
,
1320 const DATA_BLOB sub_out
,
1321 TALLOC_CTX
*out_mem_ctx
,
1324 DATA_BLOB mech_list_mic
= data_blob_null
;
1326 if (spnego_state
->simulate_w2k
) {
1328 * Windows 2000 returns the unwrapped token
1329 * also in the mech_list_mic field.
1331 * In order to verify our client code,
1332 * we need a way to have a server with this
1335 mech_list_mic
= sub_out
;
1338 return gensec_spnego_server_response(spnego_state
,
1346 static const struct spnego_neg_ops gensec_spnego_server_negTokenInit_ops
= {
1347 .name
= "server_negTokenInit",
1348 .start_fn
= gensec_spnego_server_negTokenInit_start
,
1349 .step_fn
= gensec_spnego_server_negTokenInit_step
,
1350 .finish_fn
= gensec_spnego_server_negTokenInit_finish
,
1353 static NTSTATUS
gensec_spnego_server_negTokenTarg_start(
1354 struct gensec_security
*gensec_security
,
1355 struct spnego_state
*spnego_state
,
1356 struct spnego_neg_state
*n
,
1357 struct spnego_data
*spnego_in
,
1358 TALLOC_CTX
*in_mem_ctx
,
1361 const struct spnego_negTokenTarg
*ta
= &spnego_in
->negTokenTarg
;
1364 spnego_state
->num_targs
++;
1366 if (spnego_state
->sub_sec_security
== NULL
) {
1367 DBG_ERR("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n");
1368 return NT_STATUS_INVALID_PARAMETER
;
1371 if (spnego_state
->needs_mic_check
) {
1372 if (ta
->responseToken
.length
!= 0) {
1373 DBG_WARNING("non empty response token not expected\n");
1374 return NT_STATUS_INVALID_PARAMETER
;
1377 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1378 spnego_state
->mech_types
.data
,
1379 spnego_state
->mech_types
.length
,
1380 spnego_state
->mech_types
.data
,
1381 spnego_state
->mech_types
.length
,
1383 if (!NT_STATUS_IS_OK(status
)) {
1384 DBG_WARNING("failed to verify mechListMIC: %s\n",
1389 spnego_state
->needs_mic_check
= false;
1390 spnego_state
->done_mic_check
= true;
1391 return NT_STATUS_OK
;
1394 if (!spnego_state
->sub_sec_ready
) {
1395 *in_next
= ta
->responseToken
;
1396 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1399 return NT_STATUS_OK
;
1402 static NTSTATUS
gensec_spnego_server_negTokenTarg_step(
1403 struct gensec_security
*gensec_security
,
1404 struct spnego_state
*spnego_state
,
1405 struct spnego_neg_state
*n
,
1406 struct spnego_data
*spnego_in
,
1407 NTSTATUS last_status
,
1408 TALLOC_CTX
*in_mem_ctx
,
1411 if (GENSEC_UPDATE_IS_NTERROR(last_status
)) {
1412 DBG_NOTICE("SPNEGO(%s) login failed: %s\n",
1413 spnego_state
->sub_sec_security
->ops
->name
,
1414 nt_errstr(last_status
));
1419 * This should never be reached!
1420 * The step function is only called on errors!
1422 smb_panic(__location__
);
1423 return NT_STATUS_INTERNAL_ERROR
;
1426 static NTSTATUS
gensec_spnego_server_negTokenTarg_finish(
1427 struct gensec_security
*gensec_security
,
1428 struct spnego_state
*spnego_state
,
1429 struct spnego_neg_state
*n
,
1430 struct spnego_data
*spnego_in
,
1431 NTSTATUS sub_status
,
1432 const DATA_BLOB sub_out
,
1433 TALLOC_CTX
*out_mem_ctx
,
1436 const struct spnego_negTokenTarg
*ta
= &spnego_in
->negTokenTarg
;
1437 DATA_BLOB mech_list_mic
= data_blob_null
;
1439 bool have_sign
= true;
1440 bool new_spnego
= false;
1442 status
= sub_status
;
1444 if (!spnego_state
->sub_sec_ready
) {
1446 * We're not yet ready to deal with signatures.
1448 goto server_response
;
1451 if (spnego_state
->done_mic_check
) {
1453 * We already checked the mic,
1454 * either the in last round here
1455 * in gensec_spnego_server_negTokenTarg_finish()
1456 * or during this round in
1457 * gensec_spnego_server_negTokenTarg_start().
1459 * Both cases we're sure we don't have to
1460 * call gensec_sign_packet().
1462 goto server_response
;
1465 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
1466 GENSEC_FEATURE_SIGN
);
1467 if (spnego_state
->simulate_w2k
) {
1470 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1471 GENSEC_FEATURE_NEW_SPNEGO
);
1472 if (ta
->mechListMIC
.length
> 0) {
1476 if (have_sign
&& new_spnego
) {
1477 spnego_state
->needs_mic_check
= true;
1478 spnego_state
->needs_mic_sign
= true;
1481 if (have_sign
&& ta
->mechListMIC
.length
> 0) {
1482 status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1483 spnego_state
->mech_types
.data
,
1484 spnego_state
->mech_types
.length
,
1485 spnego_state
->mech_types
.data
,
1486 spnego_state
->mech_types
.length
,
1488 if (!NT_STATUS_IS_OK(status
)) {
1489 DBG_WARNING("failed to verify mechListMIC: %s\n",
1494 spnego_state
->needs_mic_check
= false;
1495 spnego_state
->done_mic_check
= true;
1498 if (spnego_state
->needs_mic_sign
) {
1499 status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1501 spnego_state
->mech_types
.data
,
1502 spnego_state
->mech_types
.length
,
1503 spnego_state
->mech_types
.data
,
1504 spnego_state
->mech_types
.length
,
1506 if (!NT_STATUS_IS_OK(status
)) {
1507 DBG_WARNING("failed to sign mechListMIC: %s\n",
1511 spnego_state
->needs_mic_sign
= false;
1514 if (spnego_state
->needs_mic_check
) {
1515 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1519 return gensec_spnego_server_response(spnego_state
,
1527 static const struct spnego_neg_ops gensec_spnego_server_negTokenTarg_ops
= {
1528 .name
= "server_negTokenTarg",
1529 .start_fn
= gensec_spnego_server_negTokenTarg_start
,
1530 .step_fn
= gensec_spnego_server_negTokenTarg_step
,
1531 .finish_fn
= gensec_spnego_server_negTokenTarg_finish
,
1534 struct gensec_spnego_update_state
{
1535 struct tevent_context
*ev
;
1536 struct gensec_security
*gensec
;
1537 struct spnego_state
*spnego
;
1540 struct spnego_data _spnego_in
;
1541 struct spnego_data
*spnego_in
;
1550 struct spnego_neg_state
*n
;
1556 static void gensec_spnego_update_cleanup(struct tevent_req
*req
,
1557 enum tevent_req_state req_state
)
1559 struct gensec_spnego_update_state
*state
=
1560 tevent_req_data(req
,
1561 struct gensec_spnego_update_state
);
1563 switch (req_state
) {
1564 case TEVENT_REQ_USER_ERROR
:
1565 case TEVENT_REQ_TIMED_OUT
:
1566 case TEVENT_REQ_NO_MEMORY
:
1568 * A fatal error, further updates are not allowed.
1570 state
->spnego
->state_position
= SPNEGO_DONE
;
1577 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1578 const DATA_BLOB in
, TALLOC_CTX
*mem_ctx
,
1579 DATA_BLOB
*full_in
);
1580 static void gensec_spnego_update_pre(struct tevent_req
*req
);
1581 static void gensec_spnego_update_done(struct tevent_req
*subreq
);
1582 static void gensec_spnego_update_post(struct tevent_req
*req
);
1583 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1584 TALLOC_CTX
*out_mem_ctx
,
1587 static struct tevent_req
*gensec_spnego_update_send(TALLOC_CTX
*mem_ctx
,
1588 struct tevent_context
*ev
,
1589 struct gensec_security
*gensec_security
,
1592 struct spnego_state
*spnego_state
=
1593 talloc_get_type_abort(gensec_security
->private_data
,
1594 struct spnego_state
);
1595 struct tevent_req
*req
= NULL
;
1596 struct gensec_spnego_update_state
*state
= NULL
;
1600 req
= tevent_req_create(mem_ctx
, &state
,
1601 struct gensec_spnego_update_state
);
1606 state
->gensec
= gensec_security
;
1607 state
->spnego
= spnego_state
;
1608 tevent_req_set_cleanup_fn(req
, gensec_spnego_update_cleanup
);
1610 if (spnego_state
->out_frag
.length
> 0) {
1611 if (in
.length
> 0) {
1612 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1613 return tevent_req_post(req
, ev
);
1616 status
= gensec_spnego_update_out(gensec_security
,
1617 state
, &state
->out
);
1618 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1619 tevent_req_nterror(req
, status
);
1620 return tevent_req_post(req
, ev
);
1623 state
->status
= status
;
1624 tevent_req_done(req
);
1625 return tevent_req_post(req
, ev
);
1628 status
= gensec_spnego_update_in(gensec_security
, in
,
1629 state
, &state
->full_in
);
1630 state
->status
= status
;
1631 if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1632 tevent_req_done(req
);
1633 return tevent_req_post(req
, ev
);
1635 if (tevent_req_nterror(req
, status
)) {
1636 return tevent_req_post(req
, ev
);
1639 /* Check if we got a valid SPNEGO blob... */
1641 switch (spnego_state
->state_position
) {
1642 case SPNEGO_FALLBACK
:
1645 case SPNEGO_CLIENT_TARG
:
1646 case SPNEGO_SERVER_TARG
:
1647 if (state
->full_in
.length
== 0) {
1648 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1649 return tevent_req_post(req
, ev
);
1653 case SPNEGO_CLIENT_START
:
1654 case SPNEGO_SERVER_START
:
1656 if (state
->full_in
.length
== 0) {
1657 /* create_negTokenInit later */
1661 len
= spnego_read_data(state
,
1663 &state
->_spnego_in
);
1665 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
1666 DEBUG(1, ("Invalid SPNEGO request:\n"));
1667 dump_data(1, state
->full_in
.data
,
1668 state
->full_in
.length
);
1669 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1670 return tevent_req_post(req
, ev
);
1674 * This is the 'fallback' case, where we don't get
1675 * SPNEGO, and have to try all the other options (and
1676 * hope they all have a magic string they check)
1678 status
= gensec_spnego_server_try_fallback(gensec_security
,
1682 if (tevent_req_nterror(req
, status
)) {
1683 return tevent_req_post(req
, ev
);
1687 * We'll continue with SPNEGO_FALLBACK below...
1691 state
->spnego_in
= &state
->_spnego_in
;
1693 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1694 if (state
->spnego_in
->type
!= spnego_state
->expected_packet
) {
1695 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n",
1696 state
->spnego_in
->type
,
1697 spnego_state
->expected_packet
));
1698 dump_data(1, state
->full_in
.data
,
1699 state
->full_in
.length
);
1700 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1701 return tevent_req_post(req
, ev
);
1707 smb_panic(__location__
);
1711 gensec_spnego_update_pre(req
);
1712 if (!tevent_req_is_in_progress(req
)) {
1713 return tevent_req_post(req
, ev
);
1716 if (state
->sub
.needed
) {
1717 struct tevent_req
*subreq
= NULL
;
1720 * We may need one more roundtrip...
1722 subreq
= gensec_update_send(state
, state
->ev
,
1723 spnego_state
->sub_sec_security
,
1725 if (tevent_req_nomem(subreq
, req
)) {
1726 return tevent_req_post(req
, ev
);
1728 tevent_req_set_callback(subreq
,
1729 gensec_spnego_update_done
,
1731 state
->sub
.needed
= false;
1735 gensec_spnego_update_post(req
);
1736 if (!tevent_req_is_in_progress(req
)) {
1737 return tevent_req_post(req
, ev
);
1743 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1744 const DATA_BLOB in
, TALLOC_CTX
*mem_ctx
,
1747 struct spnego_state
*spnego_state
=
1748 talloc_get_type_abort(gensec_security
->private_data
,
1749 struct spnego_state
);
1753 *full_in
= data_blob_null
;
1755 switch (spnego_state
->state_position
) {
1756 case SPNEGO_FALLBACK
:
1758 spnego_state
->in_needed
= 0;
1759 return NT_STATUS_OK
;
1761 case SPNEGO_CLIENT_START
:
1762 case SPNEGO_CLIENT_TARG
:
1763 case SPNEGO_SERVER_START
:
1764 case SPNEGO_SERVER_TARG
:
1769 return NT_STATUS_INVALID_PARAMETER
;
1772 if (spnego_state
->in_needed
== 0) {
1777 * try to work out the size of the full
1778 * input token, it might be fragmented
1780 ret
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1781 if ((ret
!= 0) && (ret
!= EAGAIN
)) {
1782 ret
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1785 if ((ret
== 0) || (ret
== EAGAIN
)) {
1786 spnego_state
->in_needed
= size
;
1789 * If it is not an asn1 message
1790 * just call the next layer.
1792 spnego_state
->in_needed
= in
.length
;
1796 if (spnego_state
->in_needed
> UINT16_MAX
) {
1798 * limit the incoming message to 0xFFFF
1799 * to avoid DoS attacks.
1801 return NT_STATUS_INVALID_BUFFER_SIZE
;
1804 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1806 * If we reach this, we know we got at least
1807 * part of an asn1 message, getting 0 means
1808 * the remote peer wants us to spin.
1810 return NT_STATUS_INVALID_PARAMETER
;
1813 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1814 if (in
.length
> expected
) {
1816 * we got more than expected
1818 return NT_STATUS_INVALID_PARAMETER
;
1821 if (in
.length
== spnego_state
->in_needed
) {
1823 * if the in.length contains the full blob
1826 * Note: this implies spnego_state->in_frag.length == 0,
1827 * but we do not need to check this explicitly
1828 * because we already know that we did not get
1829 * more than expected.
1832 spnego_state
->in_needed
= 0;
1833 return NT_STATUS_OK
;
1836 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1837 in
.data
, in
.length
);
1839 return NT_STATUS_NO_MEMORY
;
1842 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1843 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1846 *full_in
= spnego_state
->in_frag
;
1847 talloc_steal(mem_ctx
, full_in
->data
);
1848 spnego_state
->in_frag
= data_blob_null
;
1849 spnego_state
->in_needed
= 0;
1850 return NT_STATUS_OK
;
1853 static void gensec_spnego_update_pre(struct tevent_req
*req
)
1855 struct gensec_spnego_update_state
*state
=
1856 tevent_req_data(req
,
1857 struct gensec_spnego_update_state
);
1858 struct spnego_state
*spnego_state
= state
->spnego
;
1859 const struct spnego_neg_ops
*ops
= NULL
;
1862 state
->sub
.needed
= false;
1863 state
->sub
.in
= data_blob_null
;
1864 state
->sub
.status
= NT_STATUS_INTERNAL_ERROR
;
1865 state
->sub
.out
= data_blob_null
;
1867 if (spnego_state
->state_position
== SPNEGO_FALLBACK
) {
1868 state
->sub
.in
= state
->full_in
;
1869 state
->full_in
= data_blob_null
;
1870 state
->sub
.needed
= true;
1874 switch (spnego_state
->state_position
) {
1875 case SPNEGO_CLIENT_START
:
1876 if (state
->spnego_in
== NULL
) {
1877 /* client to produce negTokenInit */
1878 ops
= &gensec_spnego_create_negTokenInit_ops
;
1882 ops
= &gensec_spnego_client_negTokenInit_ops
;
1885 case SPNEGO_CLIENT_TARG
:
1886 ops
= &gensec_spnego_client_negTokenTarg_ops
;
1889 case SPNEGO_SERVER_START
:
1890 if (state
->spnego_in
== NULL
) {
1891 /* server to produce negTokenInit */
1892 ops
= &gensec_spnego_create_negTokenInit_ops
;
1896 ops
= &gensec_spnego_server_negTokenInit_ops
;
1899 case SPNEGO_SERVER_TARG
:
1900 ops
= &gensec_spnego_server_negTokenTarg_ops
;
1904 smb_panic(__location__
);
1908 state
->n
= gensec_spnego_neg_state(state
, ops
);
1909 if (tevent_req_nomem(state
->n
, req
)) {
1913 status
= ops
->start_fn(state
->gensec
, spnego_state
, state
->n
,
1914 state
->spnego_in
, state
, &state
->sub
.in
);
1915 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1916 tevent_req_nterror(req
, status
);
1920 if (NT_STATUS_IS_OK(status
)) {
1922 * Call finish_fn() with an empty
1923 * blob and NT_STATUS_OK.
1925 state
->sub
.status
= NT_STATUS_OK
;
1928 * MORE_PROCESSING_REQUIRED =>
1929 * we need to call gensec_update_send().
1931 state
->sub
.needed
= true;
1935 static void gensec_spnego_update_done(struct tevent_req
*subreq
)
1937 struct tevent_req
*req
=
1938 tevent_req_callback_data(subreq
,
1940 struct gensec_spnego_update_state
*state
=
1941 tevent_req_data(req
,
1942 struct gensec_spnego_update_state
);
1943 struct spnego_state
*spnego_state
= state
->spnego
;
1945 state
->sub
.status
= gensec_update_recv(subreq
, state
, &state
->sub
.out
);
1946 TALLOC_FREE(subreq
);
1947 if (NT_STATUS_IS_OK(state
->sub
.status
)) {
1948 spnego_state
->sub_sec_ready
= true;
1951 gensec_spnego_update_post(req
);
1954 static void gensec_spnego_update_post(struct tevent_req
*req
)
1956 struct gensec_spnego_update_state
*state
=
1957 tevent_req_data(req
,
1958 struct gensec_spnego_update_state
);
1959 struct spnego_state
*spnego_state
= state
->spnego
;
1960 const struct spnego_neg_ops
*ops
= NULL
;
1963 state
->sub
.in
= data_blob_null
;
1964 state
->sub
.needed
= false;
1966 if (spnego_state
->state_position
== SPNEGO_FALLBACK
) {
1967 status
= state
->sub
.status
;
1968 spnego_state
->out_frag
= state
->sub
.out
;
1969 talloc_steal(spnego_state
, spnego_state
->out_frag
.data
);
1970 state
->sub
.out
= data_blob_null
;
1974 ops
= state
->n
->ops
;
1976 if (GENSEC_UPDATE_IS_NTERROR(state
->sub
.status
)) {
1980 * gensec_update_recv() returned an error,
1981 * let's see if the step_fn() want to
1982 * handle it and negotiate something else.
1985 status
= ops
->step_fn(state
->gensec
,
1992 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
1993 tevent_req_nterror(req
, status
);
1997 state
->sub
.out
= data_blob_null
;
1998 state
->sub
.status
= NT_STATUS_INTERNAL_ERROR
;
2000 if (NT_STATUS_IS_OK(status
)) {
2002 * Call finish_fn() with an empty
2003 * blob and NT_STATUS_OK.
2005 state
->sub
.status
= NT_STATUS_OK
;
2008 * MORE_PROCESSING_REQUIRED...
2010 state
->sub
.needed
= true;
2014 if (state
->sub
.needed
) {
2015 struct tevent_req
*subreq
= NULL
;
2018 * We may need one more roundtrip...
2020 subreq
= gensec_update_send(state
, state
->ev
,
2021 spnego_state
->sub_sec_security
,
2023 if (tevent_req_nomem(subreq
, req
)) {
2026 tevent_req_set_callback(subreq
,
2027 gensec_spnego_update_done
,
2029 state
->sub
.needed
= false;
2033 status
= ops
->finish_fn(state
->gensec
,
2040 &spnego_state
->out_frag
);
2041 TALLOC_FREE(state
->n
);
2042 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
2043 tevent_req_nterror(req
, status
);
2047 if (NT_STATUS_IS_OK(status
)) {
2048 bool reset_full
= true;
2050 reset_full
= !spnego_state
->done_mic_check
;
2052 status
= gensec_may_reset_crypto(spnego_state
->sub_sec_security
,
2054 if (tevent_req_nterror(req
, status
)) {
2060 spnego_state
->out_status
= status
;
2062 status
= gensec_spnego_update_out(state
->gensec
,
2063 state
, &state
->out
);
2064 if (GENSEC_UPDATE_IS_NTERROR(status
)) {
2065 tevent_req_nterror(req
, status
);
2069 state
->status
= status
;
2070 tevent_req_done(req
);
2074 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
2075 TALLOC_CTX
*out_mem_ctx
,
2078 struct spnego_state
*spnego_state
=
2079 talloc_get_type_abort(gensec_security
->private_data
,
2080 struct spnego_state
);
2081 DATA_BLOB out
= data_blob_null
;
2084 *_out
= data_blob_null
;
2086 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
2088 * Fast path, we can deliver everything
2091 *_out
= spnego_state
->out_frag
;
2092 if (spnego_state
->out_frag
.length
> 0) {
2093 talloc_steal(out_mem_ctx
, _out
->data
);
2094 spnego_state
->out_frag
= data_blob_null
;
2097 if (!NT_STATUS_IS_OK(spnego_state
->out_status
)) {
2098 return spnego_state
->out_status
;
2102 * We're completely done, further updates are not allowed.
2104 spnego_state
->state_position
= SPNEGO_DONE
;
2105 return gensec_child_ready(gensec_security
,
2106 spnego_state
->sub_sec_security
);
2109 out
= spnego_state
->out_frag
;
2112 * copy the remaining bytes
2114 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
2115 out
.data
+ spnego_state
->out_max_length
,
2116 out
.length
- spnego_state
->out_max_length
);
2117 if (spnego_state
->out_frag
.data
== NULL
) {
2118 return NT_STATUS_NO_MEMORY
;
2122 * truncate the buffer
2124 ok
= data_blob_realloc(spnego_state
, &out
,
2125 spnego_state
->out_max_length
);
2127 return NT_STATUS_NO_MEMORY
;
2130 talloc_steal(out_mem_ctx
, out
.data
);
2132 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
2135 static NTSTATUS
gensec_spnego_update_recv(struct tevent_req
*req
,
2136 TALLOC_CTX
*out_mem_ctx
,
2139 struct gensec_spnego_update_state
*state
=
2140 tevent_req_data(req
,
2141 struct gensec_spnego_update_state
);
2144 *out
= data_blob_null
;
2146 if (tevent_req_is_nterror(req
, &status
)) {
2147 tevent_req_received(req
);
2152 talloc_steal(out_mem_ctx
, state
->out
.data
);
2153 status
= state
->status
;
2154 tevent_req_received(req
);
2158 static const char *gensec_spnego_oids
[] = {
2163 static const struct gensec_security_ops gensec_spnego_security_ops
= {
2165 .sasl_name
= "GSS-SPNEGO",
2166 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
2167 .oid
= gensec_spnego_oids
,
2168 .client_start
= gensec_spnego_client_start
,
2169 .server_start
= gensec_spnego_server_start
,
2170 .update_send
= gensec_spnego_update_send
,
2171 .update_recv
= gensec_spnego_update_recv
,
2172 .seal_packet
= gensec_child_seal_packet
,
2173 .sign_packet
= gensec_child_sign_packet
,
2174 .sig_size
= gensec_child_sig_size
,
2175 .max_wrapped_size
= gensec_child_max_wrapped_size
,
2176 .max_input_size
= gensec_child_max_input_size
,
2177 .check_packet
= gensec_child_check_packet
,
2178 .unseal_packet
= gensec_child_unseal_packet
,
2179 .wrap
= gensec_child_wrap
,
2180 .unwrap
= gensec_child_unwrap
,
2181 .session_key
= gensec_child_session_key
,
2182 .session_info
= gensec_child_session_info
,
2183 .want_feature
= gensec_child_want_feature
,
2184 .have_feature
= gensec_child_have_feature
,
2185 .expire_time
= gensec_child_expire_time
,
2186 .final_auth_type
= gensec_child_final_auth_type
,
2188 .priority
= GENSEC_SPNEGO
,
2192 _PUBLIC_ NTSTATUS
gensec_spnego_init(TALLOC_CTX
*ctx
)
2195 ret
= gensec_register(ctx
, &gensec_spnego_security_ops
);
2196 if (!NT_STATUS_IS_OK(ret
)) {
2197 DEBUG(0,("Failed to register '%s' gensec backend!\n",
2198 gensec_spnego_security_ops
.name
));