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>");
1517 * We save error here cuz the tmp ctx goes away (very) soon.
1518 * So callers of acc_ctx_new() should NOT call it again.
1520 spnego_gss_save_error_info(*minor_status
, tmpsc
);
1522 release_spnego_ctx(&tmpsc
);
1523 ret
= GSS_S_BAD_MECH
;
1527 sc
= (spnego_gss_ctx_id_t
)*ctx
;
1529 gss_release_buffer(&tmpmin
, &sc
->DER_mechTypes
);
1530 assert(mech_wanted
!= GSS_C_NO_OID
);
1532 sc
= create_spnego_ctx();
1534 ret
= GSS_S_FAILURE
;
1535 *return_token
= NO_TOKEN_SEND
;
1536 generic_gss_release_oid(&tmpmin
, &mech_wanted
);
1539 sc
->internal_mech
= mech_wanted
;
1540 sc
->DER_mechTypes
= der_mechTypes
;
1541 der_mechTypes
.length
= 0;
1542 der_mechTypes
.value
= NULL
;
1544 if (*negState
== REQUEST_MIC
)
1547 *return_token
= INIT_TOKEN_SEND
;
1549 *ctx
= (gss_ctx_id_t
)sc
;
1550 ret
= GSS_S_COMPLETE
;
1552 gss_release_oid_set(&tmpmin
, &mechTypes
);
1553 gss_release_oid_set(&tmpmin
, &supported_mechSet
);
1554 if (der_mechTypes
.length
!= 0)
1555 gss_release_buffer(&tmpmin
, &der_mechTypes
);
1560 acc_ctx_cont(OM_uint32
*minstat
,
1563 gss_buffer_t
*responseToken
,
1564 gss_buffer_t
*mechListMIC
,
1565 OM_uint32
*negState
,
1566 send_token_flag
*return_token
)
1568 OM_uint32 ret
, tmpmin
;
1569 gss_OID supportedMech
;
1570 spnego_gss_ctx_id_t sc
;
1572 unsigned char *ptr
, *bufstart
;
1574 sc
= (spnego_gss_ctx_id_t
)*ctx
;
1575 ret
= GSS_S_DEFECTIVE_TOKEN
;
1578 supportedMech
= GSS_C_NO_OID
;
1579 *return_token
= ERROR_TOKEN_SEND
;
1580 *responseToken
= *mechListMIC
= GSS_C_NO_BUFFER
;
1582 ptr
= bufstart
= buf
->value
;
1583 #define REMAIN (buf->length - (ptr - bufstart))
1584 if (REMAIN
> INT_MAX
)
1585 return GSS_S_DEFECTIVE_TOKEN
;
1588 * Attempt to work with old Sun SPNEGO.
1590 if (*ptr
== HEADER_ID
) {
1591 ret
= g_verify_token_header(gss_mech_spnego
,
1592 &len
, &ptr
, 0, REMAIN
);
1595 return GSS_S_DEFECTIVE_TOKEN
;
1598 if (*ptr
!= (CONTEXT
| 0x01)) {
1599 return GSS_S_DEFECTIVE_TOKEN
;
1601 ret
= get_negTokenResp(minstat
, ptr
, REMAIN
,
1602 negState
, &supportedMech
,
1603 responseToken
, mechListMIC
);
1604 if (ret
!= GSS_S_COMPLETE
)
1607 if (*responseToken
== GSS_C_NO_BUFFER
&&
1608 *mechListMIC
== GSS_C_NO_BUFFER
) {
1610 ret
= GSS_S_DEFECTIVE_TOKEN
;
1613 if (supportedMech
!= GSS_C_NO_OID
) {
1614 ret
= GSS_S_DEFECTIVE_TOKEN
;
1618 *negState
= ACCEPT_INCOMPLETE
;
1619 *return_token
= CONT_TOKEN_SEND
;
1621 if (supportedMech
!= GSS_C_NO_OID
) {
1622 generic_gss_release_oid(&tmpmin
, &supportedMech
);
1629 * Verify that mech OID is either exactly the same as the negotiated
1630 * mech OID, or is a mech OID supported by the negotiated mech. MS
1631 * implementations can list a most preferred mech using an incorrect
1632 * krb5 OID while emitting a krb5 initiator mech token having the
1633 * correct krb5 mech OID.
1636 acc_ctx_vfy_oid(OM_uint32
*minor_status
,
1637 spnego_gss_ctx_id_t sc
, gss_OID mechoid
,
1638 OM_uint32
*negState
, send_token_flag
*tokflag
)
1640 OM_uint32 ret
, tmpmin
;
1641 gss_mechanism mech
= NULL
;
1642 gss_OID_set mech_set
= GSS_C_NO_OID_SET
;
1645 if (g_OID_equal(sc
->internal_mech
, mechoid
))
1646 return GSS_S_COMPLETE
;
1650 * If both mechs are kerb, we are done.
1652 if (is_kerb_mech(mechoid
) && is_kerb_mech(sc
->internal_mech
)) {
1653 return GSS_S_COMPLETE
;
1656 mech
= gssint_get_mechanism(mechoid
);
1657 if (mech
== NULL
|| mech
->gss_indicate_mechs
== NULL
) {
1658 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
1662 * Spruce-up error msg.
1664 OM_uint32 maj
, maj_sc
, min
;
1665 gss_buffer_desc oidstr
, oidstr_sc
;
1666 /* No need to free mnamestr. */
1667 const char *mnamestr
= __gss_oid_to_mech(
1669 maj_sc
= gss_oid_to_str(&min
,
1672 maj
= gss_oid_to_str(&min
, mechoid
, &oidstr
);
1673 spnego_set_error_message(sc
, *minor_status
,
1674 dgettext(TEXT_DOMAIN
,
1675 "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1676 maj
? oid_no_map
: oidstr
.value
,
1677 maj_sc
? oid_no_map
: oidstr_sc
.value
,
1678 mnamestr
? mnamestr
: mech_no_map
);
1680 (void) gss_release_buffer(&min
, &oidstr
);
1682 (void) gss_release_buffer(&min
, &oidstr_sc
);
1684 map_errcode(minor_status
);
1686 *tokflag
= ERROR_TOKEN_SEND
;
1687 return GSS_S_BAD_MECH
;
1689 ret
= mech
->gss_indicate_mechs(mech
->context
, minor_status
, &mech_set
);
1690 if (ret
!= GSS_S_COMPLETE
) {
1691 *tokflag
= NO_TOKEN_SEND
;
1692 map_error(minor_status
, mech
);
1695 ret
= gss_test_oid_set_member(minor_status
, sc
->internal_mech
,
1696 mech_set
, &present
);
1697 if (ret
!= GSS_S_COMPLETE
)
1703 * Spruce-up error msg.
1706 gss_buffer_desc oidstr
;
1707 char *mech_set_str
= mechoidset2str(mech_set
);
1708 /* No need to free mnamestr. */
1709 const char *mnamestr
=
1710 __gss_oid_to_mech(sc
->internal_mech
);
1711 maj
= gss_oid_to_str(&min
, sc
->internal_mech
, &oidstr
);
1712 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
1713 spnego_set_error_message(sc
, *minor_status
,
1714 dgettext(TEXT_DOMAIN
,
1715 "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1716 maj
? oid_no_map
: oidstr
.value
,
1717 mnamestr
? mnamestr
: mech_no_map
,
1718 mech_set_str
? mech_set_str
: "<null>");
1720 (void) gss_release_buffer(&min
, &oidstr
);
1724 map_errcode(minor_status
);
1726 *tokflag
= ERROR_TOKEN_SEND
;
1727 ret
= GSS_S_BAD_MECH
;
1730 gss_release_oid_set(&tmpmin
, &mech_set
);
1735 * Wrap call to gss_accept_sec_context() and update state
1739 acc_ctx_call_acc(OM_uint32
*minor_status
, spnego_gss_ctx_id_t sc
,
1740 gss_cred_id_t cred
, gss_buffer_t mechtok_in
,
1741 gss_OID
*mech_type
, gss_buffer_t mechtok_out
,
1742 OM_uint32
*ret_flags
, OM_uint32
*time_rec
,
1743 gss_cred_id_t
*delegated_cred_handle
,
1744 OM_uint32
*negState
, send_token_flag
*tokflag
)
1747 gss_OID_desc mechoid
;
1749 if (sc
->ctx_handle
== GSS_C_NO_CONTEXT
) {
1751 * mechoid is an alias; don't free it.
1753 ret
= gssint_get_mech_type(&mechoid
, mechtok_in
);
1754 if (ret
!= GSS_S_COMPLETE
) {
1755 *tokflag
= NO_TOKEN_SEND
;
1758 ret
= acc_ctx_vfy_oid(minor_status
, sc
, &mechoid
,
1760 if (ret
!= GSS_S_COMPLETE
)
1764 ret
= gss_accept_sec_context(minor_status
,
1768 GSS_C_NO_CHANNEL_BINDINGS
,
1774 delegated_cred_handle
);
1776 if (ret
== GSS_S_COMPLETE
) {
1779 * Force MIC to be not required even if we previously
1782 char *envstr
= getenv("MS_FORCE_NO_MIC");
1784 if (envstr
!= NULL
&& strcmp(envstr
, "1") == 0 &&
1785 !(sc
->ctx_flags
& GSS_C_MUTUAL_FLAG
) &&
1791 sc
->mech_complete
= 1;
1792 if (ret_flags
!= NULL
)
1793 *ret_flags
= sc
->ctx_flags
;
1795 if (!sc
->mic_reqd
) {
1796 *negState
= ACCEPT_COMPLETE
;
1797 ret
= GSS_S_COMPLETE
;
1799 ret
= GSS_S_CONTINUE_NEEDED
;
1801 } else if (ret
!= GSS_S_CONTINUE_NEEDED
) {
1803 *tokflag
= ERROR_TOKEN_SEND
;
1810 glue_spnego_gss_accept_sec_context(
1812 OM_uint32
*minor_status
,
1813 gss_ctx_id_t
*context_handle
,
1814 gss_cred_id_t verifier_cred_handle
,
1815 gss_buffer_t input_token
,
1816 gss_channel_bindings_t input_chan_bindings
,
1817 gss_name_t
*src_name
,
1819 gss_buffer_t output_token
,
1820 OM_uint32
*ret_flags
,
1821 OM_uint32
*time_rec
,
1822 gss_cred_id_t
*delegated_cred_handle
)
1824 return(spnego_gss_accept_sec_context(
1827 verifier_cred_handle
,
1829 input_chan_bindings
,
1835 delegated_cred_handle
));
1840 spnego_gss_accept_sec_context(
1841 OM_uint32
*minor_status
,
1842 gss_ctx_id_t
*context_handle
,
1843 gss_cred_id_t verifier_cred_handle
,
1844 gss_buffer_t input_token
,
1845 gss_channel_bindings_t input_chan_bindings
,
1846 gss_name_t
*src_name
,
1848 gss_buffer_t output_token
,
1849 OM_uint32
*ret_flags
,
1850 OM_uint32
*time_rec
,
1851 gss_cred_id_t
*delegated_cred_handle
)
1853 OM_uint32 ret
, tmpmin
, negState
;
1854 send_token_flag return_token
;
1855 gss_buffer_t mechtok_in
, mic_in
, mic_out
;
1856 gss_buffer_desc mechtok_out
= GSS_C_EMPTY_BUFFER
;
1857 spnego_gss_ctx_id_t sc
= NULL
;
1858 OM_uint32 mechstat
= GSS_S_FAILURE
;
1859 int sendTokenInit
= 0, tmpret
;
1861 mechtok_in
= mic_in
= mic_out
= GSS_C_NO_BUFFER
;
1863 if (minor_status
!= NULL
)
1865 if (output_token
!= GSS_C_NO_BUFFER
) {
1866 output_token
->length
= 0;
1867 output_token
->value
= NULL
;
1871 if (minor_status
== NULL
||
1872 output_token
== GSS_C_NO_BUFFER
||
1873 context_handle
== NULL
) {
1874 return GSS_S_CALL_INACCESSIBLE_WRITE
;
1877 if (input_token
== GSS_C_NO_BUFFER
) {
1878 return GSS_S_CALL_INACCESSIBLE_READ
;
1881 sc
= (spnego_gss_ctx_id_t
)*context_handle
;
1882 if (sc
== NULL
|| sc
->internal_mech
== GSS_C_NO_OID
) {
1883 if (src_name
!= NULL
)
1884 *src_name
= GSS_C_NO_NAME
;
1885 if (mech_type
!= NULL
)
1886 *mech_type
= GSS_C_NO_OID
;
1887 if (time_rec
!= NULL
)
1889 if (ret_flags
!= NULL
)
1891 if (delegated_cred_handle
!= NULL
)
1892 *delegated_cred_handle
= GSS_C_NO_CREDENTIAL
;
1893 if (input_token
->length
== 0) {
1894 ret
= acc_ctx_hints(minor_status
,
1896 verifier_cred_handle
,
1900 if (ret
!= GSS_S_COMPLETE
)
1903 ret
= GSS_S_CONTINUE_NEEDED
;
1905 /* Can set negState to REQUEST_MIC */
1906 ret
= acc_ctx_new(minor_status
, input_token
,
1907 context_handle
, verifier_cred_handle
,
1908 &mechtok_in
, &mic_in
,
1909 &negState
, &return_token
);
1910 if (ret
!= GSS_S_COMPLETE
)
1912 ret
= GSS_S_CONTINUE_NEEDED
;
1915 /* Can set negState to ACCEPT_INCOMPLETE */
1916 ret
= acc_ctx_cont(minor_status
, input_token
,
1917 context_handle
, &mechtok_in
,
1918 &mic_in
, &negState
, &return_token
);
1919 if (ret
!= GSS_S_COMPLETE
)
1921 ret
= GSS_S_CONTINUE_NEEDED
;
1924 sc
= (spnego_gss_ctx_id_t
)*context_handle
;
1926 * Handle mechtok_in and mic_in only if they are
1927 * present in input_token. If neither is present, whether
1928 * this is an error depends on whether this is the first
1929 * round-trip. RET is set to a default value according to
1930 * whether it is the first round-trip.
1932 mechstat
= GSS_S_FAILURE
;
1933 if (negState
!= REQUEST_MIC
&& mechtok_in
!= GSS_C_NO_BUFFER
) {
1934 ret
= acc_ctx_call_acc(minor_status
, sc
,
1935 verifier_cred_handle
, mechtok_in
,
1936 mech_type
, &mechtok_out
,
1937 ret_flags
, time_rec
,
1938 delegated_cred_handle
,
1939 &negState
, &return_token
);
1940 } else if (negState
== REQUEST_MIC
) {
1941 mechstat
= GSS_S_CONTINUE_NEEDED
;
1944 /* Solaris SPNEGO */
1945 if (*minor_status
== ERR_SPNEGO_NEGOTIATION_FAILED
)
1946 spnego_gss_save_error_info(*minor_status
, sc
);
1948 if (!HARD_ERROR(ret
) && sc
->mech_complete
&&
1949 (sc
->ctx_flags
& GSS_C_INTEG_FLAG
)) {
1951 ret
= handle_mic(minor_status
, mic_in
,
1952 (mechtok_out
.length
!= 0),
1954 &negState
, &return_token
);
1958 if (return_token
== INIT_TOKEN_SEND
&& sendTokenInit
) {
1960 tmpret
= make_spnego_tokenInit_msg(sc
, 1, mic_out
, 0,
1962 return_token
, output_token
);
1964 ret
= GSS_S_FAILURE
;
1965 } else if (return_token
!= NO_TOKEN_SEND
&&
1966 return_token
!= CHECK_MIC
) {
1967 tmpret
= make_spnego_tokenTarg_msg(negState
,
1968 sc
? sc
->internal_mech
:
1970 &mechtok_out
, mic_out
,
1974 ret
= GSS_S_FAILURE
;
1976 if (ret
== GSS_S_COMPLETE
) {
1977 *context_handle
= (gss_ctx_id_t
)sc
->ctx_handle
;
1978 if (sc
->internal_name
!= GSS_C_NO_NAME
&&
1980 *src_name
= sc
->internal_name
;
1982 release_spnego_ctx(&sc
);
1984 gss_release_buffer(&tmpmin
, &mechtok_out
);
1985 if (mechtok_in
!= GSS_C_NO_BUFFER
) {
1986 gss_release_buffer(&tmpmin
, mechtok_in
);
1989 if (mic_in
!= GSS_C_NO_BUFFER
) {
1990 gss_release_buffer(&tmpmin
, mic_in
);
1993 if (mic_out
!= GSS_C_NO_BUFFER
) {
1994 gss_release_buffer(&tmpmin
, mic_out
);
1999 #endif /* LEAN_CLIENT */
2003 glue_spnego_gss_display_status(
2005 OM_uint32
*minor_status
,
2006 OM_uint32 status_value
,
2009 OM_uint32
*message_context
,
2010 gss_buffer_t status_string
)
2012 return (spnego_gss_display_status(minor_status
,
2022 spnego_gss_display_status(
2023 OM_uint32
*minor_status
,
2024 OM_uint32 status_value
,
2027 OM_uint32
*message_context
,
2028 gss_buffer_t status_string
)
2030 dsyslog("Entering display_status\n");
2032 *message_context
= 0;
2033 switch (status_value
) {
2034 case ERR_SPNEGO_NO_MECHS_AVAILABLE
:
2036 *status_string
= make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2038 case ERR_SPNEGO_NO_CREDS_ACQUIRED
:
2040 *status_string
= make_err_msg("SPNEGO failed to acquire creds");
2042 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR
:
2044 *status_string
= make_err_msg("SPNEGO acceptor did not select a mechanism");
2046 case ERR_SPNEGO_NEGOTIATION_FAILED
:
2048 return(spnego_gss_display_status2(minor_status
,
2054 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR
:
2056 *status_string
= make_err_msg("SPNEGO acceptor did not return a valid token");
2061 * If mech_spnego calls mech_krb5 (via libgss) and an
2062 * error occurs there, give it a shot.
2065 return(krb5_gss_display_status2(minor_status
,
2068 (gss_OID
)&gss_mech_krb5_oid
,
2074 dsyslog("Leaving display_status\n");
2075 return (GSS_S_COMPLETE
);
2080 glue_spnego_gss_import_name(
2082 OM_uint32
*minor_status
,
2083 gss_buffer_t input_name_buffer
,
2084 gss_OID input_name_type
,
2085 gss_name_t
*output_name
)
2087 return(spnego_gss_import_name(minor_status
,
2095 spnego_gss_import_name(
2096 OM_uint32
*minor_status
,
2097 gss_buffer_t input_name_buffer
,
2098 gss_OID input_name_type
,
2099 gss_name_t
*output_name
)
2103 dsyslog("Entering import_name\n");
2105 status
= gss_import_name(minor_status
, input_name_buffer
,
2106 input_name_type
, output_name
);
2108 dsyslog("Leaving import_name\n");
2114 glue_spnego_gss_release_name(
2116 OM_uint32
*minor_status
,
2117 gss_name_t
*input_name
)
2119 return(spnego_gss_release_name(minor_status
, input_name
));
2124 spnego_gss_release_name(
2125 OM_uint32
*minor_status
,
2126 gss_name_t
*input_name
)
2130 dsyslog("Entering release_name\n");
2132 status
= gss_release_name(minor_status
, input_name
);
2134 dsyslog("Leaving release_name\n");
2140 glue_spnego_gss_compare_name(
2142 OM_uint32
*minor_status
,
2143 const gss_name_t name1
,
2144 const gss_name_t name2
,
2147 return(spnego_gss_compare_name(minor_status
,
2154 spnego_gss_compare_name(
2155 OM_uint32
*minor_status
,
2156 const gss_name_t name1
,
2157 const gss_name_t name2
,
2160 OM_uint32 status
= GSS_S_COMPLETE
;
2161 dsyslog("Entering compare_name\n");
2163 status
= gss_compare_name(minor_status
, name1
, name2
, name_equal
);
2165 dsyslog("Leaving compare_name\n");
2171 glue_spnego_gss_display_name(
2173 OM_uint32
*minor_status
,
2174 gss_name_t input_name
,
2175 gss_buffer_t output_name_buffer
,
2176 gss_OID
*output_name_type
)
2178 return(spnego_gss_display_name(
2187 spnego_gss_display_name(
2188 OM_uint32
*minor_status
,
2189 gss_name_t input_name
,
2190 gss_buffer_t output_name_buffer
,
2191 gss_OID
*output_name_type
)
2193 OM_uint32 status
= GSS_S_COMPLETE
;
2194 dsyslog("Entering display_name\n");
2196 status
= gss_display_name(minor_status
, input_name
,
2197 output_name_buffer
, output_name_type
);
2199 dsyslog("Leaving display_name\n");
2206 glue_spnego_gss_inquire_names_for_mech(
2208 OM_uint32
*minor_status
,
2210 gss_OID_set
*name_types
)
2212 return(spnego_gss_inquire_names_for_mech(minor_status
,
2218 spnego_gss_inquire_names_for_mech(
2219 OM_uint32
*minor_status
,
2221 gss_OID_set
*name_types
)
2223 OM_uint32 major
, minor
;
2225 dsyslog("Entering inquire_names_for_mech\n");
2227 * We only know how to handle our own mechanism.
2229 if ((mechanism
!= GSS_C_NULL_OID
) &&
2230 !g_OID_equal(gss_mech_spnego
, mechanism
)) {
2232 return (GSS_S_FAILURE
);
2235 major
= gss_create_empty_oid_set(minor_status
, name_types
);
2236 if (major
== GSS_S_COMPLETE
) {
2237 /* Now add our members. */
2238 if (((major
= gss_add_oid_set_member(minor_status
,
2239 (gss_OID
) GSS_C_NT_USER_NAME
,
2240 name_types
)) == GSS_S_COMPLETE
) &&
2241 ((major
= gss_add_oid_set_member(minor_status
,
2242 (gss_OID
) GSS_C_NT_MACHINE_UID_NAME
,
2243 name_types
)) == GSS_S_COMPLETE
) &&
2244 ((major
= gss_add_oid_set_member(minor_status
,
2245 (gss_OID
) GSS_C_NT_STRING_UID_NAME
,
2246 name_types
)) == GSS_S_COMPLETE
)) {
2247 major
= gss_add_oid_set_member(minor_status
,
2248 (gss_OID
) GSS_C_NT_HOSTBASED_SERVICE
,
2252 if (major
!= GSS_S_COMPLETE
)
2253 (void) gss_release_oid_set(&minor
, name_types
);
2256 dsyslog("Leaving inquire_names_for_mech\n");
2262 OM_uint32
*minor_status
,
2263 gss_ctx_id_t context_handle
,
2264 gss_buffer_t input_message_buffer
,
2265 gss_buffer_t output_message_buffer
,
2267 gss_qop_t
*qop_state
)
2270 ret
= gss_unwrap(minor_status
,
2272 input_message_buffer
,
2273 output_message_buffer
,
2282 OM_uint32
*minor_status
,
2283 gss_ctx_id_t context_handle
,
2286 gss_buffer_t input_message_buffer
,
2288 gss_buffer_t output_message_buffer
)
2291 ret
= gss_wrap(minor_status
,
2295 input_message_buffer
,
2297 output_message_buffer
);
2303 spnego_gss_process_context_token(
2304 OM_uint32
*minor_status
,
2305 const gss_ctx_id_t context_handle
,
2306 const gss_buffer_t token_buffer
)
2309 ret
= gss_process_context_token(minor_status
,
2317 glue_spnego_gss_delete_sec_context(
2319 OM_uint32
*minor_status
,
2320 gss_ctx_id_t
*context_handle
,
2321 gss_buffer_t output_token
)
2323 return(spnego_gss_delete_sec_context(minor_status
,
2324 context_handle
, output_token
));
2328 spnego_gss_delete_sec_context(
2329 OM_uint32
*minor_status
,
2330 gss_ctx_id_t
*context_handle
,
2331 gss_buffer_t output_token
)
2333 OM_uint32 ret
= GSS_S_COMPLETE
;
2334 spnego_gss_ctx_id_t
*ctx
=
2335 (spnego_gss_ctx_id_t
*)context_handle
;
2337 if (context_handle
== NULL
)
2338 return (GSS_S_FAILURE
);
2341 * If this is still an SPNEGO mech, release it locally.
2344 (*ctx
)->magic_num
== SPNEGO_MAGIC_ID
) {
2345 (void) release_spnego_ctx(ctx
);
2346 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2348 output_token
->length
= 0;
2349 output_token
->value
= NULL
;
2352 ret
= gss_delete_sec_context(minor_status
,
2361 glue_spnego_gss_context_time(
2363 OM_uint32
*minor_status
,
2364 const gss_ctx_id_t context_handle
,
2365 OM_uint32
*time_rec
)
2367 return(spnego_gss_context_time(minor_status
,
2373 spnego_gss_context_time(
2374 OM_uint32
*minor_status
,
2375 const gss_ctx_id_t context_handle
,
2376 OM_uint32
*time_rec
)
2379 ret
= gss_context_time(minor_status
,
2387 glue_spnego_gss_export_sec_context(
2389 OM_uint32
*minor_status
,
2390 gss_ctx_id_t
*context_handle
,
2391 gss_buffer_t interprocess_token
)
2393 return(spnego_gss_export_sec_context(minor_status
,
2395 interprocess_token
));
2398 spnego_gss_export_sec_context(
2399 OM_uint32
*minor_status
,
2400 gss_ctx_id_t
*context_handle
,
2401 gss_buffer_t interprocess_token
)
2404 ret
= gss_export_sec_context(minor_status
,
2406 interprocess_token
);
2411 glue_spnego_gss_import_sec_context(
2413 OM_uint32
*minor_status
,
2414 const gss_buffer_t interprocess_token
,
2415 gss_ctx_id_t
*context_handle
)
2417 return(spnego_gss_import_sec_context(minor_status
,
2422 spnego_gss_import_sec_context(
2423 OM_uint32
*minor_status
,
2424 const gss_buffer_t interprocess_token
,
2425 gss_ctx_id_t
*context_handle
)
2428 ret
= gss_import_sec_context(minor_status
,
2433 #endif /* LEAN_CLIENT */
2436 glue_spnego_gss_inquire_context(
2438 OM_uint32
*minor_status
,
2439 const gss_ctx_id_t context_handle
,
2440 gss_name_t
*src_name
,
2441 gss_name_t
*targ_name
,
2442 OM_uint32
*lifetime_rec
,
2444 OM_uint32
*ctx_flags
,
2445 int *locally_initiated
,
2448 return(spnego_gss_inquire_context(
2461 spnego_gss_inquire_context(
2462 OM_uint32
*minor_status
,
2463 const gss_ctx_id_t context_handle
,
2464 gss_name_t
*src_name
,
2465 gss_name_t
*targ_name
,
2466 OM_uint32
*lifetime_rec
,
2468 OM_uint32
*ctx_flags
,
2469 int *locally_initiated
,
2472 OM_uint32 ret
= GSS_S_COMPLETE
;
2474 ret
= gss_inquire_context(minor_status
,
2488 glue_spnego_gss_wrap_size_limit(
2490 OM_uint32
*minor_status
,
2491 const gss_ctx_id_t context_handle
,
2494 OM_uint32 req_output_size
,
2495 OM_uint32
*max_input_size
)
2497 return(spnego_gss_wrap_size_limit(minor_status
,
2506 spnego_gss_wrap_size_limit(
2507 OM_uint32
*minor_status
,
2508 const gss_ctx_id_t context_handle
,
2511 OM_uint32 req_output_size
,
2512 OM_uint32
*max_input_size
)
2515 ret
= gss_wrap_size_limit(minor_status
,
2524 #if 0 /* SUNW17PACresync */
2527 OM_uint32
*minor_status
,
2528 const gss_ctx_id_t context_handle
,
2530 const gss_buffer_t message_buffer
,
2531 gss_buffer_t message_token
)
2534 ret
= gss_get_mic(minor_status
,
2544 spnego_gss_verify_mic(
2545 OM_uint32
*minor_status
,
2546 const gss_ctx_id_t context_handle
,
2547 const gss_buffer_t msg_buffer
,
2548 const gss_buffer_t token_buffer
,
2549 gss_qop_t
*qop_state
)
2552 ret
= gss_verify_mic(minor_status
,
2561 spnego_gss_inquire_sec_context_by_oid(
2562 OM_uint32
*minor_status
,
2563 const gss_ctx_id_t context_handle
,
2564 const gss_OID desired_object
,
2565 gss_buffer_set_t
*data_set
)
2568 ret
= gss_inquire_sec_context_by_oid(minor_status
,
2577 * These GSS funcs not needed yet, so disable them.
2578 * Revisit for full 1.7 resync.
2582 spnego_gss_set_sec_context_option(
2583 OM_uint32
*minor_status
,
2584 gss_ctx_id_t
*context_handle
,
2585 const gss_OID desired_object
,
2586 const gss_buffer_t value
)
2589 ret
= gss_set_sec_context_option(minor_status
,
2597 spnego_gss_wrap_aead(OM_uint32
*minor_status
,
2598 gss_ctx_id_t context_handle
,
2601 gss_buffer_t input_assoc_buffer
,
2602 gss_buffer_t input_payload_buffer
,
2604 gss_buffer_t output_message_buffer
)
2607 ret
= gss_wrap_aead(minor_status
,
2612 input_payload_buffer
,
2614 output_message_buffer
);
2620 spnego_gss_unwrap_aead(OM_uint32
*minor_status
,
2621 gss_ctx_id_t context_handle
,
2622 gss_buffer_t input_message_buffer
,
2623 gss_buffer_t input_assoc_buffer
,
2624 gss_buffer_t output_payload_buffer
,
2626 gss_qop_t
*qop_state
)
2629 ret
= gss_unwrap_aead(minor_status
,
2631 input_message_buffer
,
2633 output_payload_buffer
,
2640 spnego_gss_wrap_iov(OM_uint32
*minor_status
,
2641 gss_ctx_id_t context_handle
,
2645 gss_iov_buffer_desc
*iov
,
2649 ret
= gss_wrap_iov(minor_status
,
2660 spnego_gss_unwrap_iov(OM_uint32
*minor_status
,
2661 gss_ctx_id_t context_handle
,
2663 gss_qop_t
*qop_state
,
2664 gss_iov_buffer_desc
*iov
,
2668 ret
= gss_unwrap_iov(minor_status
,
2678 spnego_gss_wrap_iov_length(OM_uint32
*minor_status
,
2679 gss_ctx_id_t context_handle
,
2683 gss_iov_buffer_desc
*iov
,
2687 ret
= gss_wrap_iov_length(minor_status
,
2699 spnego_gss_complete_auth_token(
2700 OM_uint32
*minor_status
,
2701 const gss_ctx_id_t context_handle
,
2702 gss_buffer_t input_message_buffer
)
2705 ret
= gss_complete_auth_token(minor_status
,
2707 input_message_buffer
);
2713 * We will release everything but the ctx_handle so that it
2714 * can be passed back to init/accept context. This routine should
2715 * not be called until after the ctx_handle memory is assigned to
2716 * the supplied context handle from init/accept context.
2719 release_spnego_ctx(spnego_gss_ctx_id_t
*ctx
)
2721 spnego_gss_ctx_id_t context
;
2722 OM_uint32 minor_stat
;
2725 if (context
!= NULL
) {
2726 (void) gss_release_buffer(&minor_stat
,
2727 &context
->DER_mechTypes
);
2729 (void) generic_gss_release_oid(&minor_stat
,
2730 &context
->internal_mech
);
2732 if (context
->optionStr
!= NULL
) {
2733 free(context
->optionStr
);
2734 context
->optionStr
= NULL
;
2742 * Can't use gss_indicate_mechs by itself to get available mechs for
2743 * SPNEGO because it will also return the SPNEGO mech and we do not
2744 * want to consider SPNEGO as an available security mech for
2745 * negotiation. For this reason, get_available_mechs will return
2746 * all available mechs except SPNEGO.
2748 * If a ptr to a creds list is given, this function will attempt
2749 * to acquire creds for the creds given and trim the list of
2750 * returned mechanisms to only those for which creds are valid.
2754 get_available_mechs(OM_uint32
*minor_status
,
2755 gss_name_t name
, gss_cred_usage_t usage
,
2756 gss_cred_id_t
*creds
, gss_OID_set
*rmechs
)
2760 OM_uint32 major_status
= GSS_S_COMPLETE
, tmpmin
;
2761 gss_OID_set mechs
, goodmechs
;
2763 major_status
= gss_indicate_mechs(minor_status
, &mechs
);
2765 if (major_status
!= GSS_S_COMPLETE
) {
2766 return (major_status
);
2769 major_status
= gss_create_empty_oid_set(minor_status
, rmechs
);
2771 if (major_status
!= GSS_S_COMPLETE
) {
2772 (void) gss_release_oid_set(minor_status
, &mechs
);
2773 return (major_status
);
2776 for (i
= 0; i
< mechs
->count
&& major_status
== GSS_S_COMPLETE
; i
++) {
2777 if ((mechs
->elements
[i
].length
2778 != spnego_mechanism
.mech_type
.length
) ||
2779 memcmp(mechs
->elements
[i
].elements
,
2780 spnego_mechanism
.mech_type
.elements
,
2781 spnego_mechanism
.mech_type
.length
)) {
2783 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2784 * it never inferences any of the related OIDs of the
2785 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2786 * We add KRB5_WRONG here so that old MS clients can
2787 * negotiate this mechanism, which allows extensions
2788 * in Kerberos (clock skew adjustment, refresh ccache).
2790 if (is_kerb_mech(&mechs
->elements
[i
])) {
2791 extern gss_OID_desc
* const gss_mech_krb5_wrong
;
2794 gss_add_oid_set_member(minor_status
,
2795 gss_mech_krb5_wrong
, rmechs
);
2798 major_status
= gss_add_oid_set_member(minor_status
,
2799 &mechs
->elements
[i
],
2801 if (major_status
== GSS_S_COMPLETE
)
2807 * If the caller wanted a list of creds returned,
2808 * trim the list of mechanisms down to only those
2809 * for which the creds are valid.
2811 if (found
> 0 && major_status
== GSS_S_COMPLETE
&& creds
!= NULL
) {
2812 major_status
= gss_acquire_cred(minor_status
,
2813 name
, GSS_C_INDEFINITE
,
2814 *rmechs
, usage
, creds
,
2818 * Drop the old list in favor of the new
2821 (void) gss_release_oid_set(&tmpmin
, rmechs
);
2822 if (major_status
== GSS_S_COMPLETE
) {
2823 (void) gssint_copy_oid_set(&tmpmin
,
2825 (void) gss_release_oid_set(&tmpmin
, &goodmechs
);
2829 (void) gss_release_oid_set(&tmpmin
, &mechs
);
2830 if (found
== 0 || major_status
!= GSS_S_COMPLETE
) {
2831 *minor_status
= ERR_SPNEGO_NO_MECHS_AVAILABLE
;
2832 map_errcode(minor_status
);
2833 if (major_status
== GSS_S_COMPLETE
)
2834 major_status
= GSS_S_FAILURE
;
2837 return (major_status
);
2840 /* following are token creation and reading routines */
2843 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2844 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2848 get_mech_oid(OM_uint32
*minor_status
, unsigned char **buff_in
, size_t length
)
2852 gss_OID mech_out
= NULL
;
2853 unsigned char *start
, *end
;
2855 if (length
< 1 || **buff_in
!= MECH_OID
)
2859 end
= start
+ length
;
2862 toid
.length
= *(*buff_in
)++;
2864 if ((*buff_in
+ toid
.length
) > end
)
2867 toid
.elements
= *buff_in
;
2868 *buff_in
+= toid
.length
;
2870 status
= generic_gss_copy_oid(minor_status
, &toid
, &mech_out
);
2872 if (status
!= GSS_S_COMPLETE
) {
2873 map_errcode(minor_status
);
2881 * der encode the given mechanism oid into buf_out, advancing the
2886 put_mech_oid(unsigned char **buf_out
, gss_OID_const mech
, unsigned int buflen
)
2888 if (buflen
< mech
->length
+ 2)
2890 *(*buf_out
)++ = MECH_OID
;
2891 *(*buf_out
)++ = (unsigned char) mech
->length
;
2892 memcpy((void *)(*buf_out
), mech
->elements
, mech
->length
);
2893 *buf_out
+= mech
->length
;
2898 * verify that buff_in points to an octet string, if it does not,
2899 * return NULL and don't advance the pointer. If it is an octet string
2900 * decode buff_in into a gss_buffer_t and return it, advancing the
2904 get_input_token(unsigned char **buff_in
, unsigned int buff_length
)
2906 gss_buffer_t input_token
;
2909 if (**buff_in
!= OCTET_STRING
)
2913 input_token
= (gss_buffer_t
)malloc(sizeof (gss_buffer_desc
));
2915 if (input_token
== NULL
)
2918 input_token
->length
= gssint_get_der_length(buff_in
, buff_length
, &bytes
);
2919 if ((int)input_token
->length
== -1) {
2923 input_token
->value
= malloc(input_token
->length
);
2925 if (input_token
->value
== NULL
) {
2930 (void) memcpy(input_token
->value
, *buff_in
, input_token
->length
);
2931 *buff_in
+= input_token
->length
;
2932 return (input_token
);
2936 * verify that the input token length is not 0. If it is, just return.
2937 * If the token length is greater than 0, der encode as an octet string
2938 * and place in buf_out, advancing buf_out.
2942 put_input_token(unsigned char **buf_out
, gss_buffer_t input_token
,
2943 unsigned int buflen
)
2947 /* if token length is 0, we do not want to send */
2948 if (input_token
->length
== 0)
2951 if (input_token
->length
> buflen
)
2954 *(*buf_out
)++ = OCTET_STRING
;
2955 if ((ret
= gssint_put_der_length(input_token
->length
, buf_out
,
2956 input_token
->length
)))
2958 TWRITE_STR(*buf_out
, input_token
->value
, input_token
->length
);
2963 * verify that buff_in points to a sequence of der encoding. The mech
2964 * set is the only sequence of encoded object in the token, so if it is
2965 * a sequence of encoding, decode the mechset into a gss_OID_set and
2966 * return it, advancing the buffer pointer.
2969 get_mech_set(OM_uint32
*minor_status
, unsigned char **buff_in
,
2970 unsigned int buff_length
)
2972 gss_OID_set returned_mechSet
;
2973 OM_uint32 major_status
;
2974 int length
; /* SUNW17PACresync */
2976 OM_uint32 set_length
;
2977 unsigned char *start
;
2980 if (**buff_in
!= SEQUENCE_OF
)
2986 length
= gssint_get_der_length(buff_in
, buff_length
, &bytes
);
2987 if (length
< 0) /* SUNW17PACresync - MIT17 lacks this check */
2990 major_status
= gss_create_empty_oid_set(minor_status
,
2992 if (major_status
!= GSS_S_COMPLETE
)
2995 for (set_length
= 0, i
= 0; set_length
< length
; i
++) {
2996 gss_OID_desc
*temp
= get_mech_oid(minor_status
, buff_in
,
2997 buff_length
- (*buff_in
- start
));
2999 major_status
= gss_add_oid_set_member(minor_status
,
3000 temp
, &returned_mechSet
);
3001 if (major_status
== GSS_S_COMPLETE
) {
3002 set_length
+= returned_mechSet
->elements
[i
].length
+2;
3003 if (generic_gss_release_oid(minor_status
, &temp
))
3004 map_errcode(minor_status
);
3009 return (returned_mechSet
);
3013 * Encode mechSet into buf.
3016 put_mech_set(gss_OID_set mechSet
, gss_buffer_t buf
)
3020 unsigned int tlen
, ilen
;
3023 for (i
= 0; i
< mechSet
->count
; i
++) {
3025 * 0x06 [DER LEN] [OID]
3028 gssint_der_length_size(mechSet
->elements
[i
].length
) +
3029 mechSet
->elements
[i
].length
;
3034 tlen
= 1 + gssint_der_length_size(ilen
) + ilen
;
3041 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3043 *ptr
++ = SEQUENCE_OF
;
3044 if (gssint_put_der_length(ilen
, &ptr
, REMAIN
) < 0)
3046 for (i
= 0; i
< mechSet
->count
; i
++) {
3047 if (put_mech_oid(&ptr
, &mechSet
->elements
[i
], REMAIN
) < 0) {
3056 * Verify that buff_in is pointing to a BIT_STRING with the correct
3057 * length and padding for the req_flags. If it is, decode req_flags
3058 * and return them, otherwise, return NULL.
3061 get_req_flags(unsigned char **buff_in
, OM_uint32 bodysize
,
3062 OM_uint32
*req_flags
)
3066 if (**buff_in
!= (CONTEXT
| 0x01))
3069 if (g_get_tag_and_length(buff_in
, (CONTEXT
| 0x01),
3070 bodysize
, &len
) < 0)
3071 return GSS_S_DEFECTIVE_TOKEN
;
3073 if (*(*buff_in
)++ != BIT_STRING
)
3074 return GSS_S_DEFECTIVE_TOKEN
;
3076 if (*(*buff_in
)++ != BIT_STRING_LENGTH
)
3077 return GSS_S_DEFECTIVE_TOKEN
;
3079 if (*(*buff_in
)++ != BIT_STRING_PADDING
)
3080 return GSS_S_DEFECTIVE_TOKEN
;
3082 *req_flags
= (OM_uint32
) (*(*buff_in
)++ >> 1);
3087 get_negTokenInit(OM_uint32
*minor_status
,
3089 gss_buffer_t der_mechSet
,
3090 gss_OID_set
*mechSet
,
3091 OM_uint32
*req_flags
,
3092 gss_buffer_t
*mechtok
,
3093 gss_buffer_t
*mechListMIC
)
3096 unsigned char *ptr
, *bufstart
;
3098 gss_buffer_desc tmpbuf
;
3101 der_mechSet
->length
= 0;
3102 der_mechSet
->value
= NULL
;
3103 *mechSet
= GSS_C_NO_OID_SET
;
3105 *mechtok
= *mechListMIC
= GSS_C_NO_BUFFER
;
3107 ptr
= bufstart
= buf
->value
;
3108 if ((buf
->length
- (ptr
- bufstart
)) > INT_MAX
)
3109 return GSS_S_FAILURE
;
3110 #define REMAIN (buf->length - (ptr - bufstart))
3112 err
= g_verify_token_header(gss_mech_spnego
,
3113 &len
, &ptr
, 0, REMAIN
);
3115 *minor_status
= err
;
3116 map_errcode(minor_status
);
3117 return GSS_S_FAILURE
;
3119 *minor_status
= g_verify_neg_token_init(&ptr
, REMAIN
);
3120 if (*minor_status
) {
3121 map_errcode(minor_status
);
3122 return GSS_S_FAILURE
;
3125 /* alias into input_token */
3127 tmpbuf
.length
= REMAIN
;
3128 *mechSet
= get_mech_set(minor_status
, &ptr
, REMAIN
);
3129 if (*mechSet
== NULL
)
3130 return GSS_S_FAILURE
;
3132 tmpbuf
.length
= ptr
- (unsigned char *)tmpbuf
.value
;
3133 der_mechSet
->value
= malloc(tmpbuf
.length
);
3134 if (der_mechSet
->value
== NULL
)
3135 return GSS_S_FAILURE
;
3136 memcpy(der_mechSet
->value
, tmpbuf
.value
, tmpbuf
.length
);
3137 der_mechSet
->length
= tmpbuf
.length
;
3139 err
= get_req_flags(&ptr
, REMAIN
, req_flags
);
3140 if (err
!= GSS_S_COMPLETE
) {
3143 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x02),
3144 REMAIN
, &len
) >= 0) {
3145 *mechtok
= get_input_token(&ptr
, len
);
3146 if (*mechtok
== GSS_C_NO_BUFFER
) {
3147 return GSS_S_FAILURE
;
3150 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x03),
3151 REMAIN
, &len
) >= 0) {
3152 *mechListMIC
= get_input_token(&ptr
, len
);
3153 if (*mechListMIC
== GSS_C_NO_BUFFER
) {
3154 return GSS_S_FAILURE
;
3157 return GSS_S_COMPLETE
;
3162 get_negTokenResp(OM_uint32
*minor_status
,
3163 unsigned char *buf
, unsigned int buflen
,
3164 OM_uint32
*negState
,
3165 gss_OID
*supportedMech
,
3166 gss_buffer_t
*responseToken
,
3167 gss_buffer_t
*mechListMIC
)
3169 unsigned char *ptr
, *bufstart
;
3172 unsigned int tag
, bytes
;
3174 *negState
= ACCEPT_DEFECTIVE_TOKEN
;
3175 *supportedMech
= GSS_C_NO_OID
;
3176 *responseToken
= *mechListMIC
= GSS_C_NO_BUFFER
;
3177 ptr
= bufstart
= buf
;
3178 #define REMAIN (buflen - (ptr - bufstart))
3180 if (g_get_tag_and_length(&ptr
, (CONTEXT
| 0x01), REMAIN
, &len
) < 0)
3181 return GSS_S_DEFECTIVE_TOKEN
;
3182 if (*ptr
++ == SEQUENCE
) {
3183 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3185 return GSS_S_DEFECTIVE_TOKEN
;
3192 if (tag
== CONTEXT
) {
3193 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3195 return GSS_S_DEFECTIVE_TOKEN
;
3197 if (g_get_tag_and_length(&ptr
, ENUMERATED
,
3199 return GSS_S_DEFECTIVE_TOKEN
;
3201 if (len
!= ENUMERATION_LENGTH
)
3202 return GSS_S_DEFECTIVE_TOKEN
;
3205 return GSS_S_DEFECTIVE_TOKEN
;
3213 if (tag
== (CONTEXT
| 0x01)) {
3214 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3216 return GSS_S_DEFECTIVE_TOKEN
;
3218 *supportedMech
= get_mech_oid(minor_status
, &ptr
, REMAIN
);
3219 if (*supportedMech
== GSS_C_NO_OID
)
3220 return GSS_S_DEFECTIVE_TOKEN
;
3227 if (tag
== (CONTEXT
| 0x02)) {
3228 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3230 return GSS_S_DEFECTIVE_TOKEN
;
3232 *responseToken
= get_input_token(&ptr
, REMAIN
);
3233 if (*responseToken
== GSS_C_NO_BUFFER
)
3234 return GSS_S_DEFECTIVE_TOKEN
;
3241 if (tag
== (CONTEXT
| 0x03)) {
3242 tmplen
= gssint_get_der_length(&ptr
, REMAIN
, &bytes
);
3244 return GSS_S_DEFECTIVE_TOKEN
;
3246 *mechListMIC
= get_input_token(&ptr
, REMAIN
);
3247 if (*mechListMIC
== GSS_C_NO_BUFFER
)
3248 return GSS_S_DEFECTIVE_TOKEN
;
3250 return GSS_S_COMPLETE
;
3255 * der encode the passed negResults as an ENUMERATED type and
3256 * place it in buf_out, advancing the buffer.
3260 put_negResult(unsigned char **buf_out
, OM_uint32 negResult
,
3261 unsigned int buflen
)
3265 *(*buf_out
)++ = ENUMERATED
;
3266 *(*buf_out
)++ = ENUMERATION_LENGTH
;
3267 *(*buf_out
)++ = (unsigned char) negResult
;
3272 * This routine compares the recieved mechset to the mechset that
3273 * this server can support. It looks sequentially through the mechset
3274 * and the first one that matches what the server can support is
3275 * chosen as the negotiated mechanism. If one is found, negResult
3276 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3277 * it's not the first mech, otherwise we return NULL and negResult
3280 * NOTE: There is currently no way to specify a preference order of
3281 * mechanisms supported by the acceptor.
3284 negotiate_mech_type(OM_uint32
*minor_status
,
3285 gss_OID_set supported_mechSet
,
3286 gss_OID_set mechset
,
3287 OM_uint32
*negResult
)
3289 gss_OID returned_mech
;
3294 for (i
= 0; i
< mechset
->count
; i
++) {
3295 gss_OID mech_oid
= &mechset
->elements
[i
];
3298 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3299 * we actually want to select it if the client supports, as this
3300 * will enable features on MS clients that allow credential
3301 * refresh on rekeying and caching system times from servers.
3304 /* Accept wrong mechanism OID from MS clients */
3305 if (mech_oid
->length
== gss_mech_krb5_wrong_oid
.length
&&
3306 memcmp(mech_oid
->elements
, gss_mech_krb5_wrong_oid
.elements
, mech_oid
->length
) == 0)
3307 mech_oid
= (gss_OID
)&gss_mech_krb5_oid
;
3310 gss_test_oid_set_member(minor_status
, mech_oid
, supported_mechSet
, &present
);
3315 *negResult
= ACCEPT_INCOMPLETE
;
3317 *negResult
= REQUEST_MIC
;
3319 status
= generic_gss_copy_oid(minor_status
,
3320 &mechset
->elements
[i
],
3322 if (status
!= GSS_S_COMPLETE
) {
3323 *negResult
= REJECT
;
3324 map_errcode(minor_status
);
3327 return (returned_mech
);
3329 /* Solaris SPNEGO */
3330 *minor_status
= ERR_SPNEGO_NEGOTIATION_FAILED
;
3332 *negResult
= REJECT
;
3337 * the next two routines make a token buffer suitable for
3338 * spnego_gss_display_status. These currently take the string
3339 * in name and place it in the token. Eventually, if
3340 * spnego_gss_display_status returns valid error messages,
3341 * these routines will be changes to return the error string.
3343 static spnego_token_t
3344 make_spnego_token(char *name
)
3346 return (spnego_token_t
)strdup(name
);
3349 static gss_buffer_desc
3350 make_err_msg(char *name
)
3352 gss_buffer_desc buffer
;
3356 buffer
.value
= NULL
;
3358 buffer
.length
= strlen(name
)+1;
3359 buffer
.value
= make_spnego_token(name
);
3366 * Create the client side spnego token passed back to gss_init_sec_context
3367 * and eventually up to the application program and over to the server.
3369 * Use DER rules, definite length method per RFC 2478
3372 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx
,
3374 gss_buffer_t mechListMIC
, OM_uint32 req_flags
,
3375 gss_buffer_t data
, send_token_flag sendtoken
,
3376 gss_buffer_t outbuf
)
3379 unsigned int tlen
, dataLen
= 0;
3380 unsigned int negTokenInitSize
= 0;
3381 unsigned int negTokenInitSeqSize
= 0;
3382 unsigned int negTokenInitContSize
= 0;
3383 unsigned int rspTokenSize
= 0;
3384 unsigned int mechListTokenSize
= 0;
3385 unsigned int micTokenSize
= 0;
3389 if (outbuf
== GSS_C_NO_BUFFER
)
3393 outbuf
->value
= NULL
;
3395 /* calculate the data length */
3398 * 0xa0 [DER LEN] [mechTypes]
3400 mechListTokenSize
= 1 +
3401 gssint_der_length_size(spnego_ctx
->DER_mechTypes
.length
) +
3402 spnego_ctx
->DER_mechTypes
.length
;
3403 dataLen
+= mechListTokenSize
;
3406 * If a token from gss_init_sec_context exists,
3407 * add the length of the token + the ASN.1 overhead
3411 * Encoded in final output as:
3412 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3413 * -----s--------|--------s2----------
3416 gssint_der_length_size(data
->length
) +
3418 dataLen
+= 1 + gssint_der_length_size(rspTokenSize
) +
3424 * Encoded in final output as:
3425 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3426 * --s-- -----tlen------------
3429 gssint_der_length_size(mechListMIC
->length
) +
3430 mechListMIC
->length
;
3432 gssint_der_length_size(micTokenSize
) +
3437 * Add size of DER encoding
3438 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3439 * 0x30 [DER_LEN] [data]
3442 negTokenInitContSize
= dataLen
;
3443 negTokenInitSeqSize
= 1 + gssint_der_length_size(dataLen
) + dataLen
;
3444 dataLen
= negTokenInitSeqSize
;
3447 * negTokenInitSize indicates the bytes needed to
3448 * hold the ASN.1 encoding of the entire NegTokenInit
3450 * 0xa0 [DER_LEN] + data
3453 negTokenInitSize
= 1 +
3454 gssint_der_length_size(negTokenInitSeqSize
) +
3455 negTokenInitSeqSize
;
3457 tlen
= g_token_size(gss_mech_spnego
, negTokenInitSize
);
3459 t
= (unsigned char *) malloc(tlen
);
3467 /* create the message */
3468 if ((ret
= g_make_token_header(gss_mech_spnego
, negTokenInitSize
,
3472 *ptr
++ = CONTEXT
; /* NegotiationToken identifier */
3473 if ((ret
= gssint_put_der_length(negTokenInitSeqSize
, &ptr
, tlen
)))
3477 if ((ret
= gssint_put_der_length(negTokenInitContSize
, &ptr
,
3478 tlen
- (int)(ptr
-t
))))
3481 *ptr
++ = CONTEXT
| 0x00; /* MechTypeList identifier */
3482 if ((ret
= gssint_put_der_length(spnego_ctx
->DER_mechTypes
.length
,
3483 &ptr
, tlen
- (int)(ptr
-t
))))
3486 /* We already encoded the MechSetList */
3487 (void) memcpy(ptr
, spnego_ctx
->DER_mechTypes
.value
,
3488 spnego_ctx
->DER_mechTypes
.length
);
3490 ptr
+= spnego_ctx
->DER_mechTypes
.length
;
3493 *ptr
++ = CONTEXT
| 0x02;
3494 if ((ret
= gssint_put_der_length(rspTokenSize
,
3495 &ptr
, tlen
- (int)(ptr
- t
))))
3498 if ((ret
= put_input_token(&ptr
, data
,
3499 tlen
- (int)(ptr
- t
))))
3503 if (mechListMIC
!= GSS_C_NO_BUFFER
) {
3504 *ptr
++ = CONTEXT
| 0x03;
3505 if ((ret
= gssint_put_der_length(micTokenSize
,
3506 &ptr
, tlen
- (int)(ptr
- t
))))
3509 if (negHintsCompat
) {
3510 ret
= put_neg_hints(&ptr
, mechListMIC
,
3511 tlen
- (int)(ptr
- t
));
3514 } else if ((ret
= put_input_token(&ptr
, mechListMIC
,
3515 tlen
- (int)(ptr
- t
))))
3526 outbuf
->length
= tlen
;
3527 outbuf
->value
= (void *) t
;
3533 * create the server side spnego token passed back to
3534 * gss_accept_sec_context and eventually up to the application program
3535 * and over to the client.
3538 make_spnego_tokenTarg_msg(OM_uint32 status
, gss_OID mech_wanted
,
3539 gss_buffer_t data
, gss_buffer_t mechListMIC
,
3540 send_token_flag sendtoken
,
3541 gss_buffer_t outbuf
)
3543 unsigned int tlen
= 0;
3544 unsigned int ret
= 0;
3545 unsigned int NegTokenTargSize
= 0;
3546 unsigned int NegTokenSize
= 0;
3547 unsigned int rspTokenSize
= 0;
3548 unsigned int micTokenSize
= 0;
3549 unsigned int dataLen
= 0;
3553 if (outbuf
== GSS_C_NO_BUFFER
)
3554 return (GSS_S_DEFECTIVE_TOKEN
);
3557 outbuf
->value
= NULL
;
3560 * ASN.1 encoding of the negResult
3561 * ENUMERATED type is 3 bytes
3562 * ENUMERATED TAG, Length, Value,
3563 * Plus 2 bytes for the CONTEXT id and length.
3568 * calculate data length
3570 * If this is the initial token, include length of
3571 * mech_type and the negotiation result fields.
3573 if (sendtoken
== INIT_TOKEN_SEND
) {
3574 int mechlistTokenSize
;
3576 * 1 byte for the CONTEXT ID(0xa0),
3577 * 1 byte for the OID ID(0x06)
3578 * 1 byte for OID Length field
3579 * Plus the rest... (OID Length, OID value)
3581 mechlistTokenSize
= 3 + mech_wanted
->length
+
3582 gssint_der_length_size(mech_wanted
->length
);
3584 dataLen
+= mechlistTokenSize
;
3586 if (data
!= NULL
&& data
->length
> 0) {
3587 /* Length of the inner token */
3588 rspTokenSize
= 1 + gssint_der_length_size(data
->length
) +
3591 dataLen
+= rspTokenSize
;
3593 /* Length of the outer token */
3594 dataLen
+= 1 + gssint_der_length_size(rspTokenSize
);
3596 if (mechListMIC
!= NULL
) {
3598 /* Length of the inner token */
3599 micTokenSize
= 1 + gssint_der_length_size(mechListMIC
->length
) +
3600 mechListMIC
->length
;
3602 dataLen
+= micTokenSize
;
3604 /* Length of the outer token */
3605 dataLen
+= 1 + gssint_der_length_size(micTokenSize
);
3608 * Add size of DER encoded:
3609 * NegTokenTarg [ SEQUENCE ] of
3610 * NegResult[0] ENUMERATED {
3611 * accept_completed(0),
3612 * accept_incomplete(1),
3614 * supportedMech [1] MechType OPTIONAL,
3615 * responseToken [2] OCTET STRING OPTIONAL,
3616 * mechListMIC [3] OCTET STRING OPTIONAL
3618 * size = data->length + MechListMic + SupportedMech len +
3619 * Result Length + ASN.1 overhead
3621 NegTokenTargSize
= dataLen
;
3622 dataLen
+= 1 + gssint_der_length_size(NegTokenTargSize
);
3625 * NegotiationToken [ CHOICE ]{
3626 * negTokenInit [0] NegTokenInit,
3627 * negTokenTarg [1] NegTokenTarg }
3629 NegTokenSize
= dataLen
;
3630 dataLen
+= 1 + gssint_der_length_size(NegTokenSize
);
3633 t
= (unsigned char *) malloc(tlen
);
3636 ret
= GSS_S_DEFECTIVE_TOKEN
;
3643 * Indicate that we are sending CHOICE 1
3646 *ptr
++ = CONTEXT
| 0x01;
3647 if (gssint_put_der_length(NegTokenSize
, &ptr
, dataLen
) < 0) {
3648 ret
= GSS_S_DEFECTIVE_TOKEN
;
3652 if (gssint_put_der_length(NegTokenTargSize
, &ptr
,
3653 tlen
- (int)(ptr
-t
)) < 0) {
3654 ret
= GSS_S_DEFECTIVE_TOKEN
;
3659 * First field of the NegTokenTarg SEQUENCE
3660 * is the ENUMERATED NegResult.
3663 if (gssint_put_der_length(3, &ptr
,
3664 tlen
- (int)(ptr
-t
)) < 0) {
3665 ret
= GSS_S_DEFECTIVE_TOKEN
;
3668 if (put_negResult(&ptr
, status
, tlen
- (int)(ptr
- t
)) < 0) {
3669 ret
= GSS_S_DEFECTIVE_TOKEN
;
3672 if (sendtoken
== INIT_TOKEN_SEND
) {
3674 * Next, is the Supported MechType
3676 *ptr
++ = CONTEXT
| 0x01;
3677 if (gssint_put_der_length(mech_wanted
->length
+ 2,
3679 tlen
- (int)(ptr
- t
)) < 0) {
3680 ret
= GSS_S_DEFECTIVE_TOKEN
;
3683 if (put_mech_oid(&ptr
, mech_wanted
,
3684 tlen
- (int)(ptr
- t
)) < 0) {
3685 ret
= GSS_S_DEFECTIVE_TOKEN
;
3689 if (data
!= NULL
&& data
->length
> 0) {
3690 *ptr
++ = CONTEXT
| 0x02;
3691 if (gssint_put_der_length(rspTokenSize
, &ptr
,
3692 tlen
- (int)(ptr
- t
)) < 0) {
3693 ret
= GSS_S_DEFECTIVE_TOKEN
;
3696 if (put_input_token(&ptr
, data
,
3697 tlen
- (int)(ptr
- t
)) < 0) {
3698 ret
= GSS_S_DEFECTIVE_TOKEN
;
3702 if (mechListMIC
!= NULL
) {
3703 *ptr
++ = CONTEXT
| 0x03;
3704 if (gssint_put_der_length(micTokenSize
, &ptr
,
3705 tlen
- (int)(ptr
- t
)) < 0) {
3706 ret
= GSS_S_DEFECTIVE_TOKEN
;
3709 if (put_input_token(&ptr
, mechListMIC
,
3710 tlen
- (int)(ptr
- t
)) < 0) {
3711 ret
= GSS_S_DEFECTIVE_TOKEN
;
3715 ret
= GSS_S_COMPLETE
;
3717 if (ret
!= GSS_S_COMPLETE
) {
3721 outbuf
->length
= ptr
- t
;
3722 outbuf
->value
= (void *) t
;
3728 /* determine size of token */
3730 g_token_size(gss_OID_const mech
, unsigned int body_size
)
3735 * Initialize the header size to the
3736 * MECH_OID byte + the bytes needed to indicate the
3737 * length of the OID + the OID itself.
3739 * 0x06 [MECHLENFIELD] MECHDATA
3741 hdrsize
= 1 + gssint_der_length_size(mech
->length
) + mech
->length
;
3744 * Now add the bytes needed for the initial header
3746 * 0x60 + [DER_LEN] + HDRSIZE
3748 hdrsize
+= 1 + gssint_der_length_size(body_size
+ hdrsize
);
3750 return (hdrsize
+ body_size
);
3754 * generate token header.
3756 * Use DER Definite Length method per RFC2478
3757 * Use of indefinite length encoding will not be compatible
3758 * with Microsoft or others that actually follow the spec.
3761 g_make_token_header(gss_OID_const mech
,
3762 unsigned int body_size
,
3763 unsigned char **buf
,
3764 unsigned int totallen
)
3767 unsigned int hdrsize
;
3768 unsigned char *p
= *buf
;
3770 hdrsize
= 1 + gssint_der_length_size(mech
->length
) + mech
->length
;
3772 *(*buf
)++ = HEADER_ID
;
3773 if ((ret
= gssint_put_der_length(hdrsize
+ body_size
, buf
, totallen
)))
3776 *(*buf
)++ = MECH_OID
;
3777 if ((ret
= gssint_put_der_length(mech
->length
, buf
,
3778 totallen
- (int)(p
- *buf
))))
3780 TWRITE_STR(*buf
, mech
->elements
, mech
->length
);
3785 * NOTE: This checks that the length returned by
3786 * gssint_get_der_length() is not greater than the number of octets
3787 * remaining, even though gssint_get_der_length() already checks, in
3791 g_get_tag_and_length(unsigned char **buf
, int tag
,
3792 unsigned int buflen
, unsigned int *outlen
)
3794 unsigned char *ptr
= *buf
;
3795 int ret
= -1; /* pessimists, assume failure ! */
3796 unsigned int encoded_len
;
3797 unsigned int tmplen
= 0;
3800 if (buflen
> 1 && *ptr
== tag
) {
3802 tmplen
= gssint_get_der_length(&ptr
, buflen
- 1,
3806 } else if (tmplen
> buflen
- (ptr
- *buf
)) {
3817 g_verify_neg_token_init(unsigned char **buf_in
, unsigned int cur_size
)
3819 unsigned char *buf
= *buf_in
;
3820 unsigned char *endptr
= buf
+ cur_size
;
3821 unsigned int seqsize
;
3826 * Verify this is a NegotiationToken type token
3827 * - check for a0(context specific identifier)
3828 * - get length and verify that enoughd ata exists
3830 if (g_get_tag_and_length(&buf
, CONTEXT
, cur_size
, &seqsize
) < 0)
3831 return (G_BAD_TOK_HEADER
);
3833 cur_size
= seqsize
; /* should indicate bytes remaining */
3836 * Verify the next piece, it should identify this as
3837 * a strucure of type NegTokenInit.
3839 if (*buf
++ == SEQUENCE
) {
3840 if ((seqsize
= gssint_get_der_length(&buf
, cur_size
, &bytes
)) < 0)
3841 return (G_BAD_TOK_HEADER
);
3843 * Make sure we have the entire buffer as described
3845 if (buf
+ seqsize
> endptr
)
3846 return (G_BAD_TOK_HEADER
);
3848 return (G_BAD_TOK_HEADER
);
3851 cur_size
= seqsize
; /* should indicate bytes remaining */
3854 * Verify that the first blob is a sequence of mechTypes
3856 if (*buf
++ == CONTEXT
) {
3857 if ((seqsize
= gssint_get_der_length(&buf
, cur_size
, &bytes
)) < 0)
3858 return (G_BAD_TOK_HEADER
);
3860 * Make sure we have the entire buffer as described
3862 if (buf
+ bytes
> endptr
)
3863 return (G_BAD_TOK_HEADER
);
3865 return (G_BAD_TOK_HEADER
);
3869 * At this point, *buf should be at the beginning of the
3870 * DER encoded list of mech types that are to be negotiated.
3878 /* verify token header. */
3880 g_verify_token_header(gss_OID_const mech
,
3881 unsigned int *body_size
,
3882 unsigned char **buf_in
,
3884 unsigned int toksize
)
3886 unsigned char *buf
= *buf_in
;
3893 return (G_BAD_TOK_HEADER
);
3895 if (*buf
++ != HEADER_ID
)
3896 return (G_BAD_TOK_HEADER
);
3898 if ((seqsize
= gssint_get_der_length(&buf
, toksize
, &bytes
)) < 0)
3899 return (G_BAD_TOK_HEADER
);
3901 if ((seqsize
+ bytes
) != toksize
)
3902 return (G_BAD_TOK_HEADER
);
3905 return (G_BAD_TOK_HEADER
);
3908 if (*buf
++ != MECH_OID
)
3909 return (G_BAD_TOK_HEADER
);
3912 return (G_BAD_TOK_HEADER
);
3914 toid
.length
= *buf
++;
3916 if (toksize
< toid
.length
)
3917 return (G_BAD_TOK_HEADER
);
3919 toksize
-= toid
.length
;
3921 toid
.elements
= buf
;
3924 if (!g_OID_equal(&toid
, mech
))
3928 * G_WRONG_MECH is not returned immediately because it's more important
3929 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3932 return (G_BAD_TOK_HEADER
);
3938 *body_size
= toksize
;
3945 * Return non-zero if the oid is one of the kerberos mech oids,
3946 * otherwise return zero.
3948 * N.B. There are 3 oids that represent the kerberos mech:
3949 * RFC-specified GSS_MECH_KRB5_OID,
3950 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3951 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3955 is_kerb_mech(gss_OID oid
)
3959 extern const gss_OID_set_desc
* const gss_mech_set_krb5_both
;
3961 (void) gss_test_oid_set_member(&minor
,
3962 oid
, (gss_OID_set
)gss_mech_set_krb5_both
, &answer
);