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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "auth/gensec/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/socket.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
;
51 static NTSTATUS
gensec_spnego_client_start(struct gensec_security
*gensec_security
)
53 struct spnego_state
*spnego_state
;
55 spnego_state
= talloc(gensec_security
, struct spnego_state
);
57 return NT_STATUS_NO_MEMORY
;
60 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
61 spnego_state
->state_position
= SPNEGO_CLIENT_START
;
62 spnego_state
->sub_sec_security
= NULL
;
63 spnego_state
->no_response_expected
= False
;
65 gensec_security
->private_data
= spnego_state
;
69 static NTSTATUS
gensec_spnego_server_start(struct gensec_security
*gensec_security
)
71 struct spnego_state
*spnego_state
;
73 spnego_state
= talloc(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_SERVER_START
;
80 spnego_state
->sub_sec_security
= NULL
;
81 spnego_state
->no_response_expected
= False
;
83 gensec_security
->private_data
= spnego_state
;
88 wrappers for the spnego_*() functions
90 static NTSTATUS
gensec_spnego_unseal_packet(struct gensec_security
*gensec_security
,
92 uint8_t *data
, size_t length
,
93 const uint8_t *whole_pdu
, size_t pdu_length
,
96 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
98 if (spnego_state
->state_position
!= SPNEGO_DONE
99 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
100 return NT_STATUS_INVALID_PARAMETER
;
103 return gensec_unseal_packet(spnego_state
->sub_sec_security
,
106 whole_pdu
, pdu_length
,
110 static NTSTATUS
gensec_spnego_check_packet(struct gensec_security
*gensec_security
,
112 const uint8_t *data
, size_t length
,
113 const uint8_t *whole_pdu
, size_t pdu_length
,
114 const DATA_BLOB
*sig
)
116 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
118 if (spnego_state
->state_position
!= SPNEGO_DONE
119 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
120 return NT_STATUS_INVALID_PARAMETER
;
123 return gensec_check_packet(spnego_state
->sub_sec_security
,
126 whole_pdu
, pdu_length
,
130 static NTSTATUS
gensec_spnego_seal_packet(struct gensec_security
*gensec_security
,
132 uint8_t *data
, size_t length
,
133 const uint8_t *whole_pdu
, size_t pdu_length
,
136 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
138 if (spnego_state
->state_position
!= SPNEGO_DONE
139 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
140 return NT_STATUS_INVALID_PARAMETER
;
143 return gensec_seal_packet(spnego_state
->sub_sec_security
,
146 whole_pdu
, pdu_length
,
150 static NTSTATUS
gensec_spnego_sign_packet(struct gensec_security
*gensec_security
,
152 const uint8_t *data
, size_t length
,
153 const uint8_t *whole_pdu
, size_t pdu_length
,
156 struct spnego_state
*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_sign_packet(spnego_state
->sub_sec_security
,
166 whole_pdu
, pdu_length
,
170 static NTSTATUS
gensec_spnego_wrap(struct gensec_security
*gensec_security
,
175 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
177 if (spnego_state
->state_position
!= SPNEGO_DONE
178 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
179 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
180 return NT_STATUS_INVALID_PARAMETER
;
183 return gensec_wrap(spnego_state
->sub_sec_security
,
187 static NTSTATUS
gensec_spnego_unwrap(struct gensec_security
*gensec_security
,
192 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
194 if (spnego_state
->state_position
!= SPNEGO_DONE
195 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
196 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
197 return NT_STATUS_INVALID_PARAMETER
;
200 return gensec_unwrap(spnego_state
->sub_sec_security
,
204 static NTSTATUS
gensec_spnego_wrap_packets(struct gensec_security
*gensec_security
,
208 size_t *len_processed
)
210 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
212 if (spnego_state
->state_position
!= SPNEGO_DONE
213 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
214 DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
215 return NT_STATUS_INVALID_PARAMETER
;
218 return gensec_wrap_packets(spnego_state
->sub_sec_security
,
223 static NTSTATUS
gensec_spnego_packet_full_request(struct gensec_security
*gensec_security
,
224 DATA_BLOB blob
, size_t *size
)
226 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
228 if (spnego_state
->state_position
!= SPNEGO_DONE
229 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
230 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
231 return NT_STATUS_INVALID_PARAMETER
;
234 return gensec_packet_full_request(spnego_state
->sub_sec_security
,
238 static NTSTATUS
gensec_spnego_unwrap_packets(struct gensec_security
*gensec_security
,
242 size_t *len_processed
)
244 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
246 if (spnego_state
->state_position
!= SPNEGO_DONE
247 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
248 DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
249 return NT_STATUS_INVALID_PARAMETER
;
252 return gensec_unwrap_packets(spnego_state
->sub_sec_security
,
257 static size_t gensec_spnego_sig_size(struct gensec_security
*gensec_security
, size_t data_size
)
259 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
261 if (spnego_state
->state_position
!= SPNEGO_DONE
262 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
266 return gensec_sig_size(spnego_state
->sub_sec_security
, data_size
);
269 static size_t gensec_spnego_max_input_size(struct gensec_security
*gensec_security
)
271 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
273 if (spnego_state
->state_position
!= SPNEGO_DONE
274 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
278 return gensec_max_input_size(spnego_state
->sub_sec_security
);
281 static size_t gensec_spnego_max_wrapped_size(struct gensec_security
*gensec_security
)
283 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
285 if (spnego_state
->state_position
!= SPNEGO_DONE
286 && spnego_state
->state_position
!= SPNEGO_FALLBACK
) {
290 return gensec_max_wrapped_size(spnego_state
->sub_sec_security
);
293 static NTSTATUS
gensec_spnego_session_key(struct gensec_security
*gensec_security
,
294 DATA_BLOB
*session_key
)
296 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
297 if (!spnego_state
->sub_sec_security
) {
298 return NT_STATUS_INVALID_PARAMETER
;
301 return gensec_session_key(spnego_state
->sub_sec_security
,
305 static NTSTATUS
gensec_spnego_session_info(struct gensec_security
*gensec_security
,
306 struct auth_session_info
**session_info
)
308 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
309 if (!spnego_state
->sub_sec_security
) {
310 return NT_STATUS_INVALID_PARAMETER
;
313 return gensec_session_info(spnego_state
->sub_sec_security
,
317 /** Fallback to another GENSEC mechanism, based on magic strings
319 * This is the 'fallback' case, where we don't get SPNEGO, and have to
320 * try all the other options (and hope they all have a magic string
324 static NTSTATUS
gensec_spnego_server_try_fallback(struct gensec_security
*gensec_security
,
325 struct spnego_state
*spnego_state
,
326 TALLOC_CTX
*out_mem_ctx
,
327 const DATA_BLOB in
, DATA_BLOB
*out
)
330 struct gensec_security_ops
**all_ops
331 = gensec_security_mechs(gensec_security
, out_mem_ctx
);
332 for (i
=0; all_ops
[i
]; i
++) {
335 if (!all_ops
[i
]->oid
) {
340 for (j
=0; all_ops
[i
]->oid
[j
]; j
++) {
341 if (strcasecmp(GENSEC_OID_SPNEGO
,all_ops
[i
]->oid
[j
]) == 0) {
349 if (!all_ops
[i
]->magic
) {
353 nt_status
= all_ops
[i
]->magic(gensec_security
, &in
);
354 if (!NT_STATUS_IS_OK(nt_status
)) {
358 spnego_state
->state_position
= SPNEGO_FALLBACK
;
360 nt_status
= gensec_subcontext_start(spnego_state
,
362 &spnego_state
->sub_sec_security
);
364 if (!NT_STATUS_IS_OK(nt_status
)) {
367 /* select the sub context */
368 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
370 if (!NT_STATUS_IS_OK(nt_status
)) {
373 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
374 out_mem_ctx
, in
, out
);
377 DEBUG(1, ("Failed to parse SPNEGO request\n"));
378 return NT_STATUS_INVALID_PARAMETER
;
383 Parse the netTokenInit, either from the client, to the server, or
384 from the server to the client.
387 static NTSTATUS
gensec_spnego_parse_negTokenInit(struct gensec_security
*gensec_security
,
388 struct spnego_state
*spnego_state
,
389 TALLOC_CTX
*out_mem_ctx
,
390 const char **mechType
,
391 const DATA_BLOB unwrapped_in
, DATA_BLOB
*unwrapped_out
)
394 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
395 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
397 const struct gensec_security_ops_wrapper
*all_sec
398 = gensec_security_by_oid_list(gensec_security
,
402 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
403 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
404 /* optomisitic token */
405 if (strcmp(all_sec
[i
].oid
, mechType
[0]) == 0) {
406 nt_status
= gensec_subcontext_start(spnego_state
,
408 &spnego_state
->sub_sec_security
);
409 if (!NT_STATUS_IS_OK(nt_status
)) {
412 /* select the sub context */
413 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
415 if (!NT_STATUS_IS_OK(nt_status
)) {
416 talloc_free(spnego_state
->sub_sec_security
);
417 spnego_state
->sub_sec_security
= NULL
;
421 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
425 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
426 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
427 /* Pretend we never started it (lets the first run find some incompatible demand) */
429 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
430 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
431 talloc_free(spnego_state
->sub_sec_security
);
432 spnego_state
->sub_sec_security
= NULL
;
436 spnego_state
->neg_oid
= all_sec
[i
].oid
;
442 /* Having tried any optomisitc token from the client (if we
443 * were the server), if we didn't get anywhere, walk our list
444 * in our preference order */
446 if (!spnego_state
->sub_sec_security
) {
447 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
448 nt_status
= gensec_subcontext_start(spnego_state
,
450 &spnego_state
->sub_sec_security
);
451 if (!NT_STATUS_IS_OK(nt_status
)) {
454 /* select the sub context */
455 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
457 if (!NT_STATUS_IS_OK(nt_status
)) {
458 talloc_free(spnego_state
->sub_sec_security
);
459 spnego_state
->sub_sec_security
= NULL
;
463 spnego_state
->neg_oid
= all_sec
[i
].oid
;
465 /* only get the helping start blob for the first OID */
466 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
471 /* it is likely that a NULL input token will
472 * not be liked by most server mechs, but if
473 * we are in the client, we want the first
474 * update packet to be able to abort the use
476 if (spnego_state
->state_position
!= SPNEGO_SERVER_START
) {
477 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
) ||
478 NT_STATUS_EQUAL(nt_status
, NT_STATUS_CANT_ACCESS_DOMAIN_INFO
)) {
479 /* Pretend we never started it (lets the first run find some incompatible demand) */
481 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n",
482 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
483 talloc_free(spnego_state
->sub_sec_security
);
484 spnego_state
->sub_sec_security
= NULL
;
493 if (spnego_state
->sub_sec_security
) {
494 /* it is likely that a NULL input token will
495 * not be liked by most server mechs, but this
496 * does the right thing in the CIFS client.
497 * just push us along the merry-go-round
498 * again, and hope for better luck next
501 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)) {
502 *unwrapped_out
= data_blob(NULL
, 0);
503 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
506 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_INVALID_PARAMETER
)
507 && !NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
508 && !NT_STATUS_IS_OK(nt_status
)) {
509 DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
510 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
511 talloc_free(spnego_state
->sub_sec_security
);
512 spnego_state
->sub_sec_security
= NULL
;
514 /* We started the mech correctly, and the
515 * input from the other side was valid.
516 * Return the error (say bad password, invalid
522 return nt_status
; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
525 DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
526 /* we could re-negotiate here, but it would only work
527 * if the client or server lied about what it could
528 * support the first time. Lets keep this code to
531 return NT_STATUS_INVALID_PARAMETER
;
534 /** create a negTokenInit
536 * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
538 static NTSTATUS
gensec_spnego_create_negTokenInit(struct gensec_security
*gensec_security
,
539 struct spnego_state
*spnego_state
,
540 TALLOC_CTX
*out_mem_ctx
,
541 const DATA_BLOB in
, DATA_BLOB
*out
)
544 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
545 DATA_BLOB null_data_blob
= data_blob(NULL
,0);
546 const char **mechTypes
= NULL
;
547 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
548 const struct gensec_security_ops_wrapper
*all_sec
;
549 const char *principal
= NULL
;
551 mechTypes
= gensec_security_oids(gensec_security
,
552 out_mem_ctx
, GENSEC_OID_SPNEGO
);
554 all_sec
= gensec_security_by_oid_list(gensec_security
,
558 for (i
=0; all_sec
&& all_sec
[i
].op
; i
++) {
559 struct spnego_data spnego_out
;
560 nt_status
= gensec_subcontext_start(spnego_state
,
562 &spnego_state
->sub_sec_security
);
563 if (!NT_STATUS_IS_OK(nt_status
)) {
566 /* select the sub context */
567 nt_status
= gensec_start_mech_by_ops(spnego_state
->sub_sec_security
,
569 if (!NT_STATUS_IS_OK(nt_status
)) {
570 talloc_free(spnego_state
->sub_sec_security
);
571 spnego_state
->sub_sec_security
= NULL
;
575 /* In the client, try and produce the first (optimistic) packet */
576 if (spnego_state
->state_position
== SPNEGO_CLIENT_START
) {
577 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
582 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
583 && !NT_STATUS_IS_OK(nt_status
)) {
584 DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
585 spnego_state
->sub_sec_security
->ops
->name
, nt_errstr(nt_status
)));
586 talloc_free(spnego_state
->sub_sec_security
);
587 spnego_state
->sub_sec_security
= NULL
;
588 /* Pretend we never started it (lets the first run find some incompatible demand) */
594 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
596 /* List the remaining mechs as options */
597 spnego_out
.negTokenInit
.mechTypes
= gensec_security_oids_from_ops_wrapped(out_mem_ctx
,
599 spnego_out
.negTokenInit
.reqFlags
= 0;
601 if (spnego_state
->state_position
== SPNEGO_SERVER_START
) {
602 /* server credentails */
603 struct cli_credentials
*creds
= gensec_get_credentials(gensec_security
);
605 principal
= cli_credentials_get_principal(creds
, out_mem_ctx
);
609 spnego_out
.negTokenInit
.mechListMIC
610 = data_blob_string_const(principal
);
612 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
615 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
617 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
618 DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
619 return NT_STATUS_INVALID_PARAMETER
;
623 spnego_state
->neg_oid
= all_sec
[i
].oid
;
625 if (NT_STATUS_IS_OK(nt_status
)) {
626 spnego_state
->no_response_expected
= True
;
629 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
631 talloc_free(spnego_state
->sub_sec_security
);
632 spnego_state
->sub_sec_security
= NULL
;
634 DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status
)));
635 return NT_STATUS_INVALID_PARAMETER
;
639 /** create a server negTokenTarg
641 * This is the case, where the client is the first one who sends data
644 static NTSTATUS
gensec_spnego_server_negTokenTarg(struct gensec_security
*gensec_security
,
645 struct spnego_state
*spnego_state
,
646 TALLOC_CTX
*out_mem_ctx
,
648 const DATA_BLOB unwrapped_out
, DATA_BLOB
*out
)
650 struct spnego_data spnego_out
;
651 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
654 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
655 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
656 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
657 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
659 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
660 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
661 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_INCOMPLETE
;
662 spnego_state
->state_position
= SPNEGO_SERVER_TARG
;
663 } else if (NT_STATUS_IS_OK(nt_status
)) {
664 if (unwrapped_out
.data
) {
665 spnego_out
.negTokenTarg
.supportedMech
= spnego_state
->neg_oid
;
667 spnego_out
.negTokenTarg
.negResult
= SPNEGO_ACCEPT_COMPLETED
;
668 spnego_state
->state_position
= SPNEGO_DONE
;
670 spnego_out
.negTokenTarg
.negResult
= SPNEGO_REJECT
;
671 DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status
)));
672 spnego_state
->state_position
= SPNEGO_DONE
;
675 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
676 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
677 return NT_STATUS_INVALID_PARAMETER
;
680 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
686 static NTSTATUS
gensec_spnego_update(struct gensec_security
*gensec_security
, TALLOC_CTX
*out_mem_ctx
,
687 const DATA_BLOB in
, DATA_BLOB
*out
)
689 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
690 DATA_BLOB null_data_blob
= data_blob(NULL
, 0);
691 DATA_BLOB unwrapped_out
= data_blob(NULL
, 0);
692 struct spnego_data spnego_out
;
693 struct spnego_data spnego
;
697 *out
= data_blob(NULL
, 0);
700 out_mem_ctx
= spnego_state
;
703 /* and switch into the state machine */
705 switch (spnego_state
->state_position
) {
706 case SPNEGO_FALLBACK
:
707 return gensec_update(spnego_state
->sub_sec_security
,
708 out_mem_ctx
, in
, out
);
709 case SPNEGO_SERVER_START
:
714 len
= spnego_read_data(in
, &spnego
);
716 return gensec_spnego_server_try_fallback(gensec_security
, spnego_state
,
717 out_mem_ctx
, in
, out
);
719 /* client sent NegTargetInit, we send NegTokenTarg */
721 /* OK, so it's real SPNEGO, check the packet's the one we expect */
722 if (spnego
.type
!= spnego_state
->expected_packet
) {
723 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
724 spnego_state
->expected_packet
));
725 dump_data(1, in
.data
, in
.length
);
726 spnego_free_data(&spnego
);
727 return NT_STATUS_INVALID_PARAMETER
;
730 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
733 spnego
.negTokenInit
.mechTypes
,
734 spnego
.negTokenInit
.mechToken
,
737 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
744 spnego_free_data(&spnego
);
748 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
749 out_mem_ctx
, in
, out
);
750 spnego_state
->state_position
= SPNEGO_SERVER_START
;
751 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_INIT
;
756 case SPNEGO_CLIENT_START
:
758 /* The server offers a list of mechanisms */
760 const char *my_mechs
[] = {NULL
, NULL
};
761 NTSTATUS nt_status
= NT_STATUS_INVALID_PARAMETER
;
764 /* client to produce negTokenInit */
765 nt_status
= gensec_spnego_create_negTokenInit(gensec_security
, spnego_state
,
766 out_mem_ctx
, in
, out
);
767 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
768 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
772 len
= spnego_read_data(in
, &spnego
);
775 DEBUG(1, ("Invalid SPNEGO request:\n"));
776 dump_data(1, in
.data
, in
.length
);
777 return NT_STATUS_INVALID_PARAMETER
;
780 /* OK, so it's real SPNEGO, check the packet's the one we expect */
781 if (spnego
.type
!= spnego_state
->expected_packet
) {
782 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
783 spnego_state
->expected_packet
));
784 dump_data(1, in
.data
, in
.length
);
785 spnego_free_data(&spnego
);
786 return NT_STATUS_INVALID_PARAMETER
;
789 if (spnego
.negTokenInit
.targetPrincipal
) {
790 DEBUG(5, ("Server claims it's principal name is %s\n", spnego
.negTokenInit
.targetPrincipal
));
791 gensec_set_target_principal(gensec_security
, spnego
.negTokenInit
.targetPrincipal
);
794 nt_status
= gensec_spnego_parse_negTokenInit(gensec_security
,
797 spnego
.negTokenInit
.mechTypes
,
798 spnego
.negTokenInit
.mechToken
,
801 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
) && !NT_STATUS_IS_OK(nt_status
)) {
802 spnego_free_data(&spnego
);
806 my_mechs
[0] = spnego_state
->neg_oid
;
808 spnego_out
.type
= SPNEGO_NEG_TOKEN_INIT
;
809 spnego_out
.negTokenInit
.mechTypes
= my_mechs
;
810 spnego_out
.negTokenInit
.reqFlags
= 0;
811 spnego_out
.negTokenInit
.mechListMIC
= null_data_blob
;
812 spnego_out
.negTokenInit
.mechToken
= unwrapped_out
;
814 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
815 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
816 return NT_STATUS_INVALID_PARAMETER
;
820 spnego_state
->expected_packet
= SPNEGO_NEG_TOKEN_TARG
;
821 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
823 if (NT_STATUS_IS_OK(nt_status
)) {
824 spnego_state
->no_response_expected
= True
;
827 spnego_free_data(&spnego
);
828 return NT_STATUS_MORE_PROCESSING_REQUIRED
;
830 case SPNEGO_SERVER_TARG
:
834 return NT_STATUS_INVALID_PARAMETER
;
837 len
= spnego_read_data(in
, &spnego
);
840 DEBUG(1, ("Invalid SPNEGO request:\n"));
841 dump_data(1, in
.data
, in
.length
);
842 return NT_STATUS_INVALID_PARAMETER
;
845 /* OK, so it's real SPNEGO, check the packet's the one we expect */
846 if (spnego
.type
!= spnego_state
->expected_packet
) {
847 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
848 spnego_state
->expected_packet
));
849 dump_data(1, in
.data
, in
.length
);
850 spnego_free_data(&spnego
);
851 return NT_STATUS_INVALID_PARAMETER
;
854 if (!spnego_state
->sub_sec_security
) {
855 DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
856 spnego_free_data(&spnego
);
857 return NT_STATUS_INVALID_PARAMETER
;
860 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
862 spnego
.negTokenTarg
.responseToken
,
865 nt_status
= gensec_spnego_server_negTokenTarg(gensec_security
,
872 spnego_free_data(&spnego
);
876 case SPNEGO_CLIENT_TARG
:
880 return NT_STATUS_INVALID_PARAMETER
;
883 len
= spnego_read_data(in
, &spnego
);
886 DEBUG(1, ("Invalid SPNEGO request:\n"));
887 dump_data(1, in
.data
, in
.length
);
888 return NT_STATUS_INVALID_PARAMETER
;
891 /* OK, so it's real SPNEGO, check the packet's the one we expect */
892 if (spnego
.type
!= spnego_state
->expected_packet
) {
893 DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego
.type
,
894 spnego_state
->expected_packet
));
895 dump_data(1, in
.data
, in
.length
);
896 spnego_free_data(&spnego
);
897 return NT_STATUS_INVALID_PARAMETER
;
900 if (spnego
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
901 spnego_free_data(&spnego
);
902 return NT_STATUS_ACCESS_DENIED
;
905 /* Server didn't like our choice of mech, and chose something else */
906 if ((spnego
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_INCOMPLETE
) &&
907 spnego
.negTokenTarg
.supportedMech
&&
908 strcmp(spnego
.negTokenTarg
.supportedMech
, spnego_state
->neg_oid
) != 0) {
909 DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
910 gensec_get_name_by_oid(spnego
.negTokenTarg
.supportedMech
),
911 gensec_get_name_by_oid(spnego_state
->neg_oid
)));
913 talloc_free(spnego_state
->sub_sec_security
);
914 nt_status
= gensec_subcontext_start(spnego_state
,
916 &spnego_state
->sub_sec_security
);
917 if (!NT_STATUS_IS_OK(nt_status
)) {
918 spnego_free_data(&spnego
);
921 /* select the sub context */
922 nt_status
= gensec_start_mech_by_oid(spnego_state
->sub_sec_security
,
923 spnego
.negTokenTarg
.supportedMech
);
924 if (!NT_STATUS_IS_OK(nt_status
)) {
925 spnego_free_data(&spnego
);
929 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
931 spnego
.negTokenTarg
.responseToken
,
933 spnego_state
->neg_oid
= talloc_strdup(spnego_state
, spnego
.negTokenTarg
.supportedMech
);
934 } else if (spnego_state
->no_response_expected
) {
935 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
936 DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
937 nt_status
= NT_STATUS_INVALID_PARAMETER
;
938 } else if (spnego
.negTokenTarg
.responseToken
.length
) {
939 DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
940 nt_status
= NT_STATUS_INVALID_PARAMETER
;
942 nt_status
= NT_STATUS_OK
;
945 nt_status
= gensec_update(spnego_state
->sub_sec_security
,
947 spnego
.negTokenTarg
.responseToken
,
950 if (NT_STATUS_IS_OK(nt_status
)) {
951 spnego_state
->no_response_expected
= True
;
955 spnego_free_data(&spnego
);
957 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)
958 && !NT_STATUS_IS_OK(nt_status
)) {
959 DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
960 spnego_state
->sub_sec_security
->ops
->name
,
961 nt_errstr(nt_status
)));
965 if (unwrapped_out
.length
) {
967 spnego_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
968 spnego_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
969 spnego_out
.negTokenTarg
.supportedMech
= NULL
;
970 spnego_out
.negTokenTarg
.responseToken
= unwrapped_out
;
971 spnego_out
.negTokenTarg
.mechListMIC
= null_data_blob
;
973 if (spnego_write_data(out_mem_ctx
, out
, &spnego_out
) == -1) {
974 DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
975 return NT_STATUS_INVALID_PARAMETER
;
978 spnego_state
->state_position
= SPNEGO_CLIENT_TARG
;
979 nt_status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
982 /* all done - server has accepted, and we agree */
983 *out
= null_data_blob
;
985 if (spnego
.negTokenTarg
.negResult
!= SPNEGO_ACCEPT_COMPLETED
) {
986 /* unless of course it did not accept */
987 DEBUG(1,("gensec_update ok but not accepted\n"));
988 nt_status
= NT_STATUS_INVALID_PARAMETER
;
991 spnego_state
->state_position
= SPNEGO_DONE
;
997 /* We should not be called after we are 'done' */
998 return NT_STATUS_INVALID_PARAMETER
;
1000 return NT_STATUS_INVALID_PARAMETER
;
1003 static BOOL
gensec_spnego_have_feature(struct gensec_security
*gensec_security
,
1006 struct spnego_state
*spnego_state
= gensec_security
->private_data
;
1007 if (!spnego_state
->sub_sec_security
) {
1011 return gensec_have_feature(spnego_state
->sub_sec_security
,
1015 static const char *gensec_spnego_oids
[] = {
1020 static const struct gensec_security_ops gensec_spnego_security_ops
= {
1022 .sasl_name
= "GSS-SPNEGO",
1023 .auth_type
= DCERPC_AUTH_TYPE_SPNEGO
,
1024 .oid
= gensec_spnego_oids
,
1025 .client_start
= gensec_spnego_client_start
,
1026 .server_start
= gensec_spnego_server_start
,
1027 .update
= gensec_spnego_update
,
1028 .seal_packet
= gensec_spnego_seal_packet
,
1029 .sign_packet
= gensec_spnego_sign_packet
,
1030 .sig_size
= gensec_spnego_sig_size
,
1031 .max_wrapped_size
= gensec_spnego_max_wrapped_size
,
1032 .max_input_size
= gensec_spnego_max_input_size
,
1033 .check_packet
= gensec_spnego_check_packet
,
1034 .unseal_packet
= gensec_spnego_unseal_packet
,
1035 .packet_full_request
= gensec_spnego_packet_full_request
,
1036 .wrap
= gensec_spnego_wrap
,
1037 .unwrap
= gensec_spnego_unwrap
,
1038 .wrap_packets
= gensec_spnego_wrap_packets
,
1039 .unwrap_packets
= gensec_spnego_unwrap_packets
,
1040 .session_key
= gensec_spnego_session_key
,
1041 .session_info
= gensec_spnego_session_info
,
1042 .have_feature
= gensec_spnego_have_feature
,
1044 .priority
= GENSEC_SPNEGO
1047 NTSTATUS
gensec_spnego_init(void)
1050 ret
= gensec_register(&gensec_spnego_security_ops
);
1051 if (!NT_STATUS_IS_OK(ret
)) {
1052 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1053 gensec_spnego_security_ops
.name
));