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"
32 enum spnego_state_position
{
42 enum spnego_message_type expected_packet
;
43 enum spnego_state_position state_position
;
44 struct gensec_security
*sub_sec_security
;
45 bool no_response_expected
;
53 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
55 struct spnego_state
*spnego_state
;
57 spnego_state
= talloc(gensec_security
, struct spnego_state
);
59 return NT_STATUS_NO_MEMORY
;
62 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
63 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
64 spnego_state
->sub_sec_security
= NULL
;
65 spnego_state
->no_response_expected
= false;
66 spnego_state
->mech_types
= data_blob(NULL
, 0);
68 gensec_security
->private_data
= spnego_state
;
72 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
74 struct spnego_state
*spnego_state
;
76 spnego_state
= talloc(gensec_security
, struct spnego_state
);
78 return NT_STATUS_NO_MEMORY
;
81 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
82 spnego_state
->state_position
= SPNEGO_SERVER_START
;
83 spnego_state
->sub_sec_security
= NULL
;
84 spnego_state
->no_response_expected
= false;
85 spnego_state
->mech_types
= data_blob(NULL
, 0);
87 gensec_security
->private_data
= spnego_state
;
92 wrappers for the spnego_*() functions
94 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
96 uint8_t *data
, size_t length
,
97 const uint8_t *whole_pdu
, size_t pdu_length
,
100 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
102 if (spnego_state
->state_position
!= SPNEGO_DONE
103 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
104 return NT_STATUS_INVALID_PARAMETER
;
107 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
110 whole_pdu
, pdu_length
,
114 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
116 const uint8_t *data
, size_t length
,
117 const uint8_t *whole_pdu
, size_t pdu_length
,
118 const DATA_BLOB
*sig
)
120 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
122 if (spnego_state
->state_position
!= SPNEGO_DONE
123 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
124 return NT_STATUS_INVALID_PARAMETER
;
127 return gensec_check_packet(spnego_state
->sub_sec_security
,
130 whole_pdu
, pdu_length
,
134 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
136 uint8_t *data
, size_t length
,
137 const uint8_t *whole_pdu
, size_t pdu_length
,
140 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
142 if (spnego_state
->state_position
!= SPNEGO_DONE
143 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
144 return NT_STATUS_INVALID_PARAMETER
;
147 return gensec_seal_packet(spnego_state
->sub_sec_security
,
150 whole_pdu
, pdu_length
,
154 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
156 const uint8_t *data
, size_t length
,
157 const uint8_t *whole_pdu
, size_t pdu_length
,
160 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
162 if (spnego_state
->state_position
!= SPNEGO_DONE
163 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
164 return NT_STATUS_INVALID_PARAMETER
;
167 return gensec_sign_packet(spnego_state
->sub_sec_security
,
170 whole_pdu
, pdu_length
,
174 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
179 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
181 if (spnego_state
->state_position
!= SPNEGO_DONE
182 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
183 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
184 return NT_STATUS_INVALID_PARAMETER
;
187 return gensec_wrap(spnego_state
->sub_sec_security
,
191 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
196 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
198 if (spnego_state
->state_position
!= SPNEGO_DONE
199 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
200 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
201 return NT_STATUS_INVALID_PARAMETER
;
204 return gensec_unwrap(spnego_state
->sub_sec_security
,
208 static NTSTATUS
gensec_spnego_wrap_packets(struct gensec_security
*gensec_security
,
212 size_t *len_processed
)
214 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
216 if (spnego_state
->state_position
!= SPNEGO_DONE
217 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
218 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
219 return NT_STATUS_INVALID_PARAMETER
;
222 return gensec_wrap_packets(spnego_state
->sub_sec_security
,
227 static NTSTATUS
gensec_spnego_packet_full_request(struct gensec_security
*gensec_security
,
228 DATA_BLOB blob
, size_t *size
)
230 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
232 if (spnego_state
->state_position
!= SPNEGO_DONE
233 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
234 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
235 return NT_STATUS_INVALID_PARAMETER
;
238 return gensec_packet_full_request(spnego_state
->sub_sec_security
,
242 static NTSTATUS
gensec_spnego_unwrap_packets(struct gensec_security
*gensec_security
,
246 size_t *len_processed
)
248 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
250 if (spnego_state
->state_position
!= SPNEGO_DONE
251 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
252 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
253 return NT_STATUS_INVALID_PARAMETER
;
256 return gensec_unwrap_packets(spnego_state
->sub_sec_security
,
261 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
263 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
265 if (spnego_state
->state_position
!= SPNEGO_DONE
266 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
270 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
273 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
275 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
277 if (spnego_state
->state_position
!= SPNEGO_DONE
278 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
282 return gensec_max_input_size(spnego_state
->sub_sec_security
);
285 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
287 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
289 if (spnego_state
->state_position
!= SPNEGO_DONE
290 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
294 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
297 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
298 DATA_BLOB
*session_key
)
300 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
301 if (!spnego_state
->sub_sec_security
) {
302 return NT_STATUS_INVALID_PARAMETER
;
305 return gensec_session_key(spnego_state
->sub_sec_security
,
309 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
310 struct auth_session_info
**session_info
)
312 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
313 if (!spnego_state
->sub_sec_security
) {
314 return NT_STATUS_INVALID_PARAMETER
;
317 return gensec_session_info(spnego_state
->sub_sec_security
,
321 /** Fallback to another GENSEC mechanism, based on magic strings
323 * This is the 'fallback' case, where we don't get SPNEGO, and have to
324 * try all the other options (and hope they all have a magic string
328 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
329 struct spnego_state
*spnego_state
,
330 TALLOC_CTX
*out_mem_ctx
,
331 const DATA_BLOB in
, DATA_BLOB
*out
)
334 struct gensec_security_ops
**all_ops
335 = gensec_security_mechs(gensec_security
, out_mem_ctx
);
336 for (i
=0; all_ops
[i
]; i
++) {
340 if (gensec_security
!= NULL
&&
341 !gensec_security_ops_enabled(all_ops
[i
], gensec_security
))
344 if (!all_ops
[i
]->oid
) {
349 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
350 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
358 if (!all_ops
[i
]->magic
) {
362 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
363 if (!NT_STATUS_IS_OK(nt_status
)) {
367 spnego_state
->state_position
= SPNEGO_FALLBACK
;
369 nt_status
= gensec_subcontext_start(spnego_state
,
371 &spnego_state
->sub_sec_security
);
373 if (!NT_STATUS_IS_OK(nt_status
)) {
376 /* select the sub context */
377 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
379 if (!NT_STATUS_IS_OK(nt_status
)) {
382 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
383 out_mem_ctx
, in
, out
);
386 DEBUG(1, ("Failed to parse SPNEGO request\n"));
387 return NT_STATUS_INVALID_PARAMETER
;
392 Parse the netTokenInit, either from the client, to the server, or
393 from the server to the client.
396 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
397 struct spnego_state
*spnego_state
,
398 TALLOC_CTX
*out_mem_ctx
,
399 const char **mechType
,
400 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
403 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
404 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
407 const struct gensec_security_ops_wrapper
*all_sec
408 = gensec_security_by_oid_list(gensec_security
,
413 ok
= spnego_write_mech_types(spnego_state
,
415 &spnego_state
->mech_types
);
417 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
418 return NT_STATUS_NO_MEMORY
;
421 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
422 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
423 /* optomisitic token */
424 if (strcmp(all_sec
[i
].oid
, mechType
[0]) == 0) {
425 nt_status
= gensec_subcontext_start(spnego_state
,
427 &spnego_state
->sub_sec_security
);
428 if (!NT_STATUS_IS_OK(nt_status
)) {
431 /* select the sub context */
432 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
434 if (!NT_STATUS_IS_OK(nt_status
)) {
435 talloc_free(spnego_state
->sub_sec_security
);
436 spnego_state
->sub_sec_security
= NULL
;
440 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
444 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
445 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
446 /* Pretend we never started it (lets the first run find some incompatible demand) */
448 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
449 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
450 talloc_free(spnego_state
->sub_sec_security
);
451 spnego_state
->sub_sec_security
= NULL
;
455 spnego_state
->neg_oid
= all_sec
[i
].oid
;
461 /* Having tried any optomisitc token from the client (if we
462 * were the server), if we didn't get anywhere, walk our list
463 * in our preference order */
465 if (!spnego_state
->sub_sec_security
) {
466 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
467 nt_status
= gensec_subcontext_start(spnego_state
,
469 &spnego_state
->sub_sec_security
);
470 if (!NT_STATUS_IS_OK(nt_status
)) {
473 /* select the sub context */
474 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
476 if (!NT_STATUS_IS_OK(nt_status
)) {
477 talloc_free(spnego_state
->sub_sec_security
);
478 spnego_state
->sub_sec_security
= NULL
;
482 spnego_state
->neg_oid
= all_sec
[i
].oid
;
484 /* only get the helping start blob for the first OID */
485 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
490 /* it is likely that a NULL input token will
491 * not be liked by most server mechs, but if
492 * we are in the client, we want the first
493 * update packet to be able to abort the use
495 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
496 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
497 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
498 /* Pretend we never started it (lets the first run find some incompatible demand) */
500 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n",
501 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
502 talloc_free(spnego_state
->sub_sec_security
);
503 spnego_state
->sub_sec_security
= NULL
;
512 if (spnego_state
->sub_sec_security
) {
513 /* it is likely that a NULL input token will
514 * not be liked by most server mechs, but this
515 * does the right thing in the CIFS client.
516 * just push us along the merry-go-round
517 * again, and hope for better luck next
520 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
521 *unwrapped_out
= data_blob(NULL
, 0);
522 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
525 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
526 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
527 && !NT_STATUS_IS_OK(nt_status
)) {
528 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
529 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
530 talloc_free(spnego_state
->sub_sec_security
);
531 spnego_state
->sub_sec_security
= NULL
;
533 /* We started the mech correctly, and the
534 * input from the other side was valid.
535 * Return the error (say bad password, invalid
541 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
544 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
545 /* we could re-negotiate here, but it would only work
546 * if the client or server lied about what it could
547 * support the first time. Lets keep this code to
553 /** create a negTokenInit
555 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
557 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
558 struct spnego_state
*spnego_state
,
559 TALLOC_CTX
*out_mem_ctx
,
560 const DATA_BLOB in
, DATA_BLOB
*out
)
563 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
564 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
565 const char **mechTypes
= NULL
;
566 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
567 const struct gensec_security_ops_wrapper
*all_sec
;
568 const char *principal
= NULL
;
570 mechTypes
= gensec_security_oids(gensec_security
,
571 out_mem_ctx
, GENSEC_OID_SPNEGO
);
573 all_sec
= gensec_security_by_oid_list(gensec_security
,
577 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
578 struct spnego_data spnego_out
;
579 const char **send_mech_types
;
582 nt_status
= gensec_subcontext_start(spnego_state
,
584 &spnego_state
->sub_sec_security
);
585 if (!NT_STATUS_IS_OK(nt_status
)) {
588 /* select the sub context */
589 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
591 if (!NT_STATUS_IS_OK(nt_status
)) {
592 talloc_free(spnego_state
->sub_sec_security
);
593 spnego_state
->sub_sec_security
= NULL
;
597 /* In the client, try and produce the first (optimistic) packet */
598 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
599 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
604 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
605 && !NT_STATUS_IS_OK(nt_status
)) {
606 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
607 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
608 talloc_free(spnego_state
->sub_sec_security
);
609 spnego_state
->sub_sec_security
= NULL
;
610 /* Pretend we never started it (lets the first run find some incompatible demand) */
616 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
618 send_mech_types
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
621 ok
= spnego_write_mech_types(spnego_state
,
623 &spnego_state
->mech_types
);
625 DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
626 return NT_STATUS_NO_MEMORY
;
629 /* List the remaining mechs as options */
630 spnego_out
.negTokenInit
.mechTypes
= send_mech_types
;
631 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
632 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
634 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
635 /* server credentials */
636 struct cli_credentials
*creds
= gensec_get_credentials(gensec_security
);
638 principal
= cli_credentials_get_principal(creds
, out_mem_ctx
);
642 spnego_out
.negTokenInit
.mechListMIC
643 = data_blob_string_const(principal
);
645 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
648 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
650 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
651 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
652 return NT_STATUS_INVALID_PARAMETER
;
656 spnego_state
->neg_oid
= all_sec
[i
].oid
;
658 if (NT_STATUS_IS_OK(nt_status
)) {
659 spnego_state
->no_response_expected
= true;
662 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
664 talloc_free(spnego_state
->sub_sec_security
);
665 spnego_state
->sub_sec_security
= NULL
;
667 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
668 return NT_STATUS_INVALID_PARAMETER
;
672 /** create a server negTokenTarg
674 * This is the case, where the client is the first one who sends data
677 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct gensec_security
*gensec_security
,
678 struct spnego_state
*spnego_state
,
679 TALLOC_CTX
*out_mem_ctx
,
681 const DATA_BLOB unwrapped_out
,
682 DATA_BLOB mech_list_mic
,
685 struct spnego_data spnego_out
;
686 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
689 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
690 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
691 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
692 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
694 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
695 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
696 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
697 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
698 } else if (NT_STATUS_IS_OK(nt_status
)) {
699 if (unwrapped_out
.data
) {
700 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
702 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
703 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
704 spnego_state
->state_position
= SPNEGO_DONE
;
706 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
707 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
708 spnego_state
->state_position
= SPNEGO_DONE
;
711 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
712 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
713 return NT_STATUS_INVALID_PARAMETER
;
716 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
722 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
723 const DATA_BLOB in
, DATA_BLOB
*out
)
725 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
726 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
727 DATA_BLOB mech_list_mic
= data_blob(NULL
, 0);
728 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
729 struct spnego_data spnego_out
;
730 struct spnego_data spnego
;
734 *out
= data_blob(NULL
, 0);
737 out_mem_ctx
= spnego_state
;
740 /* and switch into the state machine */
742 switch (spnego_state
->state_position
) {
743 case SPNEGO_FALLBACK
:
744 return gensec_update(spnego_state
->sub_sec_security
,
745 out_mem_ctx
, in
, out
);
746 case SPNEGO_SERVER_START
:
751 len
= spnego_read_data(gensec_security
, in
, &spnego
);
753 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
754 out_mem_ctx
, in
, out
);
756 /* client sent NegTargetInit, we send NegTokenTarg */
758 /* OK, so it's real SPNEGO, check the packet's the one we expect */
759 if (spnego
.type
!= spnego_state
->expected_packet
) {
760 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
761 spnego_state
->expected_packet
));
762 dump_data(1, in
.data
, in
.length
);
763 spnego_free_data(&spnego
);
764 return NT_STATUS_INVALID_PARAMETER
;
767 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
770 spnego
.negTokenInit
.mechTypes
,
771 spnego
.negTokenInit
.mechToken
,
774 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
782 spnego_free_data(&spnego
);
786 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
787 out_mem_ctx
, in
, out
);
788 spnego_state
->state_position
= SPNEGO_SERVER_START
;
789 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
794 case SPNEGO_CLIENT_START
:
796 /* The server offers a list of mechanisms */
798 const char *my_mechs
[] = {NULL
, NULL
};
799 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
802 /* client to produce negTokenInit */
803 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
804 out_mem_ctx
, in
, out
);
805 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
806 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
810 len
= spnego_read_data(gensec_security
, in
, &spnego
);
813 DEBUG(1, ("Invalid SPNEGO request:\n"));
814 dump_data(1, in
.data
, in
.length
);
815 return NT_STATUS_INVALID_PARAMETER
;
818 /* OK, so it's real SPNEGO, check the packet's the one we expect */
819 if (spnego
.type
!= spnego_state
->expected_packet
) {
820 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
821 spnego_state
->expected_packet
));
822 dump_data(1, in
.data
, in
.length
);
823 spnego_free_data(&spnego
);
824 return NT_STATUS_INVALID_PARAMETER
;
827 if (spnego
.negTokenInit
.targetPrincipal
) {
828 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
829 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
832 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
835 spnego
.negTokenInit
.mechTypes
,
836 spnego
.negTokenInit
.mechToken
,
839 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
840 spnego_free_data(&spnego
);
844 my_mechs
[0] = spnego_state
->neg_oid
;
846 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
847 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
848 spnego_out
.negTokenInit
.reqFlags
= null_data_blob
;
849 spnego_out
.negTokenInit
.reqFlagsPadding
= 0;
850 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
851 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
853 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
854 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
855 return NT_STATUS_INVALID_PARAMETER
;
859 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
860 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
862 if (NT_STATUS_IS_OK(nt_status
)) {
863 spnego_state
->no_response_expected
= true;
866 spnego_free_data(&spnego
);
867 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
869 case SPNEGO_SERVER_TARG
:
872 bool new_spnego
= false;
875 return NT_STATUS_INVALID_PARAMETER
;
878 len
= spnego_read_data(gensec_security
, in
, &spnego
);
881 DEBUG(1, ("Invalid SPNEGO request:\n"));
882 dump_data(1, in
.data
, in
.length
);
883 return NT_STATUS_INVALID_PARAMETER
;
886 /* OK, so it's real SPNEGO, check the packet's the one we expect */
887 if (spnego
.type
!= spnego_state
->expected_packet
) {
888 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
889 spnego_state
->expected_packet
));
890 dump_data(1, in
.data
, in
.length
);
891 spnego_free_data(&spnego
);
892 return NT_STATUS_INVALID_PARAMETER
;
895 if (!spnego_state
->sub_sec_security
) {
896 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
897 spnego_free_data(&spnego
);
898 return NT_STATUS_INVALID_PARAMETER
;
901 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
903 spnego
.negTokenTarg
.responseToken
,
905 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
907 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
909 spnego_state
->mech_types
.data
,
910 spnego_state
->mech_types
.length
,
911 spnego_state
->mech_types
.data
,
912 spnego_state
->mech_types
.length
,
913 &spnego
.negTokenTarg
.mechListMIC
);
914 if (!NT_STATUS_IS_OK(nt_status
)) {
915 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
916 nt_errstr(nt_status
)));
919 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
920 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
922 spnego_state
->mech_types
.data
,
923 spnego_state
->mech_types
.length
,
924 spnego_state
->mech_types
.data
,
925 spnego_state
->mech_types
.length
,
927 if (!NT_STATUS_IS_OK(nt_status
)) {
928 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
929 nt_errstr(nt_status
)));
933 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
941 spnego_free_data(&spnego
);
945 case SPNEGO_CLIENT_TARG
:
949 return NT_STATUS_INVALID_PARAMETER
;
952 len
= spnego_read_data(gensec_security
, in
, &spnego
);
955 DEBUG(1, ("Invalid SPNEGO request:\n"));
956 dump_data(1, in
.data
, in
.length
);
957 return NT_STATUS_INVALID_PARAMETER
;
960 /* OK, so it's real SPNEGO, check the packet's the one we expect */
961 if (spnego
.type
!= spnego_state
->expected_packet
) {
962 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
963 spnego_state
->expected_packet
));
964 dump_data(1, in
.data
, in
.length
);
965 spnego_free_data(&spnego
);
966 return NT_STATUS_INVALID_PARAMETER
;
969 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
970 spnego_free_data(&spnego
);
971 return NT_STATUS_ACCESS_DENIED
;
974 /* Server didn't like our choice of mech, and chose something else */
975 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
976 spnego
.negTokenTarg
.supportedMech
&&
977 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
978 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
979 gensec_get_name_by_oid(gensec_security
, spnego
.negTokenTarg
.supportedMech
),
980 gensec_get_name_by_oid(gensec_security
, spnego_state
->neg_oid
)));
982 talloc_free(spnego_state
->sub_sec_security
);
983 nt_status
= gensec_subcontext_start(spnego_state
,
985 &spnego_state
->sub_sec_security
);
986 if (!NT_STATUS_IS_OK(nt_status
)) {
987 spnego_free_data(&spnego
);
990 /* select the sub context */
991 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
992 spnego
.negTokenTarg
.supportedMech
);
993 if (!NT_STATUS_IS_OK(nt_status
)) {
994 spnego_free_data(&spnego
);
998 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1000 spnego
.negTokenTarg
.responseToken
,
1002 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
1003 } else if (spnego_state
->no_response_expected
) {
1004 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1005 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
1006 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1007 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
1008 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
1009 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1011 nt_status
= NT_STATUS_OK
;
1013 if (NT_STATUS_IS_OK(nt_status
) && spnego
.negTokenTarg
.mechListMIC
.length
> 0) {
1014 nt_status
= gensec_check_packet(spnego_state
->sub_sec_security
,
1016 spnego_state
->mech_types
.data
,
1017 spnego_state
->mech_types
.length
,
1018 spnego_state
->mech_types
.data
,
1019 spnego_state
->mech_types
.length
,
1020 &spnego
.negTokenTarg
.mechListMIC
);
1021 if (!NT_STATUS_IS_OK(nt_status
)) {
1022 DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
1023 nt_errstr(nt_status
)));
1027 bool new_spnego
= false;
1029 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
1031 spnego
.negTokenTarg
.responseToken
,
1034 if (NT_STATUS_IS_OK(nt_status
)
1035 && spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1036 new_spnego
= gensec_have_feature(spnego_state
->sub_sec_security
,
1037 GENSEC_FEATURE_NEW_SPNEGO
);
1039 if (NT_STATUS_IS_OK(nt_status
) && new_spnego
) {
1040 nt_status
= gensec_sign_packet(spnego_state
->sub_sec_security
,
1042 spnego_state
->mech_types
.data
,
1043 spnego_state
->mech_types
.length
,
1044 spnego_state
->mech_types
.data
,
1045 spnego_state
->mech_types
.length
,
1047 if (!NT_STATUS_IS_OK(nt_status
)) {
1048 DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
1049 nt_errstr(nt_status
)));
1052 if (NT_STATUS_IS_OK(nt_status
)) {
1053 spnego_state
->no_response_expected
= true;
1057 spnego_free_data(&spnego
);
1059 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
1060 && !NT_STATUS_IS_OK(nt_status
)) {
1061 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
1062 spnego_state
->sub_sec_security
->ops
->name
,
1063 nt_errstr(nt_status
)));
1067 if (unwrapped_out
.length
|| mech_list_mic
.length
) {
1069 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
1070 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
1071 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
1072 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
1073 spnego_out
.negTokenTarg
.mechListMIC
= mech_list_mic
;
1075 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
1076 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
1077 return NT_STATUS_INVALID_PARAMETER
;
1080 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
1081 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
1084 /* all done - server has accepted, and we agree */
1085 *out
= null_data_blob
;
1087 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
1088 /* unless of course it did not accept */
1089 DEBUG(1,("gensec_update ok but not accepted\n"));
1090 nt_status
= NT_STATUS_INVALID_PARAMETER
;
1093 spnego_state
->state_position
= SPNEGO_DONE
;
1099 /* We should not be called after we are 'done' */
1100 return NT_STATUS_INVALID_PARAMETER
;
1102 return NT_STATUS_INVALID_PARAMETER
;
1105 static void gensec_spnego_want_feature(struct gensec_security
*gensec_security
,
1108 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1110 if (!spnego_state
|| !spnego_state
->sub_sec_security
) {
1111 gensec_security
->want_features
|= feature
;
1115 gensec_want_feature(spnego_state
->sub_sec_security
,
1119 static bool gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1122 struct spnego_state
*spnego_state
= (struct spnego_state
*)gensec_security
->private_data
;
1123 if (!spnego_state
->sub_sec_security
) {
1127 return gensec_have_feature(spnego_state
->sub_sec_security
,
1131 static const char *gensec_spnego_oids
[] = {
1136 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1138 .sasl_name
= "GSS-SPNEGO",
1139 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1140 .oid
= gensec_spnego_oids
,
1141 .client_start
= gensec_spnego_client_start
,
1142 .server_start
= gensec_spnego_server_start
,
1143 .update
= gensec_spnego_update
,
1144 .seal_packet
= gensec_spnego_seal_packet
,
1145 .sign_packet
= gensec_spnego_sign_packet
,
1146 .sig_size
= gensec_spnego_sig_size
,
1147 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1148 .max_input_size
= gensec_spnego_max_input_size
,
1149 .check_packet
= gensec_spnego_check_packet
,
1150 .unseal_packet
= gensec_spnego_unseal_packet
,
1151 .packet_full_request
= gensec_spnego_packet_full_request
,
1152 .wrap
= gensec_spnego_wrap
,
1153 .unwrap
= gensec_spnego_unwrap
,
1154 .wrap_packets
= gensec_spnego_wrap_packets
,
1155 .unwrap_packets
= gensec_spnego_unwrap_packets
,
1156 .session_key
= gensec_spnego_session_key
,
1157 .session_info
= gensec_spnego_session_info
,
1158 .want_feature
= gensec_spnego_want_feature
,
1159 .have_feature
= gensec_spnego_have_feature
,
1161 .priority
= GENSEC_SPNEGO
1164 _PUBLIC_ NTSTATUS
gensec_spnego_init(void)
1167 ret
= gensec_register(&gensec_spnego_security_ops
);
1168 if (!NT_STATUS_IS_OK(ret
)) {
1169 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1170 gensec_spnego_security_ops
.name
));