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
;
412 Parse the netTokenInit, either from the client, to the server, or
413 from the server to the client.
416 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
417 struct spnego_state
*spnego_state
,
418 TALLOC_CTX
*out_mem_ctx
,
419 struct tevent_context
*ev
,
420 const char **mechType
,
421 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
424 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
425 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
428 const struct gensec_security_ops_wrapper
*all_sec
429 = gensec_security_by_oid_list(gensec_security
,
434 ok
= spnego_write_mech_types(spnego_state
,
436 &spnego_state
->mech_types
);
438 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
439 return NT_STATUS_NO_MEMORY
;
442 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
444 for (j
=0; mechType
&& mechType
[j
]; j
++) {
445 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
446 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
450 nt_status
= gensec_subcontext_start(spnego_state
,
452 &spnego_state
->sub_sec_security
);
453 if (!NT_STATUS_IS_OK(nt_status
)) {
456 /* select the sub context */
457 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
459 if (!NT_STATUS_IS_OK(nt_status
)) {
460 talloc_free(spnego_state
->sub_sec_security
);
461 spnego_state
->sub_sec_security
= NULL
;
466 /* no optimistic token */
467 spnego_state
->neg_oid
= all_sec
[i
].oid
;
468 *unwrapped_out
= data_blob_null
;
469 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
473 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
478 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
479 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
480 /* Pretend we never started it (lets the first run find some incompatible demand) */
482 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
483 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
484 talloc_free(spnego_state
->sub_sec_security
);
485 spnego_state
->sub_sec_security
= NULL
;
489 spnego_state
->neg_oid
= all_sec
[i
].oid
;
492 if (spnego_state
->sub_sec_security
) {
497 if (!spnego_state
->sub_sec_security
) {
498 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
499 return NT_STATUS_INVALID_PARAMETER
;
503 /* Having tried any optimistic token from the client (if we
504 * were the server), if we didn't get anywhere, walk our list
505 * in our preference order */
507 if (!spnego_state
->sub_sec_security
) {
508 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
509 nt_status
= gensec_subcontext_start(spnego_state
,
511 &spnego_state
->sub_sec_security
);
512 if (!NT_STATUS_IS_OK(nt_status
)) {
515 /* select the sub context */
516 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
518 if (!NT_STATUS_IS_OK(nt_status
)) {
519 talloc_free(spnego_state
->sub_sec_security
);
520 spnego_state
->sub_sec_security
= NULL
;
524 spnego_state
->neg_oid
= all_sec
[i
].oid
;
526 /* only get the helping start blob for the first OID */
527 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
533 /* it is likely that a NULL input token will
534 * not be liked by most server mechs, but if
535 * we are in the client, we want the first
536 * update packet to be able to abort the use
538 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
539 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
540 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
541 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
542 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
543 /* Pretend we never started it (lets the first run find some incompatible demand) */
545 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
546 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
547 talloc_free(spnego_state
->sub_sec_security
);
548 spnego_state
->sub_sec_security
= NULL
;
557 if (spnego_state
->sub_sec_security
) {
558 /* it is likely that a NULL input token will
559 * not be liked by most server mechs, but this
560 * does the right thing in the CIFS client.
561 * just push us along the merry-go-round
562 * again, and hope for better luck next
565 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
566 *unwrapped_out
= data_blob(NULL
, 0);
567 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
570 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
571 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
572 && !NT_STATUS_IS_OK(nt_status
)) {
573 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
574 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
575 talloc_free(spnego_state
->sub_sec_security
);
576 spnego_state
->sub_sec_security
= NULL
;
578 /* We started the mech correctly, and the
579 * input from the other side was valid.
580 * Return the error (say bad password, invalid
586 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
589 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
590 /* we could re-negotiate here, but it would only work
591 * if the client or server lied about what it could
592 * support the first time. Lets keep this code to
598 /** create a negTokenInit
600 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
602 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
603 struct spnego_state
*spnego_state
,
604 TALLOC_CTX
*out_mem_ctx
,
605 struct tevent_context
*ev
,
606 const DATA_BLOB in
, DATA_BLOB
*out
)
609 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
610 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
611 const char **mechTypes
= NULL
;
612 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
613 const struct gensec_security_ops_wrapper
*all_sec
;
615 mechTypes
= gensec_security_oids(gensec_security
,
616 out_mem_ctx
, GENSEC_OID_SPNEGO
);
618 all_sec
= gensec_security_by_oid_list(gensec_security
,
622 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
623 struct spnego_data spnego_out
;
624 const char **send_mech_types
;
627 nt_status
= gensec_subcontext_start(spnego_state
,
629 &spnego_state
->sub_sec_security
);
630 if (!NT_STATUS_IS_OK(nt_status
)) {
633 /* select the sub context */
634 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
636 if (!NT_STATUS_IS_OK(nt_status
)) {
637 talloc_free(spnego_state
->sub_sec_security
);
638 spnego_state
->sub_sec_security
= NULL
;
642 /* In the client, try and produce the first (optimistic) packet */
643 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
644 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
650 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
651 && !NT_STATUS_IS_OK(nt_status
)) {
652 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
653 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
654 talloc_free(spnego_state
->sub_sec_security
);
655 spnego_state
->sub_sec_security
= NULL
;
656 /* Pretend we never started it (lets the first run find some incompatible demand) */
662 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
664 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
667 ok
= spnego_write_mech_types(spnego_state
,
669 &spnego_state
->mech_types
);
671 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
672 return NT_STATUS_NO_MEMORY
;
675 /* List the remaining mechs as options */
676 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
677 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
678 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
680 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
681 spnego_out
.negTokenInit
.mechListMIC
682 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
684 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
687 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
689 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
690 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
691 return NT_STATUS_INVALID_PARAMETER
;
695 spnego_state
->neg_oid
= all_sec
[i
].oid
;
697 if (NT_STATUS_IS_OK(nt_status
)) {
698 spnego_state
->no_response_expected
= true;
701 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
703 talloc_free(spnego_state
->sub_sec_security
);
704 spnego_state
->sub_sec_security
= NULL
;
706 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
707 return NT_STATUS_INVALID_PARAMETER
;
711 /** create a server negTokenTarg
713 * This is the case, where the client is the first one who sends data
716 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
717 TALLOC_CTX
*out_mem_ctx
,
719 const DATA_BLOB unwrapped_out
,
720 DATA_BLOB mech_list_mic
,
723 struct spnego_data spnego_out
;
724 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
727 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
728 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
729 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
730 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
732 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
733 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
734 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
735 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
736 } else if (NT_STATUS_IS_OK(nt_status
)) {
737 if (unwrapped_out
.data
) {
738 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
740 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
741 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
742 spnego_state
->state_position
= SPNEGO_DONE
;
744 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
745 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
746 spnego_state
->state_position
= SPNEGO_DONE
;
749 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
750 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
751 return NT_STATUS_INVALID_PARAMETER
;
754 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
760 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
761 struct tevent_context
*ev
,
762 const DATA_BLOB in
, DATA_BLOB
*out
)
764 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
765 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
766 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
767 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
768 struct spnego_data spnego_out
;
769 struct spnego_data spnego
;
773 *out
= data_blob(NULL
, 0);
776 out_mem_ctx
= spnego_state
;
779 /* and switch into the state machine */
781 switch (spnego_state
->state_position
) {
782 case SPNEGO_FALLBACK
:
783 return gensec_update(spnego_state
->sub_sec_security
, ev
,
784 out_mem_ctx
, in
, out
);
785 case SPNEGO_SERVER_START
:
790 len
= spnego_read_data(gensec_security
, in
, &spnego
);
792 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
793 out_mem_ctx
, ev
, in
, out
);
795 /* client sent NegTargetInit, we send NegTokenTarg */
797 /* OK, so it's real SPNEGO, check the packet's the one we expect */
798 if (spnego
.type
!= spnego_state
->expected_packet
) {
799 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
800 spnego_state
->expected_packet
));
801 dump_data(1, in
.data
, in
.length
);
802 spnego_free_data(&spnego
);
803 return NT_STATUS_INVALID_PARAMETER
;
806 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
810 spnego
.negTokenInit
.mechTypes
,
811 spnego
.negTokenInit
.mechToken
,
814 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
821 spnego_free_data(&spnego
);
825 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
826 out_mem_ctx
, ev
, in
, out
);
827 spnego_state
->state_position
= SPNEGO_SERVER_START
;
828 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
833 case SPNEGO_CLIENT_START
:
835 /* The server offers a list of mechanisms */
837 const char *my_mechs
[] = {NULL
, NULL
};
838 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
841 /* client to produce negTokenInit */
842 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
843 out_mem_ctx
, ev
, in
, out
);
844 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
845 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
849 len
= spnego_read_data(gensec_security
, in
, &spnego
);
852 DEBUG(1, ("Invalid SPNEGO request:\n"));
853 dump_data(1, in
.data
, in
.length
);
854 return NT_STATUS_INVALID_PARAMETER
;
857 /* OK, so it's real SPNEGO, check the packet's the one we expect */
858 if (spnego
.type
!= spnego_state
->expected_packet
) {
859 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
860 spnego_state
->expected_packet
));
861 dump_data(1, in
.data
, in
.length
);
862 spnego_free_data(&spnego
);
863 return NT_STATUS_INVALID_PARAMETER
;
866 if (spnego
.negTokenInit
.targetPrincipal
867 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
868 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
869 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
870 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
874 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
878 spnego
.negTokenInit
.mechTypes
,
879 spnego
.negTokenInit
.mechToken
,
882 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
883 spnego_free_data(&spnego
);
887 my_mechs
[0] = spnego_state
->neg_oid
;
889 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
890 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
891 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
892 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
893 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
894 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
896 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
897 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
898 return NT_STATUS_INVALID_PARAMETER
;
902 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
903 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
905 if (NT_STATUS_IS_OK(nt_status
)) {
906 spnego_state
->no_response_expected
= true;
909 spnego_free_data(&spnego
);
910 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
912 case SPNEGO_SERVER_TARG
:
915 bool new_spnego
= false;
918 return NT_STATUS_INVALID_PARAMETER
;
921 len
= spnego_read_data(gensec_security
, in
, &spnego
);
924 DEBUG(1, ("Invalid SPNEGO request:\n"));
925 dump_data(1, in
.data
, in
.length
);
926 return NT_STATUS_INVALID_PARAMETER
;
929 /* OK, so it's real SPNEGO, check the packet's the one we expect */
930 if (spnego
.type
!= spnego_state
->expected_packet
) {
931 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
932 spnego_state
->expected_packet
));
933 dump_data(1, in
.data
, in
.length
);
934 spnego_free_data(&spnego
);
935 return NT_STATUS_INVALID_PARAMETER
;
938 if (!spnego_state
->sub_sec_security
) {
939 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
940 spnego_free_data(&spnego
);
941 return NT_STATUS_INVALID_PARAMETER
;
944 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
946 spnego
.negTokenTarg
.responseToken
,
948 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
950 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
951 spnego_state
->mech_types
.data
,
952 spnego_state
->mech_types
.length
,
953 spnego_state
->mech_types
.data
,
954 spnego_state
->mech_types
.length
,
955 &spnego
.negTokenTarg
.mechListMIC
);
956 if (!NT_STATUS_IS_OK(nt_status
)) {
957 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
958 nt_errstr(nt_status
)));
961 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
962 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
964 spnego_state
->mech_types
.data
,
965 spnego_state
->mech_types
.length
,
966 spnego_state
->mech_types
.data
,
967 spnego_state
->mech_types
.length
,
969 if (!NT_STATUS_IS_OK(nt_status
)) {
970 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
971 nt_errstr(nt_status
)));
975 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
982 spnego_free_data(&spnego
);
986 case SPNEGO_CLIENT_TARG
:
990 return NT_STATUS_INVALID_PARAMETER
;
993 len
= spnego_read_data(gensec_security
, in
, &spnego
);
996 DEBUG(1, ("Invalid SPNEGO request:\n"));
997 dump_data(1, in
.data
, in
.length
);
998 return NT_STATUS_INVALID_PARAMETER
;
1001 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1002 if (spnego
.type
!= spnego_state
->expected_packet
) {
1003 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
1004 spnego_state
->expected_packet
));
1005 dump_data(1, in
.data
, in
.length
);
1006 spnego_free_data(&spnego
);
1007 return NT_STATUS_INVALID_PARAMETER
;
1010 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
1011 spnego_free_data(&spnego
);
1012 return NT_STATUS_ACCESS_DENIED
;
1015 /* Server didn't like our choice of mech, and chose something else */
1016 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
1017 spnego
.negTokenTarg
.supportedMech
&&
1018 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
1019 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1020 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
),
1021 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
)));
1023 talloc_free(spnego_state
->sub_sec_security
);
1024 nt_status
= gensec_subcontext_start(spnego_state
,
1026 &spnego_state
->sub_sec_security
);
1027 if (!NT_STATUS_IS_OK(nt_status
)) {
1028 spnego_free_data(&spnego
);
1031 /* select the sub context */
1032 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
1033 spnego
.negTokenTarg
.supportedMech
);
1034 if (!NT_STATUS_IS_OK(nt_status
)) {
1035 spnego_free_data(&spnego
);
1039 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1041 spnego
.negTokenTarg
.responseToken
,
1043 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
1044 } else if (spnego_state
->no_response_expected
) {
1045 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1046 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
1047 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1048 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
1049 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
1050 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1052 nt_status
= NT_STATUS_OK
;
1054 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1055 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1056 spnego_state
->mech_types
.data
,
1057 spnego_state
->mech_types
.length
,
1058 spnego_state
->mech_types
.data
,
1059 spnego_state
->mech_types
.length
,
1060 &spnego
.negTokenTarg
.mechListMIC
);
1061 if (!NT_STATUS_IS_OK(nt_status
)) {
1062 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1063 nt_errstr(nt_status
)));
1067 bool new_spnego
= false;
1069 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1071 spnego
.negTokenTarg
.responseToken
,
1074 if (NT_STATUS_IS_OK(nt_status
)
1075 && spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1076 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1077 GENSEC_FEATURE_NEW_SPNEGO
);
1079 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
1080 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1082 spnego_state
->mech_types
.data
,
1083 spnego_state
->mech_types
.length
,
1084 spnego_state
->mech_types
.data
,
1085 spnego_state
->mech_types
.length
,
1087 if (!NT_STATUS_IS_OK(nt_status
)) {
1088 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1089 nt_errstr(nt_status
)));
1092 if (NT_STATUS_IS_OK(nt_status
)) {
1093 spnego_state
->no_response_expected
= true;
1097 spnego_free_data(&spnego
);
1099 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1100 && !NT_STATUS_IS_OK(nt_status
)) {
1101 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1102 spnego_state
->sub_sec_security
->ops
->name
,
1103 nt_errstr(nt_status
)));
1107 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1109 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1110 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1111 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1112 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1113 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1115 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1116 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1117 return NT_STATUS_INVALID_PARAMETER
;
1120 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1121 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1124 /* all done - server has accepted, and we agree */
1125 *out
= null_data_blob
;
1127 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1128 /* unless of course it did not accept */
1129 DEBUG(1,("gensec_update ok but not accepted\n"));
1130 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1133 spnego_state
->state_position
= SPNEGO_DONE
;
1139 /* We should not be called after we are 'done' */
1140 return NT_STATUS_INVALID_PARAMETER
;
1142 return NT_STATUS_INVALID_PARAMETER
;
1145 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1146 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1148 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1153 *full_in
= data_blob_null
;
1155 if (spnego_state
->in_needed
== 0) {
1159 * try to work out the size of the full
1160 * input token, it might be fragmented
1162 status
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1163 if (!NT_STATUS_IS_OK(status
) &&
1164 !NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1165 status
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1168 if (NT_STATUS_IS_OK(status
) ||
1169 NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1170 spnego_state
->in_needed
= size
;
1173 * If it is not an asn1 message
1174 * just call the next layer.
1176 spnego_state
->in_needed
= in
.length
;
1180 if (spnego_state
->in_needed
> UINT16_MAX
) {
1182 * limit the incoming message to 0xFFFF
1183 * to avoid DoS attacks.
1185 return NT_STATUS_INVALID_BUFFER_SIZE
;
1188 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1190 * If we reach this, we know we got at least
1191 * part of an asn1 message, getting 0 means
1192 * the remote peer wants us to spin.
1194 return NT_STATUS_INVALID_PARAMETER
;
1197 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1198 if (in
.length
> expected
) {
1200 * we got more than expected
1202 return NT_STATUS_INVALID_PARAMETER
;
1205 if (in
.length
== spnego_state
->in_needed
) {
1207 * if the in.length contains the full blob
1210 * Note: this implies spnego_state->in_frag.length == 0,
1211 * but we do not need to check this explicitly
1212 * because we already know that we did not get
1213 * more than expected.
1216 return NT_STATUS_OK
;
1219 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1220 in
.data
, in
.length
);
1222 return NT_STATUS_NO_MEMORY
;
1225 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1226 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1229 *full_in
= spnego_state
->in_frag
;
1230 return NT_STATUS_OK
;
1233 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1234 TALLOC_CTX
*out_mem_ctx
,
1237 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1238 DATA_BLOB out
= data_blob_null
;
1240 *_out
= data_blob_null
;
1242 if (spnego_state
->out_frag
.length
== 0) {
1243 return spnego_state
->out_status
;
1247 * There is still more data to be delivered
1248 * to the remote peer.
1251 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1253 * Fast path, we can deliver everything
1256 *_out
= spnego_state
->out_frag
;
1257 talloc_steal(out_mem_ctx
, _out
->data
);
1258 spnego_state
->out_frag
= data_blob_null
;
1259 return spnego_state
->out_status
;
1262 out
= spnego_state
->out_frag
;
1265 * copy the remaining bytes
1267 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1268 out
.data
+ spnego_state
->out_max_length
,
1269 out
.length
- spnego_state
->out_max_length
);
1270 if (spnego_state
->out_frag
.data
== NULL
) {
1271 return NT_STATUS_NO_MEMORY
;
1275 * truncate the buffer
1277 data_blob_realloc(spnego_state
, &out
, spnego_state
->out_max_length
);
1279 talloc_steal(out_mem_ctx
, out
.data
);
1281 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1284 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1285 TALLOC_CTX
*out_mem_ctx
,
1286 struct tevent_context
*ev
,
1287 const DATA_BLOB in
, DATA_BLOB
*out
)
1289 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1290 DATA_BLOB full_in
= data_blob_null
;
1293 *out
= data_blob_null
;
1295 if (spnego_state
->out_frag
.length
> 0) {
1296 if (in
.length
> 0) {
1297 return NT_STATUS_INVALID_PARAMETER
;
1300 return gensec_spnego_update_out(gensec_security
,
1305 status
= gensec_spnego_update_in(gensec_security
,
1307 if (!NT_STATUS_IS_OK(status
)) {
1311 status
= gensec_spnego_update(gensec_security
,
1314 &spnego_state
->out_frag
);
1315 data_blob_free(&spnego_state
->in_frag
);
1316 spnego_state
->in_needed
= 0;
1317 if (!NT_STATUS_IS_OK(status
) &&
1318 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1322 spnego_state
->out_status
= status
;
1324 return gensec_spnego_update_out(gensec_security
,
1329 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1332 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1334 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1335 gensec_security
->want_features
|= feature
;
1339 gensec_want_feature(spnego_state
->sub_sec_security
,
1343 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1346 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1347 if (!spnego_state
->sub_sec_security
) {
1351 return gensec_have_feature(spnego_state
->sub_sec_security
,
1355 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1357 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1359 if (!spnego_state
->sub_sec_security
) {
1360 return GENSEC_EXPIRE_TIME_INFINITY
;
1363 return gensec_expire_time(spnego_state
->sub_sec_security
);
1366 static const char *gensec_spnego_oids
[] = {
1371 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1373 .sasl_name
= "GSS-SPNEGO",
1374 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1375 .oid
= gensec_spnego_oids
,
1376 .client_start
= gensec_spnego_client_start
,
1377 .server_start
= gensec_spnego_server_start
,
1378 .update
= gensec_spnego_update_wrapper
,
1379 .seal_packet
= gensec_spnego_seal_packet
,
1380 .sign_packet
= gensec_spnego_sign_packet
,
1381 .sig_size
= gensec_spnego_sig_size
,
1382 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1383 .max_input_size
= gensec_spnego_max_input_size
,
1384 .check_packet
= gensec_spnego_check_packet
,
1385 .unseal_packet
= gensec_spnego_unseal_packet
,
1386 .packet_full_request
= gensec_spnego_packet_full_request
,
1387 .wrap
= gensec_spnego_wrap
,
1388 .unwrap
= gensec_spnego_unwrap
,
1389 .wrap_packets
= gensec_spnego_wrap_packets
,
1390 .unwrap_packets
= gensec_spnego_unwrap_packets
,
1391 .session_key
= gensec_spnego_session_key
,
1392 .session_info
= gensec_spnego_session_info
,
1393 .want_feature
= gensec_spnego_want_feature
,
1394 .have_feature
= gensec_spnego_have_feature
,
1395 .expire_time
= gensec_spnego_expire_time
,
1397 .priority
= GENSEC_SPNEGO
1400 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1403 ret
= gensec_register(&gensec_spnego_security_ops
);
1404 if (!NT_STATUS_IS_OK(ret
)) {
1405 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1406 gensec_spnego_security_ops
.name
));