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