dcerpc.idl: remove unused DCERPC_NCACN_PAYLOAD_MAX_SIZE
[Samba.git] / auth / gensec / spnego.c
blob3962d7268b25aeabb05acbd49f7f4c40bcbebbae
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;
62 bool simulate_w2k;
65 * The following is used to implement
66 * the update token fragmentation
68 size_t in_needed;
69 DATA_BLOB in_frag;
70 size_t out_max_length;
71 DATA_BLOB out_frag;
72 NTSTATUS out_status;
76 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
78 struct spnego_state *spnego_state;
80 spnego_state = talloc_zero(gensec_security, struct spnego_state);
81 if (!spnego_state) {
82 return NT_STATUS_NO_MEMORY;
85 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
86 spnego_state->state_position = SPNEGO_CLIENT_START;
87 spnego_state->sub_sec_security = NULL;
88 spnego_state->no_response_expected = false;
89 spnego_state->mech_types = data_blob(NULL, 0);
90 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
91 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
93 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
94 "spnego", "simulate_w2k", false);
96 gensec_security->private_data = spnego_state;
97 return NT_STATUS_OK;
100 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
102 struct spnego_state *spnego_state;
104 spnego_state = talloc_zero(gensec_security, struct spnego_state);
105 if (!spnego_state) {
106 return NT_STATUS_NO_MEMORY;
109 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
110 spnego_state->state_position = SPNEGO_SERVER_START;
111 spnego_state->sub_sec_security = NULL;
112 spnego_state->no_response_expected = false;
113 spnego_state->mech_types = data_blob(NULL, 0);
114 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
115 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
117 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
118 "spnego", "simulate_w2k", false);
120 gensec_security->private_data = spnego_state;
121 return NT_STATUS_OK;
125 wrappers for the spnego_*() functions
127 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
128 uint8_t *data, size_t length,
129 const uint8_t *whole_pdu, size_t pdu_length,
130 const DATA_BLOB *sig)
132 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
134 if (spnego_state->state_position != SPNEGO_DONE
135 && spnego_state->state_position != SPNEGO_FALLBACK) {
136 return NT_STATUS_INVALID_PARAMETER;
139 return gensec_unseal_packet(spnego_state->sub_sec_security,
140 data, length,
141 whole_pdu, pdu_length,
142 sig);
145 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
146 const uint8_t *data, size_t length,
147 const uint8_t *whole_pdu, size_t pdu_length,
148 const DATA_BLOB *sig)
150 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
152 if (spnego_state->state_position != SPNEGO_DONE
153 && spnego_state->state_position != SPNEGO_FALLBACK) {
154 return NT_STATUS_INVALID_PARAMETER;
157 return gensec_check_packet(spnego_state->sub_sec_security,
158 data, length,
159 whole_pdu, pdu_length,
160 sig);
163 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
164 TALLOC_CTX *mem_ctx,
165 uint8_t *data, size_t length,
166 const uint8_t *whole_pdu, size_t pdu_length,
167 DATA_BLOB *sig)
169 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
171 if (spnego_state->state_position != SPNEGO_DONE
172 && spnego_state->state_position != SPNEGO_FALLBACK) {
173 return NT_STATUS_INVALID_PARAMETER;
176 return gensec_seal_packet(spnego_state->sub_sec_security,
177 mem_ctx,
178 data, length,
179 whole_pdu, pdu_length,
180 sig);
183 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
184 TALLOC_CTX *mem_ctx,
185 const uint8_t *data, size_t length,
186 const uint8_t *whole_pdu, size_t pdu_length,
187 DATA_BLOB *sig)
189 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
191 if (spnego_state->state_position != SPNEGO_DONE
192 && spnego_state->state_position != SPNEGO_FALLBACK) {
193 return NT_STATUS_INVALID_PARAMETER;
196 return gensec_sign_packet(spnego_state->sub_sec_security,
197 mem_ctx,
198 data, length,
199 whole_pdu, pdu_length,
200 sig);
203 static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security,
204 TALLOC_CTX *mem_ctx,
205 const DATA_BLOB *in,
206 DATA_BLOB *out)
208 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
210 if (spnego_state->state_position != SPNEGO_DONE
211 && spnego_state->state_position != SPNEGO_FALLBACK) {
212 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
213 return NT_STATUS_INVALID_PARAMETER;
216 return gensec_wrap(spnego_state->sub_sec_security,
217 mem_ctx, in, out);
220 static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security,
221 TALLOC_CTX *mem_ctx,
222 const DATA_BLOB *in,
223 DATA_BLOB *out)
225 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
227 if (spnego_state->state_position != SPNEGO_DONE
228 && spnego_state->state_position != SPNEGO_FALLBACK) {
229 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
230 return NT_STATUS_INVALID_PARAMETER;
233 return gensec_unwrap(spnego_state->sub_sec_security,
234 mem_ctx, in, out);
237 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size)
239 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
241 if (spnego_state->state_position != SPNEGO_DONE
242 && spnego_state->state_position != SPNEGO_FALLBACK) {
243 return 0;
246 return gensec_sig_size(spnego_state->sub_sec_security, data_size);
249 static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
251 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
253 if (spnego_state->state_position != SPNEGO_DONE
254 && spnego_state->state_position != SPNEGO_FALLBACK) {
255 return 0;
258 return gensec_max_input_size(spnego_state->sub_sec_security);
261 static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
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 return 0;
270 return gensec_max_wrapped_size(spnego_state->sub_sec_security);
273 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
274 TALLOC_CTX *mem_ctx,
275 DATA_BLOB *session_key)
277 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
278 if (!spnego_state->sub_sec_security) {
279 return NT_STATUS_INVALID_PARAMETER;
282 return gensec_session_key(spnego_state->sub_sec_security,
283 mem_ctx,
284 session_key);
287 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
288 TALLOC_CTX *mem_ctx,
289 struct auth_session_info **session_info)
291 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
292 if (!spnego_state->sub_sec_security) {
293 return NT_STATUS_INVALID_PARAMETER;
296 return gensec_session_info(spnego_state->sub_sec_security,
297 mem_ctx,
298 session_info);
301 /** Fallback to another GENSEC mechanism, based on magic strings
303 * This is the 'fallback' case, where we don't get SPNEGO, and have to
304 * try all the other options (and hope they all have a magic string
305 * they check)
308 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
309 struct spnego_state *spnego_state,
310 struct tevent_context *ev,
311 TALLOC_CTX *out_mem_ctx,
312 const DATA_BLOB in, DATA_BLOB *out)
314 int i,j;
315 const struct gensec_security_ops **all_ops;
317 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
319 for (i=0; all_ops && all_ops[i]; i++) {
320 bool is_spnego;
321 NTSTATUS nt_status;
323 if (gensec_security != NULL &&
324 !gensec_security_ops_enabled(all_ops[i], gensec_security))
325 continue;
327 if (!all_ops[i]->oid) {
328 continue;
331 is_spnego = false;
332 for (j=0; all_ops[i]->oid[j]; j++) {
333 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
334 is_spnego = true;
337 if (is_spnego) {
338 continue;
341 if (!all_ops[i]->magic) {
342 continue;
345 nt_status = all_ops[i]->magic(gensec_security, &in);
346 if (!NT_STATUS_IS_OK(nt_status)) {
347 continue;
350 spnego_state->state_position = SPNEGO_FALLBACK;
352 nt_status = gensec_subcontext_start(spnego_state,
353 gensec_security,
354 &spnego_state->sub_sec_security);
356 if (!NT_STATUS_IS_OK(nt_status)) {
357 return nt_status;
359 /* select the sub context */
360 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
361 all_ops[i]);
362 if (!NT_STATUS_IS_OK(nt_status)) {
363 return nt_status;
365 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
366 ev, out_mem_ctx, in, out);
367 return nt_status;
369 DEBUG(1, ("Failed to parse SPNEGO request\n"));
370 return NT_STATUS_INVALID_PARAMETER;
374 Parse the netTokenInit, either from the client, to the server, or
375 from the server to the client.
378 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
379 struct spnego_state *spnego_state,
380 TALLOC_CTX *out_mem_ctx,
381 struct tevent_context *ev,
382 const char * const *mechType,
383 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
385 int i;
386 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
387 DATA_BLOB null_data_blob = data_blob(NULL,0);
388 bool ok;
390 const struct gensec_security_ops_wrapper *all_sec
391 = gensec_security_by_oid_list(gensec_security,
392 out_mem_ctx,
393 mechType,
394 GENSEC_OID_SPNEGO);
396 ok = spnego_write_mech_types(spnego_state,
397 mechType,
398 &spnego_state->mech_types);
399 if (!ok) {
400 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
401 return NT_STATUS_NO_MEMORY;
404 if (spnego_state->state_position == SPNEGO_SERVER_START) {
405 uint32_t j;
406 for (j=0; mechType && mechType[j]; j++) {
407 for (i=0; all_sec && all_sec[i].op; i++) {
408 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
409 continue;
412 nt_status = gensec_subcontext_start(spnego_state,
413 gensec_security,
414 &spnego_state->sub_sec_security);
415 if (!NT_STATUS_IS_OK(nt_status)) {
416 return nt_status;
418 /* select the sub context */
419 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
420 all_sec[i].op);
421 if (!NT_STATUS_IS_OK(nt_status)) {
422 talloc_free(spnego_state->sub_sec_security);
423 spnego_state->sub_sec_security = NULL;
424 break;
427 if (j > 0) {
428 /* no optimistic token */
429 spnego_state->neg_oid = all_sec[i].oid;
430 *unwrapped_out = data_blob_null;
431 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
433 * Indicate the downgrade and request a
434 * mic.
436 spnego_state->mic_requested = true;
437 break;
440 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
441 out_mem_ctx,
443 unwrapped_in,
444 unwrapped_out);
445 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
446 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
447 /* Pretend we never started it (lets the first run find some incompatible demand) */
449 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
450 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
451 talloc_free(spnego_state->sub_sec_security);
452 spnego_state->sub_sec_security = NULL;
453 break;
456 spnego_state->neg_oid = all_sec[i].oid;
457 break;
459 if (spnego_state->sub_sec_security) {
460 break;
464 if (!spnego_state->sub_sec_security) {
465 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
466 return NT_STATUS_INVALID_PARAMETER;
470 /* Having tried any optimistic token from the client (if we
471 * were the server), if we didn't get anywhere, walk our list
472 * in our preference order */
474 if (!spnego_state->sub_sec_security) {
475 for (i=0; all_sec && all_sec[i].op; i++) {
476 nt_status = gensec_subcontext_start(spnego_state,
477 gensec_security,
478 &spnego_state->sub_sec_security);
479 if (!NT_STATUS_IS_OK(nt_status)) {
480 return nt_status;
482 /* select the sub context */
483 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
484 all_sec[i].op);
485 if (!NT_STATUS_IS_OK(nt_status)) {
486 talloc_free(spnego_state->sub_sec_security);
487 spnego_state->sub_sec_security = NULL;
488 continue;
491 spnego_state->neg_oid = all_sec[i].oid;
493 /* only get the helping start blob for the first OID */
494 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
495 out_mem_ctx,
497 null_data_blob,
498 unwrapped_out);
500 /* it is likely that a NULL input token will
501 * not be liked by most server mechs, but if
502 * we are in the client, we want the first
503 * update packet to be able to abort the use
504 * of this mech */
505 if (spnego_state->state_position != SPNEGO_SERVER_START) {
506 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
507 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
508 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
509 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
510 /* Pretend we never started it (lets the first run find some incompatible demand) */
512 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
513 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
514 talloc_free(spnego_state->sub_sec_security);
515 spnego_state->sub_sec_security = NULL;
516 continue;
520 break;
524 if (spnego_state->sub_sec_security) {
525 /* it is likely that a NULL input token will
526 * not be liked by most server mechs, but this
527 * does the right thing in the CIFS client.
528 * just push us along the merry-go-round
529 * again, and hope for better luck next
530 * time */
532 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
533 *unwrapped_out = data_blob(NULL, 0);
534 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
537 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
538 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
539 && !NT_STATUS_IS_OK(nt_status)) {
540 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
541 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
542 talloc_free(spnego_state->sub_sec_security);
543 spnego_state->sub_sec_security = NULL;
545 /* We started the mech correctly, and the
546 * input from the other side was valid.
547 * Return the error (say bad password, invalid
548 * ticket) */
549 return nt_status;
552 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
555 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
556 /* we could re-negotiate here, but it would only work
557 * if the client or server lied about what it could
558 * support the first time. Lets keep this code to
559 * reality */
561 return nt_status;
564 /** create a negTokenInit
566 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
568 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
569 struct spnego_state *spnego_state,
570 TALLOC_CTX *out_mem_ctx,
571 struct tevent_context *ev,
572 const DATA_BLOB in, DATA_BLOB *out)
574 int i;
575 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
576 DATA_BLOB null_data_blob = data_blob(NULL,0);
577 const char **mechTypes = NULL;
578 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
579 const struct gensec_security_ops_wrapper *all_sec;
581 mechTypes = gensec_security_oids(gensec_security,
582 out_mem_ctx, GENSEC_OID_SPNEGO);
584 all_sec = gensec_security_by_oid_list(gensec_security,
585 out_mem_ctx,
586 mechTypes,
587 GENSEC_OID_SPNEGO);
588 for (i=0; all_sec && all_sec[i].op; i++) {
589 struct spnego_data spnego_out;
590 const char **send_mech_types;
591 bool ok;
593 nt_status = gensec_subcontext_start(spnego_state,
594 gensec_security,
595 &spnego_state->sub_sec_security);
596 if (!NT_STATUS_IS_OK(nt_status)) {
597 return nt_status;
599 /* select the sub context */
600 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
601 all_sec[i].op);
602 if (!NT_STATUS_IS_OK(nt_status)) {
603 talloc_free(spnego_state->sub_sec_security);
604 spnego_state->sub_sec_security = NULL;
605 continue;
608 /* In the client, try and produce the first (optimistic) packet */
609 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
610 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
611 out_mem_ctx,
613 null_data_blob,
614 &unwrapped_out);
616 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
617 && !NT_STATUS_IS_OK(nt_status)) {
618 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
619 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
620 talloc_free(spnego_state->sub_sec_security);
621 spnego_state->sub_sec_security = NULL;
622 /* Pretend we never started it (lets the first run find some incompatible demand) */
624 continue;
628 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
630 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
631 &all_sec[i]);
633 ok = spnego_write_mech_types(spnego_state,
634 send_mech_types,
635 &spnego_state->mech_types);
636 if (!ok) {
637 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
638 return NT_STATUS_NO_MEMORY;
641 /* List the remaining mechs as options */
642 spnego_out.negTokenInit.mechTypes = send_mech_types;
643 spnego_out.negTokenInit.reqFlags = null_data_blob;
644 spnego_out.negTokenInit.reqFlagsPadding = 0;
646 if (spnego_state->state_position == SPNEGO_SERVER_START) {
647 spnego_out.negTokenInit.mechListMIC
648 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
649 } else {
650 spnego_out.negTokenInit.mechListMIC = null_data_blob;
653 spnego_out.negTokenInit.mechToken = unwrapped_out;
655 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
656 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
657 return NT_STATUS_INVALID_PARAMETER;
660 /* set next state */
661 spnego_state->neg_oid = all_sec[i].oid;
663 if (NT_STATUS_IS_OK(nt_status)) {
664 spnego_state->no_response_expected = true;
667 return NT_STATUS_MORE_PROCESSING_REQUIRED;
669 talloc_free(spnego_state->sub_sec_security);
670 spnego_state->sub_sec_security = NULL;
672 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
673 return nt_status;
677 /** create a server negTokenTarg
679 * This is the case, where the client is the first one who sends data
682 static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_state,
683 TALLOC_CTX *out_mem_ctx,
684 NTSTATUS nt_status,
685 const DATA_BLOB unwrapped_out,
686 DATA_BLOB mech_list_mic,
687 DATA_BLOB *out)
689 struct spnego_data spnego_out;
690 DATA_BLOB null_data_blob = data_blob(NULL, 0);
692 /* compose reply */
693 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
694 spnego_out.negTokenTarg.responseToken = unwrapped_out;
695 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
696 spnego_out.negTokenTarg.supportedMech = NULL;
698 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
699 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
700 if (spnego_state->mic_requested) {
701 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
702 spnego_state->mic_requested = false;
703 } else {
704 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
706 spnego_state->state_position = SPNEGO_SERVER_TARG;
707 } else if (NT_STATUS_IS_OK(nt_status)) {
708 if (unwrapped_out.data) {
709 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
711 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
712 spnego_state->state_position = SPNEGO_DONE;
713 } else {
714 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
715 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
716 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
717 spnego_state->state_position = SPNEGO_DONE;
720 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
721 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
722 return NT_STATUS_INVALID_PARAMETER;
725 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
726 spnego_state->num_targs++;
728 return nt_status;
732 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
733 struct tevent_context *ev,
734 const DATA_BLOB in, DATA_BLOB *out)
736 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
737 DATA_BLOB null_data_blob = data_blob(NULL, 0);
738 DATA_BLOB mech_list_mic = data_blob(NULL, 0);
739 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
740 struct spnego_data spnego_out;
741 struct spnego_data spnego;
743 ssize_t len;
745 *out = data_blob(NULL, 0);
747 if (!out_mem_ctx) {
748 out_mem_ctx = spnego_state;
751 /* and switch into the state machine */
753 switch (spnego_state->state_position) {
754 case SPNEGO_FALLBACK:
755 return gensec_update_ev(spnego_state->sub_sec_security, ev,
756 out_mem_ctx, in, out);
757 case SPNEGO_SERVER_START:
759 NTSTATUS nt_status;
760 if (in.length) {
762 len = spnego_read_data(gensec_security, in, &spnego);
763 if (len == -1) {
764 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
765 ev, out_mem_ctx, in, out);
767 /* client sent NegTargetInit, we send NegTokenTarg */
769 /* OK, so it's real SPNEGO, check the packet's the one we expect */
770 if (spnego.type != spnego_state->expected_packet) {
771 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
772 spnego_state->expected_packet));
773 dump_data(1, in.data, in.length);
774 spnego_free_data(&spnego);
775 return NT_STATUS_INVALID_PARAMETER;
778 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
779 spnego_state,
780 out_mem_ctx,
782 spnego.negTokenInit.mechTypes,
783 spnego.negTokenInit.mechToken,
784 &unwrapped_out);
786 if (spnego_state->simulate_w2k) {
788 * Windows 2000 returns the unwrapped token
789 * also in the mech_list_mic field.
791 * In order to verify our client code,
792 * we need a way to have a server with this
793 * broken behaviour
795 mech_list_mic = unwrapped_out;
798 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
799 out_mem_ctx,
800 nt_status,
801 unwrapped_out,
802 mech_list_mic,
803 out);
805 spnego_free_data(&spnego);
807 return nt_status;
808 } else {
809 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
810 out_mem_ctx, ev, in, out);
811 spnego_state->state_position = SPNEGO_SERVER_START;
812 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
813 return nt_status;
817 case SPNEGO_CLIENT_START:
819 /* The server offers a list of mechanisms */
821 const char *my_mechs[] = {NULL, NULL};
822 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
823 bool ok;
825 if (!in.length) {
826 /* client to produce negTokenInit */
827 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
828 out_mem_ctx, ev, in, out);
829 spnego_state->state_position = SPNEGO_CLIENT_TARG;
830 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
831 return nt_status;
834 len = spnego_read_data(gensec_security, in, &spnego);
836 if (len == -1) {
837 DEBUG(1, ("Invalid SPNEGO request:\n"));
838 dump_data(1, in.data, in.length);
839 return NT_STATUS_INVALID_PARAMETER;
842 /* OK, so it's real SPNEGO, check the packet's the one we expect */
843 if (spnego.type != spnego_state->expected_packet) {
844 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
845 spnego_state->expected_packet));
846 dump_data(1, in.data, in.length);
847 spnego_free_data(&spnego);
848 return NT_STATUS_INVALID_PARAMETER;
851 if (spnego.negTokenInit.targetPrincipal
852 && strcmp(spnego.negTokenInit.targetPrincipal, ADS_IGNORE_PRINCIPAL) != 0) {
853 DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
854 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
855 gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
859 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
860 spnego_state,
861 out_mem_ctx,
863 spnego.negTokenInit.mechTypes,
864 spnego.negTokenInit.mechToken,
865 &unwrapped_out);
867 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
868 spnego_free_data(&spnego);
869 return nt_status;
872 my_mechs[0] = spnego_state->neg_oid;
873 /* compose reply */
874 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
875 spnego_out.negTokenInit.mechTypes = my_mechs;
876 spnego_out.negTokenInit.reqFlags = null_data_blob;
877 spnego_out.negTokenInit.reqFlagsPadding = 0;
878 spnego_out.negTokenInit.mechListMIC = null_data_blob;
879 spnego_out.negTokenInit.mechToken = unwrapped_out;
881 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
882 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
883 return NT_STATUS_INVALID_PARAMETER;
886 ok = spnego_write_mech_types(spnego_state,
887 my_mechs,
888 &spnego_state->mech_types);
889 if (!ok) {
890 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
891 return NT_STATUS_NO_MEMORY;
894 /* set next state */
895 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
896 spnego_state->state_position = SPNEGO_CLIENT_TARG;
898 if (NT_STATUS_IS_OK(nt_status)) {
899 spnego_state->no_response_expected = true;
902 spnego_free_data(&spnego);
903 return NT_STATUS_MORE_PROCESSING_REQUIRED;
905 case SPNEGO_SERVER_TARG:
907 NTSTATUS nt_status;
908 bool have_sign = true;
909 bool new_spnego = false;
911 if (!in.length) {
912 return NT_STATUS_INVALID_PARAMETER;
915 len = spnego_read_data(gensec_security, in, &spnego);
917 if (len == -1) {
918 DEBUG(1, ("Invalid SPNEGO request:\n"));
919 dump_data(1, in.data, in.length);
920 return NT_STATUS_INVALID_PARAMETER;
923 /* OK, so it's real SPNEGO, check the packet's the one we expect */
924 if (spnego.type != spnego_state->expected_packet) {
925 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
926 spnego_state->expected_packet));
927 dump_data(1, in.data, in.length);
928 spnego_free_data(&spnego);
929 return NT_STATUS_INVALID_PARAMETER;
932 spnego_state->num_targs++;
934 if (!spnego_state->sub_sec_security) {
935 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
936 spnego_free_data(&spnego);
937 return NT_STATUS_INVALID_PARAMETER;
940 if (spnego_state->needs_mic_check) {
941 if (spnego.negTokenTarg.responseToken.length != 0) {
942 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
943 spnego_free_data(&spnego);
944 return NT_STATUS_INVALID_PARAMETER;
947 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
948 spnego_state->mech_types.data,
949 spnego_state->mech_types.length,
950 spnego_state->mech_types.data,
951 spnego_state->mech_types.length,
952 &spnego.negTokenTarg.mechListMIC);
953 if (NT_STATUS_IS_OK(nt_status)) {
954 spnego_state->needs_mic_check = false;
955 spnego_state->done_mic_check = true;
956 } else {
957 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
958 nt_errstr(nt_status)));
960 goto server_response;
963 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
964 out_mem_ctx, ev,
965 spnego.negTokenTarg.responseToken,
966 &unwrapped_out);
967 if (!NT_STATUS_IS_OK(nt_status)) {
968 goto server_response;
971 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
972 GENSEC_FEATURE_SIGN);
973 if (spnego_state->simulate_w2k) {
974 have_sign = false;
976 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
977 GENSEC_FEATURE_NEW_SPNEGO);
978 if (spnego.negTokenTarg.mechListMIC.length > 0) {
979 new_spnego = true;
982 if (have_sign && new_spnego) {
983 spnego_state->needs_mic_check = true;
984 spnego_state->needs_mic_sign = true;
987 if (have_sign && spnego.negTokenTarg.mechListMIC.length > 0) {
988 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
989 spnego_state->mech_types.data,
990 spnego_state->mech_types.length,
991 spnego_state->mech_types.data,
992 spnego_state->mech_types.length,
993 &spnego.negTokenTarg.mechListMIC);
994 if (!NT_STATUS_IS_OK(nt_status)) {
995 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
996 nt_errstr(nt_status)));
997 goto server_response;
1000 spnego_state->needs_mic_check = false;
1001 spnego_state->done_mic_check = true;
1004 if (spnego_state->needs_mic_sign) {
1005 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1006 out_mem_ctx,
1007 spnego_state->mech_types.data,
1008 spnego_state->mech_types.length,
1009 spnego_state->mech_types.data,
1010 spnego_state->mech_types.length,
1011 &mech_list_mic);
1012 if (!NT_STATUS_IS_OK(nt_status)) {
1013 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1014 nt_errstr(nt_status)));
1015 goto server_response;
1017 spnego_state->needs_mic_sign = false;
1020 if (spnego_state->needs_mic_check) {
1021 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1024 server_response:
1025 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
1026 out_mem_ctx,
1027 nt_status,
1028 unwrapped_out,
1029 mech_list_mic,
1030 out);
1032 spnego_free_data(&spnego);
1034 return nt_status;
1036 case SPNEGO_CLIENT_TARG:
1038 NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
1040 if (!in.length) {
1041 return NT_STATUS_INVALID_PARAMETER;
1044 len = spnego_read_data(gensec_security, in, &spnego);
1046 if (len == -1) {
1047 DEBUG(1, ("Invalid SPNEGO request:\n"));
1048 dump_data(1, in.data, in.length);
1049 return NT_STATUS_INVALID_PARAMETER;
1052 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1053 if (spnego.type != spnego_state->expected_packet) {
1054 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
1055 spnego_state->expected_packet));
1056 dump_data(1, in.data, in.length);
1057 spnego_free_data(&spnego);
1058 return NT_STATUS_INVALID_PARAMETER;
1061 spnego_state->num_targs++;
1063 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1064 spnego_free_data(&spnego);
1065 return NT_STATUS_LOGON_FAILURE;
1068 if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
1069 spnego_state->mic_requested = true;
1072 /* Server didn't like our choice of mech, and chose something else */
1073 if (((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
1074 (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC)) &&
1075 spnego.negTokenTarg.supportedMech &&
1076 strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
1077 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1078 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid),
1079 gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech)));
1081 spnego_state->no_response_expected = false;
1082 talloc_free(spnego_state->sub_sec_security);
1083 nt_status = gensec_subcontext_start(spnego_state,
1084 gensec_security,
1085 &spnego_state->sub_sec_security);
1086 if (!NT_STATUS_IS_OK(nt_status)) {
1087 spnego_free_data(&spnego);
1088 return nt_status;
1090 /* select the sub context */
1091 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
1092 spnego.negTokenTarg.supportedMech);
1093 if (!NT_STATUS_IS_OK(nt_status)) {
1094 spnego_free_data(&spnego);
1095 return nt_status;
1098 spnego_state->neg_oid = talloc_strdup(spnego_state,
1099 spnego.negTokenTarg.supportedMech);
1100 if (spnego_state->neg_oid == NULL) {
1101 spnego_free_data(&spnego);
1102 return NT_STATUS_NO_MEMORY;
1106 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1107 DATA_BLOB *m = &spnego.negTokenTarg.mechListMIC;
1108 const DATA_BLOB *r = &spnego.negTokenTarg.responseToken;
1111 * Windows 2000 has a bug, it repeats the
1112 * responseToken in the mechListMIC field.
1114 if (m->length == r->length) {
1115 int cmp;
1117 cmp = memcmp(m->data, r->data, m->length);
1118 if (cmp == 0) {
1119 data_blob_free(m);
1124 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1125 if (spnego_state->no_response_expected) {
1126 spnego_state->needs_mic_check = true;
1130 if (spnego_state->needs_mic_check) {
1131 if (spnego.negTokenTarg.responseToken.length != 0) {
1132 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1133 spnego_free_data(&spnego);
1134 return NT_STATUS_INVALID_PARAMETER;
1137 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1138 spnego_state->mech_types.data,
1139 spnego_state->mech_types.length,
1140 spnego_state->mech_types.data,
1141 spnego_state->mech_types.length,
1142 &spnego.negTokenTarg.mechListMIC);
1143 if (!NT_STATUS_IS_OK(nt_status)) {
1144 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1145 nt_errstr(nt_status)));
1146 spnego_free_data(&spnego);
1147 return nt_status;
1149 spnego_state->needs_mic_check = false;
1150 spnego_state->done_mic_check = true;
1151 goto client_response;
1154 if (!spnego_state->no_response_expected) {
1155 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1156 out_mem_ctx, ev,
1157 spnego.negTokenTarg.responseToken,
1158 &unwrapped_out);
1159 if (!NT_STATUS_IS_OK(nt_status)) {
1160 goto client_response;
1163 spnego_state->no_response_expected = true;
1164 } else {
1165 nt_status = NT_STATUS_OK;
1168 if (spnego_state->no_response_expected &&
1169 !spnego_state->done_mic_check)
1171 bool have_sign = true;
1172 bool new_spnego = false;
1174 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1175 GENSEC_FEATURE_SIGN);
1176 if (spnego_state->simulate_w2k) {
1177 have_sign = false;
1179 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1180 GENSEC_FEATURE_NEW_SPNEGO);
1182 switch (spnego.negTokenTarg.negResult) {
1183 case SPNEGO_ACCEPT_COMPLETED:
1184 case SPNEGO_NONE_RESULT:
1185 if (spnego_state->num_targs == 1) {
1187 * the first exchange doesn't require
1188 * verification
1190 new_spnego = false;
1192 break;
1194 case SPNEGO_ACCEPT_INCOMPLETE:
1195 case SPNEGO_REQUEST_MIC:
1196 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1197 new_spnego = true;
1199 break;
1200 default:
1201 break;
1204 if (spnego_state->mic_requested) {
1205 if (have_sign) {
1206 new_spnego = true;
1210 if (have_sign && new_spnego) {
1211 spnego_state->needs_mic_check = true;
1212 spnego_state->needs_mic_sign = true;
1216 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1217 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1218 spnego_state->mech_types.data,
1219 spnego_state->mech_types.length,
1220 spnego_state->mech_types.data,
1221 spnego_state->mech_types.length,
1222 &spnego.negTokenTarg.mechListMIC);
1223 if (!NT_STATUS_IS_OK(nt_status)) {
1224 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1225 nt_errstr(nt_status)));
1226 spnego_free_data(&spnego);
1227 return nt_status;
1229 spnego_state->needs_mic_check = false;
1230 spnego_state->done_mic_check = true;
1233 if (spnego_state->needs_mic_sign) {
1234 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1235 out_mem_ctx,
1236 spnego_state->mech_types.data,
1237 spnego_state->mech_types.length,
1238 spnego_state->mech_types.data,
1239 spnego_state->mech_types.length,
1240 &mech_list_mic);
1241 if (!NT_STATUS_IS_OK(nt_status)) {
1242 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1243 nt_errstr(nt_status)));
1244 spnego_free_data(&spnego);
1245 return nt_status;
1247 spnego_state->needs_mic_sign = false;
1250 if (spnego_state->needs_mic_check) {
1251 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1254 client_response:
1255 spnego_free_data(&spnego);
1257 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1258 && !NT_STATUS_IS_OK(nt_status)) {
1259 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1260 spnego_state->sub_sec_security->ops->name,
1261 nt_errstr(nt_status)));
1262 return nt_status;
1265 if (unwrapped_out.length || mech_list_mic.length) {
1266 /* compose reply */
1267 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1268 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1269 spnego_out.negTokenTarg.supportedMech = NULL;
1270 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1271 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1273 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1274 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1275 return NT_STATUS_INVALID_PARAMETER;
1278 spnego_state->num_targs++;
1279 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1280 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1281 } else {
1283 /* all done - server has accepted, and we agree */
1284 *out = null_data_blob;
1286 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1287 /* unless of course it did not accept */
1288 DEBUG(1,("gensec_update ok but not accepted\n"));
1289 nt_status = NT_STATUS_INVALID_PARAMETER;
1292 spnego_state->state_position = SPNEGO_DONE;
1295 return nt_status;
1297 case SPNEGO_DONE:
1298 /* We should not be called after we are 'done' */
1299 return NT_STATUS_INVALID_PARAMETER;
1301 return NT_STATUS_INVALID_PARAMETER;
1304 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1305 const DATA_BLOB in, DATA_BLOB *full_in)
1307 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1308 size_t expected;
1309 bool ok;
1311 *full_in = data_blob_null;
1313 if (spnego_state->in_needed == 0) {
1314 size_t size = 0;
1315 int ret;
1318 * try to work out the size of the full
1319 * input token, it might be fragmented
1321 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1322 if ((ret != 0) && (ret != EAGAIN)) {
1323 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1326 if ((ret == 0) || (ret == EAGAIN)) {
1327 spnego_state->in_needed = size;
1328 } else {
1330 * If it is not an asn1 message
1331 * just call the next layer.
1333 spnego_state->in_needed = in.length;
1337 if (spnego_state->in_needed > UINT16_MAX) {
1339 * limit the incoming message to 0xFFFF
1340 * to avoid DoS attacks.
1342 return NT_STATUS_INVALID_BUFFER_SIZE;
1345 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1347 * If we reach this, we know we got at least
1348 * part of an asn1 message, getting 0 means
1349 * the remote peer wants us to spin.
1351 return NT_STATUS_INVALID_PARAMETER;
1354 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1355 if (in.length > expected) {
1357 * we got more than expected
1359 return NT_STATUS_INVALID_PARAMETER;
1362 if (in.length == spnego_state->in_needed) {
1364 * if the in.length contains the full blob
1365 * we are done.
1367 * Note: this implies spnego_state->in_frag.length == 0,
1368 * but we do not need to check this explicitly
1369 * because we already know that we did not get
1370 * more than expected.
1372 *full_in = in;
1373 return NT_STATUS_OK;
1376 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1377 in.data, in.length);
1378 if (!ok) {
1379 return NT_STATUS_NO_MEMORY;
1382 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1383 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1386 *full_in = spnego_state->in_frag;
1387 return NT_STATUS_OK;
1390 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1391 TALLOC_CTX *out_mem_ctx,
1392 DATA_BLOB *_out)
1394 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1395 DATA_BLOB out = data_blob_null;
1396 bool ok;
1398 *_out = data_blob_null;
1400 if (spnego_state->out_frag.length == 0) {
1401 return spnego_state->out_status;
1405 * There is still more data to be delivered
1406 * to the remote peer.
1409 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1411 * Fast path, we can deliver everything
1414 *_out = spnego_state->out_frag;
1415 talloc_steal(out_mem_ctx, _out->data);
1416 spnego_state->out_frag = data_blob_null;
1417 return spnego_state->out_status;
1420 out = spnego_state->out_frag;
1423 * copy the remaining bytes
1425 spnego_state->out_frag = data_blob_talloc(spnego_state,
1426 out.data + spnego_state->out_max_length,
1427 out.length - spnego_state->out_max_length);
1428 if (spnego_state->out_frag.data == NULL) {
1429 return NT_STATUS_NO_MEMORY;
1433 * truncate the buffer
1435 ok = data_blob_realloc(spnego_state, &out,
1436 spnego_state->out_max_length);
1437 if (!ok) {
1438 return NT_STATUS_NO_MEMORY;
1441 talloc_steal(out_mem_ctx, out.data);
1442 *_out = out;
1443 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1446 static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security,
1447 TALLOC_CTX *out_mem_ctx,
1448 struct tevent_context *ev,
1449 const DATA_BLOB in, DATA_BLOB *out)
1451 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1452 DATA_BLOB full_in = data_blob_null;
1453 NTSTATUS status;
1455 *out = data_blob_null;
1457 if (spnego_state->out_frag.length > 0) {
1458 if (in.length > 0) {
1459 return NT_STATUS_INVALID_PARAMETER;
1462 return gensec_spnego_update_out(gensec_security,
1463 out_mem_ctx,
1464 out);
1467 status = gensec_spnego_update_in(gensec_security,
1468 in, &full_in);
1469 if (!NT_STATUS_IS_OK(status)) {
1470 return status;
1473 status = gensec_spnego_update(gensec_security,
1474 spnego_state, ev,
1475 full_in,
1476 &spnego_state->out_frag);
1477 data_blob_free(&spnego_state->in_frag);
1478 spnego_state->in_needed = 0;
1479 if (NT_STATUS_IS_OK(status)) {
1480 bool reset_full = true;
1482 gensec_security->child_security = spnego_state->sub_sec_security;
1484 reset_full = !spnego_state->done_mic_check;
1486 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1487 reset_full);
1489 if (!NT_STATUS_IS_OK(status) &&
1490 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1491 return status;
1494 spnego_state->out_status = status;
1496 return gensec_spnego_update_out(gensec_security,
1497 out_mem_ctx,
1498 out);
1501 static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
1502 uint32_t feature)
1504 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1506 if (!spnego_state || !spnego_state->sub_sec_security) {
1507 gensec_security->want_features |= feature;
1508 return;
1511 gensec_want_feature(spnego_state->sub_sec_security,
1512 feature);
1515 static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
1516 uint32_t feature)
1518 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1519 if (!spnego_state->sub_sec_security) {
1520 return false;
1523 return gensec_have_feature(spnego_state->sub_sec_security,
1524 feature);
1527 static NTTIME gensec_spnego_expire_time(struct gensec_security *gensec_security)
1529 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1531 if (!spnego_state->sub_sec_security) {
1532 return GENSEC_EXPIRE_TIME_INFINITY;
1535 return gensec_expire_time(spnego_state->sub_sec_security);
1538 static const char *gensec_spnego_oids[] = {
1539 GENSEC_OID_SPNEGO,
1540 NULL
1543 static const struct gensec_security_ops gensec_spnego_security_ops = {
1544 .name = "spnego",
1545 .sasl_name = "GSS-SPNEGO",
1546 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1547 .oid = gensec_spnego_oids,
1548 .client_start = gensec_spnego_client_start,
1549 .server_start = gensec_spnego_server_start,
1550 .update = gensec_spnego_update_wrapper,
1551 .seal_packet = gensec_spnego_seal_packet,
1552 .sign_packet = gensec_spnego_sign_packet,
1553 .sig_size = gensec_spnego_sig_size,
1554 .max_wrapped_size = gensec_spnego_max_wrapped_size,
1555 .max_input_size = gensec_spnego_max_input_size,
1556 .check_packet = gensec_spnego_check_packet,
1557 .unseal_packet = gensec_spnego_unseal_packet,
1558 .wrap = gensec_spnego_wrap,
1559 .unwrap = gensec_spnego_unwrap,
1560 .session_key = gensec_spnego_session_key,
1561 .session_info = gensec_spnego_session_info,
1562 .want_feature = gensec_spnego_want_feature,
1563 .have_feature = gensec_spnego_have_feature,
1564 .expire_time = gensec_spnego_expire_time,
1565 .enabled = true,
1566 .priority = GENSEC_SPNEGO
1569 _PUBLIC_ NTSTATUS gensec_spnego_init(void)
1571 NTSTATUS ret;
1572 ret = gensec_register(&gensec_spnego_security_ops);
1573 if (!NT_STATUS_IS_OK(ret)) {
1574 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1575 gensec_spnego_security_ops.name));
1576 return ret;
1579 return ret;