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_proto.h"
31 #include "param/param.h"
33 enum spnego_state_position
{
43 enum spnego_message_type expected_packet
;
44 enum spnego_state_position state_position
;
45 struct gensec_security
*sub_sec_security
;
46 bool no_response_expected
;
54 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
56 struct spnego_state
*spnego_state
;
58 spnego_state
= talloc(gensec_security
, struct spnego_state
);
60 return NT_STATUS_NO_MEMORY
;
63 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
64 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
65 spnego_state
->sub_sec_security
= NULL
;
66 spnego_state
->no_response_expected
= false;
67 spnego_state
->mech_types
= data_blob(NULL
, 0);
69 gensec_security
->private_data
= spnego_state
;
73 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
75 struct spnego_state
*spnego_state
;
77 spnego_state
= talloc(gensec_security
, struct spnego_state
);
79 return NT_STATUS_NO_MEMORY
;
82 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
83 spnego_state
->state_position
= SPNEGO_SERVER_START
;
84 spnego_state
->sub_sec_security
= NULL
;
85 spnego_state
->no_response_expected
= false;
86 spnego_state
->mech_types
= data_blob(NULL
, 0);
88 gensec_security
->private_data
= spnego_state
;
93 wrappers for the spnego_*() functions
95 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
97 uint8_t *data
, size_t length
,
98 const uint8_t *whole_pdu
, size_t pdu_length
,
101 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
103 if (spnego_state
->state_position
!= SPNEGO_DONE
104 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
105 return NT_STATUS_INVALID_PARAMETER
;
108 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
111 whole_pdu
, pdu_length
,
115 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
117 const uint8_t *data
, size_t length
,
118 const uint8_t *whole_pdu
, size_t pdu_length
,
119 const DATA_BLOB
*sig
)
121 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
123 if (spnego_state
->state_position
!= SPNEGO_DONE
124 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
125 return NT_STATUS_INVALID_PARAMETER
;
128 return gensec_check_packet(spnego_state
->sub_sec_security
,
131 whole_pdu
, pdu_length
,
135 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
137 uint8_t *data
, size_t length
,
138 const uint8_t *whole_pdu
, size_t pdu_length
,
141 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
143 if (spnego_state
->state_position
!= SPNEGO_DONE
144 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
145 return NT_STATUS_INVALID_PARAMETER
;
148 return gensec_seal_packet(spnego_state
->sub_sec_security
,
151 whole_pdu
, pdu_length
,
155 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
157 const uint8_t *data
, size_t length
,
158 const uint8_t *whole_pdu
, size_t pdu_length
,
161 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
163 if (spnego_state
->state_position
!= SPNEGO_DONE
164 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
165 return NT_STATUS_INVALID_PARAMETER
;
168 return gensec_sign_packet(spnego_state
->sub_sec_security
,
171 whole_pdu
, pdu_length
,
175 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
180 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
182 if (spnego_state
->state_position
!= SPNEGO_DONE
183 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
184 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
185 return NT_STATUS_INVALID_PARAMETER
;
188 return gensec_wrap(spnego_state
->sub_sec_security
,
192 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
197 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
199 if (spnego_state
->state_position
!= SPNEGO_DONE
200 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
201 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
202 return NT_STATUS_INVALID_PARAMETER
;
205 return gensec_unwrap(spnego_state
->sub_sec_security
,
209 static NTSTATUS
gensec_spnego_wrap_packets(struct gensec_security
*gensec_security
,
213 size_t *len_processed
)
215 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
217 if (spnego_state
->state_position
!= SPNEGO_DONE
218 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
219 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
220 return NT_STATUS_INVALID_PARAMETER
;
223 return gensec_wrap_packets(spnego_state
->sub_sec_security
,
228 static NTSTATUS
gensec_spnego_packet_full_request(struct gensec_security
*gensec_security
,
229 DATA_BLOB blob
, size_t *size
)
231 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
233 if (spnego_state
->state_position
!= SPNEGO_DONE
234 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
235 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
236 return NT_STATUS_INVALID_PARAMETER
;
239 return gensec_packet_full_request(spnego_state
->sub_sec_security
,
243 static NTSTATUS
gensec_spnego_unwrap_packets(struct gensec_security
*gensec_security
,
247 size_t *len_processed
)
249 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
251 if (spnego_state
->state_position
!= SPNEGO_DONE
252 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
253 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
254 return NT_STATUS_INVALID_PARAMETER
;
257 return gensec_unwrap_packets(spnego_state
->sub_sec_security
,
262 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
264 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
266 if (spnego_state
->state_position
!= SPNEGO_DONE
267 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
271 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
274 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
276 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
278 if (spnego_state
->state_position
!= SPNEGO_DONE
279 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
283 return gensec_max_input_size(spnego_state
->sub_sec_security
);
286 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
288 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
290 if (spnego_state
->state_position
!= SPNEGO_DONE
291 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
295 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
298 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
299 DATA_BLOB
*session_key
)
301 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
302 if (!spnego_state
->sub_sec_security
) {
303 return NT_STATUS_INVALID_PARAMETER
;
306 return gensec_session_key(spnego_state
->sub_sec_security
,
310 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
311 struct auth_session_info
**session_info
)
313 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
314 if (!spnego_state
->sub_sec_security
) {
315 return NT_STATUS_INVALID_PARAMETER
;
318 return gensec_session_info(spnego_state
->sub_sec_security
,
322 /** Fallback to another GENSEC mechanism, based on magic strings
324 * This is the 'fallback' case, where we don't get SPNEGO, and have to
325 * try all the other options (and hope they all have a magic string
329 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
330 struct spnego_state
*spnego_state
,
331 TALLOC_CTX
*out_mem_ctx
,
332 const DATA_BLOB in
, DATA_BLOB
*out
)
335 struct gensec_security_ops
**all_ops
336 = gensec_security_mechs(gensec_security
, out_mem_ctx
);
337 for (i
=0; all_ops
[i
]; i
++) {
341 if (gensec_security
!= NULL
&&
342 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
345 if (!all_ops
[i
]->oid
) {
350 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
351 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
359 if (!all_ops
[i
]->magic
) {
363 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
364 if (!NT_STATUS_IS_OK(nt_status
)) {
368 spnego_state
->state_position
= SPNEGO_FALLBACK
;
370 nt_status
= gensec_subcontext_start(spnego_state
,
372 &spnego_state
->sub_sec_security
);
374 if (!NT_STATUS_IS_OK(nt_status
)) {
377 /* select the sub context */
378 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
380 if (!NT_STATUS_IS_OK(nt_status
)) {
383 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
384 out_mem_ctx
, in
, out
);
387 DEBUG(1, ("Failed to parse SPNEGO request\n"));
388 return NT_STATUS_INVALID_PARAMETER
;
393 Parse the netTokenInit, either from the client, to the server, or
394 from the server to the client.
397 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
398 struct spnego_state
*spnego_state
,
399 TALLOC_CTX
*out_mem_ctx
,
400 const char **mechType
,
401 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
404 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
405 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
408 const struct gensec_security_ops_wrapper
*all_sec
409 = gensec_security_by_oid_list(gensec_security
,
414 ok
= spnego_write_mech_types(spnego_state
,
416 &spnego_state
->mech_types
);
418 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
419 return NT_STATUS_NO_MEMORY
;
422 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
423 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
424 /* optomisitic token */
425 if (strcmp(all_sec
[i
].oid
, mechType
[0]) == 0) {
426 nt_status
= gensec_subcontext_start(spnego_state
,
428 &spnego_state
->sub_sec_security
);
429 if (!NT_STATUS_IS_OK(nt_status
)) {
432 /* select the sub context */
433 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
435 if (!NT_STATUS_IS_OK(nt_status
)) {
436 talloc_free(spnego_state
->sub_sec_security
);
437 spnego_state
->sub_sec_security
= NULL
;
441 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
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
;
456 spnego_state
->neg_oid
= all_sec
[i
].oid
;
462 /* Having tried any optomisitc token from the client (if we
463 * were the server), if we didn't get anywhere, walk our list
464 * in our preference order */
466 if (!spnego_state
->sub_sec_security
) {
467 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
468 nt_status
= gensec_subcontext_start(spnego_state
,
470 &spnego_state
->sub_sec_security
);
471 if (!NT_STATUS_IS_OK(nt_status
)) {
474 /* select the sub context */
475 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
477 if (!NT_STATUS_IS_OK(nt_status
)) {
478 talloc_free(spnego_state
->sub_sec_security
);
479 spnego_state
->sub_sec_security
= NULL
;
483 spnego_state
->neg_oid
= all_sec
[i
].oid
;
485 /* only get the helping start blob for the first OID */
486 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
491 /* it is likely that a NULL input token will
492 * not be liked by most server mechs, but if
493 * we are in the client, we want the first
494 * update packet to be able to abort the use
496 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
497 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
498 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
499 /* Pretend we never started it (lets the first run find some incompatible demand) */
501 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n",
502 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
503 talloc_free(spnego_state
->sub_sec_security
);
504 spnego_state
->sub_sec_security
= NULL
;
513 if (spnego_state
->sub_sec_security
) {
514 /* it is likely that a NULL input token will
515 * not be liked by most server mechs, but this
516 * does the right thing in the CIFS client.
517 * just push us along the merry-go-round
518 * again, and hope for better luck next
521 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
522 *unwrapped_out
= data_blob(NULL
, 0);
523 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
526 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
527 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
528 && !NT_STATUS_IS_OK(nt_status
)) {
529 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
530 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
531 talloc_free(spnego_state
->sub_sec_security
);
532 spnego_state
->sub_sec_security
= NULL
;
534 /* We started the mech correctly, and the
535 * input from the other side was valid.
536 * Return the error (say bad password, invalid
542 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
545 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
546 /* we could re-negotiate here, but it would only work
547 * if the client or server lied about what it could
548 * support the first time. Lets keep this code to
554 /** create a negTokenInit
556 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
558 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
559 struct spnego_state
*spnego_state
,
560 TALLOC_CTX
*out_mem_ctx
,
561 const DATA_BLOB in
, DATA_BLOB
*out
)
564 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
565 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
566 const char **mechTypes
= NULL
;
567 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
568 const struct gensec_security_ops_wrapper
*all_sec
;
569 const char *principal
= NULL
;
571 mechTypes
= gensec_security_oids(gensec_security
,
572 out_mem_ctx
, GENSEC_OID_SPNEGO
);
574 all_sec
= gensec_security_by_oid_list(gensec_security
,
578 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
579 struct spnego_data spnego_out
;
580 const char **send_mech_types
;
583 nt_status
= gensec_subcontext_start(spnego_state
,
585 &spnego_state
->sub_sec_security
);
586 if (!NT_STATUS_IS_OK(nt_status
)) {
589 /* select the sub context */
590 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
592 if (!NT_STATUS_IS_OK(nt_status
)) {
593 talloc_free(spnego_state
->sub_sec_security
);
594 spnego_state
->sub_sec_security
= NULL
;
598 /* In the client, try and produce the first (optimistic) packet */
599 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
600 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
605 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
606 && !NT_STATUS_IS_OK(nt_status
)) {
607 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
608 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
609 talloc_free(spnego_state
->sub_sec_security
);
610 spnego_state
->sub_sec_security
= NULL
;
611 /* Pretend we never started it (lets the first run find some incompatible demand) */
617 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
619 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
622 ok
= spnego_write_mech_types(spnego_state
,
624 &spnego_state
->mech_types
);
626 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
627 return NT_STATUS_NO_MEMORY
;
630 /* List the remaining mechs as options */
631 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
632 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
633 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
635 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
636 /* server credentials */
637 struct cli_credentials
*creds
= gensec_get_credentials(gensec_security
);
639 principal
= cli_credentials_get_principal(creds
, out_mem_ctx
);
643 spnego_out
.negTokenInit
.mechListMIC
644 = data_blob_string_const(principal
);
646 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
649 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
651 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
652 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
653 return NT_STATUS_INVALID_PARAMETER
;
657 spnego_state
->neg_oid
= all_sec
[i
].oid
;
659 if (NT_STATUS_IS_OK(nt_status
)) {
660 spnego_state
->no_response_expected
= true;
663 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
665 talloc_free(spnego_state
->sub_sec_security
);
666 spnego_state
->sub_sec_security
= NULL
;
668 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
669 return NT_STATUS_INVALID_PARAMETER
;
673 /** create a server negTokenTarg
675 * This is the case, where the client is the first one who sends data
678 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct gensec_security
*gensec_security
,
679 struct spnego_state
*spnego_state
,
680 TALLOC_CTX
*out_mem_ctx
,
682 const DATA_BLOB unwrapped_out
,
683 DATA_BLOB mech_list_mic
,
686 struct spnego_data spnego_out
;
687 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
690 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
691 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
692 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
693 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
695 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
696 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
697 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
698 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
699 } else if (NT_STATUS_IS_OK(nt_status
)) {
700 if (unwrapped_out
.data
) {
701 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
703 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
704 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
705 spnego_state
->state_position
= SPNEGO_DONE
;
707 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
708 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
709 spnego_state
->state_position
= SPNEGO_DONE
;
712 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
713 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
714 return NT_STATUS_INVALID_PARAMETER
;
717 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
723 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
724 const DATA_BLOB in
, DATA_BLOB
*out
)
726 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
727 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
728 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
729 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
730 struct spnego_data spnego_out
;
731 struct spnego_data spnego
;
735 *out
= data_blob(NULL
, 0);
738 out_mem_ctx
= spnego_state
;
741 /* and switch into the state machine */
743 switch (spnego_state
->state_position
) {
744 case SPNEGO_FALLBACK
:
745 return gensec_update(spnego_state
->sub_sec_security
,
746 out_mem_ctx
, in
, out
);
747 case SPNEGO_SERVER_START
:
752 len
= spnego_read_data(gensec_security
, in
, &spnego
);
754 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
755 out_mem_ctx
, in
, out
);
757 /* client sent NegTargetInit, we send NegTokenTarg */
759 /* OK, so it's real SPNEGO, check the packet's the one we expect */
760 if (spnego
.type
!= spnego_state
->expected_packet
) {
761 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
762 spnego_state
->expected_packet
));
763 dump_data(1, in
.data
, in
.length
);
764 spnego_free_data(&spnego
);
765 return NT_STATUS_INVALID_PARAMETER
;
768 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
771 spnego
.negTokenInit
.mechTypes
,
772 spnego
.negTokenInit
.mechToken
,
775 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
783 spnego_free_data(&spnego
);
787 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
788 out_mem_ctx
, in
, out
);
789 spnego_state
->state_position
= SPNEGO_SERVER_START
;
790 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
795 case SPNEGO_CLIENT_START
:
797 /* The server offers a list of mechanisms */
799 const char *my_mechs
[] = {NULL
, NULL
};
800 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
803 /* client to produce negTokenInit */
804 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
805 out_mem_ctx
, in
, out
);
806 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
807 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
811 len
= spnego_read_data(gensec_security
, in
, &spnego
);
814 DEBUG(1, ("Invalid SPNEGO request:\n"));
815 dump_data(1, in
.data
, in
.length
);
816 return NT_STATUS_INVALID_PARAMETER
;
819 /* OK, so it's real SPNEGO, check the packet's the one we expect */
820 if (spnego
.type
!= spnego_state
->expected_packet
) {
821 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
822 spnego_state
->expected_packet
));
823 dump_data(1, in
.data
, in
.length
);
824 spnego_free_data(&spnego
);
825 return NT_STATUS_INVALID_PARAMETER
;
828 if (spnego
.negTokenInit
.targetPrincipal
) {
829 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
830 if (lpcfg_client_use_spnego_principal(gensec_security
->settings
->lp_ctx
)) {
831 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
835 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
838 spnego
.negTokenInit
.mechTypes
,
839 spnego
.negTokenInit
.mechToken
,
842 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
843 spnego_free_data(&spnego
);
847 my_mechs
[0] = spnego_state
->neg_oid
;
849 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
850 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
851 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
852 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
853 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
854 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
856 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
857 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
858 return NT_STATUS_INVALID_PARAMETER
;
862 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
863 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
865 if (NT_STATUS_IS_OK(nt_status
)) {
866 spnego_state
->no_response_expected
= true;
869 spnego_free_data(&spnego
);
870 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
872 case SPNEGO_SERVER_TARG
:
875 bool new_spnego
= false;
878 return NT_STATUS_INVALID_PARAMETER
;
881 len
= spnego_read_data(gensec_security
, in
, &spnego
);
884 DEBUG(1, ("Invalid SPNEGO request:\n"));
885 dump_data(1, in
.data
, in
.length
);
886 return NT_STATUS_INVALID_PARAMETER
;
889 /* OK, so it's real SPNEGO, check the packet's the one we expect */
890 if (spnego
.type
!= spnego_state
->expected_packet
) {
891 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
892 spnego_state
->expected_packet
));
893 dump_data(1, in
.data
, in
.length
);
894 spnego_free_data(&spnego
);
895 return NT_STATUS_INVALID_PARAMETER
;
898 if (!spnego_state
->sub_sec_security
) {
899 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
900 spnego_free_data(&spnego
);
901 return NT_STATUS_INVALID_PARAMETER
;
904 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
906 spnego
.negTokenTarg
.responseToken
,
908 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
910 nt_status
= gensec_check_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
,
916 &spnego
.negTokenTarg
.mechListMIC
);
917 if (!NT_STATUS_IS_OK(nt_status
)) {
918 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
919 nt_errstr(nt_status
)));
922 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
923 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
925 spnego_state
->mech_types
.data
,
926 spnego_state
->mech_types
.length
,
927 spnego_state
->mech_types
.data
,
928 spnego_state
->mech_types
.length
,
930 if (!NT_STATUS_IS_OK(nt_status
)) {
931 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
932 nt_errstr(nt_status
)));
936 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
944 spnego_free_data(&spnego
);
948 case SPNEGO_CLIENT_TARG
:
952 return NT_STATUS_INVALID_PARAMETER
;
955 len
= spnego_read_data(gensec_security
, in
, &spnego
);
958 DEBUG(1, ("Invalid SPNEGO request:\n"));
959 dump_data(1, in
.data
, in
.length
);
960 return NT_STATUS_INVALID_PARAMETER
;
963 /* OK, so it's real SPNEGO, check the packet's the one we expect */
964 if (spnego
.type
!= spnego_state
->expected_packet
) {
965 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
966 spnego_state
->expected_packet
));
967 dump_data(1, in
.data
, in
.length
);
968 spnego_free_data(&spnego
);
969 return NT_STATUS_INVALID_PARAMETER
;
972 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
973 spnego_free_data(&spnego
);
974 return NT_STATUS_ACCESS_DENIED
;
977 /* Server didn't like our choice of mech, and chose something else */
978 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
979 spnego
.negTokenTarg
.supportedMech
&&
980 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
981 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
982 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
),
983 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
)));
985 talloc_free(spnego_state
->sub_sec_security
);
986 nt_status
= gensec_subcontext_start(spnego_state
,
988 &spnego_state
->sub_sec_security
);
989 if (!NT_STATUS_IS_OK(nt_status
)) {
990 spnego_free_data(&spnego
);
993 /* select the sub context */
994 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
995 spnego
.negTokenTarg
.supportedMech
);
996 if (!NT_STATUS_IS_OK(nt_status
)) {
997 spnego_free_data(&spnego
);
1001 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1003 spnego
.negTokenTarg
.responseToken
,
1005 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
1006 } else if (spnego_state
->no_response_expected
) {
1007 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1008 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
1009 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1010 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
1011 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
1012 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1014 nt_status
= NT_STATUS_OK
;
1016 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1017 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1019 spnego_state
->mech_types
.data
,
1020 spnego_state
->mech_types
.length
,
1021 spnego_state
->mech_types
.data
,
1022 spnego_state
->mech_types
.length
,
1023 &spnego
.negTokenTarg
.mechListMIC
);
1024 if (!NT_STATUS_IS_OK(nt_status
)) {
1025 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1026 nt_errstr(nt_status
)));
1030 bool new_spnego
= false;
1032 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1034 spnego
.negTokenTarg
.responseToken
,
1037 if (NT_STATUS_IS_OK(nt_status
)
1038 && spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1039 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1040 GENSEC_FEATURE_NEW_SPNEGO
);
1042 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
1043 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1045 spnego_state
->mech_types
.data
,
1046 spnego_state
->mech_types
.length
,
1047 spnego_state
->mech_types
.data
,
1048 spnego_state
->mech_types
.length
,
1050 if (!NT_STATUS_IS_OK(nt_status
)) {
1051 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1052 nt_errstr(nt_status
)));
1055 if (NT_STATUS_IS_OK(nt_status
)) {
1056 spnego_state
->no_response_expected
= true;
1060 spnego_free_data(&spnego
);
1062 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1063 && !NT_STATUS_IS_OK(nt_status
)) {
1064 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1065 spnego_state
->sub_sec_security
->ops
->name
,
1066 nt_errstr(nt_status
)));
1070 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1072 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1073 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1074 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1075 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1076 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1078 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1079 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1080 return NT_STATUS_INVALID_PARAMETER
;
1083 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1084 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1087 /* all done - server has accepted, and we agree */
1088 *out
= null_data_blob
;
1090 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1091 /* unless of course it did not accept */
1092 DEBUG(1,("gensec_update ok but not accepted\n"));
1093 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1096 spnego_state
->state_position
= SPNEGO_DONE
;
1102 /* We should not be called after we are 'done' */
1103 return NT_STATUS_INVALID_PARAMETER
;
1105 return NT_STATUS_INVALID_PARAMETER
;
1108 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1111 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1113 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1114 gensec_security
->want_features
|= feature
;
1118 gensec_want_feature(spnego_state
->sub_sec_security
,
1122 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1125 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1126 if (!spnego_state
->sub_sec_security
) {
1130 return gensec_have_feature(spnego_state
->sub_sec_security
,
1134 static const char *gensec_spnego_oids
[] = {
1139 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1141 .sasl_name
= "GSS-SPNEGO",
1142 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1143 .oid
= gensec_spnego_oids
,
1144 .client_start
= gensec_spnego_client_start
,
1145 .server_start
= gensec_spnego_server_start
,
1146 .update
= gensec_spnego_update
,
1147 .seal_packet
= gensec_spnego_seal_packet
,
1148 .sign_packet
= gensec_spnego_sign_packet
,
1149 .sig_size
= gensec_spnego_sig_size
,
1150 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1151 .max_input_size
= gensec_spnego_max_input_size
,
1152 .check_packet
= gensec_spnego_check_packet
,
1153 .unseal_packet
= gensec_spnego_unseal_packet
,
1154 .packet_full_request
= gensec_spnego_packet_full_request
,
1155 .wrap
= gensec_spnego_wrap
,
1156 .unwrap
= gensec_spnego_unwrap
,
1157 .wrap_packets
= gensec_spnego_wrap_packets
,
1158 .unwrap_packets
= gensec_spnego_unwrap_packets
,
1159 .session_key
= gensec_spnego_session_key
,
1160 .session_info
= gensec_spnego_session_info
,
1161 .want_feature
= gensec_spnego_want_feature
,
1162 .have_feature
= gensec_spnego_have_feature
,
1164 .priority
= GENSEC_SPNEGO
1167 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1170 ret
= gensec_register(&gensec_spnego_security_ops
);
1171 if (!NT_STATUS_IS_OK(ret
)) {
1172 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1173 gensec_spnego_security_ops
.name
));