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 "param/param.h"
31 #include "lib/util/asn1.h"
35 _PUBLIC_ NTSTATUS
gensec_spnego_init(void);
37 enum spnego_state_position
{
47 enum spnego_message_type expected_packet
;
48 enum spnego_state_position state_position
;
49 struct gensec_security
*sub_sec_security
;
50 bool no_response_expected
;
57 * The following is used to implement
58 * the update token fragmentation
62 size_t out_max_length
;
68 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
70 struct spnego_state
*spnego_state
;
72 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
74 return NT_STATUS_NO_MEMORY
;
77 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
78 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
79 spnego_state
->sub_sec_security
= NULL
;
80 spnego_state
->no_response_expected
= false;
81 spnego_state
->mech_types
= data_blob(NULL
, 0);
82 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
83 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
85 gensec_security
->private_data
= spnego_state
;
89 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
91 struct spnego_state
*spnego_state
;
93 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
95 return NT_STATUS_NO_MEMORY
;
98 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
99 spnego_state
->state_position
= SPNEGO_SERVER_START
;
100 spnego_state
->sub_sec_security
= NULL
;
101 spnego_state
->no_response_expected
= false;
102 spnego_state
->mech_types
= data_blob(NULL
, 0);
103 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
104 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
106 gensec_security
->private_data
= spnego_state
;
111 wrappers for the spnego_*() functions
113 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
114 uint8_t *data
, size_t length
,
115 const uint8_t *whole_pdu
, size_t pdu_length
,
116 const DATA_BLOB
*sig
)
118 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
120 if (spnego_state
->state_position
!= SPNEGO_DONE
121 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
122 return NT_STATUS_INVALID_PARAMETER
;
125 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
127 whole_pdu
, pdu_length
,
131 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
132 const uint8_t *data
, size_t length
,
133 const uint8_t *whole_pdu
, size_t pdu_length
,
134 const DATA_BLOB
*sig
)
136 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
138 if (spnego_state
->state_position
!= SPNEGO_DONE
139 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
140 return NT_STATUS_INVALID_PARAMETER
;
143 return gensec_check_packet(spnego_state
->sub_sec_security
,
145 whole_pdu
, pdu_length
,
149 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
151 uint8_t *data
, size_t length
,
152 const uint8_t *whole_pdu
, size_t pdu_length
,
155 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
157 if (spnego_state
->state_position
!= SPNEGO_DONE
158 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
159 return NT_STATUS_INVALID_PARAMETER
;
162 return gensec_seal_packet(spnego_state
->sub_sec_security
,
165 whole_pdu
, pdu_length
,
169 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
171 const uint8_t *data
, size_t length
,
172 const uint8_t *whole_pdu
, size_t pdu_length
,
175 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
177 if (spnego_state
->state_position
!= SPNEGO_DONE
178 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
179 return NT_STATUS_INVALID_PARAMETER
;
182 return gensec_sign_packet(spnego_state
->sub_sec_security
,
185 whole_pdu
, pdu_length
,
189 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
194 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
196 if (spnego_state
->state_position
!= SPNEGO_DONE
197 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
198 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
199 return NT_STATUS_INVALID_PARAMETER
;
202 return gensec_wrap(spnego_state
->sub_sec_security
,
206 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
211 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
213 if (spnego_state
->state_position
!= SPNEGO_DONE
214 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
215 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
216 return NT_STATUS_INVALID_PARAMETER
;
219 return gensec_unwrap(spnego_state
->sub_sec_security
,
223 static NTSTATUS
gensec_spnego_wrap_packets(struct gensec_security
*gensec_security
,
227 size_t *len_processed
)
229 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
231 if (spnego_state
->state_position
!= SPNEGO_DONE
232 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
233 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
234 return NT_STATUS_INVALID_PARAMETER
;
237 return gensec_wrap_packets(spnego_state
->sub_sec_security
,
242 static NTSTATUS
gensec_spnego_packet_full_request(struct gensec_security
*gensec_security
,
243 DATA_BLOB blob
, size_t *size
)
245 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
247 if (spnego_state
->state_position
!= SPNEGO_DONE
248 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
249 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
250 return NT_STATUS_INVALID_PARAMETER
;
253 return gensec_packet_full_request(spnego_state
->sub_sec_security
,
257 static NTSTATUS
gensec_spnego_unwrap_packets(struct gensec_security
*gensec_security
,
261 size_t *len_processed
)
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
) {
267 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
268 return NT_STATUS_INVALID_PARAMETER
;
271 return gensec_unwrap_packets(spnego_state
->sub_sec_security
,
276 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
278 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
280 if (spnego_state
->state_position
!= SPNEGO_DONE
281 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
285 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
288 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
290 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
292 if (spnego_state
->state_position
!= SPNEGO_DONE
293 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
297 return gensec_max_input_size(spnego_state
->sub_sec_security
);
300 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
302 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
304 if (spnego_state
->state_position
!= SPNEGO_DONE
305 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
309 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
312 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
314 DATA_BLOB
*session_key
)
316 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
317 if (!spnego_state
->sub_sec_security
) {
318 return NT_STATUS_INVALID_PARAMETER
;
321 return gensec_session_key(spnego_state
->sub_sec_security
,
326 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
328 struct auth_session_info
**session_info
)
330 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
331 if (!spnego_state
->sub_sec_security
) {
332 return NT_STATUS_INVALID_PARAMETER
;
335 return gensec_session_info(spnego_state
->sub_sec_security
,
340 /** Fallback to another GENSEC mechanism, based on magic strings
342 * This is the 'fallback' case, where we don't get SPNEGO, and have to
343 * try all the other options (and hope they all have a magic string
347 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
348 struct spnego_state
*spnego_state
,
349 struct tevent_context
*ev
,
350 TALLOC_CTX
*out_mem_ctx
,
351 const DATA_BLOB in
, DATA_BLOB
*out
)
354 struct gensec_security_ops
**all_ops
355 = gensec_security_mechs(gensec_security
, out_mem_ctx
);
356 for (i
=0; all_ops
[i
]; i
++) {
360 if (gensec_security
!= NULL
&&
361 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
364 if (!all_ops
[i
]->oid
) {
369 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
370 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
378 if (!all_ops
[i
]->magic
) {
382 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
383 if (!NT_STATUS_IS_OK(nt_status
)) {
387 spnego_state
->state_position
= SPNEGO_FALLBACK
;
389 nt_status
= gensec_subcontext_start(spnego_state
,
391 &spnego_state
->sub_sec_security
);
393 if (!NT_STATUS_IS_OK(nt_status
)) {
396 /* select the sub context */
397 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
399 if (!NT_STATUS_IS_OK(nt_status
)) {
402 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
403 ev
, out_mem_ctx
, in
, out
);
406 DEBUG(1, ("Failed to parse SPNEGO request\n"));
407 return NT_STATUS_INVALID_PARAMETER
;
411 Parse the netTokenInit, either from the client, to the server, or
412 from the server to the client.
415 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
416 struct spnego_state
*spnego_state
,
417 TALLOC_CTX
*out_mem_ctx
,
418 struct tevent_context
*ev
,
419 const char **mechType
,
420 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
423 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
424 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
427 const struct gensec_security_ops_wrapper
*all_sec
428 = gensec_security_by_oid_list(gensec_security
,
433 ok
= spnego_write_mech_types(spnego_state
,
435 &spnego_state
->mech_types
);
437 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
438 return NT_STATUS_NO_MEMORY
;
441 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
443 for (j
=0; mechType
&& mechType
[j
]; j
++) {
444 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
445 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
449 nt_status
= gensec_subcontext_start(spnego_state
,
451 &spnego_state
->sub_sec_security
);
452 if (!NT_STATUS_IS_OK(nt_status
)) {
455 /* select the sub context */
456 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
458 if (!NT_STATUS_IS_OK(nt_status
)) {
459 talloc_free(spnego_state
->sub_sec_security
);
460 spnego_state
->sub_sec_security
= NULL
;
465 /* no optimistic token */
466 spnego_state
->neg_oid
= all_sec
[i
].oid
;
467 *unwrapped_out
= data_blob_null
;
468 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
472 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
477 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
478 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
479 /* Pretend we never started it (lets the first run find some incompatible demand) */
481 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
482 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
483 talloc_free(spnego_state
->sub_sec_security
);
484 spnego_state
->sub_sec_security
= NULL
;
488 spnego_state
->neg_oid
= all_sec
[i
].oid
;
491 if (spnego_state
->sub_sec_security
) {
496 if (!spnego_state
->sub_sec_security
) {
497 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
498 return NT_STATUS_INVALID_PARAMETER
;
502 /* Having tried any optimistic token from the client (if we
503 * were the server), if we didn't get anywhere, walk our list
504 * in our preference order */
506 if (!spnego_state
->sub_sec_security
) {
507 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
508 nt_status
= gensec_subcontext_start(spnego_state
,
510 &spnego_state
->sub_sec_security
);
511 if (!NT_STATUS_IS_OK(nt_status
)) {
514 /* select the sub context */
515 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
517 if (!NT_STATUS_IS_OK(nt_status
)) {
518 talloc_free(spnego_state
->sub_sec_security
);
519 spnego_state
->sub_sec_security
= NULL
;
523 spnego_state
->neg_oid
= all_sec
[i
].oid
;
525 /* only get the helping start blob for the first OID */
526 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
532 /* it is likely that a NULL input token will
533 * not be liked by most server mechs, but if
534 * we are in the client, we want the first
535 * update packet to be able to abort the use
537 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
538 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
539 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
540 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
541 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
542 /* Pretend we never started it (lets the first run find some incompatible demand) */
544 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
545 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
546 talloc_free(spnego_state
->sub_sec_security
);
547 spnego_state
->sub_sec_security
= NULL
;
556 if (spnego_state
->sub_sec_security
) {
557 /* it is likely that a NULL input token will
558 * not be liked by most server mechs, but this
559 * does the right thing in the CIFS client.
560 * just push us along the merry-go-round
561 * again, and hope for better luck next
564 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
565 *unwrapped_out
= data_blob(NULL
, 0);
566 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
569 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
570 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
571 && !NT_STATUS_IS_OK(nt_status
)) {
572 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
573 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
574 talloc_free(spnego_state
->sub_sec_security
);
575 spnego_state
->sub_sec_security
= NULL
;
577 /* We started the mech correctly, and the
578 * input from the other side was valid.
579 * Return the error (say bad password, invalid
584 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
587 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
588 /* we could re-negotiate here, but it would only work
589 * if the client or server lied about what it could
590 * support the first time. Lets keep this code to
596 /** create a negTokenInit
598 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
600 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
601 struct spnego_state
*spnego_state
,
602 TALLOC_CTX
*out_mem_ctx
,
603 struct tevent_context
*ev
,
604 const DATA_BLOB in
, DATA_BLOB
*out
)
607 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
608 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
609 const char **mechTypes
= NULL
;
610 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
611 const struct gensec_security_ops_wrapper
*all_sec
;
613 mechTypes
= gensec_security_oids(gensec_security
,
614 out_mem_ctx
, GENSEC_OID_SPNEGO
);
616 all_sec
= gensec_security_by_oid_list(gensec_security
,
620 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
621 struct spnego_data spnego_out
;
622 const char **send_mech_types
;
625 nt_status
= gensec_subcontext_start(spnego_state
,
627 &spnego_state
->sub_sec_security
);
628 if (!NT_STATUS_IS_OK(nt_status
)) {
631 /* select the sub context */
632 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
634 if (!NT_STATUS_IS_OK(nt_status
)) {
635 talloc_free(spnego_state
->sub_sec_security
);
636 spnego_state
->sub_sec_security
= NULL
;
640 /* In the client, try and produce the first (optimistic) packet */
641 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
642 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
648 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
649 && !NT_STATUS_IS_OK(nt_status
)) {
650 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
651 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
652 talloc_free(spnego_state
->sub_sec_security
);
653 spnego_state
->sub_sec_security
= NULL
;
654 /* Pretend we never started it (lets the first run find some incompatible demand) */
660 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
662 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
665 ok
= spnego_write_mech_types(spnego_state
,
667 &spnego_state
->mech_types
);
669 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
670 return NT_STATUS_NO_MEMORY
;
673 /* List the remaining mechs as options */
674 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
675 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
676 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
678 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
679 spnego_out
.negTokenInit
.mechListMIC
680 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
682 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
685 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
687 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
688 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
689 return NT_STATUS_INVALID_PARAMETER
;
693 spnego_state
->neg_oid
= all_sec
[i
].oid
;
695 if (NT_STATUS_IS_OK(nt_status
)) {
696 spnego_state
->no_response_expected
= true;
699 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
701 talloc_free(spnego_state
->sub_sec_security
);
702 spnego_state
->sub_sec_security
= NULL
;
704 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
705 return NT_STATUS_INVALID_PARAMETER
;
709 /** create a server negTokenTarg
711 * This is the case, where the client is the first one who sends data
714 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
715 TALLOC_CTX
*out_mem_ctx
,
717 const DATA_BLOB unwrapped_out
,
718 DATA_BLOB mech_list_mic
,
721 struct spnego_data spnego_out
;
722 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
725 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
726 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
727 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
728 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
730 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
731 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
732 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
733 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
734 } else if (NT_STATUS_IS_OK(nt_status
)) {
735 if (unwrapped_out
.data
) {
736 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
738 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
739 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
740 spnego_state
->state_position
= SPNEGO_DONE
;
742 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
743 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
744 spnego_state
->state_position
= SPNEGO_DONE
;
747 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
748 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
749 return NT_STATUS_INVALID_PARAMETER
;
752 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
758 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
759 struct tevent_context
*ev
,
760 const DATA_BLOB in
, DATA_BLOB
*out
)
762 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
763 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
764 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
765 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
766 struct spnego_data spnego_out
;
767 struct spnego_data spnego
;
771 *out
= data_blob(NULL
, 0);
774 out_mem_ctx
= spnego_state
;
777 /* and switch into the state machine */
779 switch (spnego_state
->state_position
) {
780 case SPNEGO_FALLBACK
:
781 return gensec_update(spnego_state
->sub_sec_security
, ev
,
782 out_mem_ctx
, in
, out
);
783 case SPNEGO_SERVER_START
:
788 len
= spnego_read_data(gensec_security
, in
, &spnego
);
790 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
791 out_mem_ctx
, ev
, in
, out
);
793 /* client sent NegTargetInit, we send NegTokenTarg */
795 /* OK, so it's real SPNEGO, check the packet's the one we expect */
796 if (spnego
.type
!= spnego_state
->expected_packet
) {
797 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
798 spnego_state
->expected_packet
));
799 dump_data(1, in
.data
, in
.length
);
800 spnego_free_data(&spnego
);
801 return NT_STATUS_INVALID_PARAMETER
;
804 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
808 spnego
.negTokenInit
.mechTypes
,
809 spnego
.negTokenInit
.mechToken
,
812 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
819 spnego_free_data(&spnego
);
823 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
824 out_mem_ctx
, ev
, in
, out
);
825 spnego_state
->state_position
= SPNEGO_SERVER_START
;
826 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
831 case SPNEGO_CLIENT_START
:
833 /* The server offers a list of mechanisms */
835 const char *my_mechs
[] = {NULL
, NULL
};
836 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
839 /* client to produce negTokenInit */
840 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
841 out_mem_ctx
, ev
, in
, out
);
842 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
843 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
847 len
= spnego_read_data(gensec_security
, in
, &spnego
);
850 DEBUG(1, ("Invalid SPNEGO request:\n"));
851 dump_data(1, in
.data
, in
.length
);
852 return NT_STATUS_INVALID_PARAMETER
;
855 /* OK, so it's real SPNEGO, check the packet's the one we expect */
856 if (spnego
.type
!= spnego_state
->expected_packet
) {
857 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
858 spnego_state
->expected_packet
));
859 dump_data(1, in
.data
, in
.length
);
860 spnego_free_data(&spnego
);
861 return NT_STATUS_INVALID_PARAMETER
;
864 if (spnego
.negTokenInit
.targetPrincipal
865 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
866 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
867 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
868 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
872 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
876 spnego
.negTokenInit
.mechTypes
,
877 spnego
.negTokenInit
.mechToken
,
880 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
881 spnego_free_data(&spnego
);
885 my_mechs
[0] = spnego_state
->neg_oid
;
887 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
888 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
889 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
890 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
891 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
892 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
894 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
895 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
896 return NT_STATUS_INVALID_PARAMETER
;
900 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
901 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
903 if (NT_STATUS_IS_OK(nt_status
)) {
904 spnego_state
->no_response_expected
= true;
907 spnego_free_data(&spnego
);
908 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
910 case SPNEGO_SERVER_TARG
:
913 bool new_spnego
= false;
916 return NT_STATUS_INVALID_PARAMETER
;
919 len
= spnego_read_data(gensec_security
, in
, &spnego
);
922 DEBUG(1, ("Invalid SPNEGO request:\n"));
923 dump_data(1, in
.data
, in
.length
);
924 return NT_STATUS_INVALID_PARAMETER
;
927 /* OK, so it's real SPNEGO, check the packet's the one we expect */
928 if (spnego
.type
!= spnego_state
->expected_packet
) {
929 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
930 spnego_state
->expected_packet
));
931 dump_data(1, in
.data
, in
.length
);
932 spnego_free_data(&spnego
);
933 return NT_STATUS_INVALID_PARAMETER
;
936 if (!spnego_state
->sub_sec_security
) {
937 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
938 spnego_free_data(&spnego
);
939 return NT_STATUS_INVALID_PARAMETER
;
942 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
944 spnego
.negTokenTarg
.responseToken
,
946 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
948 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
949 spnego_state
->mech_types
.data
,
950 spnego_state
->mech_types
.length
,
951 spnego_state
->mech_types
.data
,
952 spnego_state
->mech_types
.length
,
953 &spnego
.negTokenTarg
.mechListMIC
);
954 if (!NT_STATUS_IS_OK(nt_status
)) {
955 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
956 nt_errstr(nt_status
)));
959 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
960 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
962 spnego_state
->mech_types
.data
,
963 spnego_state
->mech_types
.length
,
964 spnego_state
->mech_types
.data
,
965 spnego_state
->mech_types
.length
,
967 if (!NT_STATUS_IS_OK(nt_status
)) {
968 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
969 nt_errstr(nt_status
)));
973 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
980 spnego_free_data(&spnego
);
984 case SPNEGO_CLIENT_TARG
:
988 return NT_STATUS_INVALID_PARAMETER
;
991 len
= spnego_read_data(gensec_security
, in
, &spnego
);
994 DEBUG(1, ("Invalid SPNEGO request:\n"));
995 dump_data(1, in
.data
, in
.length
);
996 return NT_STATUS_INVALID_PARAMETER
;
999 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1000 if (spnego
.type
!= spnego_state
->expected_packet
) {
1001 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
1002 spnego_state
->expected_packet
));
1003 dump_data(1, in
.data
, in
.length
);
1004 spnego_free_data(&spnego
);
1005 return NT_STATUS_INVALID_PARAMETER
;
1008 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
1009 spnego_free_data(&spnego
);
1010 return NT_STATUS_ACCESS_DENIED
;
1013 /* Server didn't like our choice of mech, and chose something else */
1014 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
1015 spnego
.negTokenTarg
.supportedMech
&&
1016 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
1017 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1018 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
),
1019 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
)));
1021 talloc_free(spnego_state
->sub_sec_security
);
1022 nt_status
= gensec_subcontext_start(spnego_state
,
1024 &spnego_state
->sub_sec_security
);
1025 if (!NT_STATUS_IS_OK(nt_status
)) {
1026 spnego_free_data(&spnego
);
1029 /* select the sub context */
1030 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
1031 spnego
.negTokenTarg
.supportedMech
);
1032 if (!NT_STATUS_IS_OK(nt_status
)) {
1033 spnego_free_data(&spnego
);
1037 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1039 spnego
.negTokenTarg
.responseToken
,
1041 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
1042 } else if (spnego_state
->no_response_expected
) {
1043 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1044 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
1045 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1046 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
1047 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
1048 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1050 nt_status
= NT_STATUS_OK
;
1052 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1053 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1054 spnego_state
->mech_types
.data
,
1055 spnego_state
->mech_types
.length
,
1056 spnego_state
->mech_types
.data
,
1057 spnego_state
->mech_types
.length
,
1058 &spnego
.negTokenTarg
.mechListMIC
);
1059 if (!NT_STATUS_IS_OK(nt_status
)) {
1060 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1061 nt_errstr(nt_status
)));
1065 bool new_spnego
= false;
1067 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1069 spnego
.negTokenTarg
.responseToken
,
1072 if (NT_STATUS_IS_OK(nt_status
)
1073 && spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1074 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1075 GENSEC_FEATURE_NEW_SPNEGO
);
1077 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
1078 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1080 spnego_state
->mech_types
.data
,
1081 spnego_state
->mech_types
.length
,
1082 spnego_state
->mech_types
.data
,
1083 spnego_state
->mech_types
.length
,
1085 if (!NT_STATUS_IS_OK(nt_status
)) {
1086 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1087 nt_errstr(nt_status
)));
1090 if (NT_STATUS_IS_OK(nt_status
)) {
1091 spnego_state
->no_response_expected
= true;
1095 spnego_free_data(&spnego
);
1097 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1098 && !NT_STATUS_IS_OK(nt_status
)) {
1099 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1100 spnego_state
->sub_sec_security
->ops
->name
,
1101 nt_errstr(nt_status
)));
1105 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1107 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1108 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1109 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1110 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1111 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1113 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1114 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1115 return NT_STATUS_INVALID_PARAMETER
;
1118 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1119 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1122 /* all done - server has accepted, and we agree */
1123 *out
= null_data_blob
;
1125 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1126 /* unless of course it did not accept */
1127 DEBUG(1,("gensec_update ok but not accepted\n"));
1128 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1131 spnego_state
->state_position
= SPNEGO_DONE
;
1137 /* We should not be called after we are 'done' */
1138 return NT_STATUS_INVALID_PARAMETER
;
1140 return NT_STATUS_INVALID_PARAMETER
;
1143 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1144 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1146 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1151 *full_in
= data_blob_null
;
1153 if (spnego_state
->in_needed
== 0) {
1157 * try to work out the size of the full
1158 * input token, it might be fragmented
1160 status
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1161 if (!NT_STATUS_IS_OK(status
) &&
1162 !NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1163 status
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1166 if (NT_STATUS_IS_OK(status
) ||
1167 NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1168 spnego_state
->in_needed
= size
;
1171 * If it is not an asn1 message
1172 * just call the next layer.
1174 spnego_state
->in_needed
= in
.length
;
1178 if (spnego_state
->in_needed
> UINT16_MAX
) {
1180 * limit the incoming message to 0xFFFF
1181 * to avoid DoS attacks.
1183 return NT_STATUS_INVALID_BUFFER_SIZE
;
1186 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1188 * If we reach this, we know we got at least
1189 * part of an asn1 message, getting 0 means
1190 * the remote peer wants us to spin.
1192 return NT_STATUS_INVALID_PARAMETER
;
1195 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1196 if (in
.length
> expected
) {
1198 * we got more than expected
1200 return NT_STATUS_INVALID_PARAMETER
;
1203 if (in
.length
== spnego_state
->in_needed
) {
1205 * if the in.length contains the full blob
1208 * Note: this implies spnego_state->in_frag.length == 0,
1209 * but we do not need to check this explicitly
1210 * because we already know that we did not get
1211 * more than expected.
1214 return NT_STATUS_OK
;
1217 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1218 in
.data
, in
.length
);
1220 return NT_STATUS_NO_MEMORY
;
1223 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1224 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1227 *full_in
= spnego_state
->in_frag
;
1228 return NT_STATUS_OK
;
1231 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1232 TALLOC_CTX
*out_mem_ctx
,
1235 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1236 DATA_BLOB out
= data_blob_null
;
1238 *_out
= data_blob_null
;
1240 if (spnego_state
->out_frag
.length
== 0) {
1241 return spnego_state
->out_status
;
1245 * There is still more data to be delivered
1246 * to the remote peer.
1249 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1251 * Fast path, we can deliver everything
1254 *_out
= spnego_state
->out_frag
;
1255 talloc_steal(out_mem_ctx
, _out
->data
);
1256 spnego_state
->out_frag
= data_blob_null
;
1257 return spnego_state
->out_status
;
1260 out
= spnego_state
->out_frag
;
1263 * copy the remaining bytes
1265 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1266 out
.data
+ spnego_state
->out_max_length
,
1267 out
.length
- spnego_state
->out_max_length
);
1268 if (spnego_state
->out_frag
.data
== NULL
) {
1269 return NT_STATUS_NO_MEMORY
;
1273 * truncate the buffer
1275 data_blob_realloc(spnego_state
, &out
, spnego_state
->out_max_length
);
1277 talloc_steal(out_mem_ctx
, out
.data
);
1279 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1282 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1283 TALLOC_CTX
*out_mem_ctx
,
1284 struct tevent_context
*ev
,
1285 const DATA_BLOB in
, DATA_BLOB
*out
)
1287 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1288 DATA_BLOB full_in
= data_blob_null
;
1291 *out
= data_blob_null
;
1293 if (spnego_state
->out_frag
.length
> 0) {
1294 if (in
.length
> 0) {
1295 return NT_STATUS_INVALID_PARAMETER
;
1298 return gensec_spnego_update_out(gensec_security
,
1303 status
= gensec_spnego_update_in(gensec_security
,
1305 if (!NT_STATUS_IS_OK(status
)) {
1309 status
= gensec_spnego_update(gensec_security
,
1312 &spnego_state
->out_frag
);
1313 data_blob_free(&spnego_state
->in_frag
);
1314 spnego_state
->in_needed
= 0;
1315 if (!NT_STATUS_IS_OK(status
) &&
1316 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1320 spnego_state
->out_status
= status
;
1322 return gensec_spnego_update_out(gensec_security
,
1327 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1330 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1332 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1333 gensec_security
->want_features
|= feature
;
1337 gensec_want_feature(spnego_state
->sub_sec_security
,
1341 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1344 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1345 if (!spnego_state
->sub_sec_security
) {
1349 return gensec_have_feature(spnego_state
->sub_sec_security
,
1353 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1355 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1357 if (!spnego_state
->sub_sec_security
) {
1358 return GENSEC_EXPIRE_TIME_INFINITY
;
1361 return gensec_expire_time(spnego_state
->sub_sec_security
);
1364 static const char *gensec_spnego_oids
[] = {
1369 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1371 .sasl_name
= "GSS-SPNEGO",
1372 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1373 .oid
= gensec_spnego_oids
,
1374 .client_start
= gensec_spnego_client_start
,
1375 .server_start
= gensec_spnego_server_start
,
1376 .update
= gensec_spnego_update_wrapper
,
1377 .seal_packet
= gensec_spnego_seal_packet
,
1378 .sign_packet
= gensec_spnego_sign_packet
,
1379 .sig_size
= gensec_spnego_sig_size
,
1380 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1381 .max_input_size
= gensec_spnego_max_input_size
,
1382 .check_packet
= gensec_spnego_check_packet
,
1383 .unseal_packet
= gensec_spnego_unseal_packet
,
1384 .packet_full_request
= gensec_spnego_packet_full_request
,
1385 .wrap
= gensec_spnego_wrap
,
1386 .unwrap
= gensec_spnego_unwrap
,
1387 .wrap_packets
= gensec_spnego_wrap_packets
,
1388 .unwrap_packets
= gensec_spnego_unwrap_packets
,
1389 .session_key
= gensec_spnego_session_key
,
1390 .session_info
= gensec_spnego_session_info
,
1391 .want_feature
= gensec_spnego_want_feature
,
1392 .have_feature
= gensec_spnego_have_feature
,
1393 .expire_time
= gensec_spnego_expire_time
,
1395 .priority
= GENSEC_SPNEGO
1398 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1401 ret
= gensec_register(&gensec_spnego_security_ops
);
1402 if (!NT_STATUS_IS_OK(ret
)) {
1403 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1404 gensec_spnego_security_ops
.name
));