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
;
65 * The following is used to implement
66 * the update token fragmentation
70 size_t out_max_length
;
76 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
78 struct spnego_state
*spnego_state
;
80 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
82 return NT_STATUS_NO_MEMORY
;
85 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
86 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
87 spnego_state
->sub_sec_security
= NULL
;
88 spnego_state
->no_response_expected
= false;
89 spnego_state
->mech_types
= data_blob(NULL
, 0);
90 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
91 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
93 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
94 "spnego", "simulate_w2k", false);
96 gensec_security
->private_data
= spnego_state
;
100 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
102 struct spnego_state
*spnego_state
;
104 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
106 return NT_STATUS_NO_MEMORY
;
109 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
110 spnego_state
->state_position
= SPNEGO_SERVER_START
;
111 spnego_state
->sub_sec_security
= NULL
;
112 spnego_state
->no_response_expected
= false;
113 spnego_state
->mech_types
= data_blob(NULL
, 0);
114 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
115 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
117 spnego_state
->simulate_w2k
= gensec_setting_bool(gensec_security
->settings
,
118 "spnego", "simulate_w2k", false);
120 gensec_security
->private_data
= spnego_state
;
125 wrappers for the spnego_*() functions
127 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
128 uint8_t *data
, size_t length
,
129 const uint8_t *whole_pdu
, size_t pdu_length
,
130 const DATA_BLOB
*sig
)
132 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
134 if (spnego_state
->state_position
!= SPNEGO_DONE
135 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
136 return NT_STATUS_INVALID_PARAMETER
;
139 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
141 whole_pdu
, pdu_length
,
145 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
146 const uint8_t *data
, size_t length
,
147 const uint8_t *whole_pdu
, size_t pdu_length
,
148 const DATA_BLOB
*sig
)
150 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
152 if (spnego_state
->state_position
!= SPNEGO_DONE
153 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
154 return NT_STATUS_INVALID_PARAMETER
;
157 return gensec_check_packet(spnego_state
->sub_sec_security
,
159 whole_pdu
, pdu_length
,
163 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
165 uint8_t *data
, size_t length
,
166 const uint8_t *whole_pdu
, size_t pdu_length
,
169 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
171 if (spnego_state
->state_position
!= SPNEGO_DONE
172 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
173 return NT_STATUS_INVALID_PARAMETER
;
176 return gensec_seal_packet(spnego_state
->sub_sec_security
,
179 whole_pdu
, pdu_length
,
183 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
185 const uint8_t *data
, size_t length
,
186 const uint8_t *whole_pdu
, size_t pdu_length
,
189 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
191 if (spnego_state
->state_position
!= SPNEGO_DONE
192 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
193 return NT_STATUS_INVALID_PARAMETER
;
196 return gensec_sign_packet(spnego_state
->sub_sec_security
,
199 whole_pdu
, pdu_length
,
203 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
208 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
210 if (spnego_state
->state_position
!= SPNEGO_DONE
211 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
212 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
213 return NT_STATUS_INVALID_PARAMETER
;
216 return gensec_wrap(spnego_state
->sub_sec_security
,
220 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
225 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
227 if (spnego_state
->state_position
!= SPNEGO_DONE
228 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
229 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
230 return NT_STATUS_INVALID_PARAMETER
;
233 return gensec_unwrap(spnego_state
->sub_sec_security
,
237 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
239 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
241 if (spnego_state
->state_position
!= SPNEGO_DONE
242 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
246 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
249 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
251 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
253 if (spnego_state
->state_position
!= SPNEGO_DONE
254 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
258 return gensec_max_input_size(spnego_state
->sub_sec_security
);
261 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
263 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
265 if (spnego_state
->state_position
!= SPNEGO_DONE
266 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
270 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
273 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
275 DATA_BLOB
*session_key
)
277 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
278 if (!spnego_state
->sub_sec_security
) {
279 return NT_STATUS_INVALID_PARAMETER
;
282 return gensec_session_key(spnego_state
->sub_sec_security
,
287 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
289 struct auth_session_info
**session_info
)
291 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
292 if (!spnego_state
->sub_sec_security
) {
293 return NT_STATUS_INVALID_PARAMETER
;
296 return gensec_session_info(spnego_state
->sub_sec_security
,
301 /** Fallback to another GENSEC mechanism, based on magic strings
303 * This is the 'fallback' case, where we don't get SPNEGO, and have to
304 * try all the other options (and hope they all have a magic string
308 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
309 struct spnego_state
*spnego_state
,
310 struct tevent_context
*ev
,
311 TALLOC_CTX
*out_mem_ctx
,
312 const DATA_BLOB in
, DATA_BLOB
*out
)
315 const struct gensec_security_ops
**all_ops
;
317 all_ops
= gensec_security_mechs(gensec_security
, out_mem_ctx
);
319 for (i
=0; all_ops
&& all_ops
[i
]; i
++) {
323 if (gensec_security
!= NULL
&&
324 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
327 if (!all_ops
[i
]->oid
) {
332 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
333 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
341 if (!all_ops
[i
]->magic
) {
345 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
346 if (!NT_STATUS_IS_OK(nt_status
)) {
350 spnego_state
->state_position
= SPNEGO_FALLBACK
;
352 nt_status
= gensec_subcontext_start(spnego_state
,
354 &spnego_state
->sub_sec_security
);
356 if (!NT_STATUS_IS_OK(nt_status
)) {
359 /* select the sub context */
360 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
362 if (!NT_STATUS_IS_OK(nt_status
)) {
365 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
366 ev
, out_mem_ctx
, in
, out
);
369 DEBUG(1, ("Failed to parse SPNEGO request\n"));
370 return NT_STATUS_INVALID_PARAMETER
;
374 Parse the netTokenInit, either from the client, to the server, or
375 from the server to the client.
378 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
379 struct spnego_state
*spnego_state
,
380 TALLOC_CTX
*out_mem_ctx
,
381 struct tevent_context
*ev
,
382 const char * const *mechType
,
383 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
386 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
387 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
390 const struct gensec_security_ops_wrapper
*all_sec
391 = gensec_security_by_oid_list(gensec_security
,
396 ok
= spnego_write_mech_types(spnego_state
,
398 &spnego_state
->mech_types
);
400 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
401 return NT_STATUS_NO_MEMORY
;
404 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
406 for (j
=0; mechType
&& mechType
[j
]; j
++) {
407 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
408 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
412 nt_status
= gensec_subcontext_start(spnego_state
,
414 &spnego_state
->sub_sec_security
);
415 if (!NT_STATUS_IS_OK(nt_status
)) {
418 /* select the sub context */
419 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
421 if (!NT_STATUS_IS_OK(nt_status
)) {
422 talloc_free(spnego_state
->sub_sec_security
);
423 spnego_state
->sub_sec_security
= NULL
;
428 /* no optimistic token */
429 spnego_state
->neg_oid
= all_sec
[i
].oid
;
430 *unwrapped_out
= data_blob_null
;
431 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
433 * Indicate the downgrade and request a
436 spnego_state
->mic_requested
= true;
440 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
445 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
446 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
447 /* Pretend we never started it (lets the first run find some incompatible demand) */
449 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
450 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
451 talloc_free(spnego_state
->sub_sec_security
);
452 spnego_state
->sub_sec_security
= NULL
;
456 spnego_state
->neg_oid
= all_sec
[i
].oid
;
459 if (spnego_state
->sub_sec_security
) {
464 if (!spnego_state
->sub_sec_security
) {
465 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
466 return NT_STATUS_INVALID_PARAMETER
;
470 /* Having tried any optimistic token from the client (if we
471 * were the server), if we didn't get anywhere, walk our list
472 * in our preference order */
474 if (!spnego_state
->sub_sec_security
) {
475 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
476 nt_status
= gensec_subcontext_start(spnego_state
,
478 &spnego_state
->sub_sec_security
);
479 if (!NT_STATUS_IS_OK(nt_status
)) {
482 /* select the sub context */
483 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
485 if (!NT_STATUS_IS_OK(nt_status
)) {
486 talloc_free(spnego_state
->sub_sec_security
);
487 spnego_state
->sub_sec_security
= NULL
;
491 spnego_state
->neg_oid
= all_sec
[i
].oid
;
493 /* only get the helping start blob for the first OID */
494 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
500 /* it is likely that a NULL input token will
501 * not be liked by most server mechs, but if
502 * we are in the client, we want the first
503 * update packet to be able to abort the use
505 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
506 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
507 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
508 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
509 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
510 /* Pretend we never started it (lets the first run find some incompatible demand) */
512 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
513 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
514 talloc_free(spnego_state
->sub_sec_security
);
515 spnego_state
->sub_sec_security
= NULL
;
524 if (spnego_state
->sub_sec_security
) {
525 /* it is likely that a NULL input token will
526 * not be liked by most server mechs, but this
527 * does the right thing in the CIFS client.
528 * just push us along the merry-go-round
529 * again, and hope for better luck next
532 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
533 *unwrapped_out
= data_blob(NULL
, 0);
534 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
537 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
538 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
539 && !NT_STATUS_IS_OK(nt_status
)) {
540 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
541 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
542 talloc_free(spnego_state
->sub_sec_security
);
543 spnego_state
->sub_sec_security
= NULL
;
545 /* We started the mech correctly, and the
546 * input from the other side was valid.
547 * Return the error (say bad password, invalid
552 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
555 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
556 /* we could re-negotiate here, but it would only work
557 * if the client or server lied about what it could
558 * support the first time. Lets keep this code to
564 /** create a negTokenInit
566 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
568 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
569 struct spnego_state
*spnego_state
,
570 TALLOC_CTX
*out_mem_ctx
,
571 struct tevent_context
*ev
,
572 const DATA_BLOB in
, DATA_BLOB
*out
)
575 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
576 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
577 const char **mechTypes
= NULL
;
578 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
579 const struct gensec_security_ops_wrapper
*all_sec
;
581 mechTypes
= gensec_security_oids(gensec_security
,
582 out_mem_ctx
, GENSEC_OID_SPNEGO
);
584 all_sec
= gensec_security_by_oid_list(gensec_security
,
588 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
589 struct spnego_data spnego_out
;
590 const char **send_mech_types
;
593 nt_status
= gensec_subcontext_start(spnego_state
,
595 &spnego_state
->sub_sec_security
);
596 if (!NT_STATUS_IS_OK(nt_status
)) {
599 /* select the sub context */
600 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
602 if (!NT_STATUS_IS_OK(nt_status
)) {
603 talloc_free(spnego_state
->sub_sec_security
);
604 spnego_state
->sub_sec_security
= NULL
;
608 /* In the client, try and produce the first (optimistic) packet */
609 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
610 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
616 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
617 && !NT_STATUS_IS_OK(nt_status
)) {
618 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
619 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
620 talloc_free(spnego_state
->sub_sec_security
);
621 spnego_state
->sub_sec_security
= NULL
;
622 /* Pretend we never started it (lets the first run find some incompatible demand) */
628 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
630 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
633 ok
= spnego_write_mech_types(spnego_state
,
635 &spnego_state
->mech_types
);
637 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
638 return NT_STATUS_NO_MEMORY
;
641 /* List the remaining mechs as options */
642 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
643 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
644 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
646 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
647 spnego_out
.negTokenInit
.mechListMIC
648 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
650 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
653 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
655 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
656 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
657 return NT_STATUS_INVALID_PARAMETER
;
661 spnego_state
->neg_oid
= all_sec
[i
].oid
;
663 if (NT_STATUS_IS_OK(nt_status
)) {
664 spnego_state
->no_response_expected
= true;
667 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
669 talloc_free(spnego_state
->sub_sec_security
);
670 spnego_state
->sub_sec_security
= NULL
;
672 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
677 /** create a server negTokenTarg
679 * This is the case, where the client is the first one who sends data
682 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
683 TALLOC_CTX
*out_mem_ctx
,
685 const DATA_BLOB unwrapped_out
,
686 DATA_BLOB mech_list_mic
,
689 struct spnego_data spnego_out
;
690 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
693 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
694 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
695 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
696 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
698 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
699 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
700 if (spnego_state
->mic_requested
) {
701 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REQUEST_MIC
;
702 spnego_state
->mic_requested
= false;
704 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
706 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
707 } else if (NT_STATUS_IS_OK(nt_status
)) {
708 if (unwrapped_out
.data
) {
709 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
711 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
712 spnego_state
->state_position
= SPNEGO_DONE
;
714 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
715 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
716 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
717 spnego_state
->state_position
= SPNEGO_DONE
;
720 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
721 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
722 return NT_STATUS_INVALID_PARAMETER
;
725 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
726 spnego_state
->num_targs
++;
732 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
733 struct tevent_context
*ev
,
734 const DATA_BLOB in
, DATA_BLOB
*out
)
736 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
737 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
738 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
739 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
740 struct spnego_data spnego_out
;
741 struct spnego_data spnego
;
745 *out
= data_blob(NULL
, 0);
748 out_mem_ctx
= spnego_state
;
751 /* and switch into the state machine */
753 switch (spnego_state
->state_position
) {
754 case SPNEGO_FALLBACK
:
755 return gensec_update_ev(spnego_state
->sub_sec_security
, ev
,
756 out_mem_ctx
, in
, out
);
757 case SPNEGO_SERVER_START
:
762 len
= spnego_read_data(gensec_security
, in
, &spnego
);
764 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
765 ev
, out_mem_ctx
, in
, out
);
767 /* client sent NegTargetInit, we send NegTokenTarg */
769 /* OK, so it's real SPNEGO, check the packet's the one we expect */
770 if (spnego
.type
!= spnego_state
->expected_packet
) {
771 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
772 spnego_state
->expected_packet
));
773 dump_data(1, in
.data
, in
.length
);
774 spnego_free_data(&spnego
);
775 return NT_STATUS_INVALID_PARAMETER
;
778 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
782 spnego
.negTokenInit
.mechTypes
,
783 spnego
.negTokenInit
.mechToken
,
786 if (spnego_state
->simulate_w2k
) {
788 * Windows 2000 returns the unwrapped token
789 * also in the mech_list_mic field.
791 * In order to verify our client code,
792 * we need a way to have a server with this
795 mech_list_mic
= unwrapped_out
;
798 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
805 spnego_free_data(&spnego
);
809 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
810 out_mem_ctx
, ev
, in
, out
);
811 spnego_state
->state_position
= SPNEGO_SERVER_START
;
812 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
817 case SPNEGO_CLIENT_START
:
819 /* The server offers a list of mechanisms */
821 const char *my_mechs
[] = {NULL
, NULL
};
822 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
826 /* client to produce negTokenInit */
827 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
828 out_mem_ctx
, ev
, in
, out
);
829 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
830 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
834 len
= spnego_read_data(gensec_security
, in
, &spnego
);
837 DEBUG(1, ("Invalid SPNEGO request:\n"));
838 dump_data(1, in
.data
, in
.length
);
839 return NT_STATUS_INVALID_PARAMETER
;
842 /* OK, so it's real SPNEGO, check the packet's the one we expect */
843 if (spnego
.type
!= spnego_state
->expected_packet
) {
844 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
845 spnego_state
->expected_packet
));
846 dump_data(1, in
.data
, in
.length
);
847 spnego_free_data(&spnego
);
848 return NT_STATUS_INVALID_PARAMETER
;
851 if (spnego
.negTokenInit
.targetPrincipal
852 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
853 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
854 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
855 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
859 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
863 spnego
.negTokenInit
.mechTypes
,
864 spnego
.negTokenInit
.mechToken
,
867 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
868 spnego_free_data(&spnego
);
872 my_mechs
[0] = spnego_state
->neg_oid
;
874 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
875 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
876 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
877 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
878 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
879 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
881 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
882 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
883 return NT_STATUS_INVALID_PARAMETER
;
886 ok
= spnego_write_mech_types(spnego_state
,
888 &spnego_state
->mech_types
);
890 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
891 return NT_STATUS_NO_MEMORY
;
895 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
896 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
898 if (NT_STATUS_IS_OK(nt_status
)) {
899 spnego_state
->no_response_expected
= true;
902 spnego_free_data(&spnego
);
903 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
905 case SPNEGO_SERVER_TARG
:
908 bool have_sign
= true;
909 bool new_spnego
= false;
912 return NT_STATUS_INVALID_PARAMETER
;
915 len
= spnego_read_data(gensec_security
, in
, &spnego
);
918 DEBUG(1, ("Invalid SPNEGO request:\n"));
919 dump_data(1, in
.data
, in
.length
);
920 return NT_STATUS_INVALID_PARAMETER
;
923 /* OK, so it's real SPNEGO, check the packet's the one we expect */
924 if (spnego
.type
!= spnego_state
->expected_packet
) {
925 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
926 spnego_state
->expected_packet
));
927 dump_data(1, in
.data
, in
.length
);
928 spnego_free_data(&spnego
);
929 return NT_STATUS_INVALID_PARAMETER
;
932 spnego_state
->num_targs
++;
934 if (!spnego_state
->sub_sec_security
) {
935 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
936 spnego_free_data(&spnego
);
937 return NT_STATUS_INVALID_PARAMETER
;
940 if (spnego_state
->needs_mic_check
) {
941 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
942 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
943 spnego_free_data(&spnego
);
944 return NT_STATUS_INVALID_PARAMETER
;
947 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
948 spnego_state
->mech_types
.data
,
949 spnego_state
->mech_types
.length
,
950 spnego_state
->mech_types
.data
,
951 spnego_state
->mech_types
.length
,
952 &spnego
.negTokenTarg
.mechListMIC
);
953 if (NT_STATUS_IS_OK(nt_status
)) {
954 spnego_state
->needs_mic_check
= false;
955 spnego_state
->done_mic_check
= true;
957 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
958 nt_errstr(nt_status
)));
960 goto server_response
;
963 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
965 spnego
.negTokenTarg
.responseToken
,
967 if (!NT_STATUS_IS_OK(nt_status
)) {
968 goto server_response
;
971 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
972 GENSEC_FEATURE_SIGN
);
973 if (spnego_state
->simulate_w2k
) {
976 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
977 GENSEC_FEATURE_NEW_SPNEGO
);
978 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
982 if (have_sign
&& new_spnego
) {
983 spnego_state
->needs_mic_check
= true;
984 spnego_state
->needs_mic_sign
= true;
987 if (have_sign
&& spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
988 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
989 spnego_state
->mech_types
.data
,
990 spnego_state
->mech_types
.length
,
991 spnego_state
->mech_types
.data
,
992 spnego_state
->mech_types
.length
,
993 &spnego
.negTokenTarg
.mechListMIC
);
994 if (!NT_STATUS_IS_OK(nt_status
)) {
995 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
996 nt_errstr(nt_status
)));
997 goto server_response
;
1000 spnego_state
->needs_mic_check
= false;
1001 spnego_state
->done_mic_check
= true;
1004 if (spnego_state
->needs_mic_sign
) {
1005 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1007 spnego_state
->mech_types
.data
,
1008 spnego_state
->mech_types
.length
,
1009 spnego_state
->mech_types
.data
,
1010 spnego_state
->mech_types
.length
,
1012 if (!NT_STATUS_IS_OK(nt_status
)) {
1013 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1014 nt_errstr(nt_status
)));
1015 goto server_response
;
1017 spnego_state
->needs_mic_sign
= false;
1020 if (spnego_state
->needs_mic_check
) {
1021 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1025 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
1032 spnego_free_data(&spnego
);
1036 case SPNEGO_CLIENT_TARG
:
1038 NTSTATUS nt_status
= NT_STATUS_INTERNAL_ERROR
;
1041 return NT_STATUS_INVALID_PARAMETER
;
1044 len
= spnego_read_data(gensec_security
, in
, &spnego
);
1047 DEBUG(1, ("Invalid SPNEGO request:\n"));
1048 dump_data(1, in
.data
, in
.length
);
1049 return NT_STATUS_INVALID_PARAMETER
;
1052 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1053 if (spnego
.type
!= spnego_state
->expected_packet
) {
1054 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
1055 spnego_state
->expected_packet
));
1056 dump_data(1, in
.data
, in
.length
);
1057 spnego_free_data(&spnego
);
1058 return NT_STATUS_INVALID_PARAMETER
;
1061 spnego_state
->num_targs
++;
1063 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
1064 spnego_free_data(&spnego
);
1065 return NT_STATUS_LOGON_FAILURE
;
1068 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
) {
1069 spnego_state
->mic_requested
= true;
1072 /* Server didn't like our choice of mech, and chose something else */
1073 if (((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) ||
1074 (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
)) &&
1075 spnego
.negTokenTarg
.supportedMech
&&
1076 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
1077 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1078 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
),
1079 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
)));
1081 spnego_state
->no_response_expected
= false;
1082 talloc_free(spnego_state
->sub_sec_security
);
1083 nt_status
= gensec_subcontext_start(spnego_state
,
1085 &spnego_state
->sub_sec_security
);
1086 if (!NT_STATUS_IS_OK(nt_status
)) {
1087 spnego_free_data(&spnego
);
1090 /* select the sub context */
1091 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
1092 spnego
.negTokenTarg
.supportedMech
);
1093 if (!NT_STATUS_IS_OK(nt_status
)) {
1094 spnego_free_data(&spnego
);
1098 spnego_state
->neg_oid
= talloc_strdup(spnego_state
,
1099 spnego
.negTokenTarg
.supportedMech
);
1100 if (spnego_state
->neg_oid
== NULL
) {
1101 spnego_free_data(&spnego
);
1102 return NT_STATUS_NO_MEMORY
;
1106 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1107 DATA_BLOB
*m
= &spnego
.negTokenTarg
.mechListMIC
;
1108 const DATA_BLOB
*r
= &spnego
.negTokenTarg
.responseToken
;
1111 * Windows 2000 has a bug, it repeats the
1112 * responseToken in the mechListMIC field.
1114 if (m
->length
== r
->length
) {
1117 cmp
= memcmp(m
->data
, r
->data
, m
->length
);
1124 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1125 if (spnego_state
->no_response_expected
) {
1126 spnego_state
->needs_mic_check
= true;
1130 if (spnego_state
->needs_mic_check
) {
1131 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
1132 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1133 spnego_free_data(&spnego
);
1134 return NT_STATUS_INVALID_PARAMETER
;
1137 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1138 spnego_state
->mech_types
.data
,
1139 spnego_state
->mech_types
.length
,
1140 spnego_state
->mech_types
.data
,
1141 spnego_state
->mech_types
.length
,
1142 &spnego
.negTokenTarg
.mechListMIC
);
1143 if (!NT_STATUS_IS_OK(nt_status
)) {
1144 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1145 nt_errstr(nt_status
)));
1146 spnego_free_data(&spnego
);
1149 spnego_state
->needs_mic_check
= false;
1150 spnego_state
->done_mic_check
= true;
1151 goto client_response
;
1154 if (!spnego_state
->no_response_expected
) {
1155 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1157 spnego
.negTokenTarg
.responseToken
,
1159 if (!NT_STATUS_IS_OK(nt_status
)) {
1160 goto client_response
;
1163 spnego_state
->no_response_expected
= true;
1165 nt_status
= NT_STATUS_OK
;
1168 if (spnego_state
->no_response_expected
&&
1169 !spnego_state
->done_mic_check
)
1171 bool have_sign
= true;
1172 bool new_spnego
= false;
1174 have_sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
1175 GENSEC_FEATURE_SIGN
);
1176 if (spnego_state
->simulate_w2k
) {
1179 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1180 GENSEC_FEATURE_NEW_SPNEGO
);
1182 switch (spnego
.negTokenTarg
.negResult
) {
1183 case SPNEGO_ACCEPT_COMPLETED
:
1184 case SPNEGO_NONE_RESULT
:
1185 if (spnego_state
->num_targs
== 1) {
1187 * the first exchange doesn't require
1194 case SPNEGO_ACCEPT_INCOMPLETE
:
1195 case SPNEGO_REQUEST_MIC
:
1196 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1204 if (spnego_state
->mic_requested
) {
1210 if (have_sign
&& new_spnego
) {
1211 spnego_state
->needs_mic_check
= true;
1212 spnego_state
->needs_mic_sign
= true;
1216 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1217 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1218 spnego_state
->mech_types
.data
,
1219 spnego_state
->mech_types
.length
,
1220 spnego_state
->mech_types
.data
,
1221 spnego_state
->mech_types
.length
,
1222 &spnego
.negTokenTarg
.mechListMIC
);
1223 if (!NT_STATUS_IS_OK(nt_status
)) {
1224 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1225 nt_errstr(nt_status
)));
1226 spnego_free_data(&spnego
);
1229 spnego_state
->needs_mic_check
= false;
1230 spnego_state
->done_mic_check
= true;
1233 if (spnego_state
->needs_mic_sign
) {
1234 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1236 spnego_state
->mech_types
.data
,
1237 spnego_state
->mech_types
.length
,
1238 spnego_state
->mech_types
.data
,
1239 spnego_state
->mech_types
.length
,
1241 if (!NT_STATUS_IS_OK(nt_status
)) {
1242 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1243 nt_errstr(nt_status
)));
1244 spnego_free_data(&spnego
);
1247 spnego_state
->needs_mic_sign
= false;
1250 if (spnego_state
->needs_mic_check
) {
1251 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1255 spnego_free_data(&spnego
);
1257 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1258 && !NT_STATUS_IS_OK(nt_status
)) {
1259 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1260 spnego_state
->sub_sec_security
->ops
->name
,
1261 nt_errstr(nt_status
)));
1265 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1267 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1268 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1269 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1270 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1271 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1273 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1274 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1275 return NT_STATUS_INVALID_PARAMETER
;
1278 spnego_state
->num_targs
++;
1279 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1280 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1283 /* all done - server has accepted, and we agree */
1284 *out
= null_data_blob
;
1286 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1287 /* unless of course it did not accept */
1288 DEBUG(1,("gensec_update ok but not accepted\n"));
1289 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1292 spnego_state
->state_position
= SPNEGO_DONE
;
1298 /* We should not be called after we are 'done' */
1299 return NT_STATUS_INVALID_PARAMETER
;
1301 return NT_STATUS_INVALID_PARAMETER
;
1304 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1305 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1307 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1311 *full_in
= data_blob_null
;
1313 if (spnego_state
->in_needed
== 0) {
1318 * try to work out the size of the full
1319 * input token, it might be fragmented
1321 ret
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1322 if ((ret
!= 0) && (ret
!= EAGAIN
)) {
1323 ret
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1326 if ((ret
== 0) || (ret
== EAGAIN
)) {
1327 spnego_state
->in_needed
= size
;
1330 * If it is not an asn1 message
1331 * just call the next layer.
1333 spnego_state
->in_needed
= in
.length
;
1337 if (spnego_state
->in_needed
> UINT16_MAX
) {
1339 * limit the incoming message to 0xFFFF
1340 * to avoid DoS attacks.
1342 return NT_STATUS_INVALID_BUFFER_SIZE
;
1345 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1347 * If we reach this, we know we got at least
1348 * part of an asn1 message, getting 0 means
1349 * the remote peer wants us to spin.
1351 return NT_STATUS_INVALID_PARAMETER
;
1354 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1355 if (in
.length
> expected
) {
1357 * we got more than expected
1359 return NT_STATUS_INVALID_PARAMETER
;
1362 if (in
.length
== spnego_state
->in_needed
) {
1364 * if the in.length contains the full blob
1367 * Note: this implies spnego_state->in_frag.length == 0,
1368 * but we do not need to check this explicitly
1369 * because we already know that we did not get
1370 * more than expected.
1373 return NT_STATUS_OK
;
1376 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1377 in
.data
, in
.length
);
1379 return NT_STATUS_NO_MEMORY
;
1382 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1383 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1386 *full_in
= spnego_state
->in_frag
;
1387 return NT_STATUS_OK
;
1390 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1391 TALLOC_CTX
*out_mem_ctx
,
1394 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1395 DATA_BLOB out
= data_blob_null
;
1398 *_out
= data_blob_null
;
1400 if (spnego_state
->out_frag
.length
== 0) {
1401 return spnego_state
->out_status
;
1405 * There is still more data to be delivered
1406 * to the remote peer.
1409 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1411 * Fast path, we can deliver everything
1414 *_out
= spnego_state
->out_frag
;
1415 talloc_steal(out_mem_ctx
, _out
->data
);
1416 spnego_state
->out_frag
= data_blob_null
;
1417 return spnego_state
->out_status
;
1420 out
= spnego_state
->out_frag
;
1423 * copy the remaining bytes
1425 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1426 out
.data
+ spnego_state
->out_max_length
,
1427 out
.length
- spnego_state
->out_max_length
);
1428 if (spnego_state
->out_frag
.data
== NULL
) {
1429 return NT_STATUS_NO_MEMORY
;
1433 * truncate the buffer
1435 ok
= data_blob_realloc(spnego_state
, &out
,
1436 spnego_state
->out_max_length
);
1438 return NT_STATUS_NO_MEMORY
;
1441 talloc_steal(out_mem_ctx
, out
.data
);
1443 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1446 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1447 TALLOC_CTX
*out_mem_ctx
,
1448 struct tevent_context
*ev
,
1449 const DATA_BLOB in
, DATA_BLOB
*out
)
1451 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1452 DATA_BLOB full_in
= data_blob_null
;
1455 *out
= data_blob_null
;
1457 if (spnego_state
->out_frag
.length
> 0) {
1458 if (in
.length
> 0) {
1459 return NT_STATUS_INVALID_PARAMETER
;
1462 return gensec_spnego_update_out(gensec_security
,
1467 status
= gensec_spnego_update_in(gensec_security
,
1469 if (!NT_STATUS_IS_OK(status
)) {
1473 status
= gensec_spnego_update(gensec_security
,
1476 &spnego_state
->out_frag
);
1477 data_blob_free(&spnego_state
->in_frag
);
1478 spnego_state
->in_needed
= 0;
1479 if (NT_STATUS_IS_OK(status
)) {
1480 bool reset_full
= true;
1482 gensec_security
->child_security
= spnego_state
->sub_sec_security
;
1484 reset_full
= !spnego_state
->done_mic_check
;
1486 status
= gensec_may_reset_crypto(spnego_state
->sub_sec_security
,
1489 if (!NT_STATUS_IS_OK(status
) &&
1490 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1494 spnego_state
->out_status
= status
;
1496 return gensec_spnego_update_out(gensec_security
,
1501 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1504 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1506 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1507 gensec_security
->want_features
|= feature
;
1511 gensec_want_feature(spnego_state
->sub_sec_security
,
1515 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1518 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1519 if (!spnego_state
->sub_sec_security
) {
1523 return gensec_have_feature(spnego_state
->sub_sec_security
,
1527 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1529 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1531 if (!spnego_state
->sub_sec_security
) {
1532 return GENSEC_EXPIRE_TIME_INFINITY
;
1535 return gensec_expire_time(spnego_state
->sub_sec_security
);
1538 static const char *gensec_spnego_oids
[] = {
1543 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1545 .sasl_name
= "GSS-SPNEGO",
1546 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1547 .oid
= gensec_spnego_oids
,
1548 .client_start
= gensec_spnego_client_start
,
1549 .server_start
= gensec_spnego_server_start
,
1550 .update
= gensec_spnego_update_wrapper
,
1551 .seal_packet
= gensec_spnego_seal_packet
,
1552 .sign_packet
= gensec_spnego_sign_packet
,
1553 .sig_size
= gensec_spnego_sig_size
,
1554 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1555 .max_input_size
= gensec_spnego_max_input_size
,
1556 .check_packet
= gensec_spnego_check_packet
,
1557 .unseal_packet
= gensec_spnego_unseal_packet
,
1558 .wrap
= gensec_spnego_wrap
,
1559 .unwrap
= gensec_spnego_unwrap
,
1560 .session_key
= gensec_spnego_session_key
,
1561 .session_info
= gensec_spnego_session_info
,
1562 .want_feature
= gensec_spnego_want_feature
,
1563 .have_feature
= gensec_spnego_have_feature
,
1564 .expire_time
= gensec_spnego_expire_time
,
1566 .priority
= GENSEC_SPNEGO
1569 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1572 ret
= gensec_register(&gensec_spnego_security_ops
);
1573 if (!NT_STATUS_IS_OK(ret
)) {
1574 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1575 gensec_spnego_security_ops
.name
));