2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2008
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "../libcli/auth/spnego.h"
27 #include "librpc/gen_ndr/ndr_dcerpc.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/gensec/gensec_internal.h"
31 #include "param/param.h"
32 #include "lib/util/asn1.h"
36 _PUBLIC_ NTSTATUS
gensec_spnego_init(void);
38 enum spnego_state_position
{
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
;
58 * The following is used to implement
59 * the update token fragmentation
63 size_t out_max_length
;
69 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
71 struct spnego_state
*spnego_state
;
73 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
75 return NT_STATUS_NO_MEMORY
;
78 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
79 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
80 spnego_state
->sub_sec_security
= NULL
;
81 spnego_state
->no_response_expected
= false;
82 spnego_state
->mech_types
= data_blob(NULL
, 0);
83 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
84 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
86 gensec_security
->private_data
= spnego_state
;
90 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
92 struct spnego_state
*spnego_state
;
94 spnego_state
= talloc_zero(gensec_security
, struct spnego_state
);
96 return NT_STATUS_NO_MEMORY
;
99 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
100 spnego_state
->state_position
= SPNEGO_SERVER_START
;
101 spnego_state
->sub_sec_security
= NULL
;
102 spnego_state
->no_response_expected
= false;
103 spnego_state
->mech_types
= data_blob(NULL
, 0);
104 spnego_state
->out_max_length
= gensec_max_update_size(gensec_security
);
105 spnego_state
->out_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
107 gensec_security
->private_data
= spnego_state
;
112 wrappers for the spnego_*() functions
114 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
115 uint8_t *data
, size_t length
,
116 const uint8_t *whole_pdu
, size_t pdu_length
,
117 const DATA_BLOB
*sig
)
119 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
121 if (spnego_state
->state_position
!= SPNEGO_DONE
122 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
123 return NT_STATUS_INVALID_PARAMETER
;
126 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
128 whole_pdu
, pdu_length
,
132 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
133 const uint8_t *data
, size_t length
,
134 const uint8_t *whole_pdu
, size_t pdu_length
,
135 const DATA_BLOB
*sig
)
137 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
139 if (spnego_state
->state_position
!= SPNEGO_DONE
140 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
141 return NT_STATUS_INVALID_PARAMETER
;
144 return gensec_check_packet(spnego_state
->sub_sec_security
,
146 whole_pdu
, pdu_length
,
150 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
152 uint8_t *data
, size_t length
,
153 const uint8_t *whole_pdu
, size_t pdu_length
,
156 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
158 if (spnego_state
->state_position
!= SPNEGO_DONE
159 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
160 return NT_STATUS_INVALID_PARAMETER
;
163 return gensec_seal_packet(spnego_state
->sub_sec_security
,
166 whole_pdu
, pdu_length
,
170 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
172 const uint8_t *data
, size_t length
,
173 const uint8_t *whole_pdu
, size_t pdu_length
,
176 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
178 if (spnego_state
->state_position
!= SPNEGO_DONE
179 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
180 return NT_STATUS_INVALID_PARAMETER
;
183 return gensec_sign_packet(spnego_state
->sub_sec_security
,
186 whole_pdu
, pdu_length
,
190 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
195 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
197 if (spnego_state
->state_position
!= SPNEGO_DONE
198 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
199 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
200 return NT_STATUS_INVALID_PARAMETER
;
203 return gensec_wrap(spnego_state
->sub_sec_security
,
207 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
212 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
214 if (spnego_state
->state_position
!= SPNEGO_DONE
215 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
216 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
217 return NT_STATUS_INVALID_PARAMETER
;
220 return gensec_unwrap(spnego_state
->sub_sec_security
,
224 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
226 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
228 if (spnego_state
->state_position
!= SPNEGO_DONE
229 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
233 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
236 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
238 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
240 if (spnego_state
->state_position
!= SPNEGO_DONE
241 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
245 return gensec_max_input_size(spnego_state
->sub_sec_security
);
248 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
250 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
252 if (spnego_state
->state_position
!= SPNEGO_DONE
253 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
257 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
260 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
262 DATA_BLOB
*session_key
)
264 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
265 if (!spnego_state
->sub_sec_security
) {
266 return NT_STATUS_INVALID_PARAMETER
;
269 return gensec_session_key(spnego_state
->sub_sec_security
,
274 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
276 struct auth_session_info
**session_info
)
278 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
279 if (!spnego_state
->sub_sec_security
) {
280 return NT_STATUS_INVALID_PARAMETER
;
283 return gensec_session_info(spnego_state
->sub_sec_security
,
288 /** Fallback to another GENSEC mechanism, based on magic strings
290 * This is the 'fallback' case, where we don't get SPNEGO, and have to
291 * try all the other options (and hope they all have a magic string
295 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
296 struct spnego_state
*spnego_state
,
297 struct tevent_context
*ev
,
298 TALLOC_CTX
*out_mem_ctx
,
299 const DATA_BLOB in
, DATA_BLOB
*out
)
302 const struct gensec_security_ops
**all_ops
;
304 all_ops
= gensec_security_mechs(gensec_security
, out_mem_ctx
);
306 for (i
=0; all_ops
&& all_ops
[i
]; i
++) {
310 if (gensec_security
!= NULL
&&
311 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
314 if (!all_ops
[i
]->oid
) {
319 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
320 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
328 if (!all_ops
[i
]->magic
) {
332 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
333 if (!NT_STATUS_IS_OK(nt_status
)) {
337 spnego_state
->state_position
= SPNEGO_FALLBACK
;
339 nt_status
= gensec_subcontext_start(spnego_state
,
341 &spnego_state
->sub_sec_security
);
343 if (!NT_STATUS_IS_OK(nt_status
)) {
346 /* select the sub context */
347 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
349 if (!NT_STATUS_IS_OK(nt_status
)) {
352 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
353 ev
, out_mem_ctx
, in
, out
);
356 DEBUG(1, ("Failed to parse SPNEGO request\n"));
357 return NT_STATUS_INVALID_PARAMETER
;
361 Parse the netTokenInit, either from the client, to the server, or
362 from the server to the client.
365 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
366 struct spnego_state
*spnego_state
,
367 TALLOC_CTX
*out_mem_ctx
,
368 struct tevent_context
*ev
,
369 const char * const *mechType
,
370 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
373 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
374 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
377 const struct gensec_security_ops_wrapper
*all_sec
378 = gensec_security_by_oid_list(gensec_security
,
383 ok
= spnego_write_mech_types(spnego_state
,
385 &spnego_state
->mech_types
);
387 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
388 return NT_STATUS_NO_MEMORY
;
391 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
393 for (j
=0; mechType
&& mechType
[j
]; j
++) {
394 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
395 if (strcmp(mechType
[j
], all_sec
[i
].oid
) != 0) {
399 nt_status
= gensec_subcontext_start(spnego_state
,
401 &spnego_state
->sub_sec_security
);
402 if (!NT_STATUS_IS_OK(nt_status
)) {
405 /* select the sub context */
406 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
408 if (!NT_STATUS_IS_OK(nt_status
)) {
409 talloc_free(spnego_state
->sub_sec_security
);
410 spnego_state
->sub_sec_security
= NULL
;
415 /* no optimistic token */
416 spnego_state
->neg_oid
= all_sec
[i
].oid
;
417 *unwrapped_out
= data_blob_null
;
418 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
422 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
427 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
428 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
429 /* Pretend we never started it (lets the first run find some incompatible demand) */
431 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
432 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
433 talloc_free(spnego_state
->sub_sec_security
);
434 spnego_state
->sub_sec_security
= NULL
;
438 spnego_state
->neg_oid
= all_sec
[i
].oid
;
441 if (spnego_state
->sub_sec_security
) {
446 if (!spnego_state
->sub_sec_security
) {
447 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
448 return NT_STATUS_INVALID_PARAMETER
;
452 /* Having tried any optimistic token from the client (if we
453 * were the server), if we didn't get anywhere, walk our list
454 * in our preference order */
456 if (!spnego_state
->sub_sec_security
) {
457 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
458 nt_status
= gensec_subcontext_start(spnego_state
,
460 &spnego_state
->sub_sec_security
);
461 if (!NT_STATUS_IS_OK(nt_status
)) {
464 /* select the sub context */
465 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
467 if (!NT_STATUS_IS_OK(nt_status
)) {
468 talloc_free(spnego_state
->sub_sec_security
);
469 spnego_state
->sub_sec_security
= NULL
;
473 spnego_state
->neg_oid
= all_sec
[i
].oid
;
475 /* only get the helping start blob for the first OID */
476 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
482 /* it is likely that a NULL input token will
483 * not be liked by most server mechs, but if
484 * we are in the client, we want the first
485 * update packet to be able to abort the use
487 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
488 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
489 NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_LOGON_SERVERS
) ||
490 NT_STATUS_EQUAL(nt_status
, NT_STATUS_TIME_DIFFERENCE_AT_DC
) ||
491 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
492 /* Pretend we never started it (lets the first run find some incompatible demand) */
494 DEBUG(3, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
495 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
496 talloc_free(spnego_state
->sub_sec_security
);
497 spnego_state
->sub_sec_security
= NULL
;
506 if (spnego_state
->sub_sec_security
) {
507 /* it is likely that a NULL input token will
508 * not be liked by most server mechs, but this
509 * does the right thing in the CIFS client.
510 * just push us along the merry-go-round
511 * again, and hope for better luck next
514 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
515 *unwrapped_out
= data_blob(NULL
, 0);
516 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
519 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
520 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
521 && !NT_STATUS_IS_OK(nt_status
)) {
522 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
523 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
524 talloc_free(spnego_state
->sub_sec_security
);
525 spnego_state
->sub_sec_security
= NULL
;
527 /* We started the mech correctly, and the
528 * input from the other side was valid.
529 * Return the error (say bad password, invalid
534 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
537 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
538 /* we could re-negotiate here, but it would only work
539 * if the client or server lied about what it could
540 * support the first time. Lets keep this code to
546 /** create a negTokenInit
548 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
550 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
551 struct spnego_state
*spnego_state
,
552 TALLOC_CTX
*out_mem_ctx
,
553 struct tevent_context
*ev
,
554 const DATA_BLOB in
, DATA_BLOB
*out
)
557 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
558 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
559 const char **mechTypes
= NULL
;
560 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
561 const struct gensec_security_ops_wrapper
*all_sec
;
563 mechTypes
= gensec_security_oids(gensec_security
,
564 out_mem_ctx
, GENSEC_OID_SPNEGO
);
566 all_sec
= gensec_security_by_oid_list(gensec_security
,
570 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
571 struct spnego_data spnego_out
;
572 const char **send_mech_types
;
575 nt_status
= gensec_subcontext_start(spnego_state
,
577 &spnego_state
->sub_sec_security
);
578 if (!NT_STATUS_IS_OK(nt_status
)) {
581 /* select the sub context */
582 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
584 if (!NT_STATUS_IS_OK(nt_status
)) {
585 talloc_free(spnego_state
->sub_sec_security
);
586 spnego_state
->sub_sec_security
= NULL
;
590 /* In the client, try and produce the first (optimistic) packet */
591 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
592 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
598 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
599 && !NT_STATUS_IS_OK(nt_status
)) {
600 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
601 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
602 talloc_free(spnego_state
->sub_sec_security
);
603 spnego_state
->sub_sec_security
= NULL
;
604 /* Pretend we never started it (lets the first run find some incompatible demand) */
610 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
612 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
615 ok
= spnego_write_mech_types(spnego_state
,
617 &spnego_state
->mech_types
);
619 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
620 return NT_STATUS_NO_MEMORY
;
623 /* List the remaining mechs as options */
624 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
625 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
626 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
628 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
629 spnego_out
.negTokenInit
.mechListMIC
630 = data_blob_string_const(ADS_IGNORE_PRINCIPAL
);
632 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
635 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
637 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
638 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
639 return NT_STATUS_INVALID_PARAMETER
;
643 spnego_state
->neg_oid
= all_sec
[i
].oid
;
645 if (NT_STATUS_IS_OK(nt_status
)) {
646 spnego_state
->no_response_expected
= true;
649 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
651 talloc_free(spnego_state
->sub_sec_security
);
652 spnego_state
->sub_sec_security
= NULL
;
654 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
659 /** create a server negTokenTarg
661 * This is the case, where the client is the first one who sends data
664 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct spnego_state
*spnego_state
,
665 TALLOC_CTX
*out_mem_ctx
,
667 const DATA_BLOB unwrapped_out
,
668 DATA_BLOB mech_list_mic
,
671 struct spnego_data spnego_out
;
672 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
675 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
676 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
677 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
678 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
680 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
681 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
682 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
683 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
684 } else if (NT_STATUS_IS_OK(nt_status
)) {
685 if (unwrapped_out
.data
) {
686 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
688 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
689 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
690 spnego_state
->state_position
= SPNEGO_DONE
;
692 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
693 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
694 spnego_state
->state_position
= SPNEGO_DONE
;
697 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
698 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
699 return NT_STATUS_INVALID_PARAMETER
;
702 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
708 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
709 struct tevent_context
*ev
,
710 const DATA_BLOB in
, DATA_BLOB
*out
)
712 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
713 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
714 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
715 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
716 struct spnego_data spnego_out
;
717 struct spnego_data spnego
;
721 *out
= data_blob(NULL
, 0);
724 out_mem_ctx
= spnego_state
;
727 /* and switch into the state machine */
729 switch (spnego_state
->state_position
) {
730 case SPNEGO_FALLBACK
:
731 return gensec_update_ev(spnego_state
->sub_sec_security
, ev
,
732 out_mem_ctx
, in
, out
);
733 case SPNEGO_SERVER_START
:
738 len
= spnego_read_data(gensec_security
, in
, &spnego
);
740 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
741 out_mem_ctx
, ev
, in
, out
);
743 /* client sent NegTargetInit, we send NegTokenTarg */
745 /* OK, so it's real SPNEGO, check the packet's the one we expect */
746 if (spnego
.type
!= spnego_state
->expected_packet
) {
747 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
748 spnego_state
->expected_packet
));
749 dump_data(1, in
.data
, in
.length
);
750 spnego_free_data(&spnego
);
751 return NT_STATUS_INVALID_PARAMETER
;
754 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
758 spnego
.negTokenInit
.mechTypes
,
759 spnego
.negTokenInit
.mechToken
,
762 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
769 spnego_free_data(&spnego
);
773 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
774 out_mem_ctx
, ev
, in
, out
);
775 spnego_state
->state_position
= SPNEGO_SERVER_START
;
776 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
781 case SPNEGO_CLIENT_START
:
783 /* The server offers a list of mechanisms */
785 const char *my_mechs
[] = {NULL
, NULL
};
786 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
789 /* client to produce negTokenInit */
790 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
791 out_mem_ctx
, ev
, in
, out
);
792 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
793 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
797 len
= spnego_read_data(gensec_security
, in
, &spnego
);
800 DEBUG(1, ("Invalid SPNEGO request:\n"));
801 dump_data(1, in
.data
, in
.length
);
802 return NT_STATUS_INVALID_PARAMETER
;
805 /* OK, so it's real SPNEGO, check the packet's the one we expect */
806 if (spnego
.type
!= spnego_state
->expected_packet
) {
807 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
808 spnego_state
->expected_packet
));
809 dump_data(1, in
.data
, in
.length
);
810 spnego_free_data(&spnego
);
811 return NT_STATUS_INVALID_PARAMETER
;
814 if (spnego
.negTokenInit
.targetPrincipal
815 && strcmp(spnego
.negTokenInit
.targetPrincipal
, ADS_IGNORE_PRINCIPAL
) != 0) {
816 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
817 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
818 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
822 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
826 spnego
.negTokenInit
.mechTypes
,
827 spnego
.negTokenInit
.mechToken
,
830 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
831 spnego_free_data(&spnego
);
835 my_mechs
[0] = spnego_state
->neg_oid
;
837 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
838 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
839 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
840 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
841 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
842 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
844 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
845 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
846 return NT_STATUS_INVALID_PARAMETER
;
850 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
851 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
853 if (NT_STATUS_IS_OK(nt_status
)) {
854 spnego_state
->no_response_expected
= true;
857 spnego_free_data(&spnego
);
858 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
860 case SPNEGO_SERVER_TARG
:
863 bool new_spnego
= false;
866 return NT_STATUS_INVALID_PARAMETER
;
869 len
= spnego_read_data(gensec_security
, in
, &spnego
);
872 DEBUG(1, ("Invalid SPNEGO request:\n"));
873 dump_data(1, in
.data
, in
.length
);
874 return NT_STATUS_INVALID_PARAMETER
;
877 /* OK, so it's real SPNEGO, check the packet's the one we expect */
878 if (spnego
.type
!= spnego_state
->expected_packet
) {
879 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
880 spnego_state
->expected_packet
));
881 dump_data(1, in
.data
, in
.length
);
882 spnego_free_data(&spnego
);
883 return NT_STATUS_INVALID_PARAMETER
;
886 if (!spnego_state
->sub_sec_security
) {
887 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
888 spnego_free_data(&spnego
);
889 return NT_STATUS_INVALID_PARAMETER
;
892 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
894 spnego
.negTokenTarg
.responseToken
,
896 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
898 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
899 spnego_state
->mech_types
.data
,
900 spnego_state
->mech_types
.length
,
901 spnego_state
->mech_types
.data
,
902 spnego_state
->mech_types
.length
,
903 &spnego
.negTokenTarg
.mechListMIC
);
904 if (!NT_STATUS_IS_OK(nt_status
)) {
905 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
906 nt_errstr(nt_status
)));
909 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
910 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
912 spnego_state
->mech_types
.data
,
913 spnego_state
->mech_types
.length
,
914 spnego_state
->mech_types
.data
,
915 spnego_state
->mech_types
.length
,
917 if (!NT_STATUS_IS_OK(nt_status
)) {
918 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
919 nt_errstr(nt_status
)));
923 nt_status
= gensec_spnego_server_negTokenTarg(spnego_state
,
930 spnego_free_data(&spnego
);
934 case SPNEGO_CLIENT_TARG
:
938 return NT_STATUS_INVALID_PARAMETER
;
941 len
= spnego_read_data(gensec_security
, in
, &spnego
);
944 DEBUG(1, ("Invalid SPNEGO request:\n"));
945 dump_data(1, in
.data
, in
.length
);
946 return NT_STATUS_INVALID_PARAMETER
;
949 /* OK, so it's real SPNEGO, check the packet's the one we expect */
950 if (spnego
.type
!= spnego_state
->expected_packet
) {
951 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
952 spnego_state
->expected_packet
));
953 dump_data(1, in
.data
, in
.length
);
954 spnego_free_data(&spnego
);
955 return NT_STATUS_INVALID_PARAMETER
;
958 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
959 spnego_free_data(&spnego
);
960 return NT_STATUS_LOGON_FAILURE
;
963 /* Server didn't like our choice of mech, and chose something else */
964 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
965 spnego
.negTokenTarg
.supportedMech
&&
966 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
967 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
968 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
),
969 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
)));
971 talloc_free(spnego_state
->sub_sec_security
);
972 nt_status
= gensec_subcontext_start(spnego_state
,
974 &spnego_state
->sub_sec_security
);
975 if (!NT_STATUS_IS_OK(nt_status
)) {
976 spnego_free_data(&spnego
);
979 /* select the sub context */
980 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
981 spnego
.negTokenTarg
.supportedMech
);
982 if (!NT_STATUS_IS_OK(nt_status
)) {
983 spnego_free_data(&spnego
);
987 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
989 spnego
.negTokenTarg
.responseToken
,
991 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
992 } else if (spnego_state
->no_response_expected
) {
993 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
994 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
995 nt_status
= NT_STATUS_INVALID_PARAMETER
;
996 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
997 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
998 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1000 nt_status
= NT_STATUS_OK
;
1002 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1003 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1004 spnego_state
->mech_types
.data
,
1005 spnego_state
->mech_types
.length
,
1006 spnego_state
->mech_types
.data
,
1007 spnego_state
->mech_types
.length
,
1008 &spnego
.negTokenTarg
.mechListMIC
);
1009 if (!NT_STATUS_IS_OK(nt_status
)) {
1010 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1011 nt_errstr(nt_status
)));
1015 bool new_spnego
= false;
1017 nt_status
= gensec_update_ev(spnego_state
->sub_sec_security
,
1019 spnego
.negTokenTarg
.responseToken
,
1022 if (NT_STATUS_IS_OK(nt_status
)
1023 && spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1024 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1025 GENSEC_FEATURE_NEW_SPNEGO
);
1027 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
1028 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1030 spnego_state
->mech_types
.data
,
1031 spnego_state
->mech_types
.length
,
1032 spnego_state
->mech_types
.data
,
1033 spnego_state
->mech_types
.length
,
1035 if (!NT_STATUS_IS_OK(nt_status
)) {
1036 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1037 nt_errstr(nt_status
)));
1040 if (NT_STATUS_IS_OK(nt_status
)) {
1041 spnego_state
->no_response_expected
= true;
1045 spnego_free_data(&spnego
);
1047 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1048 && !NT_STATUS_IS_OK(nt_status
)) {
1049 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1050 spnego_state
->sub_sec_security
->ops
->name
,
1051 nt_errstr(nt_status
)));
1055 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1057 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1058 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1059 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1060 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1061 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1063 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1064 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1065 return NT_STATUS_INVALID_PARAMETER
;
1068 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1069 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1072 /* all done - server has accepted, and we agree */
1073 *out
= null_data_blob
;
1075 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1076 /* unless of course it did not accept */
1077 DEBUG(1,("gensec_update ok but not accepted\n"));
1078 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1081 spnego_state
->state_position
= SPNEGO_DONE
;
1087 /* We should not be called after we are 'done' */
1088 return NT_STATUS_INVALID_PARAMETER
;
1090 return NT_STATUS_INVALID_PARAMETER
;
1093 static NTSTATUS
gensec_spnego_update_in(struct gensec_security
*gensec_security
,
1094 const DATA_BLOB in
, DATA_BLOB
*full_in
)
1096 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1101 *full_in
= data_blob_null
;
1103 if (spnego_state
->in_needed
== 0) {
1107 * try to work out the size of the full
1108 * input token, it might be fragmented
1110 status
= asn1_peek_full_tag(in
, ASN1_APPLICATION(0), &size
);
1111 if (!NT_STATUS_IS_OK(status
) &&
1112 !NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1113 status
= asn1_peek_full_tag(in
, ASN1_CONTEXT(1), &size
);
1116 if (NT_STATUS_IS_OK(status
) ||
1117 NT_STATUS_EQUAL(status
, STATUS_MORE_ENTRIES
)) {
1118 spnego_state
->in_needed
= size
;
1121 * If it is not an asn1 message
1122 * just call the next layer.
1124 spnego_state
->in_needed
= in
.length
;
1128 if (spnego_state
->in_needed
> UINT16_MAX
) {
1130 * limit the incoming message to 0xFFFF
1131 * to avoid DoS attacks.
1133 return NT_STATUS_INVALID_BUFFER_SIZE
;
1136 if ((spnego_state
->in_needed
> 0) && (in
.length
== 0)) {
1138 * If we reach this, we know we got at least
1139 * part of an asn1 message, getting 0 means
1140 * the remote peer wants us to spin.
1142 return NT_STATUS_INVALID_PARAMETER
;
1145 expected
= spnego_state
->in_needed
- spnego_state
->in_frag
.length
;
1146 if (in
.length
> expected
) {
1148 * we got more than expected
1150 return NT_STATUS_INVALID_PARAMETER
;
1153 if (in
.length
== spnego_state
->in_needed
) {
1155 * if the in.length contains the full blob
1158 * Note: this implies spnego_state->in_frag.length == 0,
1159 * but we do not need to check this explicitly
1160 * because we already know that we did not get
1161 * more than expected.
1164 return NT_STATUS_OK
;
1167 ok
= data_blob_append(spnego_state
, &spnego_state
->in_frag
,
1168 in
.data
, in
.length
);
1170 return NT_STATUS_NO_MEMORY
;
1173 if (spnego_state
->in_needed
> spnego_state
->in_frag
.length
) {
1174 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1177 *full_in
= spnego_state
->in_frag
;
1178 return NT_STATUS_OK
;
1181 static NTSTATUS
gensec_spnego_update_out(struct gensec_security
*gensec_security
,
1182 TALLOC_CTX
*out_mem_ctx
,
1185 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1186 DATA_BLOB out
= data_blob_null
;
1188 *_out
= data_blob_null
;
1190 if (spnego_state
->out_frag
.length
== 0) {
1191 return spnego_state
->out_status
;
1195 * There is still more data to be delivered
1196 * to the remote peer.
1199 if (spnego_state
->out_frag
.length
<= spnego_state
->out_max_length
) {
1201 * Fast path, we can deliver everything
1204 *_out
= spnego_state
->out_frag
;
1205 talloc_steal(out_mem_ctx
, _out
->data
);
1206 spnego_state
->out_frag
= data_blob_null
;
1207 return spnego_state
->out_status
;
1210 out
= spnego_state
->out_frag
;
1213 * copy the remaining bytes
1215 spnego_state
->out_frag
= data_blob_talloc(spnego_state
,
1216 out
.data
+ spnego_state
->out_max_length
,
1217 out
.length
- spnego_state
->out_max_length
);
1218 if (spnego_state
->out_frag
.data
== NULL
) {
1219 return NT_STATUS_NO_MEMORY
;
1223 * truncate the buffer
1225 data_blob_realloc(spnego_state
, &out
, spnego_state
->out_max_length
);
1227 talloc_steal(out_mem_ctx
, out
.data
);
1229 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
1232 static NTSTATUS
gensec_spnego_update_wrapper(struct gensec_security
*gensec_security
,
1233 TALLOC_CTX
*out_mem_ctx
,
1234 struct tevent_context
*ev
,
1235 const DATA_BLOB in
, DATA_BLOB
*out
)
1237 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1238 DATA_BLOB full_in
= data_blob_null
;
1241 *out
= data_blob_null
;
1243 if (spnego_state
->out_frag
.length
> 0) {
1244 if (in
.length
> 0) {
1245 return NT_STATUS_INVALID_PARAMETER
;
1248 return gensec_spnego_update_out(gensec_security
,
1253 status
= gensec_spnego_update_in(gensec_security
,
1255 if (!NT_STATUS_IS_OK(status
)) {
1259 status
= gensec_spnego_update(gensec_security
,
1262 &spnego_state
->out_frag
);
1263 data_blob_free(&spnego_state
->in_frag
);
1264 spnego_state
->in_needed
= 0;
1265 if (!NT_STATUS_IS_OK(status
) &&
1266 !NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
1270 spnego_state
->out_status
= status
;
1272 return gensec_spnego_update_out(gensec_security
,
1277 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1280 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1282 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1283 gensec_security
->want_features
|= feature
;
1287 gensec_want_feature(spnego_state
->sub_sec_security
,
1291 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1294 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1295 if (!spnego_state
->sub_sec_security
) {
1299 return gensec_have_feature(spnego_state
->sub_sec_security
,
1303 static NTTIME
gensec_spnego_expire_time(struct gensec_security
*gensec_security
)
1305 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1307 if (!spnego_state
->sub_sec_security
) {
1308 return GENSEC_EXPIRE_TIME_INFINITY
;
1311 return gensec_expire_time(spnego_state
->sub_sec_security
);
1314 static const char *gensec_spnego_oids
[] = {
1319 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1321 .sasl_name
= "GSS-SPNEGO",
1322 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1323 .oid
= gensec_spnego_oids
,
1324 .client_start
= gensec_spnego_client_start
,
1325 .server_start
= gensec_spnego_server_start
,
1326 .update
= gensec_spnego_update_wrapper
,
1327 .seal_packet
= gensec_spnego_seal_packet
,
1328 .sign_packet
= gensec_spnego_sign_packet
,
1329 .sig_size
= gensec_spnego_sig_size
,
1330 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1331 .max_input_size
= gensec_spnego_max_input_size
,
1332 .check_packet
= gensec_spnego_check_packet
,
1333 .unseal_packet
= gensec_spnego_unseal_packet
,
1334 .wrap
= gensec_spnego_wrap
,
1335 .unwrap
= gensec_spnego_unwrap
,
1336 .session_key
= gensec_spnego_session_key
,
1337 .session_info
= gensec_spnego_session_info
,
1338 .want_feature
= gensec_spnego_want_feature
,
1339 .have_feature
= gensec_spnego_have_feature
,
1340 .expire_time
= gensec_spnego_expire_time
,
1342 .priority
= GENSEC_SPNEGO
1345 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1348 ret
= gensec_register(&gensec_spnego_security_ops
);
1349 if (!NT_STATUS_IS_OK(ret
)) {
1350 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1351 gensec_spnego_security_ops
.name
));