ctdb: Accept the key in hex format for the pstore command
[Samba.git] / auth / gensec / spnego.c
blob85c70e12f64e6e36e5fd50156d2b4cc3a2a9acbf
1 /*
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/>.
25 #include "includes.h"
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"
34 #undef strcasecmp
36 _PUBLIC_ NTSTATUS gensec_spnego_init(void);
38 enum spnego_state_position {
39 SPNEGO_SERVER_START,
40 SPNEGO_CLIENT_START,
41 SPNEGO_SERVER_TARG,
42 SPNEGO_CLIENT_TARG,
43 SPNEGO_FALLBACK,
44 SPNEGO_DONE
47 struct spnego_state {
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;
53 const char *neg_oid;
55 DATA_BLOB mech_types;
58 * The following is used to implement
59 * the update token fragmentation
61 size_t in_needed;
62 DATA_BLOB in_frag;
63 size_t out_max_length;
64 DATA_BLOB out_frag;
65 NTSTATUS out_status;
69 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
71 struct spnego_state *spnego_state;
73 spnego_state = talloc_zero(gensec_security, struct spnego_state);
74 if (!spnego_state) {
75 return NT_STATUS_NO_MEMORY;
78 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
79 spnego_state->state_position = SPNEGO_CLIENT_START;
80 spnego_state->sub_sec_security = NULL;
81 spnego_state->no_response_expected = false;
82 spnego_state->mech_types = data_blob(NULL, 0);
83 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
84 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
86 gensec_security->private_data = spnego_state;
87 return NT_STATUS_OK;
90 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
92 struct spnego_state *spnego_state;
94 spnego_state = talloc_zero(gensec_security, struct spnego_state);
95 if (!spnego_state) {
96 return NT_STATUS_NO_MEMORY;
99 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
100 spnego_state->state_position = SPNEGO_SERVER_START;
101 spnego_state->sub_sec_security = NULL;
102 spnego_state->no_response_expected = false;
103 spnego_state->mech_types = data_blob(NULL, 0);
104 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
105 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
107 gensec_security->private_data = spnego_state;
108 return NT_STATUS_OK;
112 wrappers for the spnego_*() functions
114 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
115 uint8_t *data, size_t length,
116 const uint8_t *whole_pdu, size_t pdu_length,
117 const DATA_BLOB *sig)
119 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
121 if (spnego_state->state_position != SPNEGO_DONE
122 && spnego_state->state_position != SPNEGO_FALLBACK) {
123 return NT_STATUS_INVALID_PARAMETER;
126 return gensec_unseal_packet(spnego_state->sub_sec_security,
127 data, length,
128 whole_pdu, pdu_length,
129 sig);
132 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
133 const uint8_t *data, size_t length,
134 const uint8_t *whole_pdu, size_t pdu_length,
135 const DATA_BLOB *sig)
137 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
139 if (spnego_state->state_position != SPNEGO_DONE
140 && spnego_state->state_position != SPNEGO_FALLBACK) {
141 return NT_STATUS_INVALID_PARAMETER;
144 return gensec_check_packet(spnego_state->sub_sec_security,
145 data, length,
146 whole_pdu, pdu_length,
147 sig);
150 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
151 TALLOC_CTX *mem_ctx,
152 uint8_t *data, size_t length,
153 const uint8_t *whole_pdu, size_t pdu_length,
154 DATA_BLOB *sig)
156 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
158 if (spnego_state->state_position != SPNEGO_DONE
159 && spnego_state->state_position != SPNEGO_FALLBACK) {
160 return NT_STATUS_INVALID_PARAMETER;
163 return gensec_seal_packet(spnego_state->sub_sec_security,
164 mem_ctx,
165 data, length,
166 whole_pdu, pdu_length,
167 sig);
170 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
171 TALLOC_CTX *mem_ctx,
172 const uint8_t *data, size_t length,
173 const uint8_t *whole_pdu, size_t pdu_length,
174 DATA_BLOB *sig)
176 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
178 if (spnego_state->state_position != SPNEGO_DONE
179 && spnego_state->state_position != SPNEGO_FALLBACK) {
180 return NT_STATUS_INVALID_PARAMETER;
183 return gensec_sign_packet(spnego_state->sub_sec_security,
184 mem_ctx,
185 data, length,
186 whole_pdu, pdu_length,
187 sig);
190 static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security,
191 TALLOC_CTX *mem_ctx,
192 const DATA_BLOB *in,
193 DATA_BLOB *out)
195 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
197 if (spnego_state->state_position != SPNEGO_DONE
198 && spnego_state->state_position != SPNEGO_FALLBACK) {
199 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
200 return NT_STATUS_INVALID_PARAMETER;
203 return gensec_wrap(spnego_state->sub_sec_security,
204 mem_ctx, in, out);
207 static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security,
208 TALLOC_CTX *mem_ctx,
209 const DATA_BLOB *in,
210 DATA_BLOB *out)
212 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
214 if (spnego_state->state_position != SPNEGO_DONE
215 && spnego_state->state_position != SPNEGO_FALLBACK) {
216 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
217 return NT_STATUS_INVALID_PARAMETER;
220 return gensec_unwrap(spnego_state->sub_sec_security,
221 mem_ctx, in, out);
224 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size)
226 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
228 if (spnego_state->state_position != SPNEGO_DONE
229 && spnego_state->state_position != SPNEGO_FALLBACK) {
230 return 0;
233 return gensec_sig_size(spnego_state->sub_sec_security, data_size);
236 static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
238 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
240 if (spnego_state->state_position != SPNEGO_DONE
241 && spnego_state->state_position != SPNEGO_FALLBACK) {
242 return 0;
245 return gensec_max_input_size(spnego_state->sub_sec_security);
248 static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
250 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
252 if (spnego_state->state_position != SPNEGO_DONE
253 && spnego_state->state_position != SPNEGO_FALLBACK) {
254 return 0;
257 return gensec_max_wrapped_size(spnego_state->sub_sec_security);
260 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
261 TALLOC_CTX *mem_ctx,
262 DATA_BLOB *session_key)
264 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
265 if (!spnego_state->sub_sec_security) {
266 return NT_STATUS_INVALID_PARAMETER;
269 return gensec_session_key(spnego_state->sub_sec_security,
270 mem_ctx,
271 session_key);
274 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
275 TALLOC_CTX *mem_ctx,
276 struct auth_session_info **session_info)
278 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
279 if (!spnego_state->sub_sec_security) {
280 return NT_STATUS_INVALID_PARAMETER;
283 return gensec_session_info(spnego_state->sub_sec_security,
284 mem_ctx,
285 session_info);
288 /** Fallback to another GENSEC mechanism, based on magic strings
290 * This is the 'fallback' case, where we don't get SPNEGO, and have to
291 * try all the other options (and hope they all have a magic string
292 * they check)
295 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
296 struct spnego_state *spnego_state,
297 struct tevent_context *ev,
298 TALLOC_CTX *out_mem_ctx,
299 const DATA_BLOB in, DATA_BLOB *out)
301 int i,j;
302 const struct gensec_security_ops **all_ops;
304 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
306 for (i=0; all_ops && all_ops[i]; i++) {
307 bool is_spnego;
308 NTSTATUS nt_status;
310 if (gensec_security != NULL &&
311 !gensec_security_ops_enabled(all_ops[i], gensec_security))
312 continue;
314 if (!all_ops[i]->oid) {
315 continue;
318 is_spnego = false;
319 for (j=0; all_ops[i]->oid[j]; j++) {
320 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
321 is_spnego = true;
324 if (is_spnego) {
325 continue;
328 if (!all_ops[i]->magic) {
329 continue;
332 nt_status = all_ops[i]->magic(gensec_security, &in);
333 if (!NT_STATUS_IS_OK(nt_status)) {
334 continue;
337 spnego_state->state_position = SPNEGO_FALLBACK;
339 nt_status = gensec_subcontext_start(spnego_state,
340 gensec_security,
341 &spnego_state->sub_sec_security);
343 if (!NT_STATUS_IS_OK(nt_status)) {
344 return nt_status;
346 /* select the sub context */
347 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
348 all_ops[i]);
349 if (!NT_STATUS_IS_OK(nt_status)) {
350 return nt_status;
352 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
353 ev, out_mem_ctx, in, out);
354 return nt_status;
356 DEBUG(1, ("Failed to parse SPNEGO request\n"));
357 return NT_STATUS_INVALID_PARAMETER;
361 Parse the netTokenInit, either from the client, to the server, or
362 from the server to the client.
365 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
366 struct spnego_state *spnego_state,
367 TALLOC_CTX *out_mem_ctx,
368 struct tevent_context *ev,
369 const char * const *mechType,
370 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
372 int i;
373 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
374 DATA_BLOB null_data_blob = data_blob(NULL,0);
375 bool ok;
377 const struct gensec_security_ops_wrapper *all_sec
378 = gensec_security_by_oid_list(gensec_security,
379 out_mem_ctx,
380 mechType,
381 GENSEC_OID_SPNEGO);
383 ok = spnego_write_mech_types(spnego_state,
384 mechType,
385 &spnego_state->mech_types);
386 if (!ok) {
387 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
388 return NT_STATUS_NO_MEMORY;
391 if (spnego_state->state_position == SPNEGO_SERVER_START) {
392 uint32_t j;
393 for (j=0; mechType && mechType[j]; j++) {
394 for (i=0; all_sec && all_sec[i].op; i++) {
395 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
396 continue;
399 nt_status = gensec_subcontext_start(spnego_state,
400 gensec_security,
401 &spnego_state->sub_sec_security);
402 if (!NT_STATUS_IS_OK(nt_status)) {
403 return nt_status;
405 /* select the sub context */
406 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
407 all_sec[i].op);
408 if (!NT_STATUS_IS_OK(nt_status)) {
409 talloc_free(spnego_state->sub_sec_security);
410 spnego_state->sub_sec_security = NULL;
411 break;
414 if (j > 0) {
415 /* no optimistic token */
416 spnego_state->neg_oid = all_sec[i].oid;
417 *unwrapped_out = data_blob_null;
418 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
419 break;
422 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
423 out_mem_ctx,
425 unwrapped_in,
426 unwrapped_out);
427 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
428 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
429 /* Pretend we never started it (lets the first run find some incompatible demand) */
431 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
432 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
433 talloc_free(spnego_state->sub_sec_security);
434 spnego_state->sub_sec_security = NULL;
435 break;
438 spnego_state->neg_oid = all_sec[i].oid;
439 break;
441 if (spnego_state->sub_sec_security) {
442 break;
446 if (!spnego_state->sub_sec_security) {
447 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
448 return NT_STATUS_INVALID_PARAMETER;
452 /* Having tried any optimistic token from the client (if we
453 * were the server), if we didn't get anywhere, walk our list
454 * in our preference order */
456 if (!spnego_state->sub_sec_security) {
457 for (i=0; all_sec && all_sec[i].op; i++) {
458 nt_status = gensec_subcontext_start(spnego_state,
459 gensec_security,
460 &spnego_state->sub_sec_security);
461 if (!NT_STATUS_IS_OK(nt_status)) {
462 return nt_status;
464 /* select the sub context */
465 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
466 all_sec[i].op);
467 if (!NT_STATUS_IS_OK(nt_status)) {
468 talloc_free(spnego_state->sub_sec_security);
469 spnego_state->sub_sec_security = NULL;
470 continue;
473 spnego_state->neg_oid = all_sec[i].oid;
475 /* only get the helping start blob for the first OID */
476 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
477 out_mem_ctx,
479 null_data_blob,
480 unwrapped_out);
482 /* it is likely that a NULL input token will
483 * not be liked by most server mechs, but if
484 * we are in the client, we want the first
485 * update packet to be able to abort the use
486 * of this mech */
487 if (spnego_state->state_position != SPNEGO_SERVER_START) {
488 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
489 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
490 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
491 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
492 /* Pretend we never started it (lets the first run find some incompatible demand) */
494 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
495 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
496 talloc_free(spnego_state->sub_sec_security);
497 spnego_state->sub_sec_security = NULL;
498 continue;
502 break;
506 if (spnego_state->sub_sec_security) {
507 /* it is likely that a NULL input token will
508 * not be liked by most server mechs, but this
509 * does the right thing in the CIFS client.
510 * just push us along the merry-go-round
511 * again, and hope for better luck next
512 * time */
514 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
515 *unwrapped_out = data_blob(NULL, 0);
516 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
519 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
520 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
521 && !NT_STATUS_IS_OK(nt_status)) {
522 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
523 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
524 talloc_free(spnego_state->sub_sec_security);
525 spnego_state->sub_sec_security = NULL;
527 /* We started the mech correctly, and the
528 * input from the other side was valid.
529 * Return the error (say bad password, invalid
530 * ticket) */
531 return nt_status;
534 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
537 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
538 /* we could re-negotiate here, but it would only work
539 * if the client or server lied about what it could
540 * support the first time. Lets keep this code to
541 * reality */
543 return nt_status;
546 /** create a negTokenInit
548 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
550 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
551 struct spnego_state *spnego_state,
552 TALLOC_CTX *out_mem_ctx,
553 struct tevent_context *ev,
554 const DATA_BLOB in, DATA_BLOB *out)
556 int i;
557 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
558 DATA_BLOB null_data_blob = data_blob(NULL,0);
559 const char **mechTypes = NULL;
560 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
561 const struct gensec_security_ops_wrapper *all_sec;
563 mechTypes = gensec_security_oids(gensec_security,
564 out_mem_ctx, GENSEC_OID_SPNEGO);
566 all_sec = gensec_security_by_oid_list(gensec_security,
567 out_mem_ctx,
568 mechTypes,
569 GENSEC_OID_SPNEGO);
570 for (i=0; all_sec && all_sec[i].op; i++) {
571 struct spnego_data spnego_out;
572 const char **send_mech_types;
573 bool ok;
575 nt_status = gensec_subcontext_start(spnego_state,
576 gensec_security,
577 &spnego_state->sub_sec_security);
578 if (!NT_STATUS_IS_OK(nt_status)) {
579 return nt_status;
581 /* select the sub context */
582 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
583 all_sec[i].op);
584 if (!NT_STATUS_IS_OK(nt_status)) {
585 talloc_free(spnego_state->sub_sec_security);
586 spnego_state->sub_sec_security = NULL;
587 continue;
590 /* In the client, try and produce the first (optimistic) packet */
591 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
592 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
593 out_mem_ctx,
595 null_data_blob,
596 &unwrapped_out);
598 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
599 && !NT_STATUS_IS_OK(nt_status)) {
600 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
601 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
602 talloc_free(spnego_state->sub_sec_security);
603 spnego_state->sub_sec_security = NULL;
604 /* Pretend we never started it (lets the first run find some incompatible demand) */
606 continue;
610 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
612 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
613 &all_sec[i]);
615 ok = spnego_write_mech_types(spnego_state,
616 send_mech_types,
617 &spnego_state->mech_types);
618 if (!ok) {
619 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
620 return NT_STATUS_NO_MEMORY;
623 /* List the remaining mechs as options */
624 spnego_out.negTokenInit.mechTypes = send_mech_types;
625 spnego_out.negTokenInit.reqFlags = null_data_blob;
626 spnego_out.negTokenInit.reqFlagsPadding = 0;
628 if (spnego_state->state_position == SPNEGO_SERVER_START) {
629 spnego_out.negTokenInit.mechListMIC
630 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
631 } else {
632 spnego_out.negTokenInit.mechListMIC = null_data_blob;
635 spnego_out.negTokenInit.mechToken = unwrapped_out;
637 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
638 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
639 return NT_STATUS_INVALID_PARAMETER;
642 /* set next state */
643 spnego_state->neg_oid = all_sec[i].oid;
645 if (NT_STATUS_IS_OK(nt_status)) {
646 spnego_state->no_response_expected = true;
649 return NT_STATUS_MORE_PROCESSING_REQUIRED;
651 talloc_free(spnego_state->sub_sec_security);
652 spnego_state->sub_sec_security = NULL;
654 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
655 return nt_status;
659 /** create a server negTokenTarg
661 * This is the case, where the client is the first one who sends data
664 static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_state,
665 TALLOC_CTX *out_mem_ctx,
666 NTSTATUS nt_status,
667 const DATA_BLOB unwrapped_out,
668 DATA_BLOB mech_list_mic,
669 DATA_BLOB *out)
671 struct spnego_data spnego_out;
672 DATA_BLOB null_data_blob = data_blob(NULL, 0);
674 /* compose reply */
675 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
676 spnego_out.negTokenTarg.responseToken = unwrapped_out;
677 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
678 spnego_out.negTokenTarg.supportedMech = NULL;
680 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
681 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
682 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
683 spnego_state->state_position = SPNEGO_SERVER_TARG;
684 } else if (NT_STATUS_IS_OK(nt_status)) {
685 if (unwrapped_out.data) {
686 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
688 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
689 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
690 spnego_state->state_position = SPNEGO_DONE;
691 } else {
692 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
693 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
694 spnego_state->state_position = SPNEGO_DONE;
697 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
698 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
699 return NT_STATUS_INVALID_PARAMETER;
702 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
704 return nt_status;
708 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
709 struct tevent_context *ev,
710 const DATA_BLOB in, DATA_BLOB *out)
712 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
713 DATA_BLOB null_data_blob = data_blob(NULL, 0);
714 DATA_BLOB mech_list_mic = data_blob(NULL, 0);
715 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
716 struct spnego_data spnego_out;
717 struct spnego_data spnego;
719 ssize_t len;
721 *out = data_blob(NULL, 0);
723 if (!out_mem_ctx) {
724 out_mem_ctx = spnego_state;
727 /* and switch into the state machine */
729 switch (spnego_state->state_position) {
730 case SPNEGO_FALLBACK:
731 return gensec_update_ev(spnego_state->sub_sec_security, ev,
732 out_mem_ctx, in, out);
733 case SPNEGO_SERVER_START:
735 NTSTATUS nt_status;
736 if (in.length) {
738 len = spnego_read_data(gensec_security, in, &spnego);
739 if (len == -1) {
740 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
741 out_mem_ctx, ev, in, out);
743 /* client sent NegTargetInit, we send NegTokenTarg */
745 /* OK, so it's real SPNEGO, check the packet's the one we expect */
746 if (spnego.type != spnego_state->expected_packet) {
747 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
748 spnego_state->expected_packet));
749 dump_data(1, in.data, in.length);
750 spnego_free_data(&spnego);
751 return NT_STATUS_INVALID_PARAMETER;
754 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
755 spnego_state,
756 out_mem_ctx,
758 spnego.negTokenInit.mechTypes,
759 spnego.negTokenInit.mechToken,
760 &unwrapped_out);
762 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
763 out_mem_ctx,
764 nt_status,
765 unwrapped_out,
766 null_data_blob,
767 out);
769 spnego_free_data(&spnego);
771 return nt_status;
772 } else {
773 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
774 out_mem_ctx, ev, in, out);
775 spnego_state->state_position = SPNEGO_SERVER_START;
776 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
777 return nt_status;
781 case SPNEGO_CLIENT_START:
783 /* The server offers a list of mechanisms */
785 const char *my_mechs[] = {NULL, NULL};
786 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
788 if (!in.length) {
789 /* client to produce negTokenInit */
790 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
791 out_mem_ctx, ev, in, out);
792 spnego_state->state_position = SPNEGO_CLIENT_TARG;
793 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
794 return nt_status;
797 len = spnego_read_data(gensec_security, in, &spnego);
799 if (len == -1) {
800 DEBUG(1, ("Invalid SPNEGO request:\n"));
801 dump_data(1, in.data, in.length);
802 return NT_STATUS_INVALID_PARAMETER;
805 /* OK, so it's real SPNEGO, check the packet's the one we expect */
806 if (spnego.type != spnego_state->expected_packet) {
807 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
808 spnego_state->expected_packet));
809 dump_data(1, in.data, in.length);
810 spnego_free_data(&spnego);
811 return NT_STATUS_INVALID_PARAMETER;
814 if (spnego.negTokenInit.targetPrincipal
815 && strcmp(spnego.negTokenInit.targetPrincipal, ADS_IGNORE_PRINCIPAL) != 0) {
816 DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
817 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
818 gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
822 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
823 spnego_state,
824 out_mem_ctx,
826 spnego.negTokenInit.mechTypes,
827 spnego.negTokenInit.mechToken,
828 &unwrapped_out);
830 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
831 spnego_free_data(&spnego);
832 return nt_status;
835 my_mechs[0] = spnego_state->neg_oid;
836 /* compose reply */
837 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
838 spnego_out.negTokenInit.mechTypes = my_mechs;
839 spnego_out.negTokenInit.reqFlags = null_data_blob;
840 spnego_out.negTokenInit.reqFlagsPadding = 0;
841 spnego_out.negTokenInit.mechListMIC = null_data_blob;
842 spnego_out.negTokenInit.mechToken = unwrapped_out;
844 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
845 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
846 return NT_STATUS_INVALID_PARAMETER;
849 /* set next state */
850 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
851 spnego_state->state_position = SPNEGO_CLIENT_TARG;
853 if (NT_STATUS_IS_OK(nt_status)) {
854 spnego_state->no_response_expected = true;
857 spnego_free_data(&spnego);
858 return NT_STATUS_MORE_PROCESSING_REQUIRED;
860 case SPNEGO_SERVER_TARG:
862 NTSTATUS nt_status;
863 bool new_spnego = false;
865 if (!in.length) {
866 return NT_STATUS_INVALID_PARAMETER;
869 len = spnego_read_data(gensec_security, in, &spnego);
871 if (len == -1) {
872 DEBUG(1, ("Invalid SPNEGO request:\n"));
873 dump_data(1, in.data, in.length);
874 return NT_STATUS_INVALID_PARAMETER;
877 /* OK, so it's real SPNEGO, check the packet's the one we expect */
878 if (spnego.type != spnego_state->expected_packet) {
879 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
880 spnego_state->expected_packet));
881 dump_data(1, in.data, in.length);
882 spnego_free_data(&spnego);
883 return NT_STATUS_INVALID_PARAMETER;
886 if (!spnego_state->sub_sec_security) {
887 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
888 spnego_free_data(&spnego);
889 return NT_STATUS_INVALID_PARAMETER;
892 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
893 out_mem_ctx, ev,
894 spnego.negTokenTarg.responseToken,
895 &unwrapped_out);
896 if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
897 new_spnego = true;
898 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
899 spnego_state->mech_types.data,
900 spnego_state->mech_types.length,
901 spnego_state->mech_types.data,
902 spnego_state->mech_types.length,
903 &spnego.negTokenTarg.mechListMIC);
904 if (!NT_STATUS_IS_OK(nt_status)) {
905 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
906 nt_errstr(nt_status)));
909 if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
910 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
911 out_mem_ctx,
912 spnego_state->mech_types.data,
913 spnego_state->mech_types.length,
914 spnego_state->mech_types.data,
915 spnego_state->mech_types.length,
916 &mech_list_mic);
917 if (!NT_STATUS_IS_OK(nt_status)) {
918 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
919 nt_errstr(nt_status)));
923 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
924 out_mem_ctx,
925 nt_status,
926 unwrapped_out,
927 mech_list_mic,
928 out);
930 spnego_free_data(&spnego);
932 return nt_status;
934 case SPNEGO_CLIENT_TARG:
936 NTSTATUS nt_status;
937 if (!in.length) {
938 return NT_STATUS_INVALID_PARAMETER;
941 len = spnego_read_data(gensec_security, in, &spnego);
943 if (len == -1) {
944 DEBUG(1, ("Invalid SPNEGO request:\n"));
945 dump_data(1, in.data, in.length);
946 return NT_STATUS_INVALID_PARAMETER;
949 /* OK, so it's real SPNEGO, check the packet's the one we expect */
950 if (spnego.type != spnego_state->expected_packet) {
951 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
952 spnego_state->expected_packet));
953 dump_data(1, in.data, in.length);
954 spnego_free_data(&spnego);
955 return NT_STATUS_INVALID_PARAMETER;
958 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
959 spnego_free_data(&spnego);
960 return NT_STATUS_LOGON_FAILURE;
963 /* Server didn't like our choice of mech, and chose something else */
964 if ((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) &&
965 spnego.negTokenTarg.supportedMech &&
966 strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
967 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
968 gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech),
969 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid)));
971 talloc_free(spnego_state->sub_sec_security);
972 nt_status = gensec_subcontext_start(spnego_state,
973 gensec_security,
974 &spnego_state->sub_sec_security);
975 if (!NT_STATUS_IS_OK(nt_status)) {
976 spnego_free_data(&spnego);
977 return nt_status;
979 /* select the sub context */
980 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
981 spnego.negTokenTarg.supportedMech);
982 if (!NT_STATUS_IS_OK(nt_status)) {
983 spnego_free_data(&spnego);
984 return nt_status;
987 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
988 out_mem_ctx, ev,
989 spnego.negTokenTarg.responseToken,
990 &unwrapped_out);
991 spnego_state->neg_oid = talloc_strdup(spnego_state, spnego.negTokenTarg.supportedMech);
992 } else if (spnego_state->no_response_expected) {
993 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
994 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
995 nt_status = NT_STATUS_INVALID_PARAMETER;
996 } else if (spnego.negTokenTarg.responseToken.length) {
997 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
998 nt_status = NT_STATUS_INVALID_PARAMETER;
999 } else {
1000 nt_status = NT_STATUS_OK;
1002 if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
1003 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1004 spnego_state->mech_types.data,
1005 spnego_state->mech_types.length,
1006 spnego_state->mech_types.data,
1007 spnego_state->mech_types.length,
1008 &spnego.negTokenTarg.mechListMIC);
1009 if (!NT_STATUS_IS_OK(nt_status)) {
1010 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1011 nt_errstr(nt_status)));
1014 } else {
1015 bool new_spnego = false;
1017 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1018 out_mem_ctx, ev,
1019 spnego.negTokenTarg.responseToken,
1020 &unwrapped_out);
1022 if (NT_STATUS_IS_OK(nt_status)
1023 && spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1024 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1025 GENSEC_FEATURE_NEW_SPNEGO);
1027 if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
1028 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1029 out_mem_ctx,
1030 spnego_state->mech_types.data,
1031 spnego_state->mech_types.length,
1032 spnego_state->mech_types.data,
1033 spnego_state->mech_types.length,
1034 &mech_list_mic);
1035 if (!NT_STATUS_IS_OK(nt_status)) {
1036 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1037 nt_errstr(nt_status)));
1040 if (NT_STATUS_IS_OK(nt_status)) {
1041 spnego_state->no_response_expected = true;
1045 spnego_free_data(&spnego);
1047 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1048 && !NT_STATUS_IS_OK(nt_status)) {
1049 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1050 spnego_state->sub_sec_security->ops->name,
1051 nt_errstr(nt_status)));
1052 return nt_status;
1055 if (unwrapped_out.length || mech_list_mic.length) {
1056 /* compose reply */
1057 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1058 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1059 spnego_out.negTokenTarg.supportedMech = NULL;
1060 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1061 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1063 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1064 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1065 return NT_STATUS_INVALID_PARAMETER;
1068 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1069 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1070 } else {
1072 /* all done - server has accepted, and we agree */
1073 *out = null_data_blob;
1075 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1076 /* unless of course it did not accept */
1077 DEBUG(1,("gensec_update ok but not accepted\n"));
1078 nt_status = NT_STATUS_INVALID_PARAMETER;
1081 spnego_state->state_position = SPNEGO_DONE;
1084 return nt_status;
1086 case SPNEGO_DONE:
1087 /* We should not be called after we are 'done' */
1088 return NT_STATUS_INVALID_PARAMETER;
1090 return NT_STATUS_INVALID_PARAMETER;
1093 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1094 const DATA_BLOB in, DATA_BLOB *full_in)
1096 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1097 size_t expected;
1098 NTSTATUS status;
1099 bool ok;
1101 *full_in = data_blob_null;
1103 if (spnego_state->in_needed == 0) {
1104 size_t size = 0;
1107 * try to work out the size of the full
1108 * input token, it might be fragmented
1110 status = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1111 if (!NT_STATUS_IS_OK(status) &&
1112 !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
1113 status = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1116 if (NT_STATUS_IS_OK(status) ||
1117 NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
1118 spnego_state->in_needed = size;
1119 } else {
1121 * If it is not an asn1 message
1122 * just call the next layer.
1124 spnego_state->in_needed = in.length;
1128 if (spnego_state->in_needed > UINT16_MAX) {
1130 * limit the incoming message to 0xFFFF
1131 * to avoid DoS attacks.
1133 return NT_STATUS_INVALID_BUFFER_SIZE;
1136 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1138 * If we reach this, we know we got at least
1139 * part of an asn1 message, getting 0 means
1140 * the remote peer wants us to spin.
1142 return NT_STATUS_INVALID_PARAMETER;
1145 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1146 if (in.length > expected) {
1148 * we got more than expected
1150 return NT_STATUS_INVALID_PARAMETER;
1153 if (in.length == spnego_state->in_needed) {
1155 * if the in.length contains the full blob
1156 * we are done.
1158 * Note: this implies spnego_state->in_frag.length == 0,
1159 * but we do not need to check this explicitly
1160 * because we already know that we did not get
1161 * more than expected.
1163 *full_in = in;
1164 return NT_STATUS_OK;
1167 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1168 in.data, in.length);
1169 if (!ok) {
1170 return NT_STATUS_NO_MEMORY;
1173 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1174 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1177 *full_in = spnego_state->in_frag;
1178 return NT_STATUS_OK;
1181 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1182 TALLOC_CTX *out_mem_ctx,
1183 DATA_BLOB *_out)
1185 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1186 DATA_BLOB out = data_blob_null;
1188 *_out = data_blob_null;
1190 if (spnego_state->out_frag.length == 0) {
1191 return spnego_state->out_status;
1195 * There is still more data to be delivered
1196 * to the remote peer.
1199 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1201 * Fast path, we can deliver everything
1204 *_out = spnego_state->out_frag;
1205 talloc_steal(out_mem_ctx, _out->data);
1206 spnego_state->out_frag = data_blob_null;
1207 return spnego_state->out_status;
1210 out = spnego_state->out_frag;
1213 * copy the remaining bytes
1215 spnego_state->out_frag = data_blob_talloc(spnego_state,
1216 out.data + spnego_state->out_max_length,
1217 out.length - spnego_state->out_max_length);
1218 if (spnego_state->out_frag.data == NULL) {
1219 return NT_STATUS_NO_MEMORY;
1223 * truncate the buffer
1225 data_blob_realloc(spnego_state, &out, spnego_state->out_max_length);
1227 talloc_steal(out_mem_ctx, out.data);
1228 *_out = out;
1229 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1232 static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security,
1233 TALLOC_CTX *out_mem_ctx,
1234 struct tevent_context *ev,
1235 const DATA_BLOB in, DATA_BLOB *out)
1237 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1238 DATA_BLOB full_in = data_blob_null;
1239 NTSTATUS status;
1241 *out = data_blob_null;
1243 if (spnego_state->out_frag.length > 0) {
1244 if (in.length > 0) {
1245 return NT_STATUS_INVALID_PARAMETER;
1248 return gensec_spnego_update_out(gensec_security,
1249 out_mem_ctx,
1250 out);
1253 status = gensec_spnego_update_in(gensec_security,
1254 in, &full_in);
1255 if (!NT_STATUS_IS_OK(status)) {
1256 return status;
1259 status = gensec_spnego_update(gensec_security,
1260 spnego_state, ev,
1261 full_in,
1262 &spnego_state->out_frag);
1263 data_blob_free(&spnego_state->in_frag);
1264 spnego_state->in_needed = 0;
1265 if (!NT_STATUS_IS_OK(status) &&
1266 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1267 return status;
1270 spnego_state->out_status = status;
1272 return gensec_spnego_update_out(gensec_security,
1273 out_mem_ctx,
1274 out);
1277 static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
1278 uint32_t feature)
1280 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1282 if (!spnego_state || !spnego_state->sub_sec_security) {
1283 gensec_security->want_features |= feature;
1284 return;
1287 gensec_want_feature(spnego_state->sub_sec_security,
1288 feature);
1291 static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
1292 uint32_t feature)
1294 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1295 if (!spnego_state->sub_sec_security) {
1296 return false;
1299 return gensec_have_feature(spnego_state->sub_sec_security,
1300 feature);
1303 static NTTIME gensec_spnego_expire_time(struct gensec_security *gensec_security)
1305 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1307 if (!spnego_state->sub_sec_security) {
1308 return GENSEC_EXPIRE_TIME_INFINITY;
1311 return gensec_expire_time(spnego_state->sub_sec_security);
1314 static const char *gensec_spnego_oids[] = {
1315 GENSEC_OID_SPNEGO,
1316 NULL
1319 static const struct gensec_security_ops gensec_spnego_security_ops = {
1320 .name = "spnego",
1321 .sasl_name = "GSS-SPNEGO",
1322 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1323 .oid = gensec_spnego_oids,
1324 .client_start = gensec_spnego_client_start,
1325 .server_start = gensec_spnego_server_start,
1326 .update = gensec_spnego_update_wrapper,
1327 .seal_packet = gensec_spnego_seal_packet,
1328 .sign_packet = gensec_spnego_sign_packet,
1329 .sig_size = gensec_spnego_sig_size,
1330 .max_wrapped_size = gensec_spnego_max_wrapped_size,
1331 .max_input_size = gensec_spnego_max_input_size,
1332 .check_packet = gensec_spnego_check_packet,
1333 .unseal_packet = gensec_spnego_unseal_packet,
1334 .wrap = gensec_spnego_wrap,
1335 .unwrap = gensec_spnego_unwrap,
1336 .session_key = gensec_spnego_session_key,
1337 .session_info = gensec_spnego_session_info,
1338 .want_feature = gensec_spnego_want_feature,
1339 .have_feature = gensec_spnego_have_feature,
1340 .expire_time = gensec_spnego_expire_time,
1341 .enabled = true,
1342 .priority = GENSEC_SPNEGO
1345 _PUBLIC_ NTSTATUS gensec_spnego_init(void)
1347 NTSTATUS ret;
1348 ret = gensec_register(&gensec_spnego_security_ops);
1349 if (!NT_STATUS_IS_OK(ret)) {
1350 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1351 gensec_spnego_security_ops.name));
1352 return ret;
1355 return ret;