vfs_acl_common: Windows style default ACL
[Samba.git] / auth / gensec / spnego.c
blob9e5e7584f1a838b4388b962f4e11ac4e92de957d
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 downgraded;
58 bool mic_requested;
59 bool needs_mic_sign;
60 bool needs_mic_check;
61 bool may_skip_mic_check;
62 bool done_mic_check;
64 bool simulate_w2k;
67 * The following is used to implement
68 * the update token fragmentation
70 size_t in_needed;
71 DATA_BLOB in_frag;
72 size_t out_max_length;
73 DATA_BLOB out_frag;
74 NTSTATUS out_status;
78 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
80 struct spnego_state *spnego_state;
82 spnego_state = talloc_zero(gensec_security, struct spnego_state);
83 if (!spnego_state) {
84 return NT_STATUS_NO_MEMORY;
87 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
88 spnego_state->state_position = SPNEGO_CLIENT_START;
89 spnego_state->sub_sec_security = NULL;
90 spnego_state->no_response_expected = false;
91 spnego_state->mech_types = data_blob(NULL, 0);
92 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
93 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
95 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
96 "spnego", "simulate_w2k", false);
98 gensec_security->private_data = spnego_state;
99 return NT_STATUS_OK;
102 static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
104 struct spnego_state *spnego_state;
106 spnego_state = talloc_zero(gensec_security, struct spnego_state);
107 if (!spnego_state) {
108 return NT_STATUS_NO_MEMORY;
111 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
112 spnego_state->state_position = SPNEGO_SERVER_START;
113 spnego_state->sub_sec_security = NULL;
114 spnego_state->no_response_expected = false;
115 spnego_state->mech_types = data_blob(NULL, 0);
116 spnego_state->out_max_length = gensec_max_update_size(gensec_security);
117 spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
119 spnego_state->simulate_w2k = gensec_setting_bool(gensec_security->settings,
120 "spnego", "simulate_w2k", false);
122 gensec_security->private_data = spnego_state;
123 return NT_STATUS_OK;
127 wrappers for the spnego_*() functions
129 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
130 uint8_t *data, size_t length,
131 const uint8_t *whole_pdu, size_t pdu_length,
132 const DATA_BLOB *sig)
134 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
136 if (spnego_state->state_position != SPNEGO_DONE
137 && spnego_state->state_position != SPNEGO_FALLBACK) {
138 return NT_STATUS_INVALID_PARAMETER;
141 return gensec_unseal_packet(spnego_state->sub_sec_security,
142 data, length,
143 whole_pdu, pdu_length,
144 sig);
147 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
148 const uint8_t *data, size_t length,
149 const uint8_t *whole_pdu, size_t pdu_length,
150 const DATA_BLOB *sig)
152 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
154 if (spnego_state->state_position != SPNEGO_DONE
155 && spnego_state->state_position != SPNEGO_FALLBACK) {
156 return NT_STATUS_INVALID_PARAMETER;
159 return gensec_check_packet(spnego_state->sub_sec_security,
160 data, length,
161 whole_pdu, pdu_length,
162 sig);
165 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
166 TALLOC_CTX *mem_ctx,
167 uint8_t *data, size_t length,
168 const uint8_t *whole_pdu, size_t pdu_length,
169 DATA_BLOB *sig)
171 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
173 if (spnego_state->state_position != SPNEGO_DONE
174 && spnego_state->state_position != SPNEGO_FALLBACK) {
175 return NT_STATUS_INVALID_PARAMETER;
178 return gensec_seal_packet(spnego_state->sub_sec_security,
179 mem_ctx,
180 data, length,
181 whole_pdu, pdu_length,
182 sig);
185 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
186 TALLOC_CTX *mem_ctx,
187 const uint8_t *data, size_t length,
188 const uint8_t *whole_pdu, size_t pdu_length,
189 DATA_BLOB *sig)
191 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
193 if (spnego_state->state_position != SPNEGO_DONE
194 && spnego_state->state_position != SPNEGO_FALLBACK) {
195 return NT_STATUS_INVALID_PARAMETER;
198 return gensec_sign_packet(spnego_state->sub_sec_security,
199 mem_ctx,
200 data, length,
201 whole_pdu, pdu_length,
202 sig);
205 static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security,
206 TALLOC_CTX *mem_ctx,
207 const DATA_BLOB *in,
208 DATA_BLOB *out)
210 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
212 if (spnego_state->state_position != SPNEGO_DONE
213 && spnego_state->state_position != SPNEGO_FALLBACK) {
214 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
215 return NT_STATUS_INVALID_PARAMETER;
218 return gensec_wrap(spnego_state->sub_sec_security,
219 mem_ctx, in, out);
222 static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security,
223 TALLOC_CTX *mem_ctx,
224 const DATA_BLOB *in,
225 DATA_BLOB *out)
227 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
229 if (spnego_state->state_position != SPNEGO_DONE
230 && spnego_state->state_position != SPNEGO_FALLBACK) {
231 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
232 return NT_STATUS_INVALID_PARAMETER;
235 return gensec_unwrap(spnego_state->sub_sec_security,
236 mem_ctx, in, out);
239 static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size)
241 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
243 if (spnego_state->state_position != SPNEGO_DONE
244 && spnego_state->state_position != SPNEGO_FALLBACK) {
245 return 0;
248 return gensec_sig_size(spnego_state->sub_sec_security, data_size);
251 static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
253 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
255 if (spnego_state->state_position != SPNEGO_DONE
256 && spnego_state->state_position != SPNEGO_FALLBACK) {
257 return 0;
260 return gensec_max_input_size(spnego_state->sub_sec_security);
263 static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
265 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
267 if (spnego_state->state_position != SPNEGO_DONE
268 && spnego_state->state_position != SPNEGO_FALLBACK) {
269 return 0;
272 return gensec_max_wrapped_size(spnego_state->sub_sec_security);
275 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
276 TALLOC_CTX *mem_ctx,
277 DATA_BLOB *session_key)
279 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
280 if (!spnego_state->sub_sec_security) {
281 return NT_STATUS_INVALID_PARAMETER;
284 return gensec_session_key(spnego_state->sub_sec_security,
285 mem_ctx,
286 session_key);
289 static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
290 TALLOC_CTX *mem_ctx,
291 struct auth_session_info **session_info)
293 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
294 if (!spnego_state->sub_sec_security) {
295 return NT_STATUS_INVALID_PARAMETER;
298 return gensec_session_info(spnego_state->sub_sec_security,
299 mem_ctx,
300 session_info);
303 /** Fallback to another GENSEC mechanism, based on magic strings
305 * This is the 'fallback' case, where we don't get SPNEGO, and have to
306 * try all the other options (and hope they all have a magic string
307 * they check)
310 static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
311 struct spnego_state *spnego_state,
312 struct tevent_context *ev,
313 TALLOC_CTX *out_mem_ctx,
314 const DATA_BLOB in, DATA_BLOB *out)
316 int i,j;
317 const struct gensec_security_ops **all_ops;
319 all_ops = gensec_security_mechs(gensec_security, out_mem_ctx);
321 for (i=0; all_ops && all_ops[i]; i++) {
322 bool is_spnego;
323 NTSTATUS nt_status;
325 if (gensec_security != NULL &&
326 !gensec_security_ops_enabled(all_ops[i], gensec_security))
327 continue;
329 if (!all_ops[i]->oid) {
330 continue;
333 is_spnego = false;
334 for (j=0; all_ops[i]->oid[j]; j++) {
335 if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
336 is_spnego = true;
339 if (is_spnego) {
340 continue;
343 if (!all_ops[i]->magic) {
344 continue;
347 nt_status = all_ops[i]->magic(gensec_security, &in);
348 if (!NT_STATUS_IS_OK(nt_status)) {
349 continue;
352 spnego_state->state_position = SPNEGO_FALLBACK;
354 nt_status = gensec_subcontext_start(spnego_state,
355 gensec_security,
356 &spnego_state->sub_sec_security);
358 if (!NT_STATUS_IS_OK(nt_status)) {
359 return nt_status;
361 /* select the sub context */
362 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
363 all_ops[i]);
364 if (!NT_STATUS_IS_OK(nt_status)) {
365 return nt_status;
367 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
368 ev, out_mem_ctx, in, out);
369 return nt_status;
371 DEBUG(1, ("Failed to parse SPNEGO request\n"));
372 return NT_STATUS_INVALID_PARAMETER;
376 Parse the netTokenInit, either from the client, to the server, or
377 from the server to the client.
380 static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
381 struct spnego_state *spnego_state,
382 TALLOC_CTX *out_mem_ctx,
383 struct tevent_context *ev,
384 const char * const *mechType,
385 const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
387 int i;
388 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
389 DATA_BLOB null_data_blob = data_blob(NULL,0);
390 bool ok;
392 const struct gensec_security_ops_wrapper *all_sec
393 = gensec_security_by_oid_list(gensec_security,
394 out_mem_ctx,
395 mechType,
396 GENSEC_OID_SPNEGO);
398 ok = spnego_write_mech_types(spnego_state,
399 mechType,
400 &spnego_state->mech_types);
401 if (!ok) {
402 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
403 return NT_STATUS_NO_MEMORY;
406 if (spnego_state->state_position == SPNEGO_SERVER_START) {
407 uint32_t j;
408 for (j=0; mechType && mechType[j]; j++) {
409 for (i=0; all_sec && all_sec[i].op; i++) {
410 if (strcmp(mechType[j], all_sec[i].oid) != 0) {
411 continue;
414 nt_status = gensec_subcontext_start(spnego_state,
415 gensec_security,
416 &spnego_state->sub_sec_security);
417 if (!NT_STATUS_IS_OK(nt_status)) {
418 return nt_status;
420 /* select the sub context */
421 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
422 all_sec[i].op);
423 if (!NT_STATUS_IS_OK(nt_status)) {
424 talloc_free(spnego_state->sub_sec_security);
425 spnego_state->sub_sec_security = NULL;
426 break;
429 if (j > 0) {
430 /* no optimistic token */
431 spnego_state->neg_oid = all_sec[i].oid;
432 *unwrapped_out = data_blob_null;
433 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
435 * Indicate the downgrade and request a
436 * mic.
438 spnego_state->downgraded = true;
439 spnego_state->mic_requested = true;
440 break;
443 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
444 out_mem_ctx,
446 unwrapped_in,
447 unwrapped_out);
448 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
449 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
450 /* Pretend we never started it (lets the first run find some incompatible demand) */
452 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
453 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
454 talloc_free(spnego_state->sub_sec_security);
455 spnego_state->sub_sec_security = NULL;
456 break;
459 spnego_state->neg_oid = all_sec[i].oid;
460 break;
462 if (spnego_state->sub_sec_security) {
463 break;
467 if (!spnego_state->sub_sec_security) {
468 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
469 return NT_STATUS_INVALID_PARAMETER;
473 /* Having tried any optimistic token from the client (if we
474 * were the server), if we didn't get anywhere, walk our list
475 * in our preference order */
477 if (!spnego_state->sub_sec_security) {
478 for (i=0; all_sec && all_sec[i].op; i++) {
479 nt_status = gensec_subcontext_start(spnego_state,
480 gensec_security,
481 &spnego_state->sub_sec_security);
482 if (!NT_STATUS_IS_OK(nt_status)) {
483 return nt_status;
485 /* select the sub context */
486 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
487 all_sec[i].op);
488 if (!NT_STATUS_IS_OK(nt_status)) {
489 talloc_free(spnego_state->sub_sec_security);
490 spnego_state->sub_sec_security = NULL;
491 continue;
494 spnego_state->neg_oid = all_sec[i].oid;
496 /* only get the helping start blob for the first OID */
497 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
498 out_mem_ctx,
500 null_data_blob,
501 unwrapped_out);
503 /* it is likely that a NULL input token will
504 * not be liked by most server mechs, but if
505 * we are in the client, we want the first
506 * update packet to be able to abort the use
507 * of this mech */
508 if (spnego_state->state_position != SPNEGO_SERVER_START) {
509 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
510 NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS) ||
511 NT_STATUS_EQUAL(nt_status, NT_STATUS_TIME_DIFFERENCE_AT_DC) ||
512 NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
513 /* Pretend we never started it (lets the first run find some incompatible demand) */
515 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
516 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
517 talloc_free(spnego_state->sub_sec_security);
518 spnego_state->sub_sec_security = NULL;
519 continue;
523 break;
527 if (spnego_state->sub_sec_security) {
528 /* it is likely that a NULL input token will
529 * not be liked by most server mechs, but this
530 * does the right thing in the CIFS client.
531 * just push us along the merry-go-round
532 * again, and hope for better luck next
533 * time */
535 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
536 *unwrapped_out = data_blob(NULL, 0);
537 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
540 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
541 && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
542 && !NT_STATUS_IS_OK(nt_status)) {
543 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
544 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
545 talloc_free(spnego_state->sub_sec_security);
546 spnego_state->sub_sec_security = NULL;
548 /* We started the mech correctly, and the
549 * input from the other side was valid.
550 * Return the error (say bad password, invalid
551 * ticket) */
552 return nt_status;
555 return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
558 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
559 /* we could re-negotiate here, but it would only work
560 * if the client or server lied about what it could
561 * support the first time. Lets keep this code to
562 * reality */
564 return nt_status;
567 /** create a negTokenInit
569 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
571 static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
572 struct spnego_state *spnego_state,
573 TALLOC_CTX *out_mem_ctx,
574 struct tevent_context *ev,
575 const DATA_BLOB in, DATA_BLOB *out)
577 int i;
578 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
579 DATA_BLOB null_data_blob = data_blob(NULL,0);
580 const char **mechTypes = NULL;
581 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
582 const struct gensec_security_ops_wrapper *all_sec;
584 mechTypes = gensec_security_oids(gensec_security,
585 out_mem_ctx, GENSEC_OID_SPNEGO);
587 all_sec = gensec_security_by_oid_list(gensec_security,
588 out_mem_ctx,
589 mechTypes,
590 GENSEC_OID_SPNEGO);
591 for (i=0; all_sec && all_sec[i].op; i++) {
592 struct spnego_data spnego_out;
593 const char **send_mech_types;
594 bool ok;
596 nt_status = gensec_subcontext_start(spnego_state,
597 gensec_security,
598 &spnego_state->sub_sec_security);
599 if (!NT_STATUS_IS_OK(nt_status)) {
600 return nt_status;
602 /* select the sub context */
603 nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
604 all_sec[i].op);
605 if (!NT_STATUS_IS_OK(nt_status)) {
606 talloc_free(spnego_state->sub_sec_security);
607 spnego_state->sub_sec_security = NULL;
608 continue;
611 /* In the client, try and produce the first (optimistic) packet */
612 if (spnego_state->state_position == SPNEGO_CLIENT_START) {
613 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
614 out_mem_ctx,
616 null_data_blob,
617 &unwrapped_out);
619 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
620 && !NT_STATUS_IS_OK(nt_status)) {
621 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
622 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
623 talloc_free(spnego_state->sub_sec_security);
624 spnego_state->sub_sec_security = NULL;
625 /* Pretend we never started it (lets the first run find some incompatible demand) */
627 continue;
631 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
633 send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
634 &all_sec[i]);
636 ok = spnego_write_mech_types(spnego_state,
637 send_mech_types,
638 &spnego_state->mech_types);
639 if (!ok) {
640 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
641 return NT_STATUS_NO_MEMORY;
644 /* List the remaining mechs as options */
645 spnego_out.negTokenInit.mechTypes = send_mech_types;
646 spnego_out.negTokenInit.reqFlags = null_data_blob;
647 spnego_out.negTokenInit.reqFlagsPadding = 0;
649 if (spnego_state->state_position == SPNEGO_SERVER_START) {
650 spnego_out.negTokenInit.mechListMIC
651 = data_blob_string_const(ADS_IGNORE_PRINCIPAL);
652 } else {
653 spnego_out.negTokenInit.mechListMIC = null_data_blob;
656 spnego_out.negTokenInit.mechToken = unwrapped_out;
658 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
659 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
660 return NT_STATUS_INVALID_PARAMETER;
663 /* set next state */
664 spnego_state->neg_oid = all_sec[i].oid;
666 if (NT_STATUS_IS_OK(nt_status)) {
667 spnego_state->no_response_expected = true;
670 return NT_STATUS_MORE_PROCESSING_REQUIRED;
672 talloc_free(spnego_state->sub_sec_security);
673 spnego_state->sub_sec_security = NULL;
675 DEBUG(10, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
676 return nt_status;
680 /** create a server negTokenTarg
682 * This is the case, where the client is the first one who sends data
685 static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_state,
686 TALLOC_CTX *out_mem_ctx,
687 NTSTATUS nt_status,
688 const DATA_BLOB unwrapped_out,
689 DATA_BLOB mech_list_mic,
690 DATA_BLOB *out)
692 struct spnego_data spnego_out;
693 DATA_BLOB null_data_blob = data_blob(NULL, 0);
695 /* compose reply */
696 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
697 spnego_out.negTokenTarg.responseToken = unwrapped_out;
698 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
699 spnego_out.negTokenTarg.supportedMech = NULL;
701 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
702 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
703 if (spnego_state->mic_requested) {
704 spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
705 spnego_state->mic_requested = false;
706 } else {
707 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
709 spnego_state->state_position = SPNEGO_SERVER_TARG;
710 } else if (NT_STATUS_IS_OK(nt_status)) {
711 if (unwrapped_out.data) {
712 spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
714 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
715 spnego_state->state_position = SPNEGO_DONE;
716 } else {
717 spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
718 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
719 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
720 spnego_state->state_position = SPNEGO_DONE;
723 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
724 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
725 return NT_STATUS_INVALID_PARAMETER;
728 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
729 spnego_state->num_targs++;
731 return nt_status;
735 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
736 struct tevent_context *ev,
737 const DATA_BLOB in, DATA_BLOB *out)
739 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
740 DATA_BLOB null_data_blob = data_blob(NULL, 0);
741 DATA_BLOB mech_list_mic = data_blob(NULL, 0);
742 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
743 struct spnego_data spnego_out;
744 struct spnego_data spnego;
746 ssize_t len;
748 *out = data_blob(NULL, 0);
750 if (!out_mem_ctx) {
751 out_mem_ctx = spnego_state;
754 /* and switch into the state machine */
756 switch (spnego_state->state_position) {
757 case SPNEGO_FALLBACK:
758 return gensec_update_ev(spnego_state->sub_sec_security, ev,
759 out_mem_ctx, in, out);
760 case SPNEGO_SERVER_START:
762 NTSTATUS nt_status;
763 if (in.length) {
765 len = spnego_read_data(gensec_security, in, &spnego);
766 if (len == -1) {
767 return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
768 ev, out_mem_ctx, in, out);
770 /* client sent NegTargetInit, we send NegTokenTarg */
772 /* OK, so it's real SPNEGO, check the packet's the one we expect */
773 if (spnego.type != spnego_state->expected_packet) {
774 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
775 spnego_state->expected_packet));
776 dump_data(1, in.data, in.length);
777 spnego_free_data(&spnego);
778 return NT_STATUS_INVALID_PARAMETER;
781 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
782 spnego_state,
783 out_mem_ctx,
785 spnego.negTokenInit.mechTypes,
786 spnego.negTokenInit.mechToken,
787 &unwrapped_out);
789 if (spnego_state->simulate_w2k) {
791 * Windows 2000 returns the unwrapped token
792 * also in the mech_list_mic field.
794 * In order to verify our client code,
795 * we need a way to have a server with this
796 * broken behaviour
798 mech_list_mic = unwrapped_out;
801 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
802 out_mem_ctx,
803 nt_status,
804 unwrapped_out,
805 mech_list_mic,
806 out);
808 spnego_free_data(&spnego);
810 return nt_status;
811 } else {
812 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
813 out_mem_ctx, ev, in, out);
814 spnego_state->state_position = SPNEGO_SERVER_START;
815 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
816 return nt_status;
820 case SPNEGO_CLIENT_START:
822 /* The server offers a list of mechanisms */
824 const char *my_mechs[] = {NULL, NULL};
825 NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
826 bool ok;
828 if (!in.length) {
829 /* client to produce negTokenInit */
830 nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
831 out_mem_ctx, ev, in, out);
832 spnego_state->state_position = SPNEGO_CLIENT_TARG;
833 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
834 return nt_status;
837 len = spnego_read_data(gensec_security, in, &spnego);
839 if (len == -1) {
840 DEBUG(1, ("Invalid SPNEGO request:\n"));
841 dump_data(1, in.data, in.length);
842 return NT_STATUS_INVALID_PARAMETER;
845 /* OK, so it's real SPNEGO, check the packet's the one we expect */
846 if (spnego.type != spnego_state->expected_packet) {
847 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
848 spnego_state->expected_packet));
849 dump_data(1, in.data, in.length);
850 spnego_free_data(&spnego);
851 return NT_STATUS_INVALID_PARAMETER;
854 if (spnego.negTokenInit.targetPrincipal
855 && strcmp(spnego.negTokenInit.targetPrincipal, ADS_IGNORE_PRINCIPAL) != 0) {
856 DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
857 if (lpcfg_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
858 gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
862 nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
863 spnego_state,
864 out_mem_ctx,
866 spnego.negTokenInit.mechTypes,
867 spnego.negTokenInit.mechToken,
868 &unwrapped_out);
870 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
871 spnego_free_data(&spnego);
872 return nt_status;
875 my_mechs[0] = spnego_state->neg_oid;
876 /* compose reply */
877 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
878 spnego_out.negTokenInit.mechTypes = my_mechs;
879 spnego_out.negTokenInit.reqFlags = null_data_blob;
880 spnego_out.negTokenInit.reqFlagsPadding = 0;
881 spnego_out.negTokenInit.mechListMIC = null_data_blob;
882 spnego_out.negTokenInit.mechToken = unwrapped_out;
884 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
885 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
886 return NT_STATUS_INVALID_PARAMETER;
889 ok = spnego_write_mech_types(spnego_state,
890 my_mechs,
891 &spnego_state->mech_types);
892 if (!ok) {
893 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
894 return NT_STATUS_NO_MEMORY;
897 /* set next state */
898 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
899 spnego_state->state_position = SPNEGO_CLIENT_TARG;
901 if (NT_STATUS_IS_OK(nt_status)) {
902 spnego_state->no_response_expected = true;
905 spnego_free_data(&spnego);
906 return NT_STATUS_MORE_PROCESSING_REQUIRED;
908 case SPNEGO_SERVER_TARG:
910 NTSTATUS nt_status;
911 bool have_sign = true;
912 bool new_spnego = false;
914 if (!in.length) {
915 return NT_STATUS_INVALID_PARAMETER;
918 len = spnego_read_data(gensec_security, in, &spnego);
920 if (len == -1) {
921 DEBUG(1, ("Invalid SPNEGO request:\n"));
922 dump_data(1, in.data, in.length);
923 return NT_STATUS_INVALID_PARAMETER;
926 /* OK, so it's real SPNEGO, check the packet's the one we expect */
927 if (spnego.type != spnego_state->expected_packet) {
928 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
929 spnego_state->expected_packet));
930 dump_data(1, in.data, in.length);
931 spnego_free_data(&spnego);
932 return NT_STATUS_INVALID_PARAMETER;
935 spnego_state->num_targs++;
937 if (!spnego_state->sub_sec_security) {
938 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
939 spnego_free_data(&spnego);
940 return NT_STATUS_INVALID_PARAMETER;
943 if (spnego_state->needs_mic_check) {
944 if (spnego.negTokenTarg.responseToken.length != 0) {
945 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
946 spnego_free_data(&spnego);
947 return NT_STATUS_INVALID_PARAMETER;
950 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
951 spnego_state->mech_types.data,
952 spnego_state->mech_types.length,
953 spnego_state->mech_types.data,
954 spnego_state->mech_types.length,
955 &spnego.negTokenTarg.mechListMIC);
956 if (NT_STATUS_IS_OK(nt_status)) {
957 spnego_state->needs_mic_check = false;
958 spnego_state->done_mic_check = true;
959 } else {
960 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
961 nt_errstr(nt_status)));
963 goto server_response;
966 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
967 out_mem_ctx, ev,
968 spnego.negTokenTarg.responseToken,
969 &unwrapped_out);
970 if (!NT_STATUS_IS_OK(nt_status)) {
971 goto server_response;
974 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
975 GENSEC_FEATURE_SIGN);
976 if (spnego_state->simulate_w2k) {
977 have_sign = false;
979 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
980 GENSEC_FEATURE_NEW_SPNEGO);
981 if (spnego.negTokenTarg.mechListMIC.length > 0) {
982 new_spnego = true;
985 if (have_sign && new_spnego) {
986 spnego_state->needs_mic_check = true;
987 spnego_state->needs_mic_sign = true;
990 if (have_sign && spnego.negTokenTarg.mechListMIC.length > 0) {
991 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
992 spnego_state->mech_types.data,
993 spnego_state->mech_types.length,
994 spnego_state->mech_types.data,
995 spnego_state->mech_types.length,
996 &spnego.negTokenTarg.mechListMIC);
997 if (!NT_STATUS_IS_OK(nt_status)) {
998 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
999 nt_errstr(nt_status)));
1000 goto server_response;
1003 spnego_state->needs_mic_check = false;
1004 spnego_state->done_mic_check = true;
1007 if (spnego_state->needs_mic_sign) {
1008 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1009 out_mem_ctx,
1010 spnego_state->mech_types.data,
1011 spnego_state->mech_types.length,
1012 spnego_state->mech_types.data,
1013 spnego_state->mech_types.length,
1014 &mech_list_mic);
1015 if (!NT_STATUS_IS_OK(nt_status)) {
1016 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1017 nt_errstr(nt_status)));
1018 goto server_response;
1020 spnego_state->needs_mic_sign = false;
1023 if (spnego_state->needs_mic_check) {
1024 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1027 server_response:
1028 nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
1029 out_mem_ctx,
1030 nt_status,
1031 unwrapped_out,
1032 mech_list_mic,
1033 out);
1035 spnego_free_data(&spnego);
1037 return nt_status;
1039 case SPNEGO_CLIENT_TARG:
1041 NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
1043 if (!in.length) {
1044 return NT_STATUS_INVALID_PARAMETER;
1047 len = spnego_read_data(gensec_security, in, &spnego);
1049 if (len == -1) {
1050 DEBUG(1, ("Invalid SPNEGO request:\n"));
1051 dump_data(1, in.data, in.length);
1052 return NT_STATUS_INVALID_PARAMETER;
1055 /* OK, so it's real SPNEGO, check the packet's the one we expect */
1056 if (spnego.type != spnego_state->expected_packet) {
1057 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
1058 spnego_state->expected_packet));
1059 dump_data(1, in.data, in.length);
1060 spnego_free_data(&spnego);
1061 return NT_STATUS_INVALID_PARAMETER;
1064 spnego_state->num_targs++;
1066 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
1067 spnego_free_data(&spnego);
1068 return NT_STATUS_LOGON_FAILURE;
1071 if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
1072 spnego_state->mic_requested = true;
1075 /* Server didn't like our choice of mech, and chose something else */
1076 if (((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
1077 (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC)) &&
1078 spnego.negTokenTarg.supportedMech &&
1079 strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
1080 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
1081 gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid),
1082 gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech)));
1083 spnego_state->downgraded = true;
1084 spnego_state->no_response_expected = false;
1085 talloc_free(spnego_state->sub_sec_security);
1086 nt_status = gensec_subcontext_start(spnego_state,
1087 gensec_security,
1088 &spnego_state->sub_sec_security);
1089 if (!NT_STATUS_IS_OK(nt_status)) {
1090 spnego_free_data(&spnego);
1091 return nt_status;
1093 /* select the sub context */
1094 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
1095 spnego.negTokenTarg.supportedMech);
1096 if (!NT_STATUS_IS_OK(nt_status)) {
1097 spnego_free_data(&spnego);
1098 return nt_status;
1101 spnego_state->neg_oid = talloc_strdup(spnego_state,
1102 spnego.negTokenTarg.supportedMech);
1103 if (spnego_state->neg_oid == NULL) {
1104 spnego_free_data(&spnego);
1105 return NT_STATUS_NO_MEMORY;
1109 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1110 DATA_BLOB *m = &spnego.negTokenTarg.mechListMIC;
1111 const DATA_BLOB *r = &spnego.negTokenTarg.responseToken;
1114 * Windows 2000 has a bug, it repeats the
1115 * responseToken in the mechListMIC field.
1117 if (m->length == r->length) {
1118 int cmp;
1120 cmp = memcmp(m->data, r->data, m->length);
1121 if (cmp == 0) {
1122 data_blob_free(m);
1127 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1128 if (spnego_state->no_response_expected) {
1129 spnego_state->needs_mic_check = true;
1133 if (spnego_state->needs_mic_check) {
1134 if (spnego.negTokenTarg.responseToken.length != 0) {
1135 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
1136 spnego_free_data(&spnego);
1137 return NT_STATUS_INVALID_PARAMETER;
1140 if (spnego.negTokenTarg.mechListMIC.length == 0
1141 && spnego_state->may_skip_mic_check) {
1143 * In this case we don't require
1144 * a mechListMIC from the server.
1146 * This works around bugs in the Azure
1147 * and Apple spnego implementations.
1149 * See
1150 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1152 spnego_state->needs_mic_check = false;
1153 nt_status = NT_STATUS_OK;
1154 goto client_response;
1157 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1158 spnego_state->mech_types.data,
1159 spnego_state->mech_types.length,
1160 spnego_state->mech_types.data,
1161 spnego_state->mech_types.length,
1162 &spnego.negTokenTarg.mechListMIC);
1163 if (!NT_STATUS_IS_OK(nt_status)) {
1164 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1165 nt_errstr(nt_status)));
1166 spnego_free_data(&spnego);
1167 return nt_status;
1169 spnego_state->needs_mic_check = false;
1170 spnego_state->done_mic_check = true;
1171 goto client_response;
1174 if (!spnego_state->no_response_expected) {
1175 nt_status = gensec_update_ev(spnego_state->sub_sec_security,
1176 out_mem_ctx, ev,
1177 spnego.negTokenTarg.responseToken,
1178 &unwrapped_out);
1179 if (!NT_STATUS_IS_OK(nt_status)) {
1180 goto client_response;
1183 spnego_state->no_response_expected = true;
1184 } else {
1185 nt_status = NT_STATUS_OK;
1188 if (spnego_state->no_response_expected &&
1189 !spnego_state->done_mic_check)
1191 bool have_sign = true;
1192 bool new_spnego = false;
1194 have_sign = gensec_have_feature(spnego_state->sub_sec_security,
1195 GENSEC_FEATURE_SIGN);
1196 if (spnego_state->simulate_w2k) {
1197 have_sign = false;
1199 new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
1200 GENSEC_FEATURE_NEW_SPNEGO);
1202 switch (spnego.negTokenTarg.negResult) {
1203 case SPNEGO_ACCEPT_COMPLETED:
1204 case SPNEGO_NONE_RESULT:
1205 if (spnego_state->num_targs == 1) {
1207 * the first exchange doesn't require
1208 * verification
1210 new_spnego = false;
1213 break;
1215 case SPNEGO_ACCEPT_INCOMPLETE:
1216 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1217 new_spnego = true;
1218 break;
1221 if (spnego_state->downgraded) {
1223 * A downgrade should be protected if
1224 * supported
1226 break;
1230 * The caller may just asked for
1231 * GENSEC_FEATURE_SESSION_KEY, this
1232 * is only reflected in the want_features.
1234 * As it will imply
1235 * gensec_have_features(GENSEC_FEATURE_SIGN)
1236 * to return true.
1238 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
1239 break;
1241 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
1242 break;
1245 * Here we're sure our preferred mech was
1246 * selected by the server and our caller doesn't
1247 * need GENSEC_FEATURE_SIGN nor
1248 * GENSEC_FEATURE_SEAL support.
1250 * In this case we don't require
1251 * a mechListMIC from the server.
1253 * This works around bugs in the Azure
1254 * and Apple spnego implementations.
1256 * See
1257 * https://bugzilla.samba.org/show_bug.cgi?id=11994
1259 spnego_state->may_skip_mic_check = true;
1260 break;
1262 case SPNEGO_REQUEST_MIC:
1263 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1264 new_spnego = true;
1266 break;
1267 default:
1268 break;
1271 if (spnego_state->mic_requested) {
1272 if (have_sign) {
1273 new_spnego = true;
1277 if (have_sign && new_spnego) {
1278 spnego_state->needs_mic_check = true;
1279 spnego_state->needs_mic_sign = true;
1283 if (spnego.negTokenTarg.mechListMIC.length > 0) {
1284 nt_status = gensec_check_packet(spnego_state->sub_sec_security,
1285 spnego_state->mech_types.data,
1286 spnego_state->mech_types.length,
1287 spnego_state->mech_types.data,
1288 spnego_state->mech_types.length,
1289 &spnego.negTokenTarg.mechListMIC);
1290 if (!NT_STATUS_IS_OK(nt_status)) {
1291 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1292 nt_errstr(nt_status)));
1293 spnego_free_data(&spnego);
1294 return nt_status;
1296 spnego_state->needs_mic_check = false;
1297 spnego_state->done_mic_check = true;
1300 if (spnego_state->needs_mic_sign) {
1301 nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
1302 out_mem_ctx,
1303 spnego_state->mech_types.data,
1304 spnego_state->mech_types.length,
1305 spnego_state->mech_types.data,
1306 spnego_state->mech_types.length,
1307 &mech_list_mic);
1308 if (!NT_STATUS_IS_OK(nt_status)) {
1309 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1310 nt_errstr(nt_status)));
1311 spnego_free_data(&spnego);
1312 return nt_status;
1314 spnego_state->needs_mic_sign = false;
1317 if (spnego_state->needs_mic_check) {
1318 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1321 client_response:
1322 spnego_free_data(&spnego);
1324 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
1325 && !NT_STATUS_IS_OK(nt_status)) {
1326 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1327 spnego_state->sub_sec_security->ops->name,
1328 nt_errstr(nt_status)));
1329 return nt_status;
1332 if (unwrapped_out.length || mech_list_mic.length) {
1333 /* compose reply */
1334 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
1335 spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
1336 spnego_out.negTokenTarg.supportedMech = NULL;
1337 spnego_out.negTokenTarg.responseToken = unwrapped_out;
1338 spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
1340 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
1341 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1342 return NT_STATUS_INVALID_PARAMETER;
1345 spnego_state->num_targs++;
1346 spnego_state->state_position = SPNEGO_CLIENT_TARG;
1347 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1348 } else {
1350 /* all done - server has accepted, and we agree */
1351 *out = null_data_blob;
1353 if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
1354 /* unless of course it did not accept */
1355 DEBUG(1,("gensec_update ok but not accepted\n"));
1356 nt_status = NT_STATUS_INVALID_PARAMETER;
1359 spnego_state->state_position = SPNEGO_DONE;
1362 return nt_status;
1364 case SPNEGO_DONE:
1365 /* We should not be called after we are 'done' */
1366 return NT_STATUS_INVALID_PARAMETER;
1368 return NT_STATUS_INVALID_PARAMETER;
1371 static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security,
1372 const DATA_BLOB in, DATA_BLOB *full_in)
1374 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1375 size_t expected;
1376 bool ok;
1378 *full_in = data_blob_null;
1380 if (spnego_state->in_needed == 0) {
1381 size_t size = 0;
1382 int ret;
1385 * try to work out the size of the full
1386 * input token, it might be fragmented
1388 ret = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size);
1389 if ((ret != 0) && (ret != EAGAIN)) {
1390 ret = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size);
1393 if ((ret == 0) || (ret == EAGAIN)) {
1394 spnego_state->in_needed = size;
1395 } else {
1397 * If it is not an asn1 message
1398 * just call the next layer.
1400 spnego_state->in_needed = in.length;
1404 if (spnego_state->in_needed > UINT16_MAX) {
1406 * limit the incoming message to 0xFFFF
1407 * to avoid DoS attacks.
1409 return NT_STATUS_INVALID_BUFFER_SIZE;
1412 if ((spnego_state->in_needed > 0) && (in.length == 0)) {
1414 * If we reach this, we know we got at least
1415 * part of an asn1 message, getting 0 means
1416 * the remote peer wants us to spin.
1418 return NT_STATUS_INVALID_PARAMETER;
1421 expected = spnego_state->in_needed - spnego_state->in_frag.length;
1422 if (in.length > expected) {
1424 * we got more than expected
1426 return NT_STATUS_INVALID_PARAMETER;
1429 if (in.length == spnego_state->in_needed) {
1431 * if the in.length contains the full blob
1432 * we are done.
1434 * Note: this implies spnego_state->in_frag.length == 0,
1435 * but we do not need to check this explicitly
1436 * because we already know that we did not get
1437 * more than expected.
1439 *full_in = in;
1440 return NT_STATUS_OK;
1443 ok = data_blob_append(spnego_state, &spnego_state->in_frag,
1444 in.data, in.length);
1445 if (!ok) {
1446 return NT_STATUS_NO_MEMORY;
1449 if (spnego_state->in_needed > spnego_state->in_frag.length) {
1450 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1453 *full_in = spnego_state->in_frag;
1454 return NT_STATUS_OK;
1457 static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security,
1458 TALLOC_CTX *out_mem_ctx,
1459 DATA_BLOB *_out)
1461 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1462 DATA_BLOB out = data_blob_null;
1463 bool ok;
1465 *_out = data_blob_null;
1467 if (spnego_state->out_frag.length == 0) {
1468 return spnego_state->out_status;
1472 * There is still more data to be delivered
1473 * to the remote peer.
1476 if (spnego_state->out_frag.length <= spnego_state->out_max_length) {
1478 * Fast path, we can deliver everything
1481 *_out = spnego_state->out_frag;
1482 talloc_steal(out_mem_ctx, _out->data);
1483 spnego_state->out_frag = data_blob_null;
1484 return spnego_state->out_status;
1487 out = spnego_state->out_frag;
1490 * copy the remaining bytes
1492 spnego_state->out_frag = data_blob_talloc(spnego_state,
1493 out.data + spnego_state->out_max_length,
1494 out.length - spnego_state->out_max_length);
1495 if (spnego_state->out_frag.data == NULL) {
1496 return NT_STATUS_NO_MEMORY;
1500 * truncate the buffer
1502 ok = data_blob_realloc(spnego_state, &out,
1503 spnego_state->out_max_length);
1504 if (!ok) {
1505 return NT_STATUS_NO_MEMORY;
1508 talloc_steal(out_mem_ctx, out.data);
1509 *_out = out;
1510 return NT_STATUS_MORE_PROCESSING_REQUIRED;
1513 static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security,
1514 TALLOC_CTX *out_mem_ctx,
1515 struct tevent_context *ev,
1516 const DATA_BLOB in, DATA_BLOB *out)
1518 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1519 DATA_BLOB full_in = data_blob_null;
1520 NTSTATUS status;
1522 *out = data_blob_null;
1524 if (spnego_state->out_frag.length > 0) {
1525 if (in.length > 0) {
1526 return NT_STATUS_INVALID_PARAMETER;
1529 return gensec_spnego_update_out(gensec_security,
1530 out_mem_ctx,
1531 out);
1534 status = gensec_spnego_update_in(gensec_security,
1535 in, &full_in);
1536 if (!NT_STATUS_IS_OK(status)) {
1537 return status;
1540 status = gensec_spnego_update(gensec_security,
1541 spnego_state, ev,
1542 full_in,
1543 &spnego_state->out_frag);
1544 data_blob_free(&spnego_state->in_frag);
1545 spnego_state->in_needed = 0;
1546 if (NT_STATUS_IS_OK(status)) {
1547 bool reset_full = true;
1549 gensec_security->child_security = spnego_state->sub_sec_security;
1551 reset_full = !spnego_state->done_mic_check;
1553 status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
1554 reset_full);
1556 if (!NT_STATUS_IS_OK(status) &&
1557 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
1558 return status;
1561 spnego_state->out_status = status;
1563 return gensec_spnego_update_out(gensec_security,
1564 out_mem_ctx,
1565 out);
1568 static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
1569 uint32_t feature)
1571 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1573 if (!spnego_state || !spnego_state->sub_sec_security) {
1574 gensec_security->want_features |= feature;
1575 return;
1578 gensec_want_feature(spnego_state->sub_sec_security,
1579 feature);
1582 static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
1583 uint32_t feature)
1585 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1586 if (!spnego_state->sub_sec_security) {
1587 return false;
1590 return gensec_have_feature(spnego_state->sub_sec_security,
1591 feature);
1594 static NTTIME gensec_spnego_expire_time(struct gensec_security *gensec_security)
1596 struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
1598 if (!spnego_state->sub_sec_security) {
1599 return GENSEC_EXPIRE_TIME_INFINITY;
1602 return gensec_expire_time(spnego_state->sub_sec_security);
1605 static const char *gensec_spnego_oids[] = {
1606 GENSEC_OID_SPNEGO,
1607 NULL
1610 static const struct gensec_security_ops gensec_spnego_security_ops = {
1611 .name = "spnego",
1612 .sasl_name = "GSS-SPNEGO",
1613 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
1614 .oid = gensec_spnego_oids,
1615 .client_start = gensec_spnego_client_start,
1616 .server_start = gensec_spnego_server_start,
1617 .update = gensec_spnego_update_wrapper,
1618 .seal_packet = gensec_spnego_seal_packet,
1619 .sign_packet = gensec_spnego_sign_packet,
1620 .sig_size = gensec_spnego_sig_size,
1621 .max_wrapped_size = gensec_spnego_max_wrapped_size,
1622 .max_input_size = gensec_spnego_max_input_size,
1623 .check_packet = gensec_spnego_check_packet,
1624 .unseal_packet = gensec_spnego_unseal_packet,
1625 .wrap = gensec_spnego_wrap,
1626 .unwrap = gensec_spnego_unwrap,
1627 .session_key = gensec_spnego_session_key,
1628 .session_info = gensec_spnego_session_info,
1629 .want_feature = gensec_spnego_want_feature,
1630 .have_feature = gensec_spnego_have_feature,
1631 .expire_time = gensec_spnego_expire_time,
1632 .enabled = true,
1633 .priority = GENSEC_SPNEGO
1636 _PUBLIC_ NTSTATUS gensec_spnego_init(void)
1638 NTSTATUS ret;
1639 ret = gensec_register(&gensec_spnego_security_ops);
1640 if (!NT_STATUS_IS_OK(ret)) {
1641 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1642 gensec_spnego_security_ops.name));
1643 return ret;
1646 return ret;