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/>.
26 #include "../libcli/auth/spnego.h"
27 #include "librpc/gen_ndr/ndr_dcerpc.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/gensec/gensec_internal.h"
31 #include "param/param.h"
32 #include "lib/util/asn1.h"
36 _PUBLIC_ NTSTATUS
gensec_spnego_init(void);
38 enum spnego_state_position
{
48 enum spnego_message_type expected_packet
;
49 enum spnego_state_position state_position
;
50 struct gensec_security
*sub_sec_security
;
51 bool no_response_expected
;
61 bool may_skip_mic_check
;
67 * The following is used to implement
68 * the update token fragmentation
72 size_t out_max_length
;
78 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
80 struct spnego_state
*spnego_state
;
82 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
84 return NT_STATUS_NO_MEMORY
;
87 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
88 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
89 spnego_state
->sub_sec_security
= NULL
;
90 spnego_state
->no_response_expected
= false;
91 spnego_state
->mech_types
= data_blob(NULL
, 0);
92 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
93 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
95 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
96 "spnego", "simulate_w2k", false);
98 gensec_security
->private_data
= spnego_state
;
102 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
104 struct spnego_state
*spnego_state
;
106 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
108 return NT_STATUS_NO_MEMORY
;
111 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
112 spnego_state
->state_position
= SPNEGO_SERVER_START
;
113 spnego_state
->sub_sec_security
= NULL
;
114 spnego_state
->no_response_expected
= false;
115 spnego_state
->mech_types
= data_blob(NULL
, 0);
116 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
117 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
119 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
120 "spnego", "simulate_w2k", false);
122 gensec_security
->private_data
= spnego_state
;
127 wrappers for the spnego_*() functions
129 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
130 uint8_t *data
, size_t length
,
131 const uint8_t *whole_pdu
, size_t pdu_length
,
132 const DATA_BLOB
*sig
)
134 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
136 if (spnego_state
->state_position
!= SPNEGO_DONE
137 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
138 return NT_STATUS_INVALID_PARAMETER
;
141 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
143 whole_pdu
, pdu_length
,
147 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
148 const uint8_t *data
, size_t length
,
149 const uint8_t *whole_pdu
, size_t pdu_length
,
150 const DATA_BLOB
*sig
)
152 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
154 if (spnego_state
->state_position
!= SPNEGO_DONE
155 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
156 return NT_STATUS_INVALID_PARAMETER
;
159 return gensec_check_packet(spnego_state
->sub_sec_security
,
161 whole_pdu
, pdu_length
,
165 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
167 uint8_t *data
, size_t length
,
168 const uint8_t *whole_pdu
, size_t pdu_length
,
171 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
173 if (spnego_state
->state_position
!= SPNEGO_DONE
174 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
175 return NT_STATUS_INVALID_PARAMETER
;
178 return gensec_seal_packet(spnego_state
->sub_sec_security
,
181 whole_pdu
, pdu_length
,
185 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
187 const uint8_t *data
, size_t length
,
188 const uint8_t *whole_pdu
, size_t pdu_length
,
191 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
193 if (spnego_state
->state_position
!= SPNEGO_DONE
194 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
195 return NT_STATUS_INVALID_PARAMETER
;
198 return gensec_sign_packet(spnego_state
->sub_sec_security
,
201 whole_pdu
, pdu_length
,
205 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
210 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
212 if (spnego_state
->state_position
!= SPNEGO_DONE
213 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
214 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
215 return NT_STATUS_INVALID_PARAMETER
;
218 return gensec_wrap(spnego_state
->sub_sec_security
,
222 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
227 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
229 if (spnego_state
->state_position
!= SPNEGO_DONE
230 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
231 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
232 return NT_STATUS_INVALID_PARAMETER
;
235 return gensec_unwrap(spnego_state
->sub_sec_security
,
239 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
241 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
243 if (spnego_state
->state_position
!= SPNEGO_DONE
244 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
248 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
251 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
253 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
255 if (spnego_state
->state_position
!= SPNEGO_DONE
256 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
260 return gensec_max_input_size(spnego_state
->sub_sec_security
);
263 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
265 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
267 if (spnego_state
->state_position
!= SPNEGO_DONE
268 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
272 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
275 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
277 DATA_BLOB
*session_key
)
279 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
280 if (!spnego_state
->sub_sec_security
) {
281 return NT_STATUS_INVALID_PARAMETER
;
284 return gensec_session_key(spnego_state
->sub_sec_security
,
289 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
291 struct auth_session_info
**session_info
)
293 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
294 if (!spnego_state
->sub_sec_security
) {
295 return NT_STATUS_INVALID_PARAMETER
;
298 return gensec_session_info(spnego_state
->sub_sec_security
,
303 /** Fallback to another GENSEC mechanism, based on magic strings
305 * This is the 'fallback' case, where we don't get SPNEGO, and have to
306 * try all the other options (and hope they all have a magic string
310 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
311 struct spnego_state
*spnego_state
,
312 struct tevent_context
*ev
,
313 TALLOC_CTX
*out_mem_ctx
,
314 const DATA_BLOB in
, DATA_BLOB
*out
)
317 const struct gensec_security_ops
**all_ops
;
319 all_ops
= gensec_security_mechs(gensec_security
, out_mem_ctx
);
321 for (i
=0; all_ops
&& all_ops
[i
]; i
++) {
325 if (gensec_security
!= NULL
&&
326 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
329 if (!all_ops
[i
]->oid
) {
334 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
335 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
343 if (!all_ops
[i
]->magic
) {
347 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
348 if (!NT_STATUS_IS_OK(nt_status
)) {
352 spnego_state
->state_position
= SPNEGO_FALLBACK
;
354 nt_status
= gensec_subcontext_start(spnego_state
,
356 &spnego_state
->sub_sec_security
);
358 if (!NT_STATUS_IS_OK(nt_status
)) {
361 /* select the sub context */
362 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
364 if (!NT_STATUS_IS_OK(nt_status
)) {
367 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
368 ev
, out_mem_ctx
, in
, out
);
371 DEBUG(1, ("Failed to parse SPNEGO request\n"));
372 return NT_STATUS_INVALID_PARAMETER
;
376 Parse the netTokenInit, either from the client, to the server, or
377 from the server to the client.
380 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
381 struct spnego_state
*spnego_state
,
382 TALLOC_CTX
*out_mem_ctx
,
383 struct tevent_context
*ev
,
384 const char * const *mechType
,
385 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
388 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
389 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
392 const struct gensec_security_ops_wrapper
*all_sec
393 = gensec_security_by_oid_list(gensec_security
,
398 ok
= spnego_write_mech_types(spnego_state
,
400 &spnego_state
->mech_types
);
402 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
403 return NT_STATUS_NO_MEMORY
;
406 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
408 for (j
=0; mechType
&& mechType
[j
]; j
++) {
409 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
410 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
414 nt_status
= gensec_subcontext_start(spnego_state
,
416 &spnego_state
->sub_sec_security
);
417 if (!NT_STATUS_IS_OK(nt_status
)) {
420 /* select the sub context */
421 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
423 if (!NT_STATUS_IS_OK(nt_status
)) {
424 talloc_free(spnego_state
->sub_sec_security
);
425 spnego_state
->sub_sec_security
= NULL
;
430 /* no optimistic token */
431 spnego_state
->neg_oid
= all_sec
[i
].oid
;
432 *unwrapped_out
= data_blob_null
;
433 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
435 * Indicate the downgrade and request a
438 spnego_state
->downgraded
= true;
439 spnego_state
->mic_requested
= true;
443 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
448 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
449 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
450 /* Pretend we never started it (lets the first run find some incompatible demand) */
452 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
453 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
454 talloc_free(spnego_state
->sub_sec_security
);
455 spnego_state
->sub_sec_security
= NULL
;
459 spnego_state
->neg_oid
= all_sec
[i
].oid
;
462 if (spnego_state
->sub_sec_security
) {
467 if (!spnego_state
->sub_sec_security
) {
468 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
469 return NT_STATUS_INVALID_PARAMETER
;
473 /* Having tried any optimistic token from the client (if we
474 * were the server), if we didn't get anywhere, walk our list
475 * in our preference order */
477 if (!spnego_state
->sub_sec_security
) {
478 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
479 nt_status
= gensec_subcontext_start(spnego_state
,
481 &spnego_state
->sub_sec_security
);
482 if (!NT_STATUS_IS_OK(nt_status
)) {
485 /* select the sub context */
486 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
488 if (!NT_STATUS_IS_OK(nt_status
)) {
489 talloc_free(spnego_state
->sub_sec_security
);
490 spnego_state
->sub_sec_security
= NULL
;
494 spnego_state
->neg_oid
= all_sec
[i
].oid
;
496 /* only get the helping start blob for the first OID */
497 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
503 /* it is likely that a NULL input token will
504 * not be liked by most server mechs, but if
505 * we are in the client, we want the first
506 * update packet to be able to abort the use
508 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
509 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
510 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
511 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
512 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
513 /* Pretend we never started it (lets the first run find some incompatible demand) */
515 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
516 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
517 talloc_free(spnego_state
->sub_sec_security
);
518 spnego_state
->sub_sec_security
= NULL
;
527 if (spnego_state
->sub_sec_security
) {
528 /* it is likely that a NULL input token will
529 * not be liked by most server mechs, but this
530 * does the right thing in the CIFS client.
531 * just push us along the merry-go-round
532 * again, and hope for better luck next
535 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
536 *unwrapped_out
= data_blob(NULL
, 0);
537 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
540 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
541 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
542 && !NT_STATUS_IS_OK(nt_status
)) {
543 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
544 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
545 talloc_free(spnego_state
->sub_sec_security
);
546 spnego_state
->sub_sec_security
= NULL
;
548 /* We started the mech correctly, and the
549 * input from the other side was valid.
550 * Return the error (say bad password, invalid
555 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
558 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
559 /* we could re-negotiate here, but it would only work
560 * if the client or server lied about what it could
561 * support the first time. Lets keep this code to
567 /** create a negTokenInit
569 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
571 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
572 struct spnego_state
*spnego_state
,
573 TALLOC_CTX
*out_mem_ctx
,
574 struct tevent_context
*ev
,
575 const DATA_BLOB in
, DATA_BLOB
*out
)
578 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
579 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
580 const char **mechTypes
= NULL
;
581 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
582 const struct gensec_security_ops_wrapper
*all_sec
;
584 mechTypes
= gensec_security_oids(gensec_security
,
585 out_mem_ctx
, GENSEC_OID_SPNEGO
);
587 all_sec
= gensec_security_by_oid_list(gensec_security
,
591 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
592 struct spnego_data spnego_out
;
593 const char **send_mech_types
;
596 nt_status
= gensec_subcontext_start(spnego_state
,
598 &spnego_state
->sub_sec_security
);
599 if (!NT_STATUS_IS_OK(nt_status
)) {
602 /* select the sub context */
603 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
605 if (!NT_STATUS_IS_OK(nt_status
)) {
606 talloc_free(spnego_state
->sub_sec_security
);
607 spnego_state
->sub_sec_security
= NULL
;
611 /* In the client, try and produce the first (optimistic) packet */
612 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
613 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
619 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
620 && !NT_STATUS_IS_OK(nt_status
)) {
621 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
622 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
623 talloc_free(spnego_state
->sub_sec_security
);
624 spnego_state
->sub_sec_security
= NULL
;
625 /* Pretend we never started it (lets the first run find some incompatible demand) */
631 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
633 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
636 ok
= spnego_write_mech_types(spnego_state
,
638 &spnego_state
->mech_types
);
640 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
641 return NT_STATUS_NO_MEMORY
;
644 /* List the remaining mechs as options */
645 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
646 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
647 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
649 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
650 spnego_out
.negTokenInit
.mechListMIC
651 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
653 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
656 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
658 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
659 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
660 return NT_STATUS_INVALID_PARAMETER
;
664 spnego_state
->neg_oid
= all_sec
[i
].oid
;
666 if (NT_STATUS_IS_OK(nt_status
)) {
667 spnego_state
->no_response_expected
= true;
670 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
672 talloc_free(spnego_state
->sub_sec_security
);
673 spnego_state
->sub_sec_security
= NULL
;
675 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
680 /** create a server negTokenTarg
682 * This is the case, where the client is the first one who sends data
685 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
686 TALLOC_CTX
*out_mem_ctx
,
688 const DATA_BLOB unwrapped_out
,
689 DATA_BLOB mech_list_mic
,
692 struct spnego_data spnego_out
;
693 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
696 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
697 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
698 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
699 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
701 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
702 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
703 if (spnego_state
->mic_requested
) {
704 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REQUEST_MIC
;
705 spnego_state
->mic_requested
= false;
707 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
709 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
710 } else if (NT_STATUS_IS_OK(nt_status
)) {
711 if (unwrapped_out
.data
) {
712 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
714 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
715 spnego_state
->state_position
= SPNEGO_DONE
;
717 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
718 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
719 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
720 spnego_state
->state_position
= SPNEGO_DONE
;
723 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
724 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
725 return NT_STATUS_INVALID_PARAMETER
;
728 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
729 spnego_state
->num_targs
++;
735 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
736 struct tevent_context
*ev
,
737 const DATA_BLOB in
, DATA_BLOB
*out
)
739 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
740 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
741 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
742 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
743 struct spnego_data spnego_out
;
744 struct spnego_data spnego
;
748 *out
= data_blob(NULL
, 0);
751 out_mem_ctx
= spnego_state
;
754 /* and switch into the state machine */
756 switch (spnego_state
->state_position
) {
757 case SPNEGO_FALLBACK
:
758 return gensec_update_ev(spnego_state
->sub_sec_security
, ev
,
759 out_mem_ctx
, in
, out
);
760 case SPNEGO_SERVER_START
:
765 len
= spnego_read_data(gensec_security
, in
, &spnego
);
767 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
768 ev
, out_mem_ctx
, in
, out
);
770 /* client sent NegTargetInit, we send NegTokenTarg */
772 /* OK, so it's real SPNEGO, check the packet's the one we expect */
773 if (spnego
.type
!= spnego_state
->expected_packet
) {
774 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
775 spnego_state
->expected_packet
));
776 dump_data(1, in
.data
, in
.length
);
777 spnego_free_data(&spnego
);
778 return NT_STATUS_INVALID_PARAMETER
;
781 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
785 spnego
.negTokenInit
.mechTypes
,
786 spnego
.negTokenInit
.mechToken
,
789 if (spnego_state
->simulate_w2k
) {
791 * Windows 2000 returns the unwrapped token
792 * also in the mech_list_mic field.
794 * In order to verify our client code,
795 * we need a way to have a server with this
798 mech_list_mic
= unwrapped_out
;
801 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
808 spnego_free_data(&spnego
);
812 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
813 out_mem_ctx
, ev
, in
, out
);
814 spnego_state
->state_position
= SPNEGO_SERVER_START
;
815 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
820 case SPNEGO_CLIENT_START
:
822 /* The server offers a list of mechanisms */
824 const char *my_mechs
[] = {NULL
, NULL
};
825 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
829 /* client to produce negTokenInit */
830 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
831 out_mem_ctx
, ev
, in
, out
);
832 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
833 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
837 len
= spnego_read_data(gensec_security
, in
, &spnego
);
840 DEBUG(1, ("Invalid SPNEGO request:\n"));
841 dump_data(1, in
.data
, in
.length
);
842 return NT_STATUS_INVALID_PARAMETER
;
845 /* OK, so it's real SPNEGO, check the packet's the one we expect */
846 if (spnego
.type
!= spnego_state
->expected_packet
) {
847 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
848 spnego_state
->expected_packet
));
849 dump_data(1, in
.data
, in
.length
);
850 spnego_free_data(&spnego
);
851 return NT_STATUS_INVALID_PARAMETER
;
854 if (spnego
.negTokenInit
.targetPrincipal
855 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
856 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
857 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
858 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
862 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
866 spnego
.negTokenInit
.mechTypes
,
867 spnego
.negTokenInit
.mechToken
,
870 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
871 spnego_free_data(&spnego
);
875 my_mechs
[0] = spnego_state
->neg_oid
;
877 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
878 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
879 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
880 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
881 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
882 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
884 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
885 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
886 return NT_STATUS_INVALID_PARAMETER
;
889 ok
= spnego_write_mech_types(spnego_state
,
891 &spnego_state
->mech_types
);
893 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
894 return NT_STATUS_NO_MEMORY
;
898 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
899 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
901 if (NT_STATUS_IS_OK(nt_status
)) {
902 spnego_state
->no_response_expected
= true;
905 spnego_free_data(&spnego
);
906 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
908 case SPNEGO_SERVER_TARG
:
911 bool have_sign
= true;
912 bool new_spnego
= false;
915 return NT_STATUS_INVALID_PARAMETER
;
918 len
= spnego_read_data(gensec_security
, in
, &spnego
);
921 DEBUG(1, ("Invalid SPNEGO request:\n"));
922 dump_data(1, in
.data
, in
.length
);
923 return NT_STATUS_INVALID_PARAMETER
;
926 /* OK, so it's real SPNEGO, check the packet's the one we expect */
927 if (spnego
.type
!= spnego_state
->expected_packet
) {
928 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
929 spnego_state
->expected_packet
));
930 dump_data(1, in
.data
, in
.length
);
931 spnego_free_data(&spnego
);
932 return NT_STATUS_INVALID_PARAMETER
;
935 spnego_state
->num_targs
++;
937 if (!spnego_state
->sub_sec_security
) {
938 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
939 spnego_free_data(&spnego
);
940 return NT_STATUS_INVALID_PARAMETER
;
943 if (spnego_state
->needs_mic_check
) {
944 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
945 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
946 spnego_free_data(&spnego
);
947 return NT_STATUS_INVALID_PARAMETER
;
950 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
951 spnego_state
->mech_types
.data
,
952 spnego_state
->mech_types
.length
,
953 spnego_state
->mech_types
.data
,
954 spnego_state
->mech_types
.length
,
955 &spnego
.negTokenTarg
.mechListMIC
);
956 if (NT_STATUS_IS_OK(nt_status
)) {
957 spnego_state
->needs_mic_check
= false;
958 spnego_state
->done_mic_check
= true;
960 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
961 nt_errstr(nt_status
)));
963 goto server_response
;
966 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
968 spnego
.negTokenTarg
.responseToken
,
970 if (!NT_STATUS_IS_OK(nt_status
)) {
971 goto server_response
;
974 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
975 GENSEC_FEATURE_SIGN
);
976 if (spnego_state
->simulate_w2k
) {
979 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
980 GENSEC_FEATURE_NEW_SPNEGO
);
981 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
985 if (have_sign
&& new_spnego
) {
986 spnego_state
->needs_mic_check
= true;
987 spnego_state
->needs_mic_sign
= true;
990 if (have_sign
&& spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
991 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
992 spnego_state
->mech_types
.data
,
993 spnego_state
->mech_types
.length
,
994 spnego_state
->mech_types
.data
,
995 spnego_state
->mech_types
.length
,
996 &spnego
.negTokenTarg
.mechListMIC
);
997 if (!NT_STATUS_IS_OK(nt_status
)) {
998 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
999 nt_errstr(nt_status
)));
1000 goto server_response
;
1003 spnego_state
->needs_mic_check
= false;
1004 spnego_state
->done_mic_check
= true;
1007 if (spnego_state
->needs_mic_sign
) {
1008 nt_status
= gensec_sign_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(nt_status
)) {
1016 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1017 nt_errstr(nt_status
)));
1018 goto server_response
;
1020 spnego_state
->needs_mic_sign
= false;
1023 if (spnego_state
->needs_mic_check
) {
1024 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1028 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
1035 spnego_free_data(&spnego
);
1039 case SPNEGO_CLIENT_TARG
:
1041 NTSTATUS nt_status
= NT_STATUS_INTERNAL_ERROR
;
1044 return NT_STATUS_INVALID_PARAMETER
;
1047 len
= spnego_read_data(gensec_security
, in
, &spnego
);
1050 DEBUG(1, ("Invalid SPNEGO request:\n"));
1051 dump_data(1, in
.data
, in
.length
);
1052 return NT_STATUS_INVALID_PARAMETER
;
1055 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1056 if (spnego
.type
!= spnego_state
->expected_packet
) {
1057 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
1058 spnego_state
->expected_packet
));
1059 dump_data(1, in
.data
, in
.length
);
1060 spnego_free_data(&spnego
);
1061 return NT_STATUS_INVALID_PARAMETER
;
1064 spnego_state
->num_targs
++;
1066 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
1067 spnego_free_data(&spnego
);
1068 return NT_STATUS_LOGON_FAILURE
;
1071 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
) {
1072 spnego_state
->mic_requested
= true;
1075 /* Server didn't like our choice of mech, and chose something else */
1076 if (((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) ||
1077 (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
)) &&
1078 spnego
.negTokenTarg
.supportedMech
&&
1079 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
1080 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1081 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
),
1082 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
)));
1083 spnego_state
->downgraded
= true;
1084 spnego_state
->no_response_expected
= false;
1085 talloc_free(spnego_state
->sub_sec_security
);
1086 nt_status
= gensec_subcontext_start(spnego_state
,
1088 &spnego_state
->sub_sec_security
);
1089 if (!NT_STATUS_IS_OK(nt_status
)) {
1090 spnego_free_data(&spnego
);
1093 /* select the sub context */
1094 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
1095 spnego
.negTokenTarg
.supportedMech
);
1096 if (!NT_STATUS_IS_OK(nt_status
)) {
1097 spnego_free_data(&spnego
);
1101 spnego_state
->neg_oid
= talloc_strdup(spnego_state
,
1102 spnego
.negTokenTarg
.supportedMech
);
1103 if (spnego_state
->neg_oid
== NULL
) {
1104 spnego_free_data(&spnego
);
1105 return NT_STATUS_NO_MEMORY
;
1109 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1110 DATA_BLOB
*m
= &spnego
.negTokenTarg
.mechListMIC
;
1111 const DATA_BLOB
*r
= &spnego
.negTokenTarg
.responseToken
;
1114 * Windows 2000 has a bug, it repeats the
1115 * responseToken in the mechListMIC field.
1117 if (m
->length
== r
->length
) {
1120 cmp
= memcmp(m
->data
, r
->data
, m
->length
);
1127 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1128 if (spnego_state
->no_response_expected
) {
1129 spnego_state
->needs_mic_check
= true;
1133 if (spnego_state
->needs_mic_check
) {
1134 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
1135 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1136 spnego_free_data(&spnego
);
1137 return NT_STATUS_INVALID_PARAMETER
;
1140 if (spnego
.negTokenTarg
.mechListMIC
.length
== 0
1141 && spnego_state
->may_skip_mic_check
) {
1143 * In this case we don't require
1144 * a mechListMIC from the server.
1146 * This works around bugs in the Azure
1147 * and Apple spnego implementations.
1150 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1152 spnego_state
->needs_mic_check
= false;
1153 nt_status
= NT_STATUS_OK
;
1154 goto client_response
;
1157 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1158 spnego_state
->mech_types
.data
,
1159 spnego_state
->mech_types
.length
,
1160 spnego_state
->mech_types
.data
,
1161 spnego_state
->mech_types
.length
,
1162 &spnego
.negTokenTarg
.mechListMIC
);
1163 if (!NT_STATUS_IS_OK(nt_status
)) {
1164 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1165 nt_errstr(nt_status
)));
1166 spnego_free_data(&spnego
);
1169 spnego_state
->needs_mic_check
= false;
1170 spnego_state
->done_mic_check
= true;
1171 goto client_response
;
1174 if (!spnego_state
->no_response_expected
) {
1175 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1177 spnego
.negTokenTarg
.responseToken
,
1179 if (!NT_STATUS_IS_OK(nt_status
)) {
1180 goto client_response
;
1183 spnego_state
->no_response_expected
= true;
1185 nt_status
= NT_STATUS_OK
;
1188 if (spnego_state
->no_response_expected
&&
1189 !spnego_state
->done_mic_check
)
1191 bool have_sign
= true;
1192 bool new_spnego
= false;
1194 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
1195 GENSEC_FEATURE_SIGN
);
1196 if (spnego_state
->simulate_w2k
) {
1199 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1200 GENSEC_FEATURE_NEW_SPNEGO
);
1202 switch (spnego
.negTokenTarg
.negResult
) {
1203 case SPNEGO_ACCEPT_COMPLETED
:
1204 case SPNEGO_NONE_RESULT
:
1205 if (spnego_state
->num_targs
== 1) {
1207 * the first exchange doesn't require
1215 case SPNEGO_ACCEPT_INCOMPLETE
:
1216 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1221 if (spnego_state
->downgraded
) {
1223 * A downgrade should be protected if
1230 * The caller may just asked for
1231 * GENSEC_FEATURE_SESSION_KEY, this
1232 * is only reflected in the want_features.
1235 * gensec_have_features(GENSEC_FEATURE_SIGN)
1238 if (gensec_security
->want_features
& GENSEC_FEATURE_SIGN
) {
1241 if (gensec_security
->want_features
& GENSEC_FEATURE_SEAL
) {
1245 * Here we're sure our preferred mech was
1246 * selected by the server and our caller doesn't
1247 * need GENSEC_FEATURE_SIGN nor
1248 * GENSEC_FEATURE_SEAL support.
1250 * In this case we don't require
1251 * a mechListMIC from the server.
1253 * This works around bugs in the Azure
1254 * and Apple spnego implementations.
1257 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1259 spnego_state
->may_skip_mic_check
= true;
1262 case SPNEGO_REQUEST_MIC
:
1263 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1271 if (spnego_state
->mic_requested
) {
1277 if (have_sign
&& new_spnego
) {
1278 spnego_state
->needs_mic_check
= true;
1279 spnego_state
->needs_mic_sign
= true;
1283 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1284 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1285 spnego_state
->mech_types
.data
,
1286 spnego_state
->mech_types
.length
,
1287 spnego_state
->mech_types
.data
,
1288 spnego_state
->mech_types
.length
,
1289 &spnego
.negTokenTarg
.mechListMIC
);
1290 if (!NT_STATUS_IS_OK(nt_status
)) {
1291 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1292 nt_errstr(nt_status
)));
1293 spnego_free_data(&spnego
);
1296 spnego_state
->needs_mic_check
= false;
1297 spnego_state
->done_mic_check
= true;
1300 if (spnego_state
->needs_mic_sign
) {
1301 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1303 spnego_state
->mech_types
.data
,
1304 spnego_state
->mech_types
.length
,
1305 spnego_state
->mech_types
.data
,
1306 spnego_state
->mech_types
.length
,
1308 if (!NT_STATUS_IS_OK(nt_status
)) {
1309 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1310 nt_errstr(nt_status
)));
1311 spnego_free_data(&spnego
);
1314 spnego_state
->needs_mic_sign
= false;
1317 if (spnego_state
->needs_mic_check
) {
1318 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1322 spnego_free_data(&spnego
);
1324 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1325 && !NT_STATUS_IS_OK(nt_status
)) {
1326 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1327 spnego_state
->sub_sec_security
->ops
->name
,
1328 nt_errstr(nt_status
)));
1332 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1334 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1335 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1336 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1337 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1338 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1340 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1341 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1342 return NT_STATUS_INVALID_PARAMETER
;
1345 spnego_state
->num_targs
++;
1346 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1347 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1350 /* all done - server has accepted, and we agree */
1351 *out
= null_data_blob
;
1353 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1354 /* unless of course it did not accept */
1355 DEBUG(1,("gensec_update ok but not accepted\n"));
1356 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1359 spnego_state
->state_position
= SPNEGO_DONE
;
1365 /* We should not be called after we are 'done' */
1366 return NT_STATUS_INVALID_PARAMETER
;
1368 return NT_STATUS_INVALID_PARAMETER
;
1371 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1372 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1374 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1378 *full_in
= data_blob_null
;
1380 if (spnego_state
->in_needed
== 0) {
1385 * try to work out the size of the full
1386 * input token, it might be fragmented
1388 ret
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1389 if ((ret
!= 0) && (ret
!= EAGAIN
)) {
1390 ret
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1393 if ((ret
== 0) || (ret
== EAGAIN
)) {
1394 spnego_state
->in_needed
= size
;
1397 * If it is not an asn1 message
1398 * just call the next layer.
1400 spnego_state
->in_needed
= in
.length
;
1404 if (spnego_state
->in_needed
> UINT16_MAX
) {
1406 * limit the incoming message to 0xFFFF
1407 * to avoid DoS attacks.
1409 return NT_STATUS_INVALID_BUFFER_SIZE
;
1412 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1414 * If we reach this, we know we got at least
1415 * part of an asn1 message, getting 0 means
1416 * the remote peer wants us to spin.
1418 return NT_STATUS_INVALID_PARAMETER
;
1421 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1422 if (in
.length
> expected
) {
1424 * we got more than expected
1426 return NT_STATUS_INVALID_PARAMETER
;
1429 if (in
.length
== spnego_state
->in_needed
) {
1431 * if the in.length contains the full blob
1434 * Note: this implies spnego_state->in_frag.length == 0,
1435 * but we do not need to check this explicitly
1436 * because we already know that we did not get
1437 * more than expected.
1440 return NT_STATUS_OK
;
1443 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1444 in
.data
, in
.length
);
1446 return NT_STATUS_NO_MEMORY
;
1449 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1450 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1453 *full_in
= spnego_state
->in_frag
;
1454 return NT_STATUS_OK
;
1457 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1458 TALLOC_CTX
*out_mem_ctx
,
1461 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1462 DATA_BLOB out
= data_blob_null
;
1465 *_out
= data_blob_null
;
1467 if (spnego_state
->out_frag
.length
== 0) {
1468 return spnego_state
->out_status
;
1472 * There is still more data to be delivered
1473 * to the remote peer.
1476 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1478 * Fast path, we can deliver everything
1481 *_out
= spnego_state
->out_frag
;
1482 talloc_steal(out_mem_ctx
, _out
->data
);
1483 spnego_state
->out_frag
= data_blob_null
;
1484 return spnego_state
->out_status
;
1487 out
= spnego_state
->out_frag
;
1490 * copy the remaining bytes
1492 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1493 out
.data
+ spnego_state
->out_max_length
,
1494 out
.length
- spnego_state
->out_max_length
);
1495 if (spnego_state
->out_frag
.data
== NULL
) {
1496 return NT_STATUS_NO_MEMORY
;
1500 * truncate the buffer
1502 ok
= data_blob_realloc(spnego_state
, &out
,
1503 spnego_state
->out_max_length
);
1505 return NT_STATUS_NO_MEMORY
;
1508 talloc_steal(out_mem_ctx
, out
.data
);
1510 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1513 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1514 TALLOC_CTX
*out_mem_ctx
,
1515 struct tevent_context
*ev
,
1516 const DATA_BLOB in
, DATA_BLOB
*out
)
1518 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1519 DATA_BLOB full_in
= data_blob_null
;
1522 *out
= data_blob_null
;
1524 if (spnego_state
->out_frag
.length
> 0) {
1525 if (in
.length
> 0) {
1526 return NT_STATUS_INVALID_PARAMETER
;
1529 return gensec_spnego_update_out(gensec_security
,
1534 status
= gensec_spnego_update_in(gensec_security
,
1536 if (!NT_STATUS_IS_OK(status
)) {
1540 status
= gensec_spnego_update(gensec_security
,
1543 &spnego_state
->out_frag
);
1544 data_blob_free(&spnego_state
->in_frag
);
1545 spnego_state
->in_needed
= 0;
1546 if (NT_STATUS_IS_OK(status
)) {
1547 bool reset_full
= true;
1549 gensec_security
->child_security
= spnego_state
->sub_sec_security
;
1551 reset_full
= !spnego_state
->done_mic_check
;
1553 status
= gensec_may_reset_crypto(spnego_state
->sub_sec_security
,
1556 if (!NT_STATUS_IS_OK(status
) &&
1557 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1561 spnego_state
->out_status
= status
;
1563 return gensec_spnego_update_out(gensec_security
,
1568 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1571 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1573 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1574 gensec_security
->want_features
|= feature
;
1578 gensec_want_feature(spnego_state
->sub_sec_security
,
1582 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1585 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1586 if (!spnego_state
->sub_sec_security
) {
1590 return gensec_have_feature(spnego_state
->sub_sec_security
,
1594 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1596 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1598 if (!spnego_state
->sub_sec_security
) {
1599 return GENSEC_EXPIRE_TIME_INFINITY
;
1602 return gensec_expire_time(spnego_state
->sub_sec_security
);
1605 static const char *gensec_spnego_oids
[] = {
1610 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1612 .sasl_name
= "GSS-SPNEGO",
1613 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1614 .oid
= gensec_spnego_oids
,
1615 .client_start
= gensec_spnego_client_start
,
1616 .server_start
= gensec_spnego_server_start
,
1617 .update
= gensec_spnego_update_wrapper
,
1618 .seal_packet
= gensec_spnego_seal_packet
,
1619 .sign_packet
= gensec_spnego_sign_packet
,
1620 .sig_size
= gensec_spnego_sig_size
,
1621 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1622 .max_input_size
= gensec_spnego_max_input_size
,
1623 .check_packet
= gensec_spnego_check_packet
,
1624 .unseal_packet
= gensec_spnego_unseal_packet
,
1625 .wrap
= gensec_spnego_wrap
,
1626 .unwrap
= gensec_spnego_unwrap
,
1627 .session_key
= gensec_spnego_session_key
,
1628 .session_info
= gensec_spnego_session_info
,
1629 .want_feature
= gensec_spnego_want_feature
,
1630 .have_feature
= gensec_spnego_have_feature
,
1631 .expire_time
= gensec_spnego_expire_time
,
1633 .priority
= GENSEC_SPNEGO
1636 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1639 ret
= gensec_register(&gensec_spnego_security_ops
);
1640 if (!NT_STATUS_IS_OK(ret
)) {
1641 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1642 gensec_spnego_security_ops
.name
));