2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
5 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
30 * A module that implements the spnego security mechanism.
31 * It is used to negotiate the security mechanism between
32 * peers using the GSS-API.
37 * Copyright (c) 2006-2008, Novell, Inc.
38 * All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions are met:
43 * * Redistributions of source code must retain the above copyright notice,
44 * this list of conditions and the following disclaimer.
45 * * Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * * The copyright holder's name is not used to endorse or promote products
49 * derived from this software without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
55 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61 * POSSIBILITY OF SUCH DAMAGE.
63 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
65 #include <sys/param.h>
74 #include "gssapiP_spnego.h"
75 #include "gssapiP_generic.h"
76 #include <gssapi_err_generic.h>
81 * MIT has diff names for these GSS utilities. Solaris needs to change
82 * them globally to get in sync w/MIT.
83 * Revisit for full 1.7 resync.
85 #define gssint_get_modOptions __gss_get_modOptions
86 #define gssint_der_length_size der_length_size
87 #define gssint_get_der_length get_der_length
88 #define gssint_put_der_length put_der_length
89 #define gssint_get_mechanism __gss_get_mechanism
90 #define gssint_copy_oid_set gss_copy_oid_set
91 #define gssint_get_mech_type __gss_get_mech_type
95 #undef g_verify_token_header
96 #undef g_make_token_header
98 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
99 typedef const gss_OID_desc
*gss_OID_const
;
101 /* der routines defined in libgss */
102 extern unsigned int gssint_der_length_size(OM_uint32
);
103 extern int gssint_get_der_length(unsigned char **, OM_uint32
, OM_uint32
*);
104 extern int gssint_put_der_length(OM_uint32
, unsigned char **, OM_uint32
);
107 /* private routines for spnego_mechanism */
108 static spnego_token_t
make_spnego_token(char *);
109 static gss_buffer_desc
make_err_msg(char *);
110 static int g_token_size(gss_OID_const
, unsigned int);
111 static int g_make_token_header(gss_OID_const
, unsigned int,
112 unsigned char **, unsigned int);
113 static int g_verify_token_header(gss_OID_const
, unsigned int *,
116 static int g_verify_neg_token_init(unsigned char **, unsigned int);
117 static gss_OID
get_mech_oid(OM_uint32
*, unsigned char **, size_t);
118 static gss_buffer_t
get_input_token(unsigned char **, unsigned int);
119 static gss_OID_set
get_mech_set(OM_uint32
*, unsigned char **, unsigned int);
120 static OM_uint32
get_req_flags(unsigned char **, OM_uint32
, OM_uint32
*);
121 static OM_uint32
get_available_mechs(OM_uint32
*, gss_name_t
,
122 gss_cred_usage_t
, gss_cred_id_t
*, gss_OID_set
*);
123 static void release_spnego_ctx(spnego_gss_ctx_id_t
*);
124 static void check_spnego_options(spnego_gss_ctx_id_t
);
125 static spnego_gss_ctx_id_t
create_spnego_ctx(void);
126 static int put_mech_set(gss_OID_set mechSet
, gss_buffer_t buf
);
127 static int put_input_token(unsigned char **, gss_buffer_t
, unsigned int);
128 static int put_mech_oid(unsigned char **, gss_OID_const
, unsigned int);
129 static int put_negResult(unsigned char **, OM_uint32
, unsigned int);
132 process_mic(OM_uint32
*, gss_buffer_t
, spnego_gss_ctx_id_t
,
133 gss_buffer_t
*, OM_uint32
*, send_token_flag
*);
135 handle_mic(OM_uint32
*, gss_buffer_t
, int, spnego_gss_ctx_id_t
,
136 gss_buffer_t
*, OM_uint32
*, send_token_flag
*);
139 init_ctx_new(OM_uint32
*, gss_cred_id_t
, gss_ctx_id_t
*,
140 gss_OID_set
*, send_token_flag
*);
142 init_ctx_nego(OM_uint32
*, spnego_gss_ctx_id_t
, OM_uint32
, gss_OID
,
143 gss_buffer_t
*, gss_buffer_t
*,
144 OM_uint32
*, send_token_flag
*);
146 init_ctx_cont(OM_uint32
*, gss_ctx_id_t
*, gss_buffer_t
,
147 gss_buffer_t
*, gss_buffer_t
*,
148 OM_uint32
*, send_token_flag
*);
150 init_ctx_reselect(OM_uint32
*, spnego_gss_ctx_id_t
, OM_uint32
,
151 gss_OID
, gss_buffer_t
*, gss_buffer_t
*,
152 OM_uint32
*, send_token_flag
*);
154 init_ctx_call_init(OM_uint32
*, spnego_gss_ctx_id_t
, gss_cred_id_t
,
155 gss_name_t
, OM_uint32
, OM_uint32
, gss_buffer_t
,
156 gss_OID
*, gss_buffer_t
, OM_uint32
*, OM_uint32
*,
157 OM_uint32
*, send_token_flag
*);
160 acc_ctx_new(OM_uint32
*, gss_buffer_t
, gss_ctx_id_t
*,
161 gss_cred_id_t
, gss_buffer_t
*,
162 gss_buffer_t
*, OM_uint32
*, send_token_flag
*);
164 acc_ctx_cont(OM_uint32
*, gss_buffer_t
, gss_ctx_id_t
*,
165 gss_buffer_t
*, gss_buffer_t
*,
166 OM_uint32
*, send_token_flag
*);
168 acc_ctx_vfy_oid(OM_uint32
*, spnego_gss_ctx_id_t
, gss_OID
,
169 OM_uint32
*, send_token_flag
*);
171 acc_ctx_call_acc(OM_uint32
*, spnego_gss_ctx_id_t
, gss_cred_id_t
,
172 gss_buffer_t
, gss_OID
*, gss_buffer_t
,
173 OM_uint32
*, OM_uint32
*, gss_cred_id_t
*,
174 OM_uint32
*, send_token_flag
*);
177 negotiate_mech_type(OM_uint32
*, gss_OID_set
, gss_OID_set
,
180 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
183 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t
,
186 OM_uint32
, gss_buffer_t
, send_token_flag
,
189 make_spnego_tokenTarg_msg(OM_uint32
, gss_OID
, gss_buffer_t
,
190 gss_buffer_t
, send_token_flag
,
194 get_negTokenInit(OM_uint32
*, gss_buffer_t
, gss_buffer_t
,
195 gss_OID_set
*, OM_uint32
*, gss_buffer_t
*,
198 get_negTokenResp(OM_uint32
*, unsigned char *, unsigned int,
199 OM_uint32
*, gss_OID
*, gss_buffer_t
*, gss_buffer_t
*);
202 is_kerb_mech(gss_OID oid
);
204 /* SPNEGO oid structure */
205 static const gss_OID_desc spnego_oids
[] = {
206 {SPNEGO_OID_LENGTH
, SPNEGO_OID
},
209 const gss_OID_desc
* const gss_mech_spnego
= spnego_oids
+0;
210 static const gss_OID_set_desc spnego_oidsets
[] = {
211 {1, (gss_OID
) spnego_oids
+0},
213 const gss_OID_set_desc
* const gss_mech_set_spnego
= spnego_oidsets
+0;
215 static int make_NegHints(OM_uint32
*, gss_cred_id_t
, gss_buffer_t
*);
216 static int put_neg_hints(unsigned char **, gss_buffer_t
, unsigned int);
218 acc_ctx_hints(OM_uint32
*, gss_ctx_id_t
*, gss_cred_id_t
,
219 gss_buffer_t
*, OM_uint32
*, send_token_flag
*);
221 #ifdef _GSS_STATIC_LINK
222 int gss_spnegoint_lib_init(void);
223 void gss_spnegoint_lib_fini(void);
225 gss_mechanism
gss_mech_initialize(void);
226 #endif /* _GSS_STATIC_LINK */
229 * The Mech OID for SPNEGO:
230 * { iso(1) org(3) dod(6) internet(1) security(5)
231 * mechanism(5) spnego(2) }
233 static struct gss_config spnego_mechanism
=
235 {SPNEGO_OID_LENGTH
, SPNEGO_OID
},
237 glue_spnego_gss_acquire_cred
,
238 glue_spnego_gss_release_cred
,
239 glue_spnego_gss_init_sec_context
,
241 glue_spnego_gss_accept_sec_context
,
244 #endif /* LEAN_CLIENT */
246 NULL
, /* gss_process_context_token */
247 glue_spnego_gss_delete_sec_context
, /* gss_delete_sec_context */
248 glue_spnego_gss_context_time
,
249 glue_spnego_gss_display_status
,
250 NULL
, /* gss_indicate_mechs */
251 glue_spnego_gss_compare_name
,
252 glue_spnego_gss_display_name
,
253 glue_spnego_gss_import_name
, /* glue */
254 glue_spnego_gss_release_name
,
255 NULL
, /* gss_inquire_cred */
256 NULL
, /* gss_add_cred */
259 glue_spnego_gss_export_sec_context
, /* gss_export_sec_context */
260 glue_spnego_gss_import_sec_context
, /* gss_import_sec_context */
262 NULL
, /* gss_export_sec_context */
263 NULL
, /* gss_import_sec_context */
264 #endif /* LEAN_CLIENT */
265 NULL
, /* gss_inquire_cred_by_mech */
266 glue_spnego_gss_inquire_names_for_mech
,
267 glue_spnego_gss_inquire_context
,
268 NULL
, /* gss_internal_release_oid */
269 glue_spnego_gss_wrap_size_limit
,
272 NULL
, /* gss_export_name */
275 NULL
, /* gss_store_cred */
276 spnego_gss_inquire_sec_context_by_oid
, /* gss_inquire_sec_context_by_oid */
279 #ifdef _GSS_STATIC_LINK
283 int gss_spnegomechglue_init(void)
285 struct gss_mech_config mech_spnego
;
287 memset(&mech_spnego
, 0, sizeof(mech_spnego
));
288 mech_spnego
.mech
= &spnego_mechanism
;
289 mech_spnego
.mechNameStr
= "spnego";
290 mech_spnego
.mech_type
= GSS_C_NO_OID
;
292 return gssint_register_mechinfo(&mech_spnego
);
295 /* Entry point for libgss */
296 gss_mechanism KRB5_CALLCONV
297 gss_mech_initialize(void)
301 err
= k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE
,
302 spnego_gss_delete_error_info
);
305 "SPNEGO gss_mech_initialize: error message TSD key register fail");
309 return (&spnego_mechanism
);
312 #if 0 /* SUNW17PACresync */
313 MAKE_INIT_FUNCTION(gss_krb5int_lib_init
);
314 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini
);
315 int gss_krb5int_lib_init(void)
318 #endif /* _GSS_STATIC_LINK */
321 int gss_spnegoint_lib_init(void)
323 #ifdef _GSS_STATIC_LINK
324 return gss_spnegomechglue_init();
328 err
= k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE
,
329 spnego_gss_delete_error_info
);
332 "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
341 static void gss_spnegoint_lib_fini(void)
347 glue_spnego_gss_acquire_cred(
349 OM_uint32
*minor_status
,
350 gss_name_t desired_name
,
352 gss_OID_set desired_mechs
,
353 gss_cred_usage_t cred_usage
,
354 gss_cred_id_t
*output_cred_handle
,
355 gss_OID_set
*actual_mechs
,
358 return(spnego_gss_acquire_cred(minor_status
,
370 spnego_gss_acquire_cred(OM_uint32
*minor_status
,
371 gss_name_t desired_name
,
373 gss_OID_set desired_mechs
,
374 gss_cred_usage_t cred_usage
,
375 gss_cred_id_t
*output_cred_handle
,
376 gss_OID_set
*actual_mechs
,
381 dsyslog("Entering spnego_gss_acquire_cred\n");
384 *actual_mechs
= NULL
;
390 * If the user did not specify a list of mechs,
391 * use get_available_mechs to collect a list of
392 * mechs for which creds are available.
394 if (desired_mechs
== GSS_C_NULL_OID_SET
) {
395 status
= get_available_mechs(minor_status
,
396 desired_name
, cred_usage
,
397 output_cred_handle
, &amechs
);
400 * The caller gave a specific list of mechanisms,
401 * so just get whatever creds are available.
402 * gss_acquire_creds will return the subset of mechs for
403 * which the given 'output_cred_handle' is valid.
405 status
= gss_acquire_cred(minor_status
,
406 desired_name
, time_req
,
407 desired_mechs
, cred_usage
,
408 output_cred_handle
, &amechs
,
412 if (actual_mechs
&& amechs
!= GSS_C_NULL_OID_SET
) {
413 (void) gssint_copy_oid_set(minor_status
, amechs
, actual_mechs
);
415 (void) gss_release_oid_set(minor_status
, &amechs
);
417 dsyslog("Leaving spnego_gss_acquire_cred\n");
423 glue_spnego_gss_release_cred(void *context
,
424 OM_uint32
*minor_status
,
425 gss_cred_id_t
*cred_handle
)
427 return( spnego_gss_release_cred(minor_status
, cred_handle
));
432 spnego_gss_release_cred(OM_uint32
*minor_status
,
433 gss_cred_id_t
*cred_handle
)
437 dsyslog("Entering spnego_gss_release_cred\n");
439 if (minor_status
== NULL
|| cred_handle
== NULL
)
440 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
444 if (*cred_handle
== GSS_C_NO_CREDENTIAL
)
445 return (GSS_S_COMPLETE
);
447 status
= gss_release_cred(minor_status
, cred_handle
);
449 dsyslog("Leaving spnego_gss_release_cred\n");
454 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx
)
456 spnego_ctx
->optionStr
= gssint_get_modOptions(
457 (const gss_OID
)&spnego_oids
[0]);
460 static spnego_gss_ctx_id_t
461 create_spnego_ctx(void)
463 spnego_gss_ctx_id_t spnego_ctx
= NULL
;
464 spnego_ctx
= (spnego_gss_ctx_id_t
)
465 malloc(sizeof (spnego_gss_ctx_id_rec
));
467 if (spnego_ctx
== NULL
) {
471 spnego_ctx
->magic_num
= SPNEGO_MAGIC_ID
;
472 spnego_ctx
->ctx_handle
= GSS_C_NO_CONTEXT
;
473 spnego_ctx
->internal_mech
= NULL
;
474 spnego_ctx
->optionStr
= NULL
;
475 spnego_ctx
->DER_mechTypes
.length
= 0;
476 spnego_ctx
->DER_mechTypes
.value
= NULL
;
477 spnego_ctx
->default_cred
= GSS_C_NO_CREDENTIAL
;
478 spnego_ctx
->mic_reqd
= 0;
479 spnego_ctx
->mic_sent
= 0;
480 spnego_ctx
->mic_rcvd
= 0;
481 spnego_ctx
->mech_complete
= 0;
482 spnego_ctx
->nego_done
= 0;
483 spnego_ctx
->internal_name
= GSS_C_NO_NAME
;
484 spnego_ctx
->actual_mech
= GSS_C_NO_OID
;
485 spnego_ctx
->err
.msg
= NULL
;
486 spnego_ctx
->err
.scratch_buf
[0] = 0;
487 check_spnego_options(spnego_ctx
);
493 * Both initiator and acceptor call here to verify and/or create
494 * mechListMIC, and to consistency-check the MIC state.
497 handle_mic(OM_uint32
*minor_status
, gss_buffer_t mic_in
,
498 int send_mechtok
, spnego_gss_ctx_id_t sc
,
499 gss_buffer_t
*mic_out
,
500 OM_uint32
*negState
, send_token_flag
*tokflag
)
505 *mic_out
= GSS_C_NO_BUFFER
;
506 if (mic_in
!= GSS_C_NO_BUFFER
) {
508 /* Reject MIC if we've already received a MIC. */
510 *tokflag
= ERROR_TOKEN_SEND
;
511 return GSS_S_DEFECTIVE_TOKEN
;
513 } else if (sc
->mic_reqd
&& !send_mechtok
) {
515 * If the peer sends the final mechanism token, it
516 * must send the MIC with that token if the
517 * negotiation requires MICs.
520 *tokflag
= ERROR_TOKEN_SEND
;
521 return GSS_S_DEFECTIVE_TOKEN
;
523 ret
= process_mic(minor_status
, mic_in
, sc
, mic_out
,
525 if (ret
!= GSS_S_COMPLETE
) {
529 assert(sc
->mic_sent
|| sc
->mic_rcvd
);
531 if (sc
->mic_sent
&& sc
->mic_rcvd
) {
532 ret
= GSS_S_COMPLETE
;
533 *negState
= ACCEPT_COMPLETE
;
534 if (*mic_out
== GSS_C_NO_BUFFER
) {
536 * We sent a MIC on the previous pass; we
537 * shouldn't be sending a mechanism token.
539 assert(!send_mechtok
);
540 *tokflag
= NO_TOKEN_SEND
;
542 *tokflag
= CONT_TOKEN_SEND
;
544 } else if (sc
->mic_reqd
) {
545 *negState
= ACCEPT_INCOMPLETE
;
546 ret
= GSS_S_CONTINUE_NEEDED
;
547 } else if (*negState
== ACCEPT_COMPLETE
) {
548 ret
= GSS_S_COMPLETE
;
550 ret
= GSS_S_CONTINUE_NEEDED
;
556 * Perform the actual verification and/or generation of mechListMIC.
559 process_mic(OM_uint32
*minor_status
, gss_buffer_t mic_in
,
560 spnego_gss_ctx_id_t sc
, gss_buffer_t
*mic_out
,
561 OM_uint32
*negState
, send_token_flag
*tokflag
)
563 OM_uint32 ret
, tmpmin
;
565 gss_buffer_desc tmpmic
= GSS_C_EMPTY_BUFFER
;
568 if (mic_in
!= GSS_C_NO_BUFFER
) {
569 ret
= gss_verify_mic(minor_status
, sc
->ctx_handle
,
572 if (ret
!= GSS_S_COMPLETE
) {
574 *tokflag
= ERROR_TOKEN_SEND
;
577 /* If we got a MIC, we must send a MIC. */
581 if (sc
->mic_reqd
&& !sc
->mic_sent
) {
582 ret
= gss_get_mic(minor_status
, sc
->ctx_handle
,
586 if (ret
!= GSS_S_COMPLETE
) {
587 gss_release_buffer(&tmpmin
, &tmpmic
);
588 *tokflag
= NO_TOKEN_SEND
;
591 *mic_out
= malloc(sizeof(gss_buffer_desc
));
592 if (*mic_out
== GSS_C_NO_BUFFER
) {
593 gss_release_buffer(&tmpmin
, &tmpmic
);
594 *tokflag
= NO_TOKEN_SEND
;
595 return GSS_S_FAILURE
;
600 return GSS_S_COMPLETE
;
604 * Initial call to spnego_gss_init_sec_context().
607 init_ctx_new(OM_uint32
*minor_status
,
610 gss_OID_set
*mechSet
,
611 send_token_flag
*tokflag
)
613 OM_uint32 ret
, tmpmin
;
614 gss_cred_id_t creds
= GSS_C_NO_CREDENTIAL
;
615 spnego_gss_ctx_id_t sc
= NULL
;
617 /* determine negotiation mech set */
618 if (cred
== GSS_C_NO_CREDENTIAL
) {
619 ret
= get_available_mechs(minor_status
, GSS_C_NO_NAME
,
620 GSS_C_INITIATE
, &creds
, mechSet
);
621 gss_release_cred(&tmpmin
, &creds
);
624 * Use the list of mechs included in the cred that we
627 ret
= gss_inquire_cred(minor_status
, cred
,
628 NULL
, NULL
, NULL
, mechSet
);
630 if (ret
!= GSS_S_COMPLETE
)
633 sc
= create_spnego_ctx();
635 return GSS_S_FAILURE
;
638 * need to pull the first mech from mechSet to do first
639 * gss_init_sec_context()
641 ret
= generic_gss_copy_oid(minor_status
, (*mechSet
)->elements
,
643 if (ret
!= GSS_S_COMPLETE
) {
644 map_errcode(minor_status
);
648 if (put_mech_set(*mechSet
, &sc
->DER_mechTypes
) < 0) {
649 generic_gss_release_oid(&tmpmin
, &sc
->internal_mech
);
654 * The actual context is not yet determined, set the output
655 * context handle to refer to the spnego context itself.
657 sc
->ctx_handle
= GSS_C_NO_CONTEXT
;
658 *ctx
= (gss_ctx_id_t
)sc
;
659 *tokflag
= INIT_TOKEN_SEND
;
660 ret
= GSS_S_CONTINUE_NEEDED
;
663 gss_release_oid_set(&tmpmin
, mechSet
);
668 * Called by second and later calls to spnego_gss_init_sec_context()
669 * to decode reply and update state.
672 init_ctx_cont(OM_uint32
*minor_status
, gss_ctx_id_t
*ctx
, gss_buffer_t buf
,
673 gss_buffer_t
*responseToken
, gss_buffer_t
*mechListMIC
,
674 OM_uint32
*negState
, send_token_flag
*tokflag
)
676 OM_uint32 ret
, tmpmin
, acc_negState
;
678 spnego_gss_ctx_id_t sc
;
679 gss_OID supportedMech
= GSS_C_NO_OID
;
681 sc
= (spnego_gss_ctx_id_t
)*ctx
;
683 *tokflag
= ERROR_TOKEN_SEND
;
686 ret
= get_negTokenResp(minor_status
, ptr
, buf
->length
,
687 &acc_negState
, &supportedMech
,
688 responseToken
, mechListMIC
);
689 if (ret
!= GSS_S_COMPLETE
)
691 if (acc_negState
== ACCEPT_DEFECTIVE_TOKEN
&&
692 supportedMech
== GSS_C_NO_OID
&&
693 *responseToken
== GSS_C_NO_BUFFER
&&
694 *mechListMIC
== GSS_C_NO_BUFFER
) {
695 /* Reject "empty" token. */
696 ret
= GSS_S_DEFECTIVE_TOKEN
;
698 if (acc_negState
== REJECT
) {
699 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
701 spnego_set_error_message(sc
, *minor_status
,
702 dgettext(TEXT_DOMAIN
,
703 "SPNEGO failed to negotiate a mechanism: server rejected request"));
704 map_errcode(minor_status
);
705 *tokflag
= NO_TOKEN_SEND
;
710 * nego_done is false for the first call to init_ctx_cont()
712 if (!sc
->nego_done
) {
713 ret
= init_ctx_nego(minor_status
, sc
,
715 supportedMech
, responseToken
,
718 } else if (!sc
->mech_complete
&&
719 *responseToken
== GSS_C_NO_BUFFER
) {
721 * mech not finished and mech token missing
723 ret
= GSS_S_DEFECTIVE_TOKEN
;
724 } else if (sc
->mic_reqd
&&
725 (sc
->ctx_flags
& GSS_C_INTEG_FLAG
)) {
726 *negState
= ACCEPT_INCOMPLETE
;
727 *tokflag
= CONT_TOKEN_SEND
;
728 ret
= GSS_S_CONTINUE_NEEDED
;
730 *negState
= ACCEPT_COMPLETE
;
731 *tokflag
= NO_TOKEN_SEND
;
732 ret
= GSS_S_COMPLETE
;
735 if (supportedMech
!= GSS_C_NO_OID
)
736 generic_gss_release_oid(&tmpmin
, &supportedMech
);
741 * Consistency checking and mechanism negotiation handling for second
742 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
743 * update internal state if acceptor has counter-proposed.
746 init_ctx_nego(OM_uint32
*minor_status
, spnego_gss_ctx_id_t sc
,
747 OM_uint32 acc_negState
, gss_OID supportedMech
,
748 gss_buffer_t
*responseToken
, gss_buffer_t
*mechListMIC
,
749 OM_uint32
*negState
, send_token_flag
*tokflag
)
754 *tokflag
= ERROR_TOKEN_SEND
;
755 ret
= GSS_S_DEFECTIVE_TOKEN
;
757 * Both supportedMech and negState must be present in first
760 if (supportedMech
== GSS_C_NO_OID
) {
761 *minor_status
= ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR
;
762 map_errcode(minor_status
);
763 return GSS_S_DEFECTIVE_TOKEN
;
765 if (acc_negState
== ACCEPT_DEFECTIVE_TOKEN
) {
766 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
768 spnego_set_error_message(sc
, *minor_status
,
769 dgettext(TEXT_DOMAIN
,
770 "SPNEGO failed to negotiate a mechanism: defective token"));
771 map_errcode(minor_status
);
772 return GSS_S_DEFECTIVE_TOKEN
;
776 * If the mechanism we sent is not the mechanism returned from
777 * the server, we need to handle the server's counter
778 * proposal. There is a bug in SAMBA servers that always send
779 * the old Kerberos mech OID, even though we sent the new one.
780 * So we will treat all the Kerberos mech OIDS as the same.
782 if (!(is_kerb_mech(supportedMech
) &&
783 is_kerb_mech(sc
->internal_mech
)) &&
784 !g_OID_equal(supportedMech
, sc
->internal_mech
)) {
785 ret
= init_ctx_reselect(minor_status
, sc
,
786 acc_negState
, supportedMech
,
787 responseToken
, mechListMIC
,
790 } else if (*responseToken
== GSS_C_NO_BUFFER
) {
791 if (sc
->mech_complete
) {
793 * Mech completed on first call to its
794 * init_sec_context(). Acceptor sends no mech
797 *negState
= ACCEPT_COMPLETE
;
798 *tokflag
= NO_TOKEN_SEND
;
799 ret
= GSS_S_COMPLETE
;
802 * Reject missing mech token when optimistic
805 *minor_status
= ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR
;
806 map_errcode(minor_status
);
807 ret
= GSS_S_DEFECTIVE_TOKEN
;
809 } else if (sc
->mech_complete
) {
810 /* Reject spurious mech token. */
811 ret
= GSS_S_DEFECTIVE_TOKEN
;
813 *negState
= ACCEPT_INCOMPLETE
;
814 *tokflag
= CONT_TOKEN_SEND
;
815 ret
= GSS_S_CONTINUE_NEEDED
;
822 * Handle acceptor's counter-proposal of an alternative mechanism.
825 init_ctx_reselect(OM_uint32
*minor_status
, spnego_gss_ctx_id_t sc
,
826 OM_uint32 acc_negState
, gss_OID supportedMech
,
827 gss_buffer_t
*responseToken
, gss_buffer_t
*mechListMIC
,
828 OM_uint32
*negState
, send_token_flag
*tokflag
)
830 OM_uint32 ret
, tmpmin
;
832 generic_gss_release_oid(&tmpmin
, &sc
->internal_mech
);
833 gss_delete_sec_context(&tmpmin
, &sc
->ctx_handle
,
836 ret
= generic_gss_copy_oid(minor_status
, supportedMech
,
838 if (ret
!= GSS_S_COMPLETE
) {
839 map_errcode(minor_status
);
840 sc
->internal_mech
= GSS_C_NO_OID
;
841 *tokflag
= NO_TOKEN_SEND
;
844 if (*responseToken
!= GSS_C_NO_BUFFER
) {
845 /* Reject spurious mech token. */
846 return GSS_S_DEFECTIVE_TOKEN
;
849 * Windows 2003 and earlier don't correctly send a
850 * negState of request-mic when counter-proposing a
851 * mechanism. They probably don't handle mechListMICs
854 if (acc_negState
!= REQUEST_MIC
)
855 return GSS_S_DEFECTIVE_TOKEN
;
857 sc
->mech_complete
= 0;
859 *negState
= REQUEST_MIC
;
860 *tokflag
= CONT_TOKEN_SEND
;
861 return GSS_S_CONTINUE_NEEDED
;
865 * Wrap call to mechanism gss_init_sec_context() and update state
869 init_ctx_call_init(OM_uint32
*minor_status
,
870 spnego_gss_ctx_id_t sc
,
871 gss_cred_id_t claimant_cred_handle
,
872 gss_name_t target_name
,
875 gss_buffer_t mechtok_in
,
876 gss_OID
*actual_mech
,
877 gss_buffer_t mechtok_out
,
878 OM_uint32
*ret_flags
,
881 send_token_flag
*send_token
)
885 ret
= gss_init_sec_context(minor_status
,
886 claimant_cred_handle
,
890 (req_flags
| GSS_C_INTEG_FLAG
),
892 GSS_C_NO_CHANNEL_BINDINGS
,
898 if (ret
== GSS_S_COMPLETE
) {
899 sc
->mech_complete
= 1;
900 if (ret_flags
!= NULL
)
901 *ret_flags
= sc
->ctx_flags
;
903 * If this isn't the first time we've been called,
904 * we're done unless a MIC needs to be
907 if (*send_token
== CONT_TOKEN_SEND
&&
908 mechtok_out
->length
== 0 &&
910 !(sc
->ctx_flags
& GSS_C_INTEG_FLAG
))) {
912 *negState
= ACCEPT_COMPLETE
;
913 ret
= GSS_S_COMPLETE
;
914 if (mechtok_out
->length
== 0) {
915 *send_token
= NO_TOKEN_SEND
;
918 *negState
= ACCEPT_INCOMPLETE
;
919 ret
= GSS_S_CONTINUE_NEEDED
;
921 } else if (ret
!= GSS_S_CONTINUE_NEEDED
) {
922 if (*send_token
== INIT_TOKEN_SEND
) {
923 /* Don't output token on error if first call. */
924 *send_token
= NO_TOKEN_SEND
;
926 *send_token
= ERROR_TOKEN_SEND
;
935 glue_spnego_gss_init_sec_context(
937 OM_uint32
*minor_status
,
938 gss_cred_id_t claimant_cred_handle
,
939 gss_ctx_id_t
*context_handle
,
940 gss_name_t target_name
,
944 gss_channel_bindings_t input_chan_bindings
,
945 gss_buffer_t input_token
,
946 gss_OID
*actual_mech
,
947 gss_buffer_t output_token
,
948 OM_uint32
*ret_flags
,
951 return(spnego_gss_init_sec_context(
953 claimant_cred_handle
,
969 spnego_gss_init_sec_context(
970 OM_uint32
*minor_status
,
971 gss_cred_id_t claimant_cred_handle
,
972 gss_ctx_id_t
*context_handle
,
973 gss_name_t target_name
,
977 gss_channel_bindings_t input_chan_bindings
,
978 gss_buffer_t input_token
,
979 gss_OID
*actual_mech
,
980 gss_buffer_t output_token
,
981 OM_uint32
*ret_flags
,
985 * send_token is used to indicate in later steps
986 * what type of token, if any should be sent or processed.
987 * NO_TOKEN_SEND = no token should be sent
988 * INIT_TOKEN_SEND = initial token will be sent
989 * CONT_TOKEN_SEND = continuing tokens to be sent
990 * CHECK_MIC = no token to be sent, but have a MIC to check.
992 send_token_flag send_token
= NO_TOKEN_SEND
;
993 OM_uint32 tmpmin
, ret
, negState
;
994 gss_buffer_t mechtok_in
, mechListMIC_in
, mechListMIC_out
;
995 gss_buffer_desc mechtok_out
= GSS_C_EMPTY_BUFFER
;
996 gss_OID_set mechSet
= GSS_C_NO_OID_SET
;
997 spnego_gss_ctx_id_t spnego_ctx
= NULL
;
999 dsyslog("Entering init_sec_context\n");
1001 mechtok_in
= mechListMIC_out
= mechListMIC_in
= GSS_C_NO_BUFFER
;
1004 if (minor_status
!= NULL
)
1006 if (output_token
!= GSS_C_NO_BUFFER
) {
1007 output_token
->length
= 0;
1008 output_token
->value
= NULL
;
1010 if (minor_status
== NULL
||
1011 output_token
== GSS_C_NO_BUFFER
||
1012 context_handle
== NULL
)
1013 return GSS_S_CALL_INACCESSIBLE_WRITE
;
1015 if (actual_mech
!= NULL
)
1016 *actual_mech
= GSS_C_NO_OID
;
1018 if (*context_handle
== GSS_C_NO_CONTEXT
) {
1019 ret
= init_ctx_new(minor_status
, claimant_cred_handle
,
1020 context_handle
, &mechSet
, &send_token
);
1021 if (ret
!= GSS_S_CONTINUE_NEEDED
) {
1025 ret
= init_ctx_cont(minor_status
, context_handle
,
1026 input_token
, &mechtok_in
,
1027 &mechListMIC_in
, &negState
, &send_token
);
1028 if (HARD_ERROR(ret
)) {
1032 spnego_ctx
= (spnego_gss_ctx_id_t
)*context_handle
;
1034 /* Solaris SPNEGO */
1035 if (*minor_status
== ERR_SPNEGO_NEGOTIATION_FAILED
)
1036 spnego_gss_save_error_info(*minor_status
, spnego_ctx
);
1038 if (!spnego_ctx
->mech_complete
) {
1039 ret
= init_ctx_call_init(
1040 minor_status
, spnego_ctx
,
1041 claimant_cred_handle
,
1042 target_name
, req_flags
,
1043 time_req
, mechtok_in
,
1044 actual_mech
, &mechtok_out
,
1045 ret_flags
, time_rec
,
1046 &negState
, &send_token
);
1048 /* create mic/check mic */
1049 if (!HARD_ERROR(ret
) && spnego_ctx
->mech_complete
&&
1050 (spnego_ctx
->ctx_flags
& GSS_C_INTEG_FLAG
)) {
1052 ret
= handle_mic(minor_status
,
1054 (mechtok_out
.length
!= 0),
1055 spnego_ctx
, &mechListMIC_out
,
1056 &negState
, &send_token
);
1059 if (send_token
== INIT_TOKEN_SEND
) {
1060 if (make_spnego_tokenInit_msg(spnego_ctx
,
1064 &mechtok_out
, send_token
,
1065 output_token
) < 0) {
1066 ret
= GSS_S_FAILURE
;
1068 } else if (send_token
!= NO_TOKEN_SEND
) {
1069 if (make_spnego_tokenTarg_msg(negState
, GSS_C_NO_OID
,
1070 &mechtok_out
, mechListMIC_out
,
1072 output_token
) < 0) {
1073 ret
= GSS_S_FAILURE
;
1076 gss_release_buffer(&tmpmin
, &mechtok_out
);
1077 if (ret
== GSS_S_COMPLETE
) {
1079 * Now, switch the output context to refer to the
1080 * negotiated mechanism's context.
1082 *context_handle
= (gss_ctx_id_t
)spnego_ctx
->ctx_handle
;
1083 if (actual_mech
!= NULL
)
1084 *actual_mech
= spnego_ctx
->actual_mech
;
1085 if (ret_flags
!= NULL
)
1086 *ret_flags
= spnego_ctx
->ctx_flags
;
1087 release_spnego_ctx(&spnego_ctx
);
1088 } else if (ret
!= GSS_S_CONTINUE_NEEDED
) {
1089 if (spnego_ctx
!= NULL
) {
1090 gss_delete_sec_context(&tmpmin
,
1091 &spnego_ctx
->ctx_handle
,
1093 release_spnego_ctx(&spnego_ctx
);
1095 *context_handle
= GSS_C_NO_CONTEXT
;
1097 if (mechtok_in
!= GSS_C_NO_BUFFER
) {
1098 gss_release_buffer(&tmpmin
, mechtok_in
);
1101 if (mechListMIC_in
!= GSS_C_NO_BUFFER
) {
1102 gss_release_buffer(&tmpmin
, mechListMIC_in
);
1103 free(mechListMIC_in
);
1105 if (mechListMIC_out
!= GSS_C_NO_BUFFER
) {
1106 gss_release_buffer(&tmpmin
, mechListMIC_out
);
1107 free(mechListMIC_out
);
1109 if (mechSet
!= GSS_C_NO_OID_SET
) {
1110 gss_release_oid_set(&tmpmin
, &mechSet
);
1113 } /* init_sec_context */
1115 /* We don't want to import KRB5 headers here */
1116 static const gss_OID_desc gss_mech_krb5_oid
=
1117 { 9, "\052\206\110\206\367\022\001\002\002" };
1118 static const gss_OID_desc gss_mech_krb5_wrong_oid
=
1119 { 9, "\052\206\110\202\367\022\001\002\002" };
1122 * verify that the input token length is not 0. If it is, just return.
1123 * If the token length is greater than 0, der encode as a sequence
1124 * and place in buf_out, advancing buf_out.
1128 put_neg_hints(unsigned char **buf_out
, gss_buffer_t input_token
,
1129 unsigned int buflen
)
1133 /* if token length is 0, we do not want to send */
1134 if (input_token
->length
== 0)
1137 if (input_token
->length
> buflen
)
1140 *(*buf_out
)++ = SEQUENCE
;
1141 if ((ret
= gssint_put_der_length(input_token
->length
, buf_out
,
1142 input_token
->length
)))
1144 TWRITE_STR(*buf_out
, input_token
->value
, input_token
->length
);
1149 * NegHints ::= SEQUENCE {
1150 * hintName [0] GeneralString OPTIONAL,
1151 * hintAddress [1] OCTET STRING OPTIONAL
1155 #define HOST_PREFIX "host@"
1156 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1159 make_NegHints(OM_uint32
*minor_status
,
1160 gss_cred_id_t cred
, gss_buffer_t
*outbuf
)
1162 gss_buffer_desc hintNameBuf
;
1163 gss_name_t hintName
= GSS_C_NO_NAME
;
1164 gss_name_t hintKerberosName
;
1165 gss_OID hintNameType
;
1166 OM_uint32 major_status
;
1168 unsigned int tlen
= 0;
1169 unsigned int hintNameSize
= 0;
1170 unsigned int negHintsSize
= 0;
1174 *outbuf
= GSS_C_NO_BUFFER
;
1176 if (cred
!= GSS_C_NO_CREDENTIAL
) {
1177 major_status
= gss_inquire_cred(minor_status
,
1183 if (major_status
!= GSS_S_COMPLETE
)
1184 return (major_status
);
1187 if (hintName
== GSS_C_NO_NAME
) {
1188 krb5_error_code code
;
1189 krb5int_access kaccess
;
1190 char hostname
[HOST_PREFIX_LEN
+ MAXHOSTNAMELEN
+ 1] = HOST_PREFIX
;
1192 code
= krb5int_accessor(&kaccess
, KRB5INT_ACCESS_VERSION
);
1194 *minor_status
= code
;
1195 return (GSS_S_FAILURE
);
1198 /* this breaks mutual authentication but Samba relies on it */
1199 code
= (*kaccess
.clean_hostname
)(NULL
, NULL
,
1200 &hostname
[HOST_PREFIX_LEN
],
1203 *minor_status
= code
;
1204 return (GSS_S_FAILURE
);
1207 hintNameBuf
.value
= hostname
;
1208 hintNameBuf
.length
= strlen(hostname
);
1210 major_status
= gss_import_name(minor_status
,
1212 GSS_C_NT_HOSTBASED_SERVICE
,
1214 if (major_status
!= GSS_S_COMPLETE
) {
1215 return (major_status
);
1219 hintNameBuf
.value
= NULL
;
1220 hintNameBuf
.length
= 0;
1222 major_status
= gss_canonicalize_name(minor_status
,
1224 (gss_OID
)&gss_mech_krb5_oid
,
1226 if (major_status
!= GSS_S_COMPLETE
) {
1227 gss_release_name(&minor
, &hintName
);
1228 return (major_status
);
1230 gss_release_name(&minor
, &hintName
);
1232 major_status
= gss_display_name(minor_status
,
1236 if (major_status
!= GSS_S_COMPLETE
) {
1237 gss_release_name(&minor
, &hintKerberosName
);
1238 return (major_status
);
1240 gss_release_name(&minor
, &hintKerberosName
);
1243 * Now encode the name hint into a NegHints ASN.1 type
1245 major_status
= GSS_S_FAILURE
;
1247 /* Length of DER encoded GeneralString */
1248 tlen
= 1 + gssint_der_length_size(hintNameBuf
.length
) +
1250 hintNameSize
= tlen
;
1252 /* Length of DER encoded hintName */
1253 tlen
+= 1 + gssint_der_length_size(hintNameSize
);
1254 negHintsSize
= tlen
;
1256 t
= (unsigned char *)malloc(tlen
);
1258 *minor_status
= ENOMEM
;
1264 *ptr
++ = CONTEXT
| 0x00; /* hintName identifier */
1265 if (gssint_put_der_length(hintNameSize
,
1266 &ptr
, tlen
- (int)(ptr
-t
)))
1269 *ptr
++ = GENERAL_STRING
;
1270 if (gssint_put_der_length(hintNameBuf
.length
,
1271 &ptr
, tlen
- (int)(ptr
-t
)))
1274 memcpy(ptr
, hintNameBuf
.value
, hintNameBuf
.length
);
1275 ptr
+= hintNameBuf
.length
;
1277 *outbuf
= (gss_buffer_t
)malloc(sizeof(gss_buffer_desc
));
1278 if (*outbuf
== NULL
) {
1279 *minor_status
= ENOMEM
;
1282 (*outbuf
)->value
= (void *)t
;
1283 (*outbuf
)->length
= ptr
- t
;
1285 t
= NULL
; /* don't free */
1288 major_status
= GSS_S_COMPLETE
;
1295 gss_release_buffer(&minor
, &hintNameBuf
);
1296 return (major_status
);
1300 acc_ctx_hints(OM_uint32
*minor_status
,
1303 gss_buffer_t
*mechListMIC
,
1304 OM_uint32
*negState
,
1305 send_token_flag
*return_token
)
1307 OM_uint32 tmpmin
, ret
;
1308 gss_OID_set supported_mechSet
;
1309 spnego_gss_ctx_id_t sc
= NULL
;
1311 *mechListMIC
= GSS_C_NO_BUFFER
;
1312 supported_mechSet
= GSS_C_NO_OID_SET
;
1313 *return_token
= ERROR_TOKEN_SEND
;
1317 *ctx
= GSS_C_NO_CONTEXT
;
1318 ret
= GSS_S_DEFECTIVE_TOKEN
;
1320 if (cred
!= GSS_C_NO_CREDENTIAL
) {
1321 ret
= gss_inquire_cred(minor_status
, cred
, NULL
, NULL
,
1322 NULL
, &supported_mechSet
);
1323 if (ret
!= GSS_S_COMPLETE
) {
1324 *return_token
= NO_TOKEN_SEND
;
1328 ret
= get_available_mechs(minor_status
, GSS_C_NO_NAME
,
1330 &supported_mechSet
);
1331 if (ret
!= GSS_S_COMPLETE
) {
1332 *return_token
= NO_TOKEN_SEND
;
1337 ret
= make_NegHints(minor_status
, cred
, mechListMIC
);
1338 if (ret
!= GSS_S_COMPLETE
) {
1339 *return_token
= NO_TOKEN_SEND
;
1344 * Select the best match between the list of mechs
1345 * that the initiator requested and the list that
1346 * the acceptor will support.
1348 sc
= create_spnego_ctx();
1350 ret
= GSS_S_FAILURE
;
1351 *return_token
= NO_TOKEN_SEND
;
1354 if (put_mech_set(supported_mechSet
, &sc
->DER_mechTypes
) < 0) {
1355 ret
= GSS_S_FAILURE
;
1356 *return_token
= NO_TOKEN_SEND
;
1359 sc
->internal_mech
= GSS_C_NO_OID
;
1361 *negState
= ACCEPT_INCOMPLETE
;
1362 *return_token
= INIT_TOKEN_SEND
;
1364 *ctx
= (gss_ctx_id_t
)sc
;
1365 ret
= GSS_S_COMPLETE
;
1368 gss_release_oid_set(&tmpmin
, &supported_mechSet
);
1375 * Input an OID set of mechs and output a string like so:
1376 * '{ x y z } (mechname0), { a b c } (mechname1) ...'.
1377 * On error return NULL.
1378 * Caller needs to free returned string.
1380 static const char *mech_no_map
= "Can't map OID to mechname via /etc/gss/mech";
1381 static const char *oid_no_map
= "Can't map OID to string";
1383 mechoidset2str(gss_OID_set mechset
)
1386 char buf
[256] = {0};
1392 for (i
= 0; i
< mechset
->count
; i
++) {
1394 gss_buffer_desc oidstr
;
1395 gss_buffer_t oidstrp
= &oidstr
;
1396 gss_OID mech_oid
= &mechset
->elements
[i
];
1397 /* No need to free mech_name. */
1398 const char *mech_name
= __gss_oid_to_mech(mech_oid
);
1401 if (strlcat(buf
, ", ", sizeof (buf
)) >= sizeof (buf
)) {
1403 gss_release_buffer(&min
, oidstrp
);
1407 /* Add '{ x y x ... }'. */
1408 maj
= gss_oid_to_str(&min
, mech_oid
, oidstrp
);
1409 if (strlcat(buf
, maj
? oid_no_map
: oidstrp
->value
,
1410 sizeof (buf
)) >= sizeof (buf
)) {
1412 gss_release_buffer(&min
, oidstrp
);
1416 gss_release_buffer(&min
, oidstrp
);
1418 /* Add '(mech name)'. */
1419 if (strlcat(buf
, " (", sizeof (buf
)) >= sizeof (buf
))
1421 if (strlcat(buf
, mech_name
? mech_name
: mech_no_map
,
1422 sizeof (buf
)) >= sizeof (buf
))
1424 if (strlcat(buf
, ") ", sizeof (buf
)) >= sizeof (buf
))
1428 /* Even if we have buf overflow, let's output what we got so far. */
1429 if (mechset
->count
) {
1435 (void) strlcpy(s
, buf
, l
);
1439 return s
? s
: NULL
;
1443 * Set negState to REJECT if the token is defective, else
1444 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1445 * preferred mechanism is supported.
1448 acc_ctx_new(OM_uint32
*minor_status
,
1452 gss_buffer_t
*mechToken
,
1453 gss_buffer_t
*mechListMIC
,
1454 OM_uint32
*negState
,
1455 send_token_flag
*return_token
)
1457 OM_uint32 tmpmin
, ret
, req_flags
;
1458 gss_OID_set supported_mechSet
, mechTypes
;
1459 gss_buffer_desc der_mechTypes
;
1460 gss_OID mech_wanted
;
1461 spnego_gss_ctx_id_t sc
= NULL
;
1463 ret
= GSS_S_DEFECTIVE_TOKEN
;
1464 der_mechTypes
.length
= 0;
1465 der_mechTypes
.value
= NULL
;
1466 *mechToken
= *mechListMIC
= GSS_C_NO_BUFFER
;
1467 supported_mechSet
= mechTypes
= GSS_C_NO_OID_SET
;
1468 *return_token
= ERROR_TOKEN_SEND
;
1472 ret
= get_negTokenInit(minor_status
, buf
, &der_mechTypes
,
1473 &mechTypes
, &req_flags
,
1474 mechToken
, mechListMIC
);
1475 if (ret
!= GSS_S_COMPLETE
) {
1478 if (cred
!= GSS_C_NO_CREDENTIAL
) {
1479 ret
= gss_inquire_cred(minor_status
, cred
, NULL
, NULL
,
1480 NULL
, &supported_mechSet
);
1481 if (ret
!= GSS_S_COMPLETE
) {
1482 *return_token
= NO_TOKEN_SEND
;
1486 ret
= get_available_mechs(minor_status
, GSS_C_NO_NAME
,
1488 &supported_mechSet
);
1489 if (ret
!= GSS_S_COMPLETE
) {
1490 *return_token
= NO_TOKEN_SEND
;
1495 * Select the best match between the list of mechs
1496 * that the initiator requested and the list that
1497 * the acceptor will support.
1499 mech_wanted
= negotiate_mech_type(minor_status
,
1503 if (*negState
== REJECT
) {
1504 /* Solaris SPNEGO: Spruce-up error msg */
1505 char *mechTypesStr
= mechoidset2str(mechTypes
);
1506 spnego_gss_ctx_id_t tmpsc
= create_spnego_ctx();
1507 if (tmpsc
&& *minor_status
== ERR_SPNEGO_NEGOTIATION_FAILED
) {
1508 spnego_set_error_message(tmpsc
, *minor_status
,
1509 dgettext(TEXT_DOMAIN
,
1510 "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
1511 mechTypesStr
? mechTypesStr
: "<null>");
1516 * We save error here cuz the tmp ctx goes away (very) soon.
1517 * So callers of acc_ctx_new() should NOT call it again.
1519 spnego_gss_save_error_info(*minor_status
, tmpsc
);
1521 release_spnego_ctx(&tmpsc
);
1522 ret
= GSS_S_BAD_MECH
;
1526 sc
= (spnego_gss_ctx_id_t
)*ctx
;
1528 gss_release_buffer(&tmpmin
, &sc
->DER_mechTypes
);
1529 assert(mech_wanted
!= GSS_C_NO_OID
);
1531 sc
= create_spnego_ctx();
1533 ret
= GSS_S_FAILURE
;
1534 *return_token
= NO_TOKEN_SEND
;
1535 generic_gss_release_oid(&tmpmin
, &mech_wanted
);
1538 sc
->internal_mech
= mech_wanted
;
1539 sc
->DER_mechTypes
= der_mechTypes
;
1540 der_mechTypes
.length
= 0;
1541 der_mechTypes
.value
= NULL
;
1543 if (*negState
== REQUEST_MIC
)
1546 *return_token
= INIT_TOKEN_SEND
;
1548 *ctx
= (gss_ctx_id_t
)sc
;
1549 ret
= GSS_S_COMPLETE
;
1551 gss_release_oid_set(&tmpmin
, &mechTypes
);
1552 gss_release_oid_set(&tmpmin
, &supported_mechSet
);
1553 if (der_mechTypes
.length
!= 0)
1554 gss_release_buffer(&tmpmin
, &der_mechTypes
);
1559 acc_ctx_cont(OM_uint32
*minstat
,
1562 gss_buffer_t
*responseToken
,
1563 gss_buffer_t
*mechListMIC
,
1564 OM_uint32
*negState
,
1565 send_token_flag
*return_token
)
1567 OM_uint32 ret
, tmpmin
;
1568 gss_OID supportedMech
;
1569 spnego_gss_ctx_id_t sc
;
1571 unsigned char *ptr
, *bufstart
;
1573 sc
= (spnego_gss_ctx_id_t
)*ctx
;
1574 ret
= GSS_S_DEFECTIVE_TOKEN
;
1577 supportedMech
= GSS_C_NO_OID
;
1578 *return_token
= ERROR_TOKEN_SEND
;
1579 *responseToken
= *mechListMIC
= GSS_C_NO_BUFFER
;
1581 ptr
= bufstart
= buf
->value
;
1582 #define REMAIN (buf->length - (ptr - bufstart))
1583 if (REMAIN
> INT_MAX
)
1584 return GSS_S_DEFECTIVE_TOKEN
;
1587 * Attempt to work with old Sun SPNEGO.
1589 if (*ptr
== HEADER_ID
) {
1590 ret
= g_verify_token_header(gss_mech_spnego
,
1591 &len
, &ptr
, 0, REMAIN
);
1594 return GSS_S_DEFECTIVE_TOKEN
;
1597 if (*ptr
!= (CONTEXT
| 0x01)) {
1598 return GSS_S_DEFECTIVE_TOKEN
;
1600 ret
= get_negTokenResp(minstat
, ptr
, REMAIN
,
1601 negState
, &supportedMech
,
1602 responseToken
, mechListMIC
);
1603 if (ret
!= GSS_S_COMPLETE
)
1606 if (*responseToken
== GSS_C_NO_BUFFER
&&
1607 *mechListMIC
== GSS_C_NO_BUFFER
) {
1609 ret
= GSS_S_DEFECTIVE_TOKEN
;
1612 if (supportedMech
!= GSS_C_NO_OID
) {
1613 ret
= GSS_S_DEFECTIVE_TOKEN
;
1617 *negState
= ACCEPT_INCOMPLETE
;
1618 *return_token
= CONT_TOKEN_SEND
;
1620 if (supportedMech
!= GSS_C_NO_OID
) {
1621 generic_gss_release_oid(&tmpmin
, &supportedMech
);
1628 * Verify that mech OID is either exactly the same as the negotiated
1629 * mech OID, or is a mech OID supported by the negotiated mech. MS
1630 * implementations can list a most preferred mech using an incorrect
1631 * krb5 OID while emitting a krb5 initiator mech token having the
1632 * correct krb5 mech OID.
1635 acc_ctx_vfy_oid(OM_uint32
*minor_status
,
1636 spnego_gss_ctx_id_t sc
, gss_OID mechoid
,
1637 OM_uint32
*negState
, send_token_flag
*tokflag
)
1639 OM_uint32 ret
, tmpmin
;
1640 gss_mechanism mech
= NULL
;
1641 gss_OID_set mech_set
= GSS_C_NO_OID_SET
;
1644 if (g_OID_equal(sc
->internal_mech
, mechoid
))
1645 return GSS_S_COMPLETE
;
1649 * If both mechs are kerb, we are done.
1651 if (is_kerb_mech(mechoid
) && is_kerb_mech(sc
->internal_mech
)) {
1652 return GSS_S_COMPLETE
;
1655 mech
= gssint_get_mechanism(mechoid
);
1656 if (mech
== NULL
|| mech
->gss_indicate_mechs
== NULL
) {
1657 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
1661 * Spruce-up error msg.
1663 OM_uint32 maj
, maj_sc
, min
;
1664 gss_buffer_desc oidstr
, oidstr_sc
;
1665 /* No need to free mnamestr. */
1666 const char *mnamestr
= __gss_oid_to_mech(
1668 maj_sc
= gss_oid_to_str(&min
,
1671 maj
= gss_oid_to_str(&min
, mechoid
, &oidstr
);
1672 spnego_set_error_message(sc
, *minor_status
,
1673 dgettext(TEXT_DOMAIN
,
1674 "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1675 maj
? oid_no_map
: oidstr
.value
,
1676 maj_sc
? oid_no_map
: oidstr_sc
.value
,
1677 mnamestr
? mnamestr
: mech_no_map
);
1679 (void) gss_release_buffer(&min
, &oidstr
);
1681 (void) gss_release_buffer(&min
, &oidstr_sc
);
1683 map_errcode(minor_status
);
1685 *tokflag
= ERROR_TOKEN_SEND
;
1686 return GSS_S_BAD_MECH
;
1688 ret
= mech
->gss_indicate_mechs(mech
->context
, minor_status
, &mech_set
);
1689 if (ret
!= GSS_S_COMPLETE
) {
1690 *tokflag
= NO_TOKEN_SEND
;
1691 map_error(minor_status
, mech
);
1694 ret
= gss_test_oid_set_member(minor_status
, sc
->internal_mech
,
1695 mech_set
, &present
);
1696 if (ret
!= GSS_S_COMPLETE
)
1702 * Spruce-up error msg.
1705 gss_buffer_desc oidstr
;
1706 char *mech_set_str
= mechoidset2str(mech_set
);
1707 /* No need to free mnamestr. */
1708 const char *mnamestr
=
1709 __gss_oid_to_mech(sc
->internal_mech
);
1710 maj
= gss_oid_to_str(&min
, sc
->internal_mech
, &oidstr
);
1711 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
1712 spnego_set_error_message(sc
, *minor_status
,
1713 dgettext(TEXT_DOMAIN
,
1714 "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1715 maj
? oid_no_map
: oidstr
.value
,
1716 mnamestr
? mnamestr
: mech_no_map
,
1717 mech_set_str
? mech_set_str
: "<null>");
1719 (void) gss_release_buffer(&min
, &oidstr
);
1722 map_errcode(minor_status
);
1724 *tokflag
= ERROR_TOKEN_SEND
;
1725 ret
= GSS_S_BAD_MECH
;
1728 gss_release_oid_set(&tmpmin
, &mech_set
);
1733 * Wrap call to gss_accept_sec_context() and update state
1737 acc_ctx_call_acc(OM_uint32
*minor_status
, spnego_gss_ctx_id_t sc
,
1738 gss_cred_id_t cred
, gss_buffer_t mechtok_in
,
1739 gss_OID
*mech_type
, gss_buffer_t mechtok_out
,
1740 OM_uint32
*ret_flags
, OM_uint32
*time_rec
,
1741 gss_cred_id_t
*delegated_cred_handle
,
1742 OM_uint32
*negState
, send_token_flag
*tokflag
)
1745 gss_OID_desc mechoid
;
1747 if (sc
->ctx_handle
== GSS_C_NO_CONTEXT
) {
1749 * mechoid is an alias; don't free it.
1751 ret
= gssint_get_mech_type(&mechoid
, mechtok_in
);
1752 if (ret
!= GSS_S_COMPLETE
) {
1753 *tokflag
= NO_TOKEN_SEND
;
1756 ret
= acc_ctx_vfy_oid(minor_status
, sc
, &mechoid
,
1758 if (ret
!= GSS_S_COMPLETE
)
1762 ret
= gss_accept_sec_context(minor_status
,
1766 GSS_C_NO_CHANNEL_BINDINGS
,
1772 delegated_cred_handle
);
1774 if (ret
== GSS_S_COMPLETE
) {
1777 * Force MIC to be not required even if we previously
1780 char *envstr
= getenv("MS_FORCE_NO_MIC");
1782 if (envstr
!= NULL
&& strcmp(envstr
, "1") == 0 &&
1783 !(sc
->ctx_flags
& GSS_C_MUTUAL_FLAG
) &&
1789 sc
->mech_complete
= 1;
1790 if (ret_flags
!= NULL
)
1791 *ret_flags
= sc
->ctx_flags
;
1793 if (!sc
->mic_reqd
) {
1794 *negState
= ACCEPT_COMPLETE
;
1795 ret
= GSS_S_COMPLETE
;
1797 ret
= GSS_S_CONTINUE_NEEDED
;
1799 } else if (ret
!= GSS_S_CONTINUE_NEEDED
) {
1801 *tokflag
= ERROR_TOKEN_SEND
;
1808 glue_spnego_gss_accept_sec_context(
1810 OM_uint32
*minor_status
,
1811 gss_ctx_id_t
*context_handle
,
1812 gss_cred_id_t verifier_cred_handle
,
1813 gss_buffer_t input_token
,
1814 gss_channel_bindings_t input_chan_bindings
,
1815 gss_name_t
*src_name
,
1817 gss_buffer_t output_token
,
1818 OM_uint32
*ret_flags
,
1819 OM_uint32
*time_rec
,
1820 gss_cred_id_t
*delegated_cred_handle
)
1822 return(spnego_gss_accept_sec_context(
1825 verifier_cred_handle
,
1827 input_chan_bindings
,
1833 delegated_cred_handle
));
1838 spnego_gss_accept_sec_context(
1839 OM_uint32
*minor_status
,
1840 gss_ctx_id_t
*context_handle
,
1841 gss_cred_id_t verifier_cred_handle
,
1842 gss_buffer_t input_token
,
1843 gss_channel_bindings_t input_chan_bindings
,
1844 gss_name_t
*src_name
,
1846 gss_buffer_t output_token
,
1847 OM_uint32
*ret_flags
,
1848 OM_uint32
*time_rec
,
1849 gss_cred_id_t
*delegated_cred_handle
)
1851 OM_uint32 ret
, tmpmin
, negState
;
1852 send_token_flag return_token
;
1853 gss_buffer_t mechtok_in
, mic_in
, mic_out
;
1854 gss_buffer_desc mechtok_out
= GSS_C_EMPTY_BUFFER
;
1855 spnego_gss_ctx_id_t sc
= NULL
;
1856 OM_uint32 mechstat
= GSS_S_FAILURE
;
1857 int sendTokenInit
= 0, tmpret
;
1859 mechtok_in
= mic_in
= mic_out
= GSS_C_NO_BUFFER
;
1861 if (minor_status
!= NULL
)
1863 if (output_token
!= GSS_C_NO_BUFFER
) {
1864 output_token
->length
= 0;
1865 output_token
->value
= NULL
;
1869 if (minor_status
== NULL
||
1870 output_token
== GSS_C_NO_BUFFER
||
1871 context_handle
== NULL
) {
1872 return GSS_S_CALL_INACCESSIBLE_WRITE
;
1875 if (input_token
== GSS_C_NO_BUFFER
) {
1876 return GSS_S_CALL_INACCESSIBLE_READ
;
1879 sc
= (spnego_gss_ctx_id_t
)*context_handle
;
1880 if (sc
== NULL
|| sc
->internal_mech
== GSS_C_NO_OID
) {
1881 if (src_name
!= NULL
)
1882 *src_name
= GSS_C_NO_NAME
;
1883 if (mech_type
!= NULL
)
1884 *mech_type
= GSS_C_NO_OID
;
1885 if (time_rec
!= NULL
)
1887 if (ret_flags
!= NULL
)
1889 if (delegated_cred_handle
!= NULL
)
1890 *delegated_cred_handle
= GSS_C_NO_CREDENTIAL
;
1891 if (input_token
->length
== 0) {
1892 ret
= acc_ctx_hints(minor_status
,
1894 verifier_cred_handle
,
1898 if (ret
!= GSS_S_COMPLETE
)
1901 ret
= GSS_S_CONTINUE_NEEDED
;
1903 /* Can set negState to REQUEST_MIC */
1904 ret
= acc_ctx_new(minor_status
, input_token
,
1905 context_handle
, verifier_cred_handle
,
1906 &mechtok_in
, &mic_in
,
1907 &negState
, &return_token
);
1908 if (ret
!= GSS_S_COMPLETE
)
1910 ret
= GSS_S_CONTINUE_NEEDED
;
1913 /* Can set negState to ACCEPT_INCOMPLETE */
1914 ret
= acc_ctx_cont(minor_status
, input_token
,
1915 context_handle
, &mechtok_in
,
1916 &mic_in
, &negState
, &return_token
);
1917 if (ret
!= GSS_S_COMPLETE
)
1919 ret
= GSS_S_CONTINUE_NEEDED
;
1922 sc
= (spnego_gss_ctx_id_t
)*context_handle
;
1924 * Handle mechtok_in and mic_in only if they are
1925 * present in input_token. If neither is present, whether
1926 * this is an error depends on whether this is the first
1927 * round-trip. RET is set to a default value according to
1928 * whether it is the first round-trip.
1930 mechstat
= GSS_S_FAILURE
;
1931 if (negState
!= REQUEST_MIC
&& mechtok_in
!= GSS_C_NO_BUFFER
) {
1932 ret
= acc_ctx_call_acc(minor_status
, sc
,
1933 verifier_cred_handle
, mechtok_in
,
1934 mech_type
, &mechtok_out
,
1935 ret_flags
, time_rec
,
1936 delegated_cred_handle
,
1937 &negState
, &return_token
);
1938 } else if (negState
== REQUEST_MIC
) {
1939 mechstat
= GSS_S_CONTINUE_NEEDED
;
1942 /* Solaris SPNEGO */
1943 if (*minor_status
== ERR_SPNEGO_NEGOTIATION_FAILED
)
1944 spnego_gss_save_error_info(*minor_status
, sc
);
1946 if (!HARD_ERROR(ret
) && sc
->mech_complete
&&
1947 (sc
->ctx_flags
& GSS_C_INTEG_FLAG
)) {
1949 ret
= handle_mic(minor_status
, mic_in
,
1950 (mechtok_out
.length
!= 0),
1952 &negState
, &return_token
);
1956 if (return_token
== INIT_TOKEN_SEND
&& sendTokenInit
) {
1958 tmpret
= make_spnego_tokenInit_msg(sc
, 1, mic_out
, 0,
1960 return_token
, output_token
);
1962 ret
= GSS_S_FAILURE
;
1963 } else if (return_token
!= NO_TOKEN_SEND
&&
1964 return_token
!= CHECK_MIC
) {
1965 tmpret
= make_spnego_tokenTarg_msg(negState
,
1966 sc
? sc
->internal_mech
:
1968 &mechtok_out
, mic_out
,
1972 ret
= GSS_S_FAILURE
;
1974 if (ret
== GSS_S_COMPLETE
) {
1975 *context_handle
= (gss_ctx_id_t
)sc
->ctx_handle
;
1976 if (sc
->internal_name
!= GSS_C_NO_NAME
&&
1978 *src_name
= sc
->internal_name
;
1980 release_spnego_ctx(&sc
);
1982 gss_release_buffer(&tmpmin
, &mechtok_out
);
1983 if (mechtok_in
!= GSS_C_NO_BUFFER
) {
1984 gss_release_buffer(&tmpmin
, mechtok_in
);
1987 if (mic_in
!= GSS_C_NO_BUFFER
) {
1988 gss_release_buffer(&tmpmin
, mic_in
);
1991 if (mic_out
!= GSS_C_NO_BUFFER
) {
1992 gss_release_buffer(&tmpmin
, mic_out
);
1997 #endif /* LEAN_CLIENT */
2001 glue_spnego_gss_display_status(
2003 OM_uint32
*minor_status
,
2004 OM_uint32 status_value
,
2007 OM_uint32
*message_context
,
2008 gss_buffer_t status_string
)
2010 return (spnego_gss_display_status(minor_status
,
2020 spnego_gss_display_status(
2021 OM_uint32
*minor_status
,
2022 OM_uint32 status_value
,
2025 OM_uint32
*message_context
,
2026 gss_buffer_t status_string
)
2028 dsyslog("Entering display_status\n");
2030 *message_context
= 0;
2031 switch (status_value
) {
2032 case ERR_SPNEGO_NO_MECHS_AVAILABLE
:
2034 *status_string
= make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2036 case ERR_SPNEGO_NO_CREDS_ACQUIRED
:
2038 *status_string
= make_err_msg("SPNEGO failed to acquire creds");
2040 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR
:
2042 *status_string
= make_err_msg("SPNEGO acceptor did not select a mechanism");
2044 case ERR_SPNEGO_NEGOTIATION_FAILED
:
2046 return(spnego_gss_display_status2(minor_status
,
2052 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR
:
2054 *status_string
= make_err_msg("SPNEGO acceptor did not return a valid token");
2059 * If mech_spnego calls mech_krb5 (via libgss) and an
2060 * error occurs there, give it a shot.
2063 return(krb5_gss_display_status2(minor_status
,
2066 (gss_OID
)&gss_mech_krb5_oid
,
2072 dsyslog("Leaving display_status\n");
2073 return (GSS_S_COMPLETE
);
2078 glue_spnego_gss_import_name(
2080 OM_uint32
*minor_status
,
2081 gss_buffer_t input_name_buffer
,
2082 gss_OID input_name_type
,
2083 gss_name_t
*output_name
)
2085 return(spnego_gss_import_name(minor_status
,
2093 spnego_gss_import_name(
2094 OM_uint32
*minor_status
,
2095 gss_buffer_t input_name_buffer
,
2096 gss_OID input_name_type
,
2097 gss_name_t
*output_name
)
2101 dsyslog("Entering import_name\n");
2103 status
= gss_import_name(minor_status
, input_name_buffer
,
2104 input_name_type
, output_name
);
2106 dsyslog("Leaving import_name\n");
2112 glue_spnego_gss_release_name(
2114 OM_uint32
*minor_status
,
2115 gss_name_t
*input_name
)
2117 return(spnego_gss_release_name(minor_status
, input_name
));
2122 spnego_gss_release_name(
2123 OM_uint32
*minor_status
,
2124 gss_name_t
*input_name
)
2128 dsyslog("Entering release_name\n");
2130 status
= gss_release_name(minor_status
, input_name
);
2132 dsyslog("Leaving release_name\n");
2138 glue_spnego_gss_compare_name(
2140 OM_uint32
*minor_status
,
2141 const gss_name_t name1
,
2142 const gss_name_t name2
,
2145 return(spnego_gss_compare_name(minor_status
,
2152 spnego_gss_compare_name(
2153 OM_uint32
*minor_status
,
2154 const gss_name_t name1
,
2155 const gss_name_t name2
,
2158 OM_uint32 status
= GSS_S_COMPLETE
;
2159 dsyslog("Entering compare_name\n");
2161 status
= gss_compare_name(minor_status
, name1
, name2
, name_equal
);
2163 dsyslog("Leaving compare_name\n");
2169 glue_spnego_gss_display_name(
2171 OM_uint32
*minor_status
,
2172 gss_name_t input_name
,
2173 gss_buffer_t output_name_buffer
,
2174 gss_OID
*output_name_type
)
2176 return(spnego_gss_display_name(
2185 spnego_gss_display_name(
2186 OM_uint32
*minor_status
,
2187 gss_name_t input_name
,
2188 gss_buffer_t output_name_buffer
,
2189 gss_OID
*output_name_type
)
2191 OM_uint32 status
= GSS_S_COMPLETE
;
2192 dsyslog("Entering display_name\n");
2194 status
= gss_display_name(minor_status
, input_name
,
2195 output_name_buffer
, output_name_type
);
2197 dsyslog("Leaving display_name\n");
2204 glue_spnego_gss_inquire_names_for_mech(
2206 OM_uint32
*minor_status
,
2208 gss_OID_set
*name_types
)
2210 return(spnego_gss_inquire_names_for_mech(minor_status
,
2216 spnego_gss_inquire_names_for_mech(
2217 OM_uint32
*minor_status
,
2219 gss_OID_set
*name_types
)
2221 OM_uint32 major
, minor
;
2223 dsyslog("Entering inquire_names_for_mech\n");
2225 * We only know how to handle our own mechanism.
2227 if ((mechanism
!= GSS_C_NULL_OID
) &&
2228 !g_OID_equal(gss_mech_spnego
, mechanism
)) {
2230 return (GSS_S_FAILURE
);
2233 major
= gss_create_empty_oid_set(minor_status
, name_types
);
2234 if (major
== GSS_S_COMPLETE
) {
2235 /* Now add our members. */
2236 if (((major
= gss_add_oid_set_member(minor_status
,
2237 (gss_OID
) GSS_C_NT_USER_NAME
,
2238 name_types
)) == GSS_S_COMPLETE
) &&
2239 ((major
= gss_add_oid_set_member(minor_status
,
2240 (gss_OID
) GSS_C_NT_MACHINE_UID_NAME
,
2241 name_types
)) == GSS_S_COMPLETE
) &&
2242 ((major
= gss_add_oid_set_member(minor_status
,
2243 (gss_OID
) GSS_C_NT_STRING_UID_NAME
,
2244 name_types
)) == GSS_S_COMPLETE
)) {
2245 major
= gss_add_oid_set_member(minor_status
,
2246 (gss_OID
) GSS_C_NT_HOSTBASED_SERVICE
,
2250 if (major
!= GSS_S_COMPLETE
)
2251 (void) gss_release_oid_set(&minor
, name_types
);
2254 dsyslog("Leaving inquire_names_for_mech\n");
2260 OM_uint32
*minor_status
,
2261 gss_ctx_id_t context_handle
,
2262 gss_buffer_t input_message_buffer
,
2263 gss_buffer_t output_message_buffer
,
2265 gss_qop_t
*qop_state
)
2268 ret
= gss_unwrap(minor_status
,
2270 input_message_buffer
,
2271 output_message_buffer
,
2280 OM_uint32
*minor_status
,
2281 gss_ctx_id_t context_handle
,
2284 gss_buffer_t input_message_buffer
,
2286 gss_buffer_t output_message_buffer
)
2289 ret
= gss_wrap(minor_status
,
2293 input_message_buffer
,
2295 output_message_buffer
);
2301 spnego_gss_process_context_token(
2302 OM_uint32
*minor_status
,
2303 const gss_ctx_id_t context_handle
,
2304 const gss_buffer_t token_buffer
)
2307 ret
= gss_process_context_token(minor_status
,
2315 glue_spnego_gss_delete_sec_context(
2317 OM_uint32
*minor_status
,
2318 gss_ctx_id_t
*context_handle
,
2319 gss_buffer_t output_token
)
2321 return(spnego_gss_delete_sec_context(minor_status
,
2322 context_handle
, output_token
));
2326 spnego_gss_delete_sec_context(
2327 OM_uint32
*minor_status
,
2328 gss_ctx_id_t
*context_handle
,
2329 gss_buffer_t output_token
)
2331 OM_uint32 ret
= GSS_S_COMPLETE
;
2332 spnego_gss_ctx_id_t
*ctx
=
2333 (spnego_gss_ctx_id_t
*)context_handle
;
2335 if (context_handle
== NULL
)
2336 return (GSS_S_FAILURE
);
2339 * If this is still an SPNEGO mech, release it locally.
2342 (*ctx
)->magic_num
== SPNEGO_MAGIC_ID
) {
2343 (void) release_spnego_ctx(ctx
);
2344 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2346 output_token
->length
= 0;
2347 output_token
->value
= NULL
;
2350 ret
= gss_delete_sec_context(minor_status
,
2359 glue_spnego_gss_context_time(
2361 OM_uint32
*minor_status
,
2362 const gss_ctx_id_t context_handle
,
2363 OM_uint32
*time_rec
)
2365 return(spnego_gss_context_time(minor_status
,
2371 spnego_gss_context_time(
2372 OM_uint32
*minor_status
,
2373 const gss_ctx_id_t context_handle
,
2374 OM_uint32
*time_rec
)
2377 ret
= gss_context_time(minor_status
,
2385 glue_spnego_gss_export_sec_context(
2387 OM_uint32
*minor_status
,
2388 gss_ctx_id_t
*context_handle
,
2389 gss_buffer_t interprocess_token
)
2391 return(spnego_gss_export_sec_context(minor_status
,
2393 interprocess_token
));
2396 spnego_gss_export_sec_context(
2397 OM_uint32
*minor_status
,
2398 gss_ctx_id_t
*context_handle
,
2399 gss_buffer_t interprocess_token
)
2402 ret
= gss_export_sec_context(minor_status
,
2404 interprocess_token
);
2409 glue_spnego_gss_import_sec_context(
2411 OM_uint32
*minor_status
,
2412 const gss_buffer_t interprocess_token
,
2413 gss_ctx_id_t
*context_handle
)
2415 return(spnego_gss_import_sec_context(minor_status
,
2420 spnego_gss_import_sec_context(
2421 OM_uint32
*minor_status
,
2422 const gss_buffer_t interprocess_token
,
2423 gss_ctx_id_t
*context_handle
)
2426 ret
= gss_import_sec_context(minor_status
,
2431 #endif /* LEAN_CLIENT */
2434 glue_spnego_gss_inquire_context(
2436 OM_uint32
*minor_status
,
2437 const gss_ctx_id_t context_handle
,
2438 gss_name_t
*src_name
,
2439 gss_name_t
*targ_name
,
2440 OM_uint32
*lifetime_rec
,
2442 OM_uint32
*ctx_flags
,
2443 int *locally_initiated
,
2446 return(spnego_gss_inquire_context(
2459 spnego_gss_inquire_context(
2460 OM_uint32
*minor_status
,
2461 const gss_ctx_id_t context_handle
,
2462 gss_name_t
*src_name
,
2463 gss_name_t
*targ_name
,
2464 OM_uint32
*lifetime_rec
,
2466 OM_uint32
*ctx_flags
,
2467 int *locally_initiated
,
2470 OM_uint32 ret
= GSS_S_COMPLETE
;
2472 ret
= gss_inquire_context(minor_status
,
2486 glue_spnego_gss_wrap_size_limit(
2488 OM_uint32
*minor_status
,
2489 const gss_ctx_id_t context_handle
,
2492 OM_uint32 req_output_size
,
2493 OM_uint32
*max_input_size
)
2495 return(spnego_gss_wrap_size_limit(minor_status
,
2504 spnego_gss_wrap_size_limit(
2505 OM_uint32
*minor_status
,
2506 const gss_ctx_id_t context_handle
,
2509 OM_uint32 req_output_size
,
2510 OM_uint32
*max_input_size
)
2513 ret
= gss_wrap_size_limit(minor_status
,
2522 #if 0 /* SUNW17PACresync */
2525 OM_uint32
*minor_status
,
2526 const gss_ctx_id_t context_handle
,
2528 const gss_buffer_t message_buffer
,
2529 gss_buffer_t message_token
)
2532 ret
= gss_get_mic(minor_status
,
2542 spnego_gss_verify_mic(
2543 OM_uint32
*minor_status
,
2544 const gss_ctx_id_t context_handle
,
2545 const gss_buffer_t msg_buffer
,
2546 const gss_buffer_t token_buffer
,
2547 gss_qop_t
*qop_state
)
2550 ret
= gss_verify_mic(minor_status
,
2559 spnego_gss_inquire_sec_context_by_oid(
2560 OM_uint32
*minor_status
,
2561 const gss_ctx_id_t context_handle
,
2562 const gss_OID desired_object
,
2563 gss_buffer_set_t
*data_set
)
2566 ret
= gss_inquire_sec_context_by_oid(minor_status
,
2575 * These GSS funcs not needed yet, so disable them.
2576 * Revisit for full 1.7 resync.
2580 spnego_gss_set_sec_context_option(
2581 OM_uint32
*minor_status
,
2582 gss_ctx_id_t
*context_handle
,
2583 const gss_OID desired_object
,
2584 const gss_buffer_t value
)
2587 ret
= gss_set_sec_context_option(minor_status
,
2595 spnego_gss_wrap_aead(OM_uint32
*minor_status
,
2596 gss_ctx_id_t context_handle
,
2599 gss_buffer_t input_assoc_buffer
,
2600 gss_buffer_t input_payload_buffer
,
2602 gss_buffer_t output_message_buffer
)
2605 ret
= gss_wrap_aead(minor_status
,
2610 input_payload_buffer
,
2612 output_message_buffer
);
2618 spnego_gss_unwrap_aead(OM_uint32
*minor_status
,
2619 gss_ctx_id_t context_handle
,
2620 gss_buffer_t input_message_buffer
,
2621 gss_buffer_t input_assoc_buffer
,
2622 gss_buffer_t output_payload_buffer
,
2624 gss_qop_t
*qop_state
)
2627 ret
= gss_unwrap_aead(minor_status
,
2629 input_message_buffer
,
2631 output_payload_buffer
,
2638 spnego_gss_wrap_iov(OM_uint32
*minor_status
,
2639 gss_ctx_id_t context_handle
,
2643 gss_iov_buffer_desc
*iov
,
2647 ret
= gss_wrap_iov(minor_status
,
2658 spnego_gss_unwrap_iov(OM_uint32
*minor_status
,
2659 gss_ctx_id_t context_handle
,
2661 gss_qop_t
*qop_state
,
2662 gss_iov_buffer_desc
*iov
,
2666 ret
= gss_unwrap_iov(minor_status
,
2676 spnego_gss_wrap_iov_length(OM_uint32
*minor_status
,
2677 gss_ctx_id_t context_handle
,
2681 gss_iov_buffer_desc
*iov
,
2685 ret
= gss_wrap_iov_length(minor_status
,
2697 spnego_gss_complete_auth_token(
2698 OM_uint32
*minor_status
,
2699 const gss_ctx_id_t context_handle
,
2700 gss_buffer_t input_message_buffer
)
2703 ret
= gss_complete_auth_token(minor_status
,
2705 input_message_buffer
);
2711 * We will release everything but the ctx_handle so that it
2712 * can be passed back to init/accept context. This routine should
2713 * not be called until after the ctx_handle memory is assigned to
2714 * the supplied context handle from init/accept context.
2717 release_spnego_ctx(spnego_gss_ctx_id_t
*ctx
)
2719 spnego_gss_ctx_id_t context
;
2720 OM_uint32 minor_stat
;
2723 if (context
!= NULL
) {
2724 (void) gss_release_buffer(&minor_stat
,
2725 &context
->DER_mechTypes
);
2727 (void) generic_gss_release_oid(&minor_stat
,
2728 &context
->internal_mech
);
2730 if (context
->optionStr
!= NULL
) {
2731 free(context
->optionStr
);
2732 context
->optionStr
= NULL
;
2740 * Can't use gss_indicate_mechs by itself to get available mechs for
2741 * SPNEGO because it will also return the SPNEGO mech and we do not
2742 * want to consider SPNEGO as an available security mech for
2743 * negotiation. For this reason, get_available_mechs will return
2744 * all available mechs except SPNEGO.
2746 * If a ptr to a creds list is given, this function will attempt
2747 * to acquire creds for the creds given and trim the list of
2748 * returned mechanisms to only those for which creds are valid.
2752 get_available_mechs(OM_uint32
*minor_status
,
2753 gss_name_t name
, gss_cred_usage_t usage
,
2754 gss_cred_id_t
*creds
, gss_OID_set
*rmechs
)
2758 OM_uint32 major_status
= GSS_S_COMPLETE
, tmpmin
;
2759 gss_OID_set mechs
, goodmechs
;
2761 major_status
= gss_indicate_mechs(minor_status
, &mechs
);
2763 if (major_status
!= GSS_S_COMPLETE
) {
2764 return (major_status
);
2767 major_status
= gss_create_empty_oid_set(minor_status
, rmechs
);
2769 if (major_status
!= GSS_S_COMPLETE
) {
2770 (void) gss_release_oid_set(minor_status
, &mechs
);
2771 return (major_status
);
2774 for (i
= 0; i
< mechs
->count
&& major_status
== GSS_S_COMPLETE
; i
++) {
2775 if ((mechs
->elements
[i
].length
2776 != spnego_mechanism
.mech_type
.length
) ||
2777 memcmp(mechs
->elements
[i
].elements
,
2778 spnego_mechanism
.mech_type
.elements
,
2779 spnego_mechanism
.mech_type
.length
)) {
2781 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2782 * it never inferences any of the related OIDs of the
2783 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2784 * We add KRB5_WRONG here so that old MS clients can
2785 * negotiate this mechanism, which allows extensions
2786 * in Kerberos (clock skew adjustment, refresh ccache).
2788 if (is_kerb_mech(&mechs
->elements
[i
])) {
2789 extern gss_OID_desc
* const gss_mech_krb5_wrong
;
2792 gss_add_oid_set_member(minor_status
,
2793 gss_mech_krb5_wrong
, rmechs
);
2796 major_status
= gss_add_oid_set_member(minor_status
,
2797 &mechs
->elements
[i
],
2799 if (major_status
== GSS_S_COMPLETE
)
2805 * If the caller wanted a list of creds returned,
2806 * trim the list of mechanisms down to only those
2807 * for which the creds are valid.
2809 if (found
> 0 && major_status
== GSS_S_COMPLETE
&& creds
!= NULL
) {
2810 major_status
= gss_acquire_cred(minor_status
,
2811 name
, GSS_C_INDEFINITE
,
2812 *rmechs
, usage
, creds
,
2816 * Drop the old list in favor of the new
2819 (void) gss_release_oid_set(&tmpmin
, rmechs
);
2820 if (major_status
== GSS_S_COMPLETE
) {
2821 (void) gssint_copy_oid_set(&tmpmin
,
2823 (void) gss_release_oid_set(&tmpmin
, &goodmechs
);
2827 (void) gss_release_oid_set(&tmpmin
, &mechs
);
2828 if (found
== 0 || major_status
!= GSS_S_COMPLETE
) {
2829 *minor_status
= ERR_SPNEGO_NO_MECHS_AVAILABLE
;
2830 map_errcode(minor_status
);
2831 if (major_status
== GSS_S_COMPLETE
)
2832 major_status
= GSS_S_FAILURE
;
2835 return (major_status
);
2838 /* following are token creation and reading routines */
2841 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2842 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2846 get_mech_oid(OM_uint32
*minor_status
, unsigned char **buff_in
, size_t length
)
2850 gss_OID mech_out
= NULL
;
2851 unsigned char *start
, *end
;
2853 if (length
< 1 || **buff_in
!= MECH_OID
)
2857 end
= start
+ length
;
2860 toid
.length
= *(*buff_in
)++;
2862 if ((*buff_in
+ toid
.length
) > end
)
2865 toid
.elements
= *buff_in
;
2866 *buff_in
+= toid
.length
;
2868 status
= generic_gss_copy_oid(minor_status
, &toid
, &mech_out
);
2870 if (status
!= GSS_S_COMPLETE
) {
2871 map_errcode(minor_status
);
2879 * der encode the given mechanism oid into buf_out, advancing the
2884 put_mech_oid(unsigned char **buf_out
, gss_OID_const mech
, unsigned int buflen
)
2886 if (buflen
< mech
->length
+ 2)
2888 *(*buf_out
)++ = MECH_OID
;
2889 *(*buf_out
)++ = (unsigned char) mech
->length
;
2890 memcpy((void *)(*buf_out
), mech
->elements
, mech
->length
);
2891 *buf_out
+= mech
->length
;
2896 * verify that buff_in points to an octet string, if it does not,
2897 * return NULL and don't advance the pointer. If it is an octet string
2898 * decode buff_in into a gss_buffer_t and return it, advancing the
2902 get_input_token(unsigned char **buff_in
, unsigned int buff_length
)
2904 gss_buffer_t input_token
;
2907 if (**buff_in
!= OCTET_STRING
)
2911 input_token
= (gss_buffer_t
)malloc(sizeof (gss_buffer_desc
));
2913 if (input_token
== NULL
)
2916 input_token
->length
= gssint_get_der_length(buff_in
, buff_length
, &bytes
);
2917 if ((int)input_token
->length
== -1) {
2921 input_token
->value
= malloc(input_token
->length
);
2923 if (input_token
->value
== NULL
) {
2928 (void) memcpy(input_token
->value
, *buff_in
, input_token
->length
);
2929 *buff_in
+= input_token
->length
;
2930 return (input_token
);
2934 * verify that the input token length is not 0. If it is, just return.
2935 * If the token length is greater than 0, der encode as an octet string
2936 * and place in buf_out, advancing buf_out.
2940 put_input_token(unsigned char **buf_out
, gss_buffer_t input_token
,
2941 unsigned int buflen
)
2945 /* if token length is 0, we do not want to send */
2946 if (input_token
->length
== 0)
2949 if (input_token
->length
> buflen
)
2952 *(*buf_out
)++ = OCTET_STRING
;
2953 if ((ret
= gssint_put_der_length(input_token
->length
, buf_out
,
2954 input_token
->length
)))
2956 TWRITE_STR(*buf_out
, input_token
->value
, input_token
->length
);
2961 * verify that buff_in points to a sequence of der encoding. The mech
2962 * set is the only sequence of encoded object in the token, so if it is
2963 * a sequence of encoding, decode the mechset into a gss_OID_set and
2964 * return it, advancing the buffer pointer.
2967 get_mech_set(OM_uint32
*minor_status
, unsigned char **buff_in
,
2968 unsigned int buff_length
)
2970 gss_OID_set returned_mechSet
;
2971 OM_uint32 major_status
;
2972 int length
; /* SUNW17PACresync */
2974 OM_uint32 set_length
;
2975 unsigned char *start
;
2978 if (**buff_in
!= SEQUENCE_OF
)
2984 length
= gssint_get_der_length(buff_in
, buff_length
, &bytes
);
2985 if (length
< 0) /* SUNW17PACresync - MIT17 lacks this check */
2988 major_status
= gss_create_empty_oid_set(minor_status
,
2990 if (major_status
!= GSS_S_COMPLETE
)
2993 for (set_length
= 0, i
= 0; set_length
< length
; i
++) {
2994 gss_OID_desc
*temp
= get_mech_oid(minor_status
, buff_in
,
2995 buff_length
- (*buff_in
- start
));
2997 major_status
= gss_add_oid_set_member(minor_status
,
2998 temp
, &returned_mechSet
);
2999 if (major_status
== GSS_S_COMPLETE
) {
3000 set_length
+= returned_mechSet
->elements
[i
].length
+2;
3001 if (generic_gss_release_oid(minor_status
, &temp
))
3002 map_errcode(minor_status
);
3007 return (returned_mechSet
);
3011 * Encode mechSet into buf.
3014 put_mech_set(gss_OID_set mechSet
, gss_buffer_t buf
)
3018 unsigned int tlen
, ilen
;
3021 for (i
= 0; i
< mechSet
->count
; i
++) {
3023 * 0x06 [DER LEN] [OID]
3026 gssint_der_length_size(mechSet
->elements
[i
].length
) +
3027 mechSet
->elements
[i
].length
;
3032 tlen
= 1 + gssint_der_length_size(ilen
) + ilen
;
3039 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3041 *ptr
++ = SEQUENCE_OF
;
3042 if (gssint_put_der_length(ilen
, &ptr
, REMAIN
) < 0)
3044 for (i
= 0; i
< mechSet
->count
; i
++) {
3045 if (put_mech_oid(&ptr
, &mechSet
->elements
[i
], REMAIN
) < 0) {
3054 * Verify that buff_in is pointing to a BIT_STRING with the correct
3055 * length and padding for the req_flags. If it is, decode req_flags
3056 * and return them, otherwise, return NULL.
3059 get_req_flags(unsigned char **buff_in
, OM_uint32 bodysize
,
3060 OM_uint32
*req_flags
)
3064 if (**buff_in
!= (CONTEXT
| 0x01))
3067 if (g_get_tag_and_length(buff_in
, (CONTEXT
| 0x01),
3068 bodysize
, &len
) < 0)
3069 return GSS_S_DEFECTIVE_TOKEN
;
3071 if (*(*buff_in
)++ != BIT_STRING
)
3072 return GSS_S_DEFECTIVE_TOKEN
;
3074 if (*(*buff_in
)++ != BIT_STRING_LENGTH
)
3075 return GSS_S_DEFECTIVE_TOKEN
;
3077 if (*(*buff_in
)++ != BIT_STRING_PADDING
)
3078 return GSS_S_DEFECTIVE_TOKEN
;
3080 *req_flags
= (OM_uint32
) (*(*buff_in
)++ >> 1);
3085 get_negTokenInit(OM_uint32
*minor_status
,
3087 gss_buffer_t der_mechSet
,
3088 gss_OID_set
*mechSet
,
3089 OM_uint32
*req_flags
,
3090 gss_buffer_t
*mechtok
,
3091 gss_buffer_t
*mechListMIC
)
3094 unsigned char *ptr
, *bufstart
;
3096 gss_buffer_desc tmpbuf
;
3099 der_mechSet
->length
= 0;
3100 der_mechSet
->value
= NULL
;
3101 *mechSet
= GSS_C_NO_OID_SET
;
3103 *mechtok
= *mechListMIC
= GSS_C_NO_BUFFER
;
3105 ptr
= bufstart
= buf
->value
;
3106 if ((buf
->length
- (ptr
- bufstart
)) > INT_MAX
)
3107 return GSS_S_FAILURE
;
3108 #define REMAIN (buf->length - (ptr - bufstart))
3110 err
= g_verify_token_header(gss_mech_spnego
,
3111 &len
, &ptr
, 0, REMAIN
);
3113 *minor_status
= err
;
3114 map_errcode(minor_status
);
3115 return GSS_S_FAILURE
;
3117 *minor_status
= g_verify_neg_token_init(&ptr
, REMAIN
);
3118 if (*minor_status
) {
3119 map_errcode(minor_status
);
3120 return GSS_S_FAILURE
;
3123 /* alias into input_token */
3125 tmpbuf
.length
= REMAIN
;
3126 *mechSet
= get_mech_set(minor_status
, &ptr
, REMAIN
);
3127 if (*mechSet
== NULL
)
3128 return GSS_S_FAILURE
;
3130 tmpbuf
.length
= ptr
- (unsigned char *)tmpbuf
.value
;
3131 der_mechSet
->value
= malloc(tmpbuf
.length
);
3132 if (der_mechSet
->value
== NULL
)
3133 return GSS_S_FAILURE
;
3134 memcpy(der_mechSet
->value
, tmpbuf
.value
, tmpbuf
.length
);
3135 der_mechSet
->length
= tmpbuf
.length
;
3137 err
= get_req_flags(&ptr
, REMAIN
, req_flags
);
3138 if (err
!= GSS_S_COMPLETE
) {
3141 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x02),
3142 REMAIN
, &len
) >= 0) {
3143 *mechtok
= get_input_token(&ptr
, len
);
3144 if (*mechtok
== GSS_C_NO_BUFFER
) {
3145 return GSS_S_FAILURE
;
3148 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x03),
3149 REMAIN
, &len
) >= 0) {
3150 *mechListMIC
= get_input_token(&ptr
, len
);
3151 if (*mechListMIC
== GSS_C_NO_BUFFER
) {
3152 return GSS_S_FAILURE
;
3155 return GSS_S_COMPLETE
;
3160 get_negTokenResp(OM_uint32
*minor_status
,
3161 unsigned char *buf
, unsigned int buflen
,
3162 OM_uint32
*negState
,
3163 gss_OID
*supportedMech
,
3164 gss_buffer_t
*responseToken
,
3165 gss_buffer_t
*mechListMIC
)
3167 unsigned char *ptr
, *bufstart
;
3170 unsigned int tag
, bytes
;
3172 *negState
= ACCEPT_DEFECTIVE_TOKEN
;
3173 *supportedMech
= GSS_C_NO_OID
;
3174 *responseToken
= *mechListMIC
= GSS_C_NO_BUFFER
;
3175 ptr
= bufstart
= buf
;
3176 #define REMAIN (buflen - (ptr - bufstart))
3178 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x01), REMAIN
, &len
) < 0)
3179 return GSS_S_DEFECTIVE_TOKEN
;
3180 if (*ptr
++ == SEQUENCE
) {
3181 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3183 return GSS_S_DEFECTIVE_TOKEN
;
3190 if (tag
== CONTEXT
) {
3191 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3193 return GSS_S_DEFECTIVE_TOKEN
;
3195 if (g_get_tag_and_length(&ptr
, ENUMERATED
,
3197 return GSS_S_DEFECTIVE_TOKEN
;
3199 if (len
!= ENUMERATION_LENGTH
)
3200 return GSS_S_DEFECTIVE_TOKEN
;
3203 return GSS_S_DEFECTIVE_TOKEN
;
3211 if (tag
== (CONTEXT
| 0x01)) {
3212 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3214 return GSS_S_DEFECTIVE_TOKEN
;
3216 *supportedMech
= get_mech_oid(minor_status
, &ptr
, REMAIN
);
3217 if (*supportedMech
== GSS_C_NO_OID
)
3218 return GSS_S_DEFECTIVE_TOKEN
;
3225 if (tag
== (CONTEXT
| 0x02)) {
3226 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3228 return GSS_S_DEFECTIVE_TOKEN
;
3230 *responseToken
= get_input_token(&ptr
, REMAIN
);
3231 if (*responseToken
== GSS_C_NO_BUFFER
)
3232 return GSS_S_DEFECTIVE_TOKEN
;
3239 if (tag
== (CONTEXT
| 0x03)) {
3240 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3242 return GSS_S_DEFECTIVE_TOKEN
;
3244 *mechListMIC
= get_input_token(&ptr
, REMAIN
);
3245 if (*mechListMIC
== GSS_C_NO_BUFFER
)
3246 return GSS_S_DEFECTIVE_TOKEN
;
3248 return GSS_S_COMPLETE
;
3253 * der encode the passed negResults as an ENUMERATED type and
3254 * place it in buf_out, advancing the buffer.
3258 put_negResult(unsigned char **buf_out
, OM_uint32 negResult
,
3259 unsigned int buflen
)
3263 *(*buf_out
)++ = ENUMERATED
;
3264 *(*buf_out
)++ = ENUMERATION_LENGTH
;
3265 *(*buf_out
)++ = (unsigned char) negResult
;
3270 * This routine compares the recieved mechset to the mechset that
3271 * this server can support. It looks sequentially through the mechset
3272 * and the first one that matches what the server can support is
3273 * chosen as the negotiated mechanism. If one is found, negResult
3274 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3275 * it's not the first mech, otherwise we return NULL and negResult
3278 * NOTE: There is currently no way to specify a preference order of
3279 * mechanisms supported by the acceptor.
3282 negotiate_mech_type(OM_uint32
*minor_status
,
3283 gss_OID_set supported_mechSet
,
3284 gss_OID_set mechset
,
3285 OM_uint32
*negResult
)
3287 gss_OID returned_mech
;
3292 for (i
= 0; i
< mechset
->count
; i
++) {
3293 gss_OID mech_oid
= &mechset
->elements
[i
];
3296 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3297 * we actually want to select it if the client supports, as this
3298 * will enable features on MS clients that allow credential
3299 * refresh on rekeying and caching system times from servers.
3302 /* Accept wrong mechanism OID from MS clients */
3303 if (mech_oid
->length
== gss_mech_krb5_wrong_oid
.length
&&
3304 memcmp(mech_oid
->elements
, gss_mech_krb5_wrong_oid
.elements
, mech_oid
->length
) == 0)
3305 mech_oid
= (gss_OID
)&gss_mech_krb5_oid
;
3308 gss_test_oid_set_member(minor_status
, mech_oid
, supported_mechSet
, &present
);
3313 *negResult
= ACCEPT_INCOMPLETE
;
3315 *negResult
= REQUEST_MIC
;
3317 status
= generic_gss_copy_oid(minor_status
,
3318 &mechset
->elements
[i
],
3320 if (status
!= GSS_S_COMPLETE
) {
3321 *negResult
= REJECT
;
3322 map_errcode(minor_status
);
3325 return (returned_mech
);
3327 /* Solaris SPNEGO */
3328 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
3330 *negResult
= REJECT
;
3335 * the next two routines make a token buffer suitable for
3336 * spnego_gss_display_status. These currently take the string
3337 * in name and place it in the token. Eventually, if
3338 * spnego_gss_display_status returns valid error messages,
3339 * these routines will be changes to return the error string.
3341 static spnego_token_t
3342 make_spnego_token(char *name
)
3344 return (spnego_token_t
)strdup(name
);
3347 static gss_buffer_desc
3348 make_err_msg(char *name
)
3350 gss_buffer_desc buffer
;
3354 buffer
.value
= NULL
;
3356 buffer
.length
= strlen(name
)+1;
3357 buffer
.value
= make_spnego_token(name
);
3364 * Create the client side spnego token passed back to gss_init_sec_context
3365 * and eventually up to the application program and over to the server.
3367 * Use DER rules, definite length method per RFC 2478
3370 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx
,
3372 gss_buffer_t mechListMIC
, OM_uint32 req_flags
,
3373 gss_buffer_t data
, send_token_flag sendtoken
,
3374 gss_buffer_t outbuf
)
3377 unsigned int tlen
, dataLen
= 0;
3378 unsigned int negTokenInitSize
= 0;
3379 unsigned int negTokenInitSeqSize
= 0;
3380 unsigned int negTokenInitContSize
= 0;
3381 unsigned int rspTokenSize
= 0;
3382 unsigned int mechListTokenSize
= 0;
3383 unsigned int micTokenSize
= 0;
3387 if (outbuf
== GSS_C_NO_BUFFER
)
3391 outbuf
->value
= NULL
;
3393 /* calculate the data length */
3396 * 0xa0 [DER LEN] [mechTypes]
3398 mechListTokenSize
= 1 +
3399 gssint_der_length_size(spnego_ctx
->DER_mechTypes
.length
) +
3400 spnego_ctx
->DER_mechTypes
.length
;
3401 dataLen
+= mechListTokenSize
;
3404 * If a token from gss_init_sec_context exists,
3405 * add the length of the token + the ASN.1 overhead
3409 * Encoded in final output as:
3410 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3411 * -----s--------|--------s2----------
3414 gssint_der_length_size(data
->length
) +
3416 dataLen
+= 1 + gssint_der_length_size(rspTokenSize
) +
3422 * Encoded in final output as:
3423 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3424 * --s-- -----tlen------------
3427 gssint_der_length_size(mechListMIC
->length
) +
3428 mechListMIC
->length
;
3430 gssint_der_length_size(micTokenSize
) +
3435 * Add size of DER encoding
3436 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3437 * 0x30 [DER_LEN] [data]
3440 negTokenInitContSize
= dataLen
;
3441 negTokenInitSeqSize
= 1 + gssint_der_length_size(dataLen
) + dataLen
;
3442 dataLen
= negTokenInitSeqSize
;
3445 * negTokenInitSize indicates the bytes needed to
3446 * hold the ASN.1 encoding of the entire NegTokenInit
3448 * 0xa0 [DER_LEN] + data
3451 negTokenInitSize
= 1 +
3452 gssint_der_length_size(negTokenInitSeqSize
) +
3453 negTokenInitSeqSize
;
3455 tlen
= g_token_size(gss_mech_spnego
, negTokenInitSize
);
3457 t
= (unsigned char *) malloc(tlen
);
3465 /* create the message */
3466 if ((ret
= g_make_token_header(gss_mech_spnego
, negTokenInitSize
,
3470 *ptr
++ = CONTEXT
; /* NegotiationToken identifier */
3471 if ((ret
= gssint_put_der_length(negTokenInitSeqSize
, &ptr
, tlen
)))
3475 if ((ret
= gssint_put_der_length(negTokenInitContSize
, &ptr
,
3476 tlen
- (int)(ptr
-t
))))
3479 *ptr
++ = CONTEXT
| 0x00; /* MechTypeList identifier */
3480 if ((ret
= gssint_put_der_length(spnego_ctx
->DER_mechTypes
.length
,
3481 &ptr
, tlen
- (int)(ptr
-t
))))
3484 /* We already encoded the MechSetList */
3485 (void) memcpy(ptr
, spnego_ctx
->DER_mechTypes
.value
,
3486 spnego_ctx
->DER_mechTypes
.length
);
3488 ptr
+= spnego_ctx
->DER_mechTypes
.length
;
3491 *ptr
++ = CONTEXT
| 0x02;
3492 if ((ret
= gssint_put_der_length(rspTokenSize
,
3493 &ptr
, tlen
- (int)(ptr
- t
))))
3496 if ((ret
= put_input_token(&ptr
, data
,
3497 tlen
- (int)(ptr
- t
))))
3501 if (mechListMIC
!= GSS_C_NO_BUFFER
) {
3502 *ptr
++ = CONTEXT
| 0x03;
3503 if ((ret
= gssint_put_der_length(micTokenSize
,
3504 &ptr
, tlen
- (int)(ptr
- t
))))
3507 if (negHintsCompat
) {
3508 ret
= put_neg_hints(&ptr
, mechListMIC
,
3509 tlen
- (int)(ptr
- t
));
3512 } else if ((ret
= put_input_token(&ptr
, mechListMIC
,
3513 tlen
- (int)(ptr
- t
))))
3523 outbuf
->length
= tlen
;
3524 outbuf
->value
= (void *) t
;
3530 * create the server side spnego token passed back to
3531 * gss_accept_sec_context and eventually up to the application program
3532 * and over to the client.
3535 make_spnego_tokenTarg_msg(OM_uint32 status
, gss_OID mech_wanted
,
3536 gss_buffer_t data
, gss_buffer_t mechListMIC
,
3537 send_token_flag sendtoken
,
3538 gss_buffer_t outbuf
)
3540 unsigned int tlen
= 0;
3541 unsigned int ret
= 0;
3542 unsigned int NegTokenTargSize
= 0;
3543 unsigned int NegTokenSize
= 0;
3544 unsigned int rspTokenSize
= 0;
3545 unsigned int micTokenSize
= 0;
3546 unsigned int dataLen
= 0;
3550 if (outbuf
== GSS_C_NO_BUFFER
)
3551 return (GSS_S_DEFECTIVE_TOKEN
);
3554 outbuf
->value
= NULL
;
3557 * ASN.1 encoding of the negResult
3558 * ENUMERATED type is 3 bytes
3559 * ENUMERATED TAG, Length, Value,
3560 * Plus 2 bytes for the CONTEXT id and length.
3565 * calculate data length
3567 * If this is the initial token, include length of
3568 * mech_type and the negotiation result fields.
3570 if (sendtoken
== INIT_TOKEN_SEND
) {
3571 int mechlistTokenSize
;
3573 * 1 byte for the CONTEXT ID(0xa0),
3574 * 1 byte for the OID ID(0x06)
3575 * 1 byte for OID Length field
3576 * Plus the rest... (OID Length, OID value)
3578 mechlistTokenSize
= 3 + mech_wanted
->length
+
3579 gssint_der_length_size(mech_wanted
->length
);
3581 dataLen
+= mechlistTokenSize
;
3583 if (data
!= NULL
&& data
->length
> 0) {
3584 /* Length of the inner token */
3585 rspTokenSize
= 1 + gssint_der_length_size(data
->length
) +
3588 dataLen
+= rspTokenSize
;
3590 /* Length of the outer token */
3591 dataLen
+= 1 + gssint_der_length_size(rspTokenSize
);
3593 if (mechListMIC
!= NULL
) {
3595 /* Length of the inner token */
3596 micTokenSize
= 1 + gssint_der_length_size(mechListMIC
->length
) +
3597 mechListMIC
->length
;
3599 dataLen
+= micTokenSize
;
3601 /* Length of the outer token */
3602 dataLen
+= 1 + gssint_der_length_size(micTokenSize
);
3605 * Add size of DER encoded:
3606 * NegTokenTarg [ SEQUENCE ] of
3607 * NegResult[0] ENUMERATED {
3608 * accept_completed(0),
3609 * accept_incomplete(1),
3611 * supportedMech [1] MechType OPTIONAL,
3612 * responseToken [2] OCTET STRING OPTIONAL,
3613 * mechListMIC [3] OCTET STRING OPTIONAL
3615 * size = data->length + MechListMic + SupportedMech len +
3616 * Result Length + ASN.1 overhead
3618 NegTokenTargSize
= dataLen
;
3619 dataLen
+= 1 + gssint_der_length_size(NegTokenTargSize
);
3622 * NegotiationToken [ CHOICE ]{
3623 * negTokenInit [0] NegTokenInit,
3624 * negTokenTarg [1] NegTokenTarg }
3626 NegTokenSize
= dataLen
;
3627 dataLen
+= 1 + gssint_der_length_size(NegTokenSize
);
3630 t
= (unsigned char *) malloc(tlen
);
3633 ret
= GSS_S_DEFECTIVE_TOKEN
;
3640 * Indicate that we are sending CHOICE 1
3643 *ptr
++ = CONTEXT
| 0x01;
3644 if (gssint_put_der_length(NegTokenSize
, &ptr
, dataLen
) < 0) {
3645 ret
= GSS_S_DEFECTIVE_TOKEN
;
3649 if (gssint_put_der_length(NegTokenTargSize
, &ptr
,
3650 tlen
- (int)(ptr
-t
)) < 0) {
3651 ret
= GSS_S_DEFECTIVE_TOKEN
;
3656 * First field of the NegTokenTarg SEQUENCE
3657 * is the ENUMERATED NegResult.
3660 if (gssint_put_der_length(3, &ptr
,
3661 tlen
- (int)(ptr
-t
)) < 0) {
3662 ret
= GSS_S_DEFECTIVE_TOKEN
;
3665 if (put_negResult(&ptr
, status
, tlen
- (int)(ptr
- t
)) < 0) {
3666 ret
= GSS_S_DEFECTIVE_TOKEN
;
3669 if (sendtoken
== INIT_TOKEN_SEND
) {
3671 * Next, is the Supported MechType
3673 *ptr
++ = CONTEXT
| 0x01;
3674 if (gssint_put_der_length(mech_wanted
->length
+ 2,
3676 tlen
- (int)(ptr
- t
)) < 0) {
3677 ret
= GSS_S_DEFECTIVE_TOKEN
;
3680 if (put_mech_oid(&ptr
, mech_wanted
,
3681 tlen
- (int)(ptr
- t
)) < 0) {
3682 ret
= GSS_S_DEFECTIVE_TOKEN
;
3686 if (data
!= NULL
&& data
->length
> 0) {
3687 *ptr
++ = CONTEXT
| 0x02;
3688 if (gssint_put_der_length(rspTokenSize
, &ptr
,
3689 tlen
- (int)(ptr
- t
)) < 0) {
3690 ret
= GSS_S_DEFECTIVE_TOKEN
;
3693 if (put_input_token(&ptr
, data
,
3694 tlen
- (int)(ptr
- t
)) < 0) {
3695 ret
= GSS_S_DEFECTIVE_TOKEN
;
3699 if (mechListMIC
!= NULL
) {
3700 *ptr
++ = CONTEXT
| 0x03;
3701 if (gssint_put_der_length(micTokenSize
, &ptr
,
3702 tlen
- (int)(ptr
- t
)) < 0) {
3703 ret
= GSS_S_DEFECTIVE_TOKEN
;
3706 if (put_input_token(&ptr
, mechListMIC
,
3707 tlen
- (int)(ptr
- t
)) < 0) {
3708 ret
= GSS_S_DEFECTIVE_TOKEN
;
3712 ret
= GSS_S_COMPLETE
;
3714 if (ret
!= GSS_S_COMPLETE
) {
3717 outbuf
->length
= ptr
- t
;
3718 outbuf
->value
= (void *) t
;
3724 /* determine size of token */
3726 g_token_size(gss_OID_const mech
, unsigned int body_size
)
3731 * Initialize the header size to the
3732 * MECH_OID byte + the bytes needed to indicate the
3733 * length of the OID + the OID itself.
3735 * 0x06 [MECHLENFIELD] MECHDATA
3737 hdrsize
= 1 + gssint_der_length_size(mech
->length
) + mech
->length
;
3740 * Now add the bytes needed for the initial header
3742 * 0x60 + [DER_LEN] + HDRSIZE
3744 hdrsize
+= 1 + gssint_der_length_size(body_size
+ hdrsize
);
3746 return (hdrsize
+ body_size
);
3750 * generate token header.
3752 * Use DER Definite Length method per RFC2478
3753 * Use of indefinite length encoding will not be compatible
3754 * with Microsoft or others that actually follow the spec.
3757 g_make_token_header(gss_OID_const mech
,
3758 unsigned int body_size
,
3759 unsigned char **buf
,
3760 unsigned int totallen
)
3763 unsigned int hdrsize
;
3764 unsigned char *p
= *buf
;
3766 hdrsize
= 1 + gssint_der_length_size(mech
->length
) + mech
->length
;
3768 *(*buf
)++ = HEADER_ID
;
3769 if ((ret
= gssint_put_der_length(hdrsize
+ body_size
, buf
, totallen
)))
3772 *(*buf
)++ = MECH_OID
;
3773 if ((ret
= gssint_put_der_length(mech
->length
, buf
,
3774 totallen
- (int)(p
- *buf
))))
3776 TWRITE_STR(*buf
, mech
->elements
, mech
->length
);
3781 * NOTE: This checks that the length returned by
3782 * gssint_get_der_length() is not greater than the number of octets
3783 * remaining, even though gssint_get_der_length() already checks, in
3787 g_get_tag_and_length(unsigned char **buf
, int tag
,
3788 unsigned int buflen
, unsigned int *outlen
)
3790 unsigned char *ptr
= *buf
;
3791 int ret
= -1; /* pessimists, assume failure ! */
3792 unsigned int encoded_len
;
3793 unsigned int tmplen
= 0;
3796 if (buflen
> 1 && *ptr
== tag
) {
3798 tmplen
= gssint_get_der_length(&ptr
, buflen
- 1,
3802 } else if (tmplen
> buflen
- (ptr
- *buf
)) {
3813 g_verify_neg_token_init(unsigned char **buf_in
, unsigned int cur_size
)
3815 unsigned char *buf
= *buf_in
;
3816 unsigned char *endptr
= buf
+ cur_size
;
3817 unsigned int seqsize
;
3822 * Verify this is a NegotiationToken type token
3823 * - check for a0(context specific identifier)
3824 * - get length and verify that enoughd ata exists
3826 if (g_get_tag_and_length(&buf
, CONTEXT
, cur_size
, &seqsize
) < 0)
3827 return (G_BAD_TOK_HEADER
);
3829 cur_size
= seqsize
; /* should indicate bytes remaining */
3832 * Verify the next piece, it should identify this as
3833 * a strucure of type NegTokenInit.
3835 if (*buf
++ == SEQUENCE
) {
3836 if ((seqsize
= gssint_get_der_length(&buf
, cur_size
, &bytes
)) < 0)
3837 return (G_BAD_TOK_HEADER
);
3839 * Make sure we have the entire buffer as described
3841 if (buf
+ seqsize
> endptr
)
3842 return (G_BAD_TOK_HEADER
);
3844 return (G_BAD_TOK_HEADER
);
3847 cur_size
= seqsize
; /* should indicate bytes remaining */
3850 * Verify that the first blob is a sequence of mechTypes
3852 if (*buf
++ == CONTEXT
) {
3853 if ((seqsize
= gssint_get_der_length(&buf
, cur_size
, &bytes
)) < 0)
3854 return (G_BAD_TOK_HEADER
);
3856 * Make sure we have the entire buffer as described
3858 if (buf
+ bytes
> endptr
)
3859 return (G_BAD_TOK_HEADER
);
3861 return (G_BAD_TOK_HEADER
);
3865 * At this point, *buf should be at the beginning of the
3866 * DER encoded list of mech types that are to be negotiated.
3874 /* verify token header. */
3876 g_verify_token_header(gss_OID_const mech
,
3877 unsigned int *body_size
,
3878 unsigned char **buf_in
,
3880 unsigned int toksize
)
3882 unsigned char *buf
= *buf_in
;
3889 return (G_BAD_TOK_HEADER
);
3891 if (*buf
++ != HEADER_ID
)
3892 return (G_BAD_TOK_HEADER
);
3894 if ((seqsize
= gssint_get_der_length(&buf
, toksize
, &bytes
)) < 0)
3895 return (G_BAD_TOK_HEADER
);
3897 if ((seqsize
+ bytes
) != toksize
)
3898 return (G_BAD_TOK_HEADER
);
3901 return (G_BAD_TOK_HEADER
);
3904 if (*buf
++ != MECH_OID
)
3905 return (G_BAD_TOK_HEADER
);
3908 return (G_BAD_TOK_HEADER
);
3910 toid
.length
= *buf
++;
3912 if (toksize
< toid
.length
)
3913 return (G_BAD_TOK_HEADER
);
3915 toksize
-= toid
.length
;
3917 toid
.elements
= buf
;
3920 if (!g_OID_equal(&toid
, mech
))
3924 * G_WRONG_MECH is not returned immediately because it's more important
3925 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3928 return (G_BAD_TOK_HEADER
);
3934 *body_size
= toksize
;
3941 * Return non-zero if the oid is one of the kerberos mech oids,
3942 * otherwise return zero.
3944 * N.B. There are 3 oids that represent the kerberos mech:
3945 * RFC-specified GSS_MECH_KRB5_OID,
3946 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3947 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3951 is_kerb_mech(gss_OID oid
)
3955 extern const gss_OID_set_desc
* const gss_mech_set_krb5_both
;
3957 (void) gss_test_oid_set_member(&minor
,
3958 oid
, (gss_OID_set
)gss_mech_set_krb5_both
, &answer
);