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
;
63 * The following is used to implement
64 * the update token fragmentation
68 size_t out_max_length
;
74 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
76 struct spnego_state
*spnego_state
;
78 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
80 return NT_STATUS_NO_MEMORY
;
83 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
84 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
85 spnego_state
->sub_sec_security
= NULL
;
86 spnego_state
->no_response_expected
= false;
87 spnego_state
->mech_types
= data_blob(NULL
, 0);
88 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
89 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
91 gensec_security
->private_data
= spnego_state
;
95 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
97 struct spnego_state
*spnego_state
;
99 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
101 return NT_STATUS_NO_MEMORY
;
104 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
105 spnego_state
->state_position
= SPNEGO_SERVER_START
;
106 spnego_state
->sub_sec_security
= NULL
;
107 spnego_state
->no_response_expected
= false;
108 spnego_state
->mech_types
= data_blob(NULL
, 0);
109 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
110 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
112 gensec_security
->private_data
= spnego_state
;
117 wrappers for the spnego_*() functions
119 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
120 uint8_t *data
, size_t length
,
121 const uint8_t *whole_pdu
, size_t pdu_length
,
122 const DATA_BLOB
*sig
)
124 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
126 if (spnego_state
->state_position
!= SPNEGO_DONE
127 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
128 return NT_STATUS_INVALID_PARAMETER
;
131 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
133 whole_pdu
, pdu_length
,
137 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
138 const uint8_t *data
, size_t length
,
139 const uint8_t *whole_pdu
, size_t pdu_length
,
140 const DATA_BLOB
*sig
)
142 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
144 if (spnego_state
->state_position
!= SPNEGO_DONE
145 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
146 return NT_STATUS_INVALID_PARAMETER
;
149 return gensec_check_packet(spnego_state
->sub_sec_security
,
151 whole_pdu
, pdu_length
,
155 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
157 uint8_t *data
, size_t length
,
158 const uint8_t *whole_pdu
, size_t pdu_length
,
161 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
163 if (spnego_state
->state_position
!= SPNEGO_DONE
164 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
165 return NT_STATUS_INVALID_PARAMETER
;
168 return gensec_seal_packet(spnego_state
->sub_sec_security
,
171 whole_pdu
, pdu_length
,
175 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
177 const uint8_t *data
, size_t length
,
178 const uint8_t *whole_pdu
, size_t pdu_length
,
181 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
183 if (spnego_state
->state_position
!= SPNEGO_DONE
184 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
185 return NT_STATUS_INVALID_PARAMETER
;
188 return gensec_sign_packet(spnego_state
->sub_sec_security
,
191 whole_pdu
, pdu_length
,
195 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
200 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
202 if (spnego_state
->state_position
!= SPNEGO_DONE
203 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
204 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
205 return NT_STATUS_INVALID_PARAMETER
;
208 return gensec_wrap(spnego_state
->sub_sec_security
,
212 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
217 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
219 if (spnego_state
->state_position
!= SPNEGO_DONE
220 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
221 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
222 return NT_STATUS_INVALID_PARAMETER
;
225 return gensec_unwrap(spnego_state
->sub_sec_security
,
229 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
231 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
233 if (spnego_state
->state_position
!= SPNEGO_DONE
234 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
238 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
241 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
243 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
245 if (spnego_state
->state_position
!= SPNEGO_DONE
246 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
250 return gensec_max_input_size(spnego_state
->sub_sec_security
);
253 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
255 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
257 if (spnego_state
->state_position
!= SPNEGO_DONE
258 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
262 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
265 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
267 DATA_BLOB
*session_key
)
269 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
270 if (!spnego_state
->sub_sec_security
) {
271 return NT_STATUS_INVALID_PARAMETER
;
274 return gensec_session_key(spnego_state
->sub_sec_security
,
279 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
281 struct auth_session_info
**session_info
)
283 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
284 if (!spnego_state
->sub_sec_security
) {
285 return NT_STATUS_INVALID_PARAMETER
;
288 return gensec_session_info(spnego_state
->sub_sec_security
,
293 /** Fallback to another GENSEC mechanism, based on magic strings
295 * This is the 'fallback' case, where we don't get SPNEGO, and have to
296 * try all the other options (and hope they all have a magic string
300 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
301 struct spnego_state
*spnego_state
,
302 struct tevent_context
*ev
,
303 TALLOC_CTX
*out_mem_ctx
,
304 const DATA_BLOB in
, DATA_BLOB
*out
)
307 const struct gensec_security_ops
**all_ops
;
309 all_ops
= gensec_security_mechs(gensec_security
, out_mem_ctx
);
311 for (i
=0; all_ops
&& all_ops
[i
]; i
++) {
315 if (gensec_security
!= NULL
&&
316 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
319 if (!all_ops
[i
]->oid
) {
324 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
325 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
333 if (!all_ops
[i
]->magic
) {
337 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
338 if (!NT_STATUS_IS_OK(nt_status
)) {
342 spnego_state
->state_position
= SPNEGO_FALLBACK
;
344 nt_status
= gensec_subcontext_start(spnego_state
,
346 &spnego_state
->sub_sec_security
);
348 if (!NT_STATUS_IS_OK(nt_status
)) {
351 /* select the sub context */
352 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
354 if (!NT_STATUS_IS_OK(nt_status
)) {
357 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
358 ev
, out_mem_ctx
, in
, out
);
361 DEBUG(1, ("Failed to parse SPNEGO request\n"));
362 return NT_STATUS_INVALID_PARAMETER
;
366 Parse the netTokenInit, either from the client, to the server, or
367 from the server to the client.
370 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
371 struct spnego_state
*spnego_state
,
372 TALLOC_CTX
*out_mem_ctx
,
373 struct tevent_context
*ev
,
374 const char * const *mechType
,
375 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
378 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
379 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
382 const struct gensec_security_ops_wrapper
*all_sec
383 = gensec_security_by_oid_list(gensec_security
,
388 ok
= spnego_write_mech_types(spnego_state
,
390 &spnego_state
->mech_types
);
392 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
393 return NT_STATUS_NO_MEMORY
;
396 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
398 for (j
=0; mechType
&& mechType
[j
]; j
++) {
399 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
400 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
404 nt_status
= gensec_subcontext_start(spnego_state
,
406 &spnego_state
->sub_sec_security
);
407 if (!NT_STATUS_IS_OK(nt_status
)) {
410 /* select the sub context */
411 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
413 if (!NT_STATUS_IS_OK(nt_status
)) {
414 talloc_free(spnego_state
->sub_sec_security
);
415 spnego_state
->sub_sec_security
= NULL
;
420 /* no optimistic token */
421 spnego_state
->neg_oid
= all_sec
[i
].oid
;
422 *unwrapped_out
= data_blob_null
;
423 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
425 * Indicate the downgrade and request a
428 spnego_state
->mic_requested
= true;
432 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
437 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
438 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
439 /* Pretend we never started it (lets the first run find some incompatible demand) */
441 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
442 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
443 talloc_free(spnego_state
->sub_sec_security
);
444 spnego_state
->sub_sec_security
= NULL
;
448 spnego_state
->neg_oid
= all_sec
[i
].oid
;
451 if (spnego_state
->sub_sec_security
) {
456 if (!spnego_state
->sub_sec_security
) {
457 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
458 return NT_STATUS_INVALID_PARAMETER
;
462 /* Having tried any optimistic token from the client (if we
463 * were the server), if we didn't get anywhere, walk our list
464 * in our preference order */
466 if (!spnego_state
->sub_sec_security
) {
467 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
468 nt_status
= gensec_subcontext_start(spnego_state
,
470 &spnego_state
->sub_sec_security
);
471 if (!NT_STATUS_IS_OK(nt_status
)) {
474 /* select the sub context */
475 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
477 if (!NT_STATUS_IS_OK(nt_status
)) {
478 talloc_free(spnego_state
->sub_sec_security
);
479 spnego_state
->sub_sec_security
= NULL
;
483 spnego_state
->neg_oid
= all_sec
[i
].oid
;
485 /* only get the helping start blob for the first OID */
486 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
492 /* it is likely that a NULL input token will
493 * not be liked by most server mechs, but if
494 * we are in the client, we want the first
495 * update packet to be able to abort the use
497 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
498 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
499 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
500 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
501 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
502 /* Pretend we never started it (lets the first run find some incompatible demand) */
504 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
505 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
506 talloc_free(spnego_state
->sub_sec_security
);
507 spnego_state
->sub_sec_security
= NULL
;
516 if (spnego_state
->sub_sec_security
) {
517 /* it is likely that a NULL input token will
518 * not be liked by most server mechs, but this
519 * does the right thing in the CIFS client.
520 * just push us along the merry-go-round
521 * again, and hope for better luck next
524 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
525 *unwrapped_out
= data_blob(NULL
, 0);
526 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
529 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
530 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
531 && !NT_STATUS_IS_OK(nt_status
)) {
532 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
533 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
534 talloc_free(spnego_state
->sub_sec_security
);
535 spnego_state
->sub_sec_security
= NULL
;
537 /* We started the mech correctly, and the
538 * input from the other side was valid.
539 * Return the error (say bad password, invalid
544 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
547 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
548 /* we could re-negotiate here, but it would only work
549 * if the client or server lied about what it could
550 * support the first time. Lets keep this code to
556 /** create a negTokenInit
558 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
560 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
561 struct spnego_state
*spnego_state
,
562 TALLOC_CTX
*out_mem_ctx
,
563 struct tevent_context
*ev
,
564 const DATA_BLOB in
, DATA_BLOB
*out
)
567 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
568 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
569 const char **mechTypes
= NULL
;
570 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
571 const struct gensec_security_ops_wrapper
*all_sec
;
573 mechTypes
= gensec_security_oids(gensec_security
,
574 out_mem_ctx
, GENSEC_OID_SPNEGO
);
576 all_sec
= gensec_security_by_oid_list(gensec_security
,
580 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
581 struct spnego_data spnego_out
;
582 const char **send_mech_types
;
585 nt_status
= gensec_subcontext_start(spnego_state
,
587 &spnego_state
->sub_sec_security
);
588 if (!NT_STATUS_IS_OK(nt_status
)) {
591 /* select the sub context */
592 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
594 if (!NT_STATUS_IS_OK(nt_status
)) {
595 talloc_free(spnego_state
->sub_sec_security
);
596 spnego_state
->sub_sec_security
= NULL
;
600 /* In the client, try and produce the first (optimistic) packet */
601 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
602 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
608 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
609 && !NT_STATUS_IS_OK(nt_status
)) {
610 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
611 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
612 talloc_free(spnego_state
->sub_sec_security
);
613 spnego_state
->sub_sec_security
= NULL
;
614 /* Pretend we never started it (lets the first run find some incompatible demand) */
620 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
622 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
625 ok
= spnego_write_mech_types(spnego_state
,
627 &spnego_state
->mech_types
);
629 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
630 return NT_STATUS_NO_MEMORY
;
633 /* List the remaining mechs as options */
634 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
635 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
636 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
638 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
639 spnego_out
.negTokenInit
.mechListMIC
640 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
642 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
645 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
647 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
648 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
649 return NT_STATUS_INVALID_PARAMETER
;
653 spnego_state
->neg_oid
= all_sec
[i
].oid
;
655 if (NT_STATUS_IS_OK(nt_status
)) {
656 spnego_state
->no_response_expected
= true;
659 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
661 talloc_free(spnego_state
->sub_sec_security
);
662 spnego_state
->sub_sec_security
= NULL
;
664 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
669 /** create a server negTokenTarg
671 * This is the case, where the client is the first one who sends data
674 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
675 TALLOC_CTX
*out_mem_ctx
,
677 const DATA_BLOB unwrapped_out
,
678 DATA_BLOB mech_list_mic
,
681 struct spnego_data spnego_out
;
682 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
685 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
686 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
687 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
688 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
690 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
691 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
692 if (spnego_state
->mic_requested
) {
693 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REQUEST_MIC
;
694 spnego_state
->mic_requested
= false;
696 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
698 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
699 } else if (NT_STATUS_IS_OK(nt_status
)) {
700 if (unwrapped_out
.data
) {
701 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
703 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
704 spnego_state
->state_position
= SPNEGO_DONE
;
706 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
707 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
708 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
709 spnego_state
->state_position
= SPNEGO_DONE
;
712 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
713 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
714 return NT_STATUS_INVALID_PARAMETER
;
717 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
718 spnego_state
->num_targs
++;
724 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
725 struct tevent_context
*ev
,
726 const DATA_BLOB in
, DATA_BLOB
*out
)
728 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
729 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
730 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
731 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
732 struct spnego_data spnego_out
;
733 struct spnego_data spnego
;
737 *out
= data_blob(NULL
, 0);
740 out_mem_ctx
= spnego_state
;
743 /* and switch into the state machine */
745 switch (spnego_state
->state_position
) {
746 case SPNEGO_FALLBACK
:
747 return gensec_update_ev(spnego_state
->sub_sec_security
, ev
,
748 out_mem_ctx
, in
, out
);
749 case SPNEGO_SERVER_START
:
754 len
= spnego_read_data(gensec_security
, in
, &spnego
);
756 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
757 ev
, out_mem_ctx
, in
, out
);
759 /* client sent NegTargetInit, we send NegTokenTarg */
761 /* OK, so it's real SPNEGO, check the packet's the one we expect */
762 if (spnego
.type
!= spnego_state
->expected_packet
) {
763 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
764 spnego_state
->expected_packet
));
765 dump_data(1, in
.data
, in
.length
);
766 spnego_free_data(&spnego
);
767 return NT_STATUS_INVALID_PARAMETER
;
770 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
774 spnego
.negTokenInit
.mechTypes
,
775 spnego
.negTokenInit
.mechToken
,
778 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
785 spnego_free_data(&spnego
);
789 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
790 out_mem_ctx
, ev
, in
, out
);
791 spnego_state
->state_position
= SPNEGO_SERVER_START
;
792 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
797 case SPNEGO_CLIENT_START
:
799 /* The server offers a list of mechanisms */
801 const char *my_mechs
[] = {NULL
, NULL
};
802 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
806 /* client to produce negTokenInit */
807 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
808 out_mem_ctx
, ev
, in
, out
);
809 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
810 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
814 len
= spnego_read_data(gensec_security
, in
, &spnego
);
817 DEBUG(1, ("Invalid SPNEGO request:\n"));
818 dump_data(1, in
.data
, in
.length
);
819 return NT_STATUS_INVALID_PARAMETER
;
822 /* OK, so it's real SPNEGO, check the packet's the one we expect */
823 if (spnego
.type
!= spnego_state
->expected_packet
) {
824 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
825 spnego_state
->expected_packet
));
826 dump_data(1, in
.data
, in
.length
);
827 spnego_free_data(&spnego
);
828 return NT_STATUS_INVALID_PARAMETER
;
831 if (spnego
.negTokenInit
.targetPrincipal
832 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
833 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
834 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
835 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
839 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
843 spnego
.negTokenInit
.mechTypes
,
844 spnego
.negTokenInit
.mechToken
,
847 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
848 spnego_free_data(&spnego
);
852 my_mechs
[0] = spnego_state
->neg_oid
;
854 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
855 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
856 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
857 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
858 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
859 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
861 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
862 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
863 return NT_STATUS_INVALID_PARAMETER
;
866 ok
= spnego_write_mech_types(spnego_state
,
868 &spnego_state
->mech_types
);
870 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
871 return NT_STATUS_NO_MEMORY
;
875 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
876 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
878 if (NT_STATUS_IS_OK(nt_status
)) {
879 spnego_state
->no_response_expected
= true;
882 spnego_free_data(&spnego
);
883 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
885 case SPNEGO_SERVER_TARG
:
888 bool new_spnego
= false;
891 return NT_STATUS_INVALID_PARAMETER
;
894 len
= spnego_read_data(gensec_security
, in
, &spnego
);
897 DEBUG(1, ("Invalid SPNEGO request:\n"));
898 dump_data(1, in
.data
, in
.length
);
899 return NT_STATUS_INVALID_PARAMETER
;
902 /* OK, so it's real SPNEGO, check the packet's the one we expect */
903 if (spnego
.type
!= spnego_state
->expected_packet
) {
904 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
905 spnego_state
->expected_packet
));
906 dump_data(1, in
.data
, in
.length
);
907 spnego_free_data(&spnego
);
908 return NT_STATUS_INVALID_PARAMETER
;
911 spnego_state
->num_targs
++;
913 if (!spnego_state
->sub_sec_security
) {
914 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
915 spnego_free_data(&spnego
);
916 return NT_STATUS_INVALID_PARAMETER
;
919 if (spnego_state
->needs_mic_check
) {
920 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
921 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
922 spnego_free_data(&spnego
);
923 return NT_STATUS_INVALID_PARAMETER
;
926 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
927 spnego_state
->mech_types
.data
,
928 spnego_state
->mech_types
.length
,
929 spnego_state
->mech_types
.data
,
930 spnego_state
->mech_types
.length
,
931 &spnego
.negTokenTarg
.mechListMIC
);
932 if (NT_STATUS_IS_OK(nt_status
)) {
933 spnego_state
->needs_mic_check
= false;
934 spnego_state
->done_mic_check
= true;
936 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
937 nt_errstr(nt_status
)));
939 goto server_response
;
942 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
944 spnego
.negTokenTarg
.responseToken
,
946 if (!NT_STATUS_IS_OK(nt_status
)) {
947 goto server_response
;
950 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
951 GENSEC_FEATURE_NEW_SPNEGO
);
952 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
957 spnego_state
->needs_mic_check
= true;
958 spnego_state
->needs_mic_sign
= true;
961 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
962 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
963 spnego_state
->mech_types
.data
,
964 spnego_state
->mech_types
.length
,
965 spnego_state
->mech_types
.data
,
966 spnego_state
->mech_types
.length
,
967 &spnego
.negTokenTarg
.mechListMIC
);
968 if (!NT_STATUS_IS_OK(nt_status
)) {
969 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
970 nt_errstr(nt_status
)));
971 goto server_response
;
974 spnego_state
->needs_mic_check
= false;
975 spnego_state
->done_mic_check
= true;
978 if (spnego_state
->needs_mic_sign
) {
979 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
981 spnego_state
->mech_types
.data
,
982 spnego_state
->mech_types
.length
,
983 spnego_state
->mech_types
.data
,
984 spnego_state
->mech_types
.length
,
986 if (!NT_STATUS_IS_OK(nt_status
)) {
987 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
988 nt_errstr(nt_status
)));
989 goto server_response
;
991 spnego_state
->needs_mic_sign
= false;
994 if (spnego_state
->needs_mic_check
) {
995 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
999 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
1006 spnego_free_data(&spnego
);
1010 case SPNEGO_CLIENT_TARG
:
1012 NTSTATUS nt_status
= NT_STATUS_INTERNAL_ERROR
;
1015 return NT_STATUS_INVALID_PARAMETER
;
1018 len
= spnego_read_data(gensec_security
, in
, &spnego
);
1021 DEBUG(1, ("Invalid SPNEGO request:\n"));
1022 dump_data(1, in
.data
, in
.length
);
1023 return NT_STATUS_INVALID_PARAMETER
;
1026 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1027 if (spnego
.type
!= spnego_state
->expected_packet
) {
1028 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
1029 spnego_state
->expected_packet
));
1030 dump_data(1, in
.data
, in
.length
);
1031 spnego_free_data(&spnego
);
1032 return NT_STATUS_INVALID_PARAMETER
;
1035 spnego_state
->num_targs
++;
1037 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
1038 spnego_free_data(&spnego
);
1039 return NT_STATUS_LOGON_FAILURE
;
1042 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
) {
1043 spnego_state
->mic_requested
= true;
1046 /* Server didn't like our choice of mech, and chose something else */
1047 if (((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) ||
1048 (spnego
.negTokenTarg
.negResult
== SPNEGO_REQUEST_MIC
)) &&
1049 spnego
.negTokenTarg
.supportedMech
&&
1050 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
1051 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1052 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
),
1053 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
)));
1055 spnego_state
->no_response_expected
= false;
1056 talloc_free(spnego_state
->sub_sec_security
);
1057 nt_status
= gensec_subcontext_start(spnego_state
,
1059 &spnego_state
->sub_sec_security
);
1060 if (!NT_STATUS_IS_OK(nt_status
)) {
1061 spnego_free_data(&spnego
);
1064 /* select the sub context */
1065 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
1066 spnego
.negTokenTarg
.supportedMech
);
1067 if (!NT_STATUS_IS_OK(nt_status
)) {
1068 spnego_free_data(&spnego
);
1072 spnego_state
->neg_oid
= talloc_strdup(spnego_state
,
1073 spnego
.negTokenTarg
.supportedMech
);
1074 if (spnego_state
->neg_oid
== NULL
) {
1075 spnego_free_data(&spnego
);
1076 return NT_STATUS_NO_MEMORY
;
1080 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1081 if (spnego_state
->no_response_expected
) {
1082 spnego_state
->needs_mic_check
= true;
1086 if (spnego_state
->needs_mic_check
) {
1087 if (spnego
.negTokenTarg
.responseToken
.length
!= 0) {
1088 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1089 spnego_free_data(&spnego
);
1090 return NT_STATUS_INVALID_PARAMETER
;
1093 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1094 spnego_state
->mech_types
.data
,
1095 spnego_state
->mech_types
.length
,
1096 spnego_state
->mech_types
.data
,
1097 spnego_state
->mech_types
.length
,
1098 &spnego
.negTokenTarg
.mechListMIC
);
1099 if (!NT_STATUS_IS_OK(nt_status
)) {
1100 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1101 nt_errstr(nt_status
)));
1102 spnego_free_data(&spnego
);
1105 spnego_state
->needs_mic_check
= false;
1106 spnego_state
->done_mic_check
= true;
1107 goto client_response
;
1110 if (!spnego_state
->no_response_expected
) {
1111 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1113 spnego
.negTokenTarg
.responseToken
,
1115 if (!NT_STATUS_IS_OK(nt_status
)) {
1116 goto client_response
;
1119 spnego_state
->no_response_expected
= true;
1121 nt_status
= NT_STATUS_OK
;
1124 if (spnego_state
->no_response_expected
&&
1125 !spnego_state
->done_mic_check
)
1127 bool new_spnego
= false;
1129 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1130 GENSEC_FEATURE_NEW_SPNEGO
);
1132 switch (spnego
.negTokenTarg
.negResult
) {
1133 case SPNEGO_ACCEPT_COMPLETED
:
1134 case SPNEGO_NONE_RESULT
:
1135 if (spnego_state
->num_targs
== 1) {
1137 * the first exchange doesn't require
1144 case SPNEGO_ACCEPT_INCOMPLETE
:
1145 case SPNEGO_REQUEST_MIC
:
1146 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1154 if (spnego_state
->mic_requested
) {
1157 sign
= gensec_have_feature(spnego_state
->sub_sec_security
,
1158 GENSEC_FEATURE_SIGN
);
1165 spnego_state
->needs_mic_check
= true;
1166 spnego_state
->needs_mic_sign
= true;
1170 if (spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1171 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1172 spnego_state
->mech_types
.data
,
1173 spnego_state
->mech_types
.length
,
1174 spnego_state
->mech_types
.data
,
1175 spnego_state
->mech_types
.length
,
1176 &spnego
.negTokenTarg
.mechListMIC
);
1177 if (!NT_STATUS_IS_OK(nt_status
)) {
1178 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1179 nt_errstr(nt_status
)));
1180 spnego_free_data(&spnego
);
1183 spnego_state
->needs_mic_check
= false;
1184 spnego_state
->done_mic_check
= true;
1187 if (spnego_state
->needs_mic_sign
) {
1188 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1190 spnego_state
->mech_types
.data
,
1191 spnego_state
->mech_types
.length
,
1192 spnego_state
->mech_types
.data
,
1193 spnego_state
->mech_types
.length
,
1195 if (!NT_STATUS_IS_OK(nt_status
)) {
1196 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1197 nt_errstr(nt_status
)));
1198 spnego_free_data(&spnego
);
1201 spnego_state
->needs_mic_sign
= false;
1204 if (spnego_state
->needs_mic_check
) {
1205 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1209 spnego_free_data(&spnego
);
1211 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1212 && !NT_STATUS_IS_OK(nt_status
)) {
1213 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1214 spnego_state
->sub_sec_security
->ops
->name
,
1215 nt_errstr(nt_status
)));
1219 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1221 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1222 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1223 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1224 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1225 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1227 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1228 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1229 return NT_STATUS_INVALID_PARAMETER
;
1232 spnego_state
->num_targs
++;
1233 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1234 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1237 /* all done - server has accepted, and we agree */
1238 *out
= null_data_blob
;
1240 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1241 /* unless of course it did not accept */
1242 DEBUG(1,("gensec_update ok but not accepted\n"));
1243 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1246 spnego_state
->state_position
= SPNEGO_DONE
;
1252 /* We should not be called after we are 'done' */
1253 return NT_STATUS_INVALID_PARAMETER
;
1255 return NT_STATUS_INVALID_PARAMETER
;
1258 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1259 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1261 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1265 *full_in
= data_blob_null
;
1267 if (spnego_state
->in_needed
== 0) {
1272 * try to work out the size of the full
1273 * input token, it might be fragmented
1275 ret
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1276 if ((ret
!= 0) && (ret
!= EAGAIN
)) {
1277 ret
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1280 if ((ret
== 0) || (ret
== EAGAIN
)) {
1281 spnego_state
->in_needed
= size
;
1284 * If it is not an asn1 message
1285 * just call the next layer.
1287 spnego_state
->in_needed
= in
.length
;
1291 if (spnego_state
->in_needed
> UINT16_MAX
) {
1293 * limit the incoming message to 0xFFFF
1294 * to avoid DoS attacks.
1296 return NT_STATUS_INVALID_BUFFER_SIZE
;
1299 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1301 * If we reach this, we know we got at least
1302 * part of an asn1 message, getting 0 means
1303 * the remote peer wants us to spin.
1305 return NT_STATUS_INVALID_PARAMETER
;
1308 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1309 if (in
.length
> expected
) {
1311 * we got more than expected
1313 return NT_STATUS_INVALID_PARAMETER
;
1316 if (in
.length
== spnego_state
->in_needed
) {
1318 * if the in.length contains the full blob
1321 * Note: this implies spnego_state->in_frag.length == 0,
1322 * but we do not need to check this explicitly
1323 * because we already know that we did not get
1324 * more than expected.
1327 return NT_STATUS_OK
;
1330 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1331 in
.data
, in
.length
);
1333 return NT_STATUS_NO_MEMORY
;
1336 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1337 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1340 *full_in
= spnego_state
->in_frag
;
1341 return NT_STATUS_OK
;
1344 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1345 TALLOC_CTX
*out_mem_ctx
,
1348 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1349 DATA_BLOB out
= data_blob_null
;
1351 *_out
= data_blob_null
;
1353 if (spnego_state
->out_frag
.length
== 0) {
1354 return spnego_state
->out_status
;
1358 * There is still more data to be delivered
1359 * to the remote peer.
1362 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1364 * Fast path, we can deliver everything
1367 *_out
= spnego_state
->out_frag
;
1368 talloc_steal(out_mem_ctx
, _out
->data
);
1369 spnego_state
->out_frag
= data_blob_null
;
1370 return spnego_state
->out_status
;
1373 out
= spnego_state
->out_frag
;
1376 * copy the remaining bytes
1378 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1379 out
.data
+ spnego_state
->out_max_length
,
1380 out
.length
- spnego_state
->out_max_length
);
1381 if (spnego_state
->out_frag
.data
== NULL
) {
1382 return NT_STATUS_NO_MEMORY
;
1386 * truncate the buffer
1388 data_blob_realloc(spnego_state
, &out
, spnego_state
->out_max_length
);
1390 talloc_steal(out_mem_ctx
, out
.data
);
1392 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1395 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1396 TALLOC_CTX
*out_mem_ctx
,
1397 struct tevent_context
*ev
,
1398 const DATA_BLOB in
, DATA_BLOB
*out
)
1400 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1401 DATA_BLOB full_in
= data_blob_null
;
1404 *out
= data_blob_null
;
1406 if (spnego_state
->out_frag
.length
> 0) {
1407 if (in
.length
> 0) {
1408 return NT_STATUS_INVALID_PARAMETER
;
1411 return gensec_spnego_update_out(gensec_security
,
1416 status
= gensec_spnego_update_in(gensec_security
,
1418 if (!NT_STATUS_IS_OK(status
)) {
1422 status
= gensec_spnego_update(gensec_security
,
1425 &spnego_state
->out_frag
);
1426 data_blob_free(&spnego_state
->in_frag
);
1427 spnego_state
->in_needed
= 0;
1428 if (NT_STATUS_IS_OK(status
)) {
1429 bool reset_full
= true;
1431 gensec_security
->child_security
= spnego_state
->sub_sec_security
;
1433 reset_full
= !spnego_state
->done_mic_check
;
1435 status
= gensec_may_reset_crypto(spnego_state
->sub_sec_security
,
1438 if (!NT_STATUS_IS_OK(status
) &&
1439 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1443 spnego_state
->out_status
= status
;
1445 return gensec_spnego_update_out(gensec_security
,
1450 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1453 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1455 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1456 gensec_security
->want_features
|= feature
;
1460 gensec_want_feature(spnego_state
->sub_sec_security
,
1464 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1467 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1468 if (!spnego_state
->sub_sec_security
) {
1472 return gensec_have_feature(spnego_state
->sub_sec_security
,
1476 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1478 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1480 if (!spnego_state
->sub_sec_security
) {
1481 return GENSEC_EXPIRE_TIME_INFINITY
;
1484 return gensec_expire_time(spnego_state
->sub_sec_security
);
1487 static const char *gensec_spnego_oids
[] = {
1492 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1494 .sasl_name
= "GSS-SPNEGO",
1495 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1496 .oid
= gensec_spnego_oids
,
1497 .client_start
= gensec_spnego_client_start
,
1498 .server_start
= gensec_spnego_server_start
,
1499 .update
= gensec_spnego_update_wrapper
,
1500 .seal_packet
= gensec_spnego_seal_packet
,
1501 .sign_packet
= gensec_spnego_sign_packet
,
1502 .sig_size
= gensec_spnego_sig_size
,
1503 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1504 .max_input_size
= gensec_spnego_max_input_size
,
1505 .check_packet
= gensec_spnego_check_packet
,
1506 .unseal_packet
= gensec_spnego_unseal_packet
,
1507 .wrap
= gensec_spnego_wrap
,
1508 .unwrap
= gensec_spnego_unwrap
,
1509 .session_key
= gensec_spnego_session_key
,
1510 .session_info
= gensec_spnego_session_info
,
1511 .want_feature
= gensec_spnego_want_feature
,
1512 .have_feature
= gensec_spnego_have_feature
,
1513 .expire_time
= gensec_spnego_expire_time
,
1515 .priority
= GENSEC_SPNEGO
1518 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1521 ret
= gensec_register(&gensec_spnego_security_ops
);
1522 if (!NT_STATUS_IS_OK(ret
)) {
1523 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1524 gensec_spnego_security_ops
.name
));