2 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
5 * Export of this software from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11 * distribute this software and its documentation for any purpose and
12 * without fee is hereby granted, provided that the above copyright
13 * notice appear in all copies and that both that copyright notice and
14 * this permission notice appear in supporting documentation, and that
15 * the name of M.I.T. not be used in advertising or publicity pertaining
16 * to distribution of the software without specific, written prior
17 * permission. Furthermore if you modify this software you must label
18 * your software as modified software and not distribute it in such a
19 * fashion that it might be confused with the original M.I.T. software.
20 * M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
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. SPNEGO is specified in RFC 4178.
36 * Copyright (c) 2006-2008, Novell, Inc.
37 * All rights reserved.
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions are met:
42 * * Redistributions of source code must retain the above copyright notice,
43 * this list of conditions and the following disclaimer.
44 * * Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * * The copyright holder's name is not used to endorse or promote products
48 * derived from this software without specific prior written permission.
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
62 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
71 #include "gssapiP_spnego.h"
72 #include <gssapi_err_generic.h>
76 #undef g_verify_token_header
77 #undef g_make_token_header
79 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
80 typedef const gss_OID_desc *gss_OID_const;
82 /* der routines defined in libgss */
83 extern unsigned int gssint_der_length_size(OM_uint32);
84 extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
85 extern int gssint_put_der_length(OM_uint32, unsigned char **, unsigned int);
88 /* private routines for spnego_mechanism */
89 static spnego_token_t make_spnego_token(char *);
90 static gss_buffer_desc make_err_msg(char *);
91 static int g_token_size(gss_OID_const, unsigned int);
92 static int g_make_token_header(gss_OID_const, unsigned int,
93 unsigned char **, unsigned int);
94 static int g_verify_token_header(gss_OID_const, unsigned int *,
97 static int g_verify_neg_token_init(unsigned char **, unsigned int);
98 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
99 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
100 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
101 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
102 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
103 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
104 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
105 gss_cred_usage_t, gss_OID_set *);
106 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
107 static void check_spnego_options(spnego_gss_ctx_id_t);
108 static spnego_gss_ctx_id_t create_spnego_ctx(void);
109 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
110 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
111 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
112 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
115 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
116 gss_buffer_t *, OM_uint32 *, send_token_flag *);
118 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
119 gss_buffer_t *, OM_uint32 *, send_token_flag *);
122 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
123 gss_OID_set *, send_token_flag *);
125 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
126 gss_buffer_t *, gss_buffer_t *,
127 OM_uint32 *, send_token_flag *);
129 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
130 gss_buffer_t *, gss_buffer_t *,
131 OM_uint32 *, send_token_flag *);
133 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
134 gss_OID, gss_buffer_t *, gss_buffer_t *,
135 OM_uint32 *, send_token_flag *);
137 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
138 gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
139 gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
140 OM_uint32 *, send_token_flag *);
143 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
144 spnego_gss_cred_id_t, gss_buffer_t *,
145 gss_buffer_t *, OM_uint32 *, send_token_flag *);
147 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
148 gss_buffer_t *, gss_buffer_t *,
149 OM_uint32 *, send_token_flag *);
151 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
152 OM_uint32 *, send_token_flag *);
154 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
155 gss_buffer_t, gss_OID *, gss_buffer_t,
156 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
157 OM_uint32 *, send_token_flag *);
160 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
163 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
166 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
169 OM_uint32, gss_buffer_t, send_token_flag,
172 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
173 gss_buffer_t, send_token_flag,
177 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
178 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
181 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
182 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
185 is_kerb_mech(gss_OID oid);
187 /* SPNEGO oid structure */
188 static const gss_OID_desc spnego_oids[] = {
189 {SPNEGO_OID_LENGTH, SPNEGO_OID},
192 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
193 static const gss_OID_set_desc spnego_oidsets[] = {
194 {1, (gss_OID) spnego_oids+0},
196 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
198 static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
199 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
201 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
202 gss_buffer_t *, OM_uint32 *, send_token_flag *);
205 * The Mech OID for SPNEGO:
206 * { iso(1) org(3) dod(6) internet(1) security(5)
207 * mechanism(5) spnego(2) }
209 static struct gss_config spnego_mechanism =
211 {SPNEGO_OID_LENGTH, SPNEGO_OID},
213 spnego_gss_acquire_cred,
214 spnego_gss_release_cred,
215 spnego_gss_init_sec_context,
217 spnego_gss_accept_sec_context,
220 #endif /* LEAN_CLIENT */
221 NULL, /* gss_process_context_token */
222 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
223 spnego_gss_context_time, /* gss_context_time */
224 spnego_gss_get_mic, /* gss_get_mic */
225 spnego_gss_verify_mic, /* gss_verify_mic */
226 spnego_gss_wrap, /* gss_wrap */
227 spnego_gss_unwrap, /* gss_unwrap */
228 spnego_gss_display_status,
229 NULL, /* gss_indicate_mechs */
230 spnego_gss_compare_name,
231 spnego_gss_display_name,
232 spnego_gss_import_name,
233 spnego_gss_release_name,
234 spnego_gss_inquire_cred, /* gss_inquire_cred */
235 NULL, /* gss_add_cred */
237 spnego_gss_export_sec_context, /* gss_export_sec_context */
238 spnego_gss_import_sec_context, /* gss_import_sec_context */
240 NULL, /* gss_export_sec_context */
241 NULL, /* gss_import_sec_context */
242 #endif /* LEAN_CLIENT */
243 NULL, /* gss_inquire_cred_by_mech */
244 spnego_gss_inquire_names_for_mech,
245 spnego_gss_inquire_context, /* gss_inquire_context */
246 NULL, /* gss_internal_release_oid */
247 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
248 NULL, /* gss_export_name */
249 NULL, /* gss_store_cred */
250 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
251 spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
252 spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
253 spnego_gss_set_cred_option, /* gssspi_set_cred_option */
254 NULL, /* gssspi_mech_invoke */
255 spnego_gss_wrap_aead,
256 spnego_gss_unwrap_aead,
258 spnego_gss_unwrap_iov,
259 spnego_gss_wrap_iov_length,
260 spnego_gss_complete_auth_token,
261 spnego_gss_acquire_cred_impersonate_name,
262 NULL, /* gss_add_cred_impersonate_name */
263 spnego_gss_display_name_ext,
264 spnego_gss_inquire_name,
265 spnego_gss_get_name_attribute,
266 spnego_gss_set_name_attribute,
267 spnego_gss_delete_name_attribute,
268 spnego_gss_export_name_composite,
269 spnego_gss_map_name_to_any,
270 spnego_gss_release_any_name_mapping,
271 spnego_gss_pseudo_random,
272 spnego_gss_set_neg_mechs,
273 spnego_gss_inquire_saslname_for_mech,
274 spnego_gss_inquire_mech_for_saslname,
275 spnego_gss_inquire_attrs_for_mech,
278 static struct gss_config_ext spnego_mechanism_ext =
280 spnego_gss_acquire_cred_with_password
283 #ifdef _GSS_STATIC_LINK
286 static int gss_spnegomechglue_init(void)
288 struct gss_mech_config mech_spnego;
290 memset(&mech_spnego, 0, sizeof(mech_spnego));
291 mech_spnego.mech = &spnego_mechanism;
292 mech_spnego.mech_ext = &spnego_mechanism_ext;
293 mech_spnego.mechNameStr = "spnego";
294 mech_spnego.mech_type = GSS_C_NO_OID;
296 return gssint_register_mechinfo(&mech_spnego);
299 gss_mechanism KRB5_CALLCONV
300 gss_mech_initialize(void)
302 return (&spnego_mechanism);
305 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
306 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
307 int gss_krb5int_lib_init(void);
308 #endif /* _GSS_STATIC_LINK */
310 int gss_spnegoint_lib_init(void)
312 #ifdef _GSS_STATIC_LINK
313 return gss_spnegomechglue_init();
319 void gss_spnegoint_lib_fini(void)
325 spnego_gss_acquire_cred(OM_uint32 *minor_status,
326 gss_name_t desired_name,
328 gss_OID_set desired_mechs,
329 gss_cred_usage_t cred_usage,
330 gss_cred_id_t *output_cred_handle,
331 gss_OID_set *actual_mechs,
336 gss_cred_id_t mcred = NULL;
337 spnego_gss_cred_id_t spcred = NULL;
338 dsyslog("Entering spnego_gss_acquire_cred\n");
341 *actual_mechs = NULL;
346 /* We will obtain a mechglue credential and wrap it in a
347 * spnego_gss_cred_id_rec structure. Allocate the wrapper. */
348 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
349 if (spcred == NULL) {
350 *minor_status = ENOMEM;
351 return (GSS_S_FAILURE);
353 spcred->neg_mechs = GSS_C_NULL_OID_SET;
356 * If the user did not specify a list of mechs,
357 * use get_available_mechs to collect a list of
358 * mechs for which creds are available.
360 if (desired_mechs == GSS_C_NULL_OID_SET) {
361 status = get_available_mechs(minor_status,
362 desired_name, cred_usage,
366 * The caller gave a specific list of mechanisms,
367 * so just get whatever creds are available.
368 * gss_acquire_creds will return the subset of mechs for
369 * which the given 'output_cred_handle' is valid.
371 status = gss_acquire_cred(minor_status,
372 desired_name, time_req,
373 desired_mechs, cred_usage,
374 &mcred, &amechs, time_rec);
377 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
378 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
380 (void) gss_release_oid_set(minor_status, &amechs);
382 if (status == GSS_S_COMPLETE) {
383 spcred->mcred = mcred;
384 *output_cred_handle = (gss_cred_id_t)spcred;
387 *output_cred_handle = GSS_C_NO_CREDENTIAL;
390 dsyslog("Leaving spnego_gss_acquire_cred\n");
396 spnego_gss_release_cred(OM_uint32 *minor_status,
397 gss_cred_id_t *cred_handle)
399 spnego_gss_cred_id_t spcred = NULL;
401 dsyslog("Entering spnego_gss_release_cred\n");
403 if (minor_status == NULL || cred_handle == NULL)
404 return (GSS_S_CALL_INACCESSIBLE_WRITE);
408 if (*cred_handle == GSS_C_NO_CREDENTIAL)
409 return (GSS_S_COMPLETE);
411 spcred = (spnego_gss_cred_id_t)*cred_handle;
412 *cred_handle = GSS_C_NO_CREDENTIAL;
413 gss_release_oid_set(minor_status, &spcred->neg_mechs);
414 gss_release_cred(minor_status, &spcred->mcred);
417 dsyslog("Leaving spnego_gss_release_cred\n");
418 return (GSS_S_COMPLETE);
422 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
424 spnego_ctx->optionStr = gssint_get_modOptions(
425 (const gss_OID)&spnego_oids[0]);
428 static spnego_gss_ctx_id_t
429 create_spnego_ctx(void)
431 spnego_gss_ctx_id_t spnego_ctx = NULL;
432 spnego_ctx = (spnego_gss_ctx_id_t)
433 malloc(sizeof (spnego_gss_ctx_id_rec));
435 if (spnego_ctx == NULL) {
439 spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
440 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
441 spnego_ctx->internal_mech = NULL;
442 spnego_ctx->optionStr = NULL;
443 spnego_ctx->DER_mechTypes.length = 0;
444 spnego_ctx->DER_mechTypes.value = NULL;
445 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
446 spnego_ctx->mic_reqd = 0;
447 spnego_ctx->mic_sent = 0;
448 spnego_ctx->mic_rcvd = 0;
449 spnego_ctx->mech_complete = 0;
450 spnego_ctx->nego_done = 0;
451 spnego_ctx->internal_name = GSS_C_NO_NAME;
452 spnego_ctx->actual_mech = GSS_C_NO_OID;
454 check_spnego_options(spnego_ctx);
460 * Both initiator and acceptor call here to verify and/or create mechListMIC,
461 * and to consistency-check the MIC state. handle_mic is invoked only if the
462 * negotiated mech has completed and supports MICs.
465 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
466 int send_mechtok, spnego_gss_ctx_id_t sc,
467 gss_buffer_t *mic_out,
468 OM_uint32 *negState, send_token_flag *tokflag)
473 *mic_out = GSS_C_NO_BUFFER;
474 if (mic_in != GSS_C_NO_BUFFER) {
476 /* Reject MIC if we've already received a MIC. */
478 *tokflag = ERROR_TOKEN_SEND;
479 return GSS_S_DEFECTIVE_TOKEN;
481 } else if (sc->mic_reqd && !send_mechtok) {
483 * If the peer sends the final mechanism token, it
484 * must send the MIC with that token if the
485 * negotiation requires MICs.
488 *tokflag = ERROR_TOKEN_SEND;
489 return GSS_S_DEFECTIVE_TOKEN;
491 ret = process_mic(minor_status, mic_in, sc, mic_out,
493 if (ret != GSS_S_COMPLETE) {
497 assert(sc->mic_sent || sc->mic_rcvd);
499 if (sc->mic_sent && sc->mic_rcvd) {
500 ret = GSS_S_COMPLETE;
501 *negState = ACCEPT_COMPLETE;
502 if (*mic_out == GSS_C_NO_BUFFER) {
504 * We sent a MIC on the previous pass; we
505 * shouldn't be sending a mechanism token.
507 assert(!send_mechtok);
508 *tokflag = NO_TOKEN_SEND;
510 *tokflag = CONT_TOKEN_SEND;
512 } else if (sc->mic_reqd) {
513 *negState = ACCEPT_INCOMPLETE;
514 ret = GSS_S_CONTINUE_NEEDED;
515 } else if (*negState == ACCEPT_COMPLETE) {
516 ret = GSS_S_COMPLETE;
518 ret = GSS_S_CONTINUE_NEEDED;
524 * Perform the actual verification and/or generation of mechListMIC.
527 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
528 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
529 OM_uint32 *negState, send_token_flag *tokflag)
531 OM_uint32 ret, tmpmin;
533 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
536 if (mic_in != GSS_C_NO_BUFFER) {
537 ret = gss_verify_mic(minor_status, sc->ctx_handle,
540 if (ret != GSS_S_COMPLETE) {
542 *tokflag = ERROR_TOKEN_SEND;
545 /* If we got a MIC, we must send a MIC. */
549 if (sc->mic_reqd && !sc->mic_sent) {
550 ret = gss_get_mic(minor_status, sc->ctx_handle,
554 if (ret != GSS_S_COMPLETE) {
555 gss_release_buffer(&tmpmin, &tmpmic);
556 *tokflag = NO_TOKEN_SEND;
559 *mic_out = malloc(sizeof(gss_buffer_desc));
560 if (*mic_out == GSS_C_NO_BUFFER) {
561 gss_release_buffer(&tmpmin, &tmpmic);
562 *tokflag = NO_TOKEN_SEND;
563 return GSS_S_FAILURE;
568 return GSS_S_COMPLETE;
572 * Initial call to spnego_gss_init_sec_context().
575 init_ctx_new(OM_uint32 *minor_status,
576 spnego_gss_cred_id_t spcred,
578 gss_OID_set *mechSet,
579 send_token_flag *tokflag)
581 OM_uint32 ret, tmpmin;
582 spnego_gss_ctx_id_t sc = NULL;
584 /* determine negotiation mech set */
585 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
587 if (ret != GSS_S_COMPLETE)
590 sc = create_spnego_ctx();
592 return GSS_S_FAILURE;
595 * need to pull the first mech from mechSet to do first
596 * gss_init_sec_context()
598 ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
600 if (ret != GSS_S_COMPLETE) {
601 map_errcode(minor_status);
605 if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
606 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
611 * The actual context is not yet determined, set the output
612 * context handle to refer to the spnego context itself.
614 sc->ctx_handle = GSS_C_NO_CONTEXT;
615 *ctx = (gss_ctx_id_t)sc;
616 *tokflag = INIT_TOKEN_SEND;
617 ret = GSS_S_CONTINUE_NEEDED;
620 gss_release_oid_set(&tmpmin, mechSet);
625 * Called by second and later calls to spnego_gss_init_sec_context()
626 * to decode reply and update state.
629 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
630 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
631 OM_uint32 *negState, send_token_flag *tokflag)
633 OM_uint32 ret, tmpmin, acc_negState;
635 spnego_gss_ctx_id_t sc;
636 gss_OID supportedMech = GSS_C_NO_OID;
638 sc = (spnego_gss_ctx_id_t)*ctx;
640 *tokflag = ERROR_TOKEN_SEND;
643 ret = get_negTokenResp(minor_status, ptr, buf->length,
644 &acc_negState, &supportedMech,
645 responseToken, mechListMIC);
646 if (ret != GSS_S_COMPLETE)
648 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
649 supportedMech == GSS_C_NO_OID &&
650 *responseToken == GSS_C_NO_BUFFER &&
651 *mechListMIC == GSS_C_NO_BUFFER) {
652 /* Reject "empty" token. */
653 ret = GSS_S_DEFECTIVE_TOKEN;
655 if (acc_negState == REJECT) {
656 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
657 map_errcode(minor_status);
658 *tokflag = NO_TOKEN_SEND;
663 * nego_done is false for the first call to init_ctx_cont()
665 if (!sc->nego_done) {
666 ret = init_ctx_nego(minor_status, sc,
668 supportedMech, responseToken,
671 } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
672 (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
673 /* Missing or spurious token from acceptor. */
674 ret = GSS_S_DEFECTIVE_TOKEN;
675 } else if (!sc->mech_complete ||
677 (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
678 /* Not obviously done; we may decide we're done later in
679 * init_ctx_call_init or handle_mic. */
680 *negState = ACCEPT_INCOMPLETE;
681 *tokflag = CONT_TOKEN_SEND;
682 ret = GSS_S_CONTINUE_NEEDED;
684 /* mech finished on last pass and no MIC required, so done. */
685 *negState = ACCEPT_COMPLETE;
686 *tokflag = NO_TOKEN_SEND;
687 ret = GSS_S_COMPLETE;
690 if (supportedMech != GSS_C_NO_OID)
691 generic_gss_release_oid(&tmpmin, &supportedMech);
696 * Consistency checking and mechanism negotiation handling for second
697 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
698 * update internal state if acceptor has counter-proposed.
701 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
702 OM_uint32 acc_negState, gss_OID supportedMech,
703 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
704 OM_uint32 *negState, send_token_flag *tokflag)
709 *tokflag = ERROR_TOKEN_SEND;
710 ret = GSS_S_DEFECTIVE_TOKEN;
712 * Both supportedMech and negState must be present in first
715 if (supportedMech == GSS_C_NO_OID) {
716 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
717 map_errcode(minor_status);
718 return GSS_S_DEFECTIVE_TOKEN;
720 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
721 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
722 map_errcode(minor_status);
723 return GSS_S_DEFECTIVE_TOKEN;
727 * If the mechanism we sent is not the mechanism returned from
728 * the server, we need to handle the server's counter
729 * proposal. There is a bug in SAMBA servers that always send
730 * the old Kerberos mech OID, even though we sent the new one.
731 * So we will treat all the Kerberos mech OIDS as the same.
733 if (!(is_kerb_mech(supportedMech) &&
734 is_kerb_mech(sc->internal_mech)) &&
735 !g_OID_equal(supportedMech, sc->internal_mech)) {
736 ret = init_ctx_reselect(minor_status, sc,
737 acc_negState, supportedMech,
738 responseToken, mechListMIC,
741 } else if (*responseToken == GSS_C_NO_BUFFER) {
742 if (sc->mech_complete) {
744 * Mech completed on first call to its
745 * init_sec_context(). Acceptor sends no mech
748 *negState = ACCEPT_COMPLETE;
749 *tokflag = NO_TOKEN_SEND;
750 ret = GSS_S_COMPLETE;
753 * Reject missing mech token when optimistic
756 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
757 map_errcode(minor_status);
758 ret = GSS_S_DEFECTIVE_TOKEN;
760 } else if (sc->mech_complete) {
761 /* Reject spurious mech token. */
762 ret = GSS_S_DEFECTIVE_TOKEN;
764 *negState = ACCEPT_INCOMPLETE;
765 *tokflag = CONT_TOKEN_SEND;
766 ret = GSS_S_CONTINUE_NEEDED;
773 * Handle acceptor's counter-proposal of an alternative mechanism.
776 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
777 OM_uint32 acc_negState, gss_OID supportedMech,
778 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
779 OM_uint32 *negState, send_token_flag *tokflag)
781 OM_uint32 ret, tmpmin;
783 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
784 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
787 ret = generic_gss_copy_oid(minor_status, supportedMech,
789 if (ret != GSS_S_COMPLETE) {
790 map_errcode(minor_status);
791 sc->internal_mech = GSS_C_NO_OID;
792 *tokflag = NO_TOKEN_SEND;
795 if (*responseToken != GSS_C_NO_BUFFER) {
796 /* Reject spurious mech token. */
797 return GSS_S_DEFECTIVE_TOKEN;
800 * Windows 2003 and earlier don't correctly send a
801 * negState of request-mic when counter-proposing a
802 * mechanism. They probably don't handle mechListMICs
805 if (acc_negState != REQUEST_MIC)
806 return GSS_S_DEFECTIVE_TOKEN;
808 sc->mech_complete = 0;
810 *negState = REQUEST_MIC;
811 *tokflag = CONT_TOKEN_SEND;
812 return GSS_S_CONTINUE_NEEDED;
816 * Wrap call to mechanism gss_init_sec_context() and update state
820 init_ctx_call_init(OM_uint32 *minor_status,
821 spnego_gss_ctx_id_t sc,
822 spnego_gss_cred_id_t spcred,
823 gss_name_t target_name,
826 gss_buffer_t mechtok_in,
827 gss_OID *actual_mech,
828 gss_buffer_t mechtok_out,
829 OM_uint32 *ret_flags,
832 send_token_flag *send_token)
837 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
838 ret = gss_init_sec_context(minor_status,
843 (req_flags | GSS_C_INTEG_FLAG),
845 GSS_C_NO_CHANNEL_BINDINGS,
851 if (ret == GSS_S_COMPLETE) {
852 sc->mech_complete = 1;
853 if (ret_flags != NULL)
854 *ret_flags = sc->ctx_flags;
856 * Microsoft SPNEGO implementations expect an even number of
857 * token exchanges. So if we're sending a final token, ask for
858 * a zero-length token back from the server. Also ask for a
859 * token back if this is the first token or if a MIC exchange
862 if (*send_token == CONT_TOKEN_SEND &&
863 mechtok_out->length == 0 &&
865 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
866 /* The exchange is complete. */
867 *negState = ACCEPT_COMPLETE;
868 ret = GSS_S_COMPLETE;
869 *send_token = NO_TOKEN_SEND;
871 /* Ask for one more hop. */
872 *negState = ACCEPT_INCOMPLETE;
873 ret = GSS_S_CONTINUE_NEEDED;
875 } else if (ret != GSS_S_CONTINUE_NEEDED) {
876 if (*send_token == INIT_TOKEN_SEND) {
877 /* Don't output token on error if first call. */
878 *send_token = NO_TOKEN_SEND;
880 *send_token = ERROR_TOKEN_SEND;
889 spnego_gss_init_sec_context(
890 OM_uint32 *minor_status,
891 gss_cred_id_t claimant_cred_handle,
892 gss_ctx_id_t *context_handle,
893 gss_name_t target_name,
897 gss_channel_bindings_t input_chan_bindings,
898 gss_buffer_t input_token,
899 gss_OID *actual_mech,
900 gss_buffer_t output_token,
901 OM_uint32 *ret_flags,
904 send_token_flag send_token = NO_TOKEN_SEND;
905 OM_uint32 tmpmin, ret, negState;
906 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
907 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
908 gss_OID_set mechSet = GSS_C_NO_OID_SET;
909 spnego_gss_cred_id_t spcred = NULL;
910 spnego_gss_ctx_id_t spnego_ctx = NULL;
912 dsyslog("Entering init_sec_context\n");
914 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
918 * This function works in three steps:
920 * 1. Perform mechanism negotiation.
921 * 2. Invoke the negotiated or optimistic mech's gss_init_sec_context
922 * function and examine the results.
923 * 3. Process or generate MICs if necessary.
925 * The three steps share responsibility for determining when the
926 * exchange is complete. If the selected mech completed in a previous
927 * call and no MIC exchange is expected, then step 1 will decide. If
928 * the selected mech completes in this call and no MIC exchange is
929 * expected, then step 2 will decide. If a MIC exchange is expected,
930 * then step 3 will decide. If an error occurs in any step, the
931 * exchange will be aborted, possibly with an error token.
933 * negState determines the state of the negotiation, and is
934 * communicated to the acceptor if a continuing token is sent.
935 * send_token is used to indicate what type of token, if any, should be
939 /* Validate arguments. */
940 if (minor_status != NULL)
942 if (output_token != GSS_C_NO_BUFFER) {
943 output_token->length = 0;
944 output_token->value = NULL;
946 if (minor_status == NULL ||
947 output_token == GSS_C_NO_BUFFER ||
948 context_handle == NULL)
949 return GSS_S_CALL_INACCESSIBLE_WRITE;
951 if (actual_mech != NULL)
952 *actual_mech = GSS_C_NO_OID;
954 /* Step 1: perform mechanism negotiation. */
955 spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
956 if (*context_handle == GSS_C_NO_CONTEXT) {
957 ret = init_ctx_new(minor_status, spcred,
958 context_handle, &mechSet, &send_token);
959 if (ret != GSS_S_CONTINUE_NEEDED) {
963 ret = init_ctx_cont(minor_status, context_handle,
964 input_token, &mechtok_in,
965 &mechListMIC_in, &negState, &send_token);
966 if (HARD_ERROR(ret)) {
971 /* Step 2: invoke the selected or optimistic mechanism's
972 * gss_init_sec_context function, if it didn't complete previously. */
973 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
974 if (!spnego_ctx->mech_complete) {
975 ret = init_ctx_call_init(
976 minor_status, spnego_ctx, spcred,
977 target_name, req_flags,
978 time_req, mechtok_in,
979 actual_mech, &mechtok_out,
981 &negState, &send_token);
984 /* Step 3: process or generate the MIC, if the negotiated mech is
985 * complete and supports MICs. */
986 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
987 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
989 ret = handle_mic(minor_status,
991 (mechtok_out.length != 0),
992 spnego_ctx, &mechListMIC_out,
993 &negState, &send_token);
996 if (send_token == INIT_TOKEN_SEND) {
997 if (make_spnego_tokenInit_msg(spnego_ctx,
1001 &mechtok_out, send_token,
1002 output_token) < 0) {
1003 ret = GSS_S_FAILURE;
1005 } else if (send_token != NO_TOKEN_SEND) {
1006 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1007 &mechtok_out, mechListMIC_out,
1009 output_token) < 0) {
1010 ret = GSS_S_FAILURE;
1013 gss_release_buffer(&tmpmin, &mechtok_out);
1014 if (ret == GSS_S_COMPLETE) {
1016 * Now, switch the output context to refer to the
1017 * negotiated mechanism's context.
1019 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1020 if (actual_mech != NULL)
1021 *actual_mech = spnego_ctx->actual_mech;
1022 if (ret_flags != NULL)
1023 *ret_flags = spnego_ctx->ctx_flags;
1024 release_spnego_ctx(&spnego_ctx);
1025 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1026 if (spnego_ctx != NULL) {
1027 gss_delete_sec_context(&tmpmin,
1028 &spnego_ctx->ctx_handle,
1030 release_spnego_ctx(&spnego_ctx);
1032 *context_handle = GSS_C_NO_CONTEXT;
1034 if (mechtok_in != GSS_C_NO_BUFFER) {
1035 gss_release_buffer(&tmpmin, mechtok_in);
1038 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1039 gss_release_buffer(&tmpmin, mechListMIC_in);
1040 free(mechListMIC_in);
1042 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1043 gss_release_buffer(&tmpmin, mechListMIC_out);
1044 free(mechListMIC_out);
1046 if (mechSet != GSS_C_NO_OID_SET) {
1047 gss_release_oid_set(&tmpmin, &mechSet);
1050 } /* init_sec_context */
1052 /* We don't want to import KRB5 headers here */
1053 static const gss_OID_desc gss_mech_krb5_oid =
1054 { 9, "\052\206\110\206\367\022\001\002\002" };
1055 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1056 { 9, "\052\206\110\202\367\022\001\002\002" };
1059 * verify that the input token length is not 0. If it is, just return.
1060 * If the token length is greater than 0, der encode as a sequence
1061 * and place in buf_out, advancing buf_out.
1065 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1066 unsigned int buflen)
1070 /* if token length is 0, we do not want to send */
1071 if (input_token->length == 0)
1074 if (input_token->length > buflen)
1077 *(*buf_out)++ = SEQUENCE;
1078 if ((ret = gssint_put_der_length(input_token->length, buf_out,
1079 input_token->length)))
1081 TWRITE_STR(*buf_out, input_token->value, input_token->length);
1086 * NegHints ::= SEQUENCE {
1087 * hintName [0] GeneralString OPTIONAL,
1088 * hintAddress [1] OCTET STRING OPTIONAL
1092 #define HOST_PREFIX "host@"
1093 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1096 make_NegHints(OM_uint32 *minor_status,
1097 spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
1099 gss_buffer_desc hintNameBuf;
1100 gss_name_t hintName = GSS_C_NO_NAME;
1101 gss_name_t hintKerberosName;
1102 gss_OID hintNameType;
1103 OM_uint32 major_status;
1105 unsigned int tlen = 0;
1106 unsigned int hintNameSize = 0;
1107 unsigned int negHintsSize = 0;
1111 *outbuf = GSS_C_NO_BUFFER;
1113 if (spcred != NULL) {
1114 major_status = gss_inquire_cred(minor_status,
1120 if (major_status != GSS_S_COMPLETE)
1121 return (major_status);
1124 if (hintName == GSS_C_NO_NAME) {
1125 krb5_error_code code;
1126 krb5int_access kaccess;
1127 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1129 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1131 *minor_status = code;
1132 return (GSS_S_FAILURE);
1135 /* this breaks mutual authentication but Samba relies on it */
1136 code = (*kaccess.clean_hostname)(NULL, NULL,
1137 &hostname[HOST_PREFIX_LEN],
1140 *minor_status = code;
1141 return (GSS_S_FAILURE);
1144 hintNameBuf.value = hostname;
1145 hintNameBuf.length = strlen(hostname);
1147 major_status = gss_import_name(minor_status,
1149 GSS_C_NT_HOSTBASED_SERVICE,
1151 if (major_status != GSS_S_COMPLETE) {
1152 return (major_status);
1156 hintNameBuf.value = NULL;
1157 hintNameBuf.length = 0;
1159 major_status = gss_canonicalize_name(minor_status,
1161 (gss_OID)&gss_mech_krb5_oid,
1163 if (major_status != GSS_S_COMPLETE) {
1164 gss_release_name(&minor, &hintName);
1165 return (major_status);
1167 gss_release_name(&minor, &hintName);
1169 major_status = gss_display_name(minor_status,
1173 if (major_status != GSS_S_COMPLETE) {
1174 gss_release_name(&minor, &hintName);
1175 return (major_status);
1177 gss_release_name(&minor, &hintKerberosName);
1180 * Now encode the name hint into a NegHints ASN.1 type
1182 major_status = GSS_S_FAILURE;
1184 /* Length of DER encoded GeneralString */
1185 tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1187 hintNameSize = tlen;
1189 /* Length of DER encoded hintName */
1190 tlen += 1 + gssint_der_length_size(hintNameSize);
1191 negHintsSize = tlen;
1193 t = (unsigned char *)malloc(tlen);
1195 *minor_status = ENOMEM;
1201 *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1202 if (gssint_put_der_length(hintNameSize,
1203 &ptr, tlen - (int)(ptr-t)))
1206 *ptr++ = GENERAL_STRING;
1207 if (gssint_put_der_length(hintNameBuf.length,
1208 &ptr, tlen - (int)(ptr-t)))
1211 memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1212 ptr += hintNameBuf.length;
1214 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1215 if (*outbuf == NULL) {
1216 *minor_status = ENOMEM;
1219 (*outbuf)->value = (void *)t;
1220 (*outbuf)->length = ptr - t;
1222 t = NULL; /* don't free */
1225 major_status = GSS_S_COMPLETE;
1232 gss_release_buffer(&minor, &hintNameBuf);
1234 return (major_status);
1238 * Support the Microsoft NegHints extension to SPNEGO for compatibility with
1239 * some versions of Samba. See:
1240 * http://msdn.microsoft.com/en-us/library/cc247039(PROT.10).aspx
1243 acc_ctx_hints(OM_uint32 *minor_status,
1245 spnego_gss_cred_id_t spcred,
1246 gss_buffer_t *mechListMIC,
1247 OM_uint32 *negState,
1248 send_token_flag *return_token)
1250 OM_uint32 tmpmin, ret;
1251 gss_OID_set supported_mechSet;
1252 spnego_gss_ctx_id_t sc = NULL;
1254 *mechListMIC = GSS_C_NO_BUFFER;
1255 supported_mechSet = GSS_C_NO_OID_SET;
1256 *return_token = NO_TOKEN_SEND;
1260 /* A hint request must be the first token received. */
1261 if (*ctx != GSS_C_NO_CONTEXT)
1262 return GSS_S_DEFECTIVE_TOKEN;
1264 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1265 &supported_mechSet);
1266 if (ret != GSS_S_COMPLETE)
1269 ret = make_NegHints(minor_status, spcred, mechListMIC);
1270 if (ret != GSS_S_COMPLETE)
1273 sc = create_spnego_ctx();
1275 ret = GSS_S_FAILURE;
1278 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1279 ret = GSS_S_FAILURE;
1282 sc->internal_mech = GSS_C_NO_OID;
1284 *negState = ACCEPT_INCOMPLETE;
1285 *return_token = INIT_TOKEN_SEND;
1287 *ctx = (gss_ctx_id_t)sc;
1288 ret = GSS_S_COMPLETE;
1291 gss_release_oid_set(&tmpmin, &supported_mechSet);
1297 * Set negState to REJECT if the token is defective, else
1298 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1299 * preferred mechanism is supported.
1302 acc_ctx_new(OM_uint32 *minor_status,
1305 spnego_gss_cred_id_t spcred,
1306 gss_buffer_t *mechToken,
1307 gss_buffer_t *mechListMIC,
1308 OM_uint32 *negState,
1309 send_token_flag *return_token)
1311 OM_uint32 tmpmin, ret, req_flags;
1312 gss_OID_set supported_mechSet, mechTypes;
1313 gss_buffer_desc der_mechTypes;
1314 gss_OID mech_wanted;
1315 spnego_gss_ctx_id_t sc = NULL;
1317 ret = GSS_S_DEFECTIVE_TOKEN;
1318 der_mechTypes.length = 0;
1319 der_mechTypes.value = NULL;
1320 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1321 supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1322 *return_token = ERROR_TOKEN_SEND;
1326 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1327 &mechTypes, &req_flags,
1328 mechToken, mechListMIC);
1329 if (ret != GSS_S_COMPLETE) {
1332 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1333 &supported_mechSet);
1334 if (ret != GSS_S_COMPLETE) {
1335 *return_token = NO_TOKEN_SEND;
1339 * Select the best match between the list of mechs
1340 * that the initiator requested and the list that
1341 * the acceptor will support.
1343 mech_wanted = negotiate_mech_type(minor_status,
1347 if (*negState == REJECT) {
1348 ret = GSS_S_BAD_MECH;
1351 sc = (spnego_gss_ctx_id_t)*ctx;
1353 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1354 assert(mech_wanted != GSS_C_NO_OID);
1356 sc = create_spnego_ctx();
1358 ret = GSS_S_FAILURE;
1359 *return_token = NO_TOKEN_SEND;
1360 generic_gss_release_oid(&tmpmin, &mech_wanted);
1363 sc->internal_mech = mech_wanted;
1364 sc->DER_mechTypes = der_mechTypes;
1365 der_mechTypes.length = 0;
1366 der_mechTypes.value = NULL;
1368 if (*negState == REQUEST_MIC)
1371 *return_token = INIT_TOKEN_SEND;
1373 *ctx = (gss_ctx_id_t)sc;
1374 ret = GSS_S_COMPLETE;
1376 gss_release_oid_set(&tmpmin, &mechTypes);
1377 gss_release_oid_set(&tmpmin, &supported_mechSet);
1378 if (der_mechTypes.length != 0)
1379 gss_release_buffer(&tmpmin, &der_mechTypes);
1385 acc_ctx_cont(OM_uint32 *minstat,
1388 gss_buffer_t *responseToken,
1389 gss_buffer_t *mechListMIC,
1390 OM_uint32 *negState,
1391 send_token_flag *return_token)
1393 OM_uint32 ret, tmpmin;
1394 gss_OID supportedMech;
1395 spnego_gss_ctx_id_t sc;
1397 unsigned char *ptr, *bufstart;
1399 sc = (spnego_gss_ctx_id_t)*ctx;
1400 ret = GSS_S_DEFECTIVE_TOKEN;
1403 supportedMech = GSS_C_NO_OID;
1404 *return_token = ERROR_TOKEN_SEND;
1405 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1407 ptr = bufstart = buf->value;
1408 #define REMAIN (buf->length - (ptr - bufstart))
1409 if (REMAIN > INT_MAX)
1410 return GSS_S_DEFECTIVE_TOKEN;
1413 * Attempt to work with old Sun SPNEGO.
1415 if (*ptr == HEADER_ID) {
1416 ret = g_verify_token_header(gss_mech_spnego,
1417 &len, &ptr, 0, REMAIN);
1420 return GSS_S_DEFECTIVE_TOKEN;
1423 if (*ptr != (CONTEXT | 0x01)) {
1424 return GSS_S_DEFECTIVE_TOKEN;
1426 ret = get_negTokenResp(minstat, ptr, REMAIN,
1427 negState, &supportedMech,
1428 responseToken, mechListMIC);
1429 if (ret != GSS_S_COMPLETE)
1432 if (*responseToken == GSS_C_NO_BUFFER &&
1433 *mechListMIC == GSS_C_NO_BUFFER) {
1435 ret = GSS_S_DEFECTIVE_TOKEN;
1438 if (supportedMech != GSS_C_NO_OID) {
1439 ret = GSS_S_DEFECTIVE_TOKEN;
1443 *negState = ACCEPT_INCOMPLETE;
1444 *return_token = CONT_TOKEN_SEND;
1446 if (supportedMech != GSS_C_NO_OID) {
1447 generic_gss_release_oid(&tmpmin, &supportedMech);
1454 * Verify that mech OID is either exactly the same as the negotiated
1455 * mech OID, or is a mech OID supported by the negotiated mech. MS
1456 * implementations can list a most preferred mech using an incorrect
1457 * krb5 OID while emitting a krb5 initiator mech token having the
1458 * correct krb5 mech OID.
1461 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1462 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1463 OM_uint32 *negState, send_token_flag *tokflag)
1465 OM_uint32 ret, tmpmin;
1466 gss_mechanism mech = NULL;
1467 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1470 if (g_OID_equal(sc->internal_mech, mechoid))
1471 return GSS_S_COMPLETE;
1473 mech = gssint_get_mechanism(sc->internal_mech);
1474 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1475 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1476 map_errcode(minor_status);
1478 *tokflag = ERROR_TOKEN_SEND;
1479 return GSS_S_BAD_MECH;
1481 ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1482 if (ret != GSS_S_COMPLETE) {
1483 *tokflag = NO_TOKEN_SEND;
1484 map_error(minor_status, mech);
1487 ret = gss_test_oid_set_member(minor_status, mechoid,
1488 mech_set, &present);
1489 if (ret != GSS_S_COMPLETE)
1492 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1493 map_errcode(minor_status);
1495 *tokflag = ERROR_TOKEN_SEND;
1496 ret = GSS_S_BAD_MECH;
1499 gss_release_oid_set(&tmpmin, &mech_set);
1504 * Wrap call to gss_accept_sec_context() and update state
1508 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1509 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1510 gss_OID *mech_type, gss_buffer_t mechtok_out,
1511 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1512 gss_cred_id_t *delegated_cred_handle,
1513 OM_uint32 *negState, send_token_flag *tokflag)
1516 gss_OID_desc mechoid;
1517 gss_cred_id_t mcred;
1519 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1521 * mechoid is an alias; don't free it.
1523 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1524 if (ret != GSS_S_COMPLETE) {
1525 *tokflag = NO_TOKEN_SEND;
1528 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1530 if (ret != GSS_S_COMPLETE)
1534 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1535 ret = gss_accept_sec_context(minor_status,
1539 GSS_C_NO_CHANNEL_BINDINGS,
1545 delegated_cred_handle);
1546 if (ret == GSS_S_COMPLETE) {
1549 * Force MIC to be not required even if we previously
1552 char *envstr = getenv("MS_FORCE_NO_MIC");
1554 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1555 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1561 sc->mech_complete = 1;
1562 if (ret_flags != NULL)
1563 *ret_flags = sc->ctx_flags;
1565 if (!sc->mic_reqd ||
1566 !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1567 /* No MIC exchange required, so we're done. */
1568 *negState = ACCEPT_COMPLETE;
1569 ret = GSS_S_COMPLETE;
1571 /* handle_mic will decide if we're done. */
1572 ret = GSS_S_CONTINUE_NEEDED;
1574 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1576 *tokflag = ERROR_TOKEN_SEND;
1583 spnego_gss_accept_sec_context(
1584 OM_uint32 *minor_status,
1585 gss_ctx_id_t *context_handle,
1586 gss_cred_id_t verifier_cred_handle,
1587 gss_buffer_t input_token,
1588 gss_channel_bindings_t input_chan_bindings,
1589 gss_name_t *src_name,
1591 gss_buffer_t output_token,
1592 OM_uint32 *ret_flags,
1593 OM_uint32 *time_rec,
1594 gss_cred_id_t *delegated_cred_handle)
1596 OM_uint32 ret, tmpmin, negState;
1597 send_token_flag return_token;
1598 gss_buffer_t mechtok_in, mic_in, mic_out;
1599 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1600 spnego_gss_ctx_id_t sc = NULL;
1601 spnego_gss_cred_id_t spcred = NULL;
1602 OM_uint32 mechstat = GSS_S_FAILURE;
1603 int sendTokenInit = 0, tmpret;
1605 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1608 * This function works in three steps:
1610 * 1. Perform mechanism negotiation.
1611 * 2. Invoke the negotiated mech's gss_accept_sec_context function
1612 * and examine the results.
1613 * 3. Process or generate MICs if necessary.
1615 * Step one determines whether the negotiation requires a MIC exchange,
1616 * while steps two and three share responsibility for determining when
1617 * the exchange is complete. If the selected mech completes in this
1618 * call and no MIC exchange is expected, then step 2 will decide. If a
1619 * MIC exchange is expected, then step 3 will decide. If an error
1620 * occurs in any step, the exchange will be aborted, possibly with an
1623 * negState determines the state of the negotiation, and is
1624 * communicated to the acceptor if a continuing token is sent.
1625 * return_token is used to indicate what type of token, if any, should
1629 /* Validate arguments. */
1630 if (minor_status != NULL)
1632 if (output_token != GSS_C_NO_BUFFER) {
1633 output_token->length = 0;
1634 output_token->value = NULL;
1637 if (minor_status == NULL ||
1638 output_token == GSS_C_NO_BUFFER ||
1639 context_handle == NULL)
1640 return GSS_S_CALL_INACCESSIBLE_WRITE;
1642 if (input_token == GSS_C_NO_BUFFER)
1643 return GSS_S_CALL_INACCESSIBLE_READ;
1645 /* Step 1: Perform mechanism negotiation. */
1646 sc = (spnego_gss_ctx_id_t)*context_handle;
1647 spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1648 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1649 /* Process an initial token or request for NegHints. */
1650 if (src_name != NULL)
1651 *src_name = GSS_C_NO_NAME;
1652 if (mech_type != NULL)
1653 *mech_type = GSS_C_NO_OID;
1654 if (time_rec != NULL)
1656 if (ret_flags != NULL)
1658 if (delegated_cred_handle != NULL)
1659 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1660 if (input_token->length == 0) {
1661 ret = acc_ctx_hints(minor_status,
1662 context_handle, spcred,
1666 if (ret != GSS_S_COMPLETE)
1669 ret = GSS_S_CONTINUE_NEEDED;
1671 /* Can set negState to REQUEST_MIC */
1672 ret = acc_ctx_new(minor_status, input_token,
1673 context_handle, spcred,
1674 &mechtok_in, &mic_in,
1675 &negState, &return_token);
1676 if (ret != GSS_S_COMPLETE)
1678 ret = GSS_S_CONTINUE_NEEDED;
1681 /* Process a response token. Can set negState to
1682 * ACCEPT_INCOMPLETE. */
1683 ret = acc_ctx_cont(minor_status, input_token,
1684 context_handle, &mechtok_in,
1685 &mic_in, &negState, &return_token);
1686 if (ret != GSS_S_COMPLETE)
1688 ret = GSS_S_CONTINUE_NEEDED;
1691 /* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1693 sc = (spnego_gss_ctx_id_t)*context_handle;
1695 * Handle mechtok_in and mic_in only if they are
1696 * present in input_token. If neither is present, whether
1697 * this is an error depends on whether this is the first
1698 * round-trip. RET is set to a default value according to
1699 * whether it is the first round-trip.
1701 mechstat = GSS_S_FAILURE;
1702 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1703 ret = acc_ctx_call_acc(minor_status, sc, spcred,
1704 mechtok_in, mech_type, &mechtok_out,
1705 ret_flags, time_rec,
1706 delegated_cred_handle,
1707 &negState, &return_token);
1708 } else if (negState == REQUEST_MIC) {
1709 mechstat = GSS_S_CONTINUE_NEEDED;
1712 /* Step 3: process or generate the MIC, if the negotiated mech is
1713 * complete and supports MICs. */
1714 if (!HARD_ERROR(ret) && sc->mech_complete &&
1715 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1717 ret = handle_mic(minor_status, mic_in,
1718 (mechtok_out.length != 0),
1720 &negState, &return_token);
1723 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1725 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1727 return_token, output_token);
1729 ret = GSS_S_FAILURE;
1730 } else if (return_token != NO_TOKEN_SEND &&
1731 return_token != CHECK_MIC) {
1732 tmpret = make_spnego_tokenTarg_msg(negState,
1733 sc ? sc->internal_mech :
1735 &mechtok_out, mic_out,
1739 ret = GSS_S_FAILURE;
1741 if (ret == GSS_S_COMPLETE) {
1742 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1743 if (sc->internal_name != GSS_C_NO_NAME &&
1745 *src_name = sc->internal_name;
1746 sc->internal_name = GSS_C_NO_NAME;
1748 release_spnego_ctx(&sc);
1749 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1751 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1753 release_spnego_ctx(&sc);
1755 *context_handle = GSS_C_NO_CONTEXT;
1757 gss_release_buffer(&tmpmin, &mechtok_out);
1758 if (mechtok_in != GSS_C_NO_BUFFER) {
1759 gss_release_buffer(&tmpmin, mechtok_in);
1762 if (mic_in != GSS_C_NO_BUFFER) {
1763 gss_release_buffer(&tmpmin, mic_in);
1766 if (mic_out != GSS_C_NO_BUFFER) {
1767 gss_release_buffer(&tmpmin, mic_out);
1772 #endif /* LEAN_CLIENT */
1777 spnego_gss_display_status(
1778 OM_uint32 *minor_status,
1779 OM_uint32 status_value,
1782 OM_uint32 *message_context,
1783 gss_buffer_t status_string)
1785 dsyslog("Entering display_status\n");
1787 *message_context = 0;
1788 switch (status_value) {
1789 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1791 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
1793 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1795 *status_string = make_err_msg("SPNEGO failed to acquire creds");
1797 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1799 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
1801 case ERR_SPNEGO_NEGOTIATION_FAILED:
1803 *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
1805 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1807 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
1810 status_string->length = 0;
1811 status_string->value = "";
1815 dsyslog("Leaving display_status\n");
1816 return (GSS_S_COMPLETE);
1822 spnego_gss_import_name(
1823 OM_uint32 *minor_status,
1824 gss_buffer_t input_name_buffer,
1825 gss_OID input_name_type,
1826 gss_name_t *output_name)
1830 dsyslog("Entering import_name\n");
1832 status = gss_import_name(minor_status, input_name_buffer,
1833 input_name_type, output_name);
1835 dsyslog("Leaving import_name\n");
1841 spnego_gss_release_name(
1842 OM_uint32 *minor_status,
1843 gss_name_t *input_name)
1847 dsyslog("Entering release_name\n");
1849 status = gss_release_name(minor_status, input_name);
1851 dsyslog("Leaving release_name\n");
1856 spnego_gss_inquire_cred(
1857 OM_uint32 *minor_status,
1858 gss_cred_id_t cred_handle,
1860 OM_uint32 *lifetime,
1862 gss_OID_set *mechanisms)
1865 spnego_gss_cred_id_t spcred = NULL;
1866 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1867 OM_uint32 tmp_minor_status;
1868 OM_uint32 initiator_lifetime, acceptor_lifetime;
1870 dsyslog("Entering inquire_cred\n");
1873 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1874 * supplied we call gss_inquire_cred_by_mech() on the
1875 * first non-SPNEGO mechanism.
1877 spcred = (spnego_gss_cred_id_t)cred_handle;
1878 if (spcred == NULL) {
1879 status = get_available_mechs(minor_status,
1884 if (status != GSS_S_COMPLETE) {
1885 dsyslog("Leaving inquire_cred\n");
1889 if ((*mechanisms)->count == 0) {
1890 gss_release_cred(&tmp_minor_status, &creds);
1891 gss_release_oid_set(&tmp_minor_status, mechanisms);
1892 dsyslog("Leaving inquire_cred\n");
1893 return (GSS_S_DEFECTIVE_CREDENTIAL);
1896 assert((*mechanisms)->elements != NULL);
1898 status = gss_inquire_cred_by_mech(minor_status,
1900 &(*mechanisms)->elements[0],
1902 &initiator_lifetime,
1905 if (status != GSS_S_COMPLETE) {
1906 gss_release_cred(&tmp_minor_status, &creds);
1907 dsyslog("Leaving inquire_cred\n");
1911 if (lifetime != NULL)
1912 *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1913 acceptor_lifetime : initiator_lifetime;
1915 gss_release_cred(&tmp_minor_status, &creds);
1917 status = gss_inquire_cred(minor_status, spcred->mcred,
1919 cred_usage, mechanisms);
1922 dsyslog("Leaving inquire_cred\n");
1929 spnego_gss_compare_name(
1930 OM_uint32 *minor_status,
1931 const gss_name_t name1,
1932 const gss_name_t name2,
1935 OM_uint32 status = GSS_S_COMPLETE;
1936 dsyslog("Entering compare_name\n");
1938 status = gss_compare_name(minor_status, name1, name2, name_equal);
1940 dsyslog("Leaving compare_name\n");
1947 spnego_gss_display_name(
1948 OM_uint32 *minor_status,
1949 gss_name_t input_name,
1950 gss_buffer_t output_name_buffer,
1951 gss_OID *output_name_type)
1953 OM_uint32 status = GSS_S_COMPLETE;
1954 dsyslog("Entering display_name\n");
1956 status = gss_display_name(minor_status, input_name,
1957 output_name_buffer, output_name_type);
1959 dsyslog("Leaving display_name\n");
1966 spnego_gss_inquire_names_for_mech(
1967 OM_uint32 *minor_status,
1969 gss_OID_set *name_types)
1971 OM_uint32 major, minor;
1973 dsyslog("Entering inquire_names_for_mech\n");
1975 * We only know how to handle our own mechanism.
1977 if ((mechanism != GSS_C_NULL_OID) &&
1978 !g_OID_equal(gss_mech_spnego, mechanism)) {
1980 return (GSS_S_FAILURE);
1983 major = gss_create_empty_oid_set(minor_status, name_types);
1984 if (major == GSS_S_COMPLETE) {
1985 /* Now add our members. */
1986 if (((major = gss_add_oid_set_member(minor_status,
1987 (gss_OID) GSS_C_NT_USER_NAME,
1988 name_types)) == GSS_S_COMPLETE) &&
1989 ((major = gss_add_oid_set_member(minor_status,
1990 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1991 name_types)) == GSS_S_COMPLETE) &&
1992 ((major = gss_add_oid_set_member(minor_status,
1993 (gss_OID) GSS_C_NT_STRING_UID_NAME,
1994 name_types)) == GSS_S_COMPLETE)) {
1995 major = gss_add_oid_set_member(minor_status,
1996 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2000 if (major != GSS_S_COMPLETE)
2001 (void) gss_release_oid_set(&minor, name_types);
2004 dsyslog("Leaving inquire_names_for_mech\n");
2010 OM_uint32 *minor_status,
2011 gss_ctx_id_t context_handle,
2012 gss_buffer_t input_message_buffer,
2013 gss_buffer_t output_message_buffer,
2015 gss_qop_t *qop_state)
2018 ret = gss_unwrap(minor_status,
2020 input_message_buffer,
2021 output_message_buffer,
2030 OM_uint32 *minor_status,
2031 gss_ctx_id_t context_handle,
2034 gss_buffer_t input_message_buffer,
2036 gss_buffer_t output_message_buffer)
2039 ret = gss_wrap(minor_status,
2043 input_message_buffer,
2045 output_message_buffer);
2051 spnego_gss_process_context_token(
2052 OM_uint32 *minor_status,
2053 const gss_ctx_id_t context_handle,
2054 const gss_buffer_t token_buffer)
2057 ret = gss_process_context_token(minor_status,
2065 spnego_gss_delete_sec_context(
2066 OM_uint32 *minor_status,
2067 gss_ctx_id_t *context_handle,
2068 gss_buffer_t output_token)
2070 OM_uint32 ret = GSS_S_COMPLETE;
2071 spnego_gss_ctx_id_t *ctx =
2072 (spnego_gss_ctx_id_t *)context_handle;
2074 if (context_handle == NULL)
2075 return (GSS_S_FAILURE);
2078 * If this is still an SPNEGO mech, release it locally.
2081 (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2082 (void) gss_delete_sec_context(minor_status,
2083 &(*ctx)->ctx_handle,
2085 (void) release_spnego_ctx(ctx);
2087 ret = gss_delete_sec_context(minor_status,
2096 spnego_gss_context_time(
2097 OM_uint32 *minor_status,
2098 const gss_ctx_id_t context_handle,
2099 OM_uint32 *time_rec)
2102 ret = gss_context_time(minor_status,
2109 spnego_gss_export_sec_context(
2110 OM_uint32 *minor_status,
2111 gss_ctx_id_t *context_handle,
2112 gss_buffer_t interprocess_token)
2115 ret = gss_export_sec_context(minor_status,
2117 interprocess_token);
2122 spnego_gss_import_sec_context(
2123 OM_uint32 *minor_status,
2124 const gss_buffer_t interprocess_token,
2125 gss_ctx_id_t *context_handle)
2128 ret = gss_import_sec_context(minor_status,
2133 #endif /* LEAN_CLIENT */
2136 spnego_gss_inquire_context(
2137 OM_uint32 *minor_status,
2138 const gss_ctx_id_t context_handle,
2139 gss_name_t *src_name,
2140 gss_name_t *targ_name,
2141 OM_uint32 *lifetime_rec,
2143 OM_uint32 *ctx_flags,
2144 int *locally_initiated,
2147 OM_uint32 ret = GSS_S_COMPLETE;
2149 ret = gss_inquire_context(minor_status,
2163 spnego_gss_wrap_size_limit(
2164 OM_uint32 *minor_status,
2165 const gss_ctx_id_t context_handle,
2168 OM_uint32 req_output_size,
2169 OM_uint32 *max_input_size)
2172 ret = gss_wrap_size_limit(minor_status,
2183 OM_uint32 *minor_status,
2184 const gss_ctx_id_t context_handle,
2186 const gss_buffer_t message_buffer,
2187 gss_buffer_t message_token)
2190 ret = gss_get_mic(minor_status,
2199 spnego_gss_verify_mic(
2200 OM_uint32 *minor_status,
2201 const gss_ctx_id_t context_handle,
2202 const gss_buffer_t msg_buffer,
2203 const gss_buffer_t token_buffer,
2204 gss_qop_t *qop_state)
2207 ret = gss_verify_mic(minor_status,
2216 spnego_gss_inquire_sec_context_by_oid(
2217 OM_uint32 *minor_status,
2218 const gss_ctx_id_t context_handle,
2219 const gss_OID desired_object,
2220 gss_buffer_set_t *data_set)
2223 ret = gss_inquire_sec_context_by_oid(minor_status,
2231 spnego_gss_inquire_cred_by_oid(
2232 OM_uint32 *minor_status,
2233 const gss_cred_id_t cred_handle,
2234 const gss_OID desired_object,
2235 gss_buffer_set_t *data_set)
2238 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2239 gss_cred_id_t mcred;
2240 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2241 ret = gss_inquire_cred_by_oid(minor_status,
2249 spnego_gss_set_cred_option(
2250 OM_uint32 *minor_status,
2251 gss_cred_id_t *cred_handle,
2252 const gss_OID desired_object,
2253 const gss_buffer_t value)
2256 OM_uint32 tmp_minor_status;
2257 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2258 gss_cred_id_t mcred;
2260 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2261 ret = gss_set_cred_option(minor_status,
2265 if (ret == GSS_S_COMPLETE && spcred == NULL) {
2267 * If the mechanism allocated a new credential handle, then
2268 * we need to wrap it up in an SPNEGO credential handle.
2271 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2272 if (spcred == NULL) {
2273 gss_release_cred(&tmp_minor_status, &mcred);
2274 *minor_status = ENOMEM;
2275 return (GSS_S_FAILURE);
2277 spcred->mcred = mcred;
2278 spcred->neg_mechs = GSS_C_NULL_OID_SET;
2279 *cred_handle = (gss_cred_id_t)spcred;
2286 spnego_gss_set_sec_context_option(
2287 OM_uint32 *minor_status,
2288 gss_ctx_id_t *context_handle,
2289 const gss_OID desired_object,
2290 const gss_buffer_t value)
2293 ret = gss_set_sec_context_option(minor_status,
2301 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2302 gss_ctx_id_t context_handle,
2305 gss_buffer_t input_assoc_buffer,
2306 gss_buffer_t input_payload_buffer,
2308 gss_buffer_t output_message_buffer)
2311 ret = gss_wrap_aead(minor_status,
2316 input_payload_buffer,
2318 output_message_buffer);
2324 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2325 gss_ctx_id_t context_handle,
2326 gss_buffer_t input_message_buffer,
2327 gss_buffer_t input_assoc_buffer,
2328 gss_buffer_t output_payload_buffer,
2330 gss_qop_t *qop_state)
2333 ret = gss_unwrap_aead(minor_status,
2335 input_message_buffer,
2337 output_payload_buffer,
2344 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2345 gss_ctx_id_t context_handle,
2349 gss_iov_buffer_desc *iov,
2353 ret = gss_wrap_iov(minor_status,
2364 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2365 gss_ctx_id_t context_handle,
2367 gss_qop_t *qop_state,
2368 gss_iov_buffer_desc *iov,
2372 ret = gss_unwrap_iov(minor_status,
2382 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2383 gss_ctx_id_t context_handle,
2387 gss_iov_buffer_desc *iov,
2391 ret = gss_wrap_iov_length(minor_status,
2403 spnego_gss_complete_auth_token(
2404 OM_uint32 *minor_status,
2405 const gss_ctx_id_t context_handle,
2406 gss_buffer_t input_message_buffer)
2409 ret = gss_complete_auth_token(minor_status,
2411 input_message_buffer);
2416 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2417 const gss_cred_id_t impersonator_cred_handle,
2418 const gss_name_t desired_name,
2420 gss_OID_set desired_mechs,
2421 gss_cred_usage_t cred_usage,
2422 gss_cred_id_t *output_cred_handle,
2423 gss_OID_set *actual_mechs,
2424 OM_uint32 *time_rec)
2427 gss_OID_set amechs = GSS_C_NULL_OID_SET;
2428 spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2429 gss_cred_id_t mcred;
2431 dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2434 *actual_mechs = NULL;
2439 if (desired_mechs == GSS_C_NO_OID_SET) {
2440 status = gss_inquire_cred(minor_status,
2441 impersonator_cred_handle,
2444 if (status != GSS_S_COMPLETE)
2447 desired_mechs = amechs;
2450 imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2451 status = gss_acquire_cred_impersonate_name(minor_status,
2452 imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL,
2453 desired_name, time_req,
2454 desired_mechs, cred_usage,
2455 &mcred, actual_mechs,
2458 if (amechs != GSS_C_NULL_OID_SET)
2459 (void) gss_release_oid_set(minor_status, &amechs);
2461 out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2462 if (out_spcred == NULL) {
2463 gss_release_cred(minor_status, &mcred);
2464 *minor_status = ENOMEM;
2465 return (GSS_S_FAILURE);
2467 out_spcred->mcred = mcred;
2468 out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
2469 *output_cred_handle = (gss_cred_id_t)out_spcred;
2471 dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2476 spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2477 const gss_name_t desired_name,
2478 const gss_buffer_t password,
2480 const gss_OID_set desired_mechs,
2481 gss_cred_usage_t cred_usage,
2482 gss_cred_id_t *output_cred_handle,
2483 gss_OID_set *actual_mechs,
2484 OM_uint32 *time_rec)
2486 OM_uint32 status, tmpmin;
2487 gss_OID_set amechs = GSS_C_NULL_OID_SET, dmechs;
2488 gss_cred_id_t mcred = NULL;
2489 spnego_gss_cred_id_t spcred = NULL;
2491 dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2494 *actual_mechs = NULL;
2499 dmechs = desired_mechs;
2500 if (desired_mechs == GSS_C_NULL_OID_SET) {
2501 status = get_available_mechs(minor_status, desired_name,
2502 cred_usage, NULL, &amechs);
2506 status = gss_acquire_cred_with_password(minor_status, desired_name,
2507 password, time_req, dmechs,
2509 actual_mechs, time_rec);
2510 if (status != GSS_S_COMPLETE)
2513 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2514 if (spcred == NULL) {
2515 *minor_status = ENOMEM;
2516 status = GSS_S_FAILURE;
2519 spcred->neg_mechs = GSS_C_NULL_OID_SET;
2520 spcred->mcred = mcred;
2521 mcred = GSS_C_NO_CREDENTIAL;
2522 *output_cred_handle = (gss_cred_id_t)spcred;
2526 (void) gss_release_oid_set(&tmpmin, &amechs);
2527 (void) gss_release_cred(&tmpmin, &mcred);
2529 dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2534 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2536 gss_OID display_as_name_type,
2537 gss_buffer_t display_name)
2540 ret = gss_display_name_ext(minor_status,
2542 display_as_name_type,
2549 spnego_gss_inquire_name(OM_uint32 *minor_status,
2553 gss_buffer_set_t *attrs)
2556 ret = gss_inquire_name(minor_status,
2565 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2571 gss_buffer_t display_value,
2575 ret = gss_get_name_attribute(minor_status,
2587 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2594 ret = gss_set_name_attribute(minor_status,
2603 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2608 ret = gss_delete_name_attribute(minor_status,
2615 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2617 gss_buffer_t exp_composite_name)
2620 ret = gss_export_name_composite(minor_status,
2622 exp_composite_name);
2627 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2630 gss_buffer_t type_id,
2634 ret = gss_map_name_to_any(minor_status,
2643 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2645 gss_buffer_t type_id,
2649 ret = gss_release_any_name_mapping(minor_status,
2657 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2658 gss_ctx_id_t context,
2660 const gss_buffer_t prf_in,
2661 ssize_t desired_output_len,
2662 gss_buffer_t prf_out)
2665 ret = gss_pseudo_random(minor_status,
2675 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2676 gss_cred_id_t cred_handle,
2677 const gss_OID_set mech_list)
2680 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2682 /* Store mech_list in spcred for use in negotiation logic. */
2683 gss_release_oid_set(minor_status, &spcred->neg_mechs);
2684 ret = generic_gss_copy_oid_set(minor_status, mech_list,
2685 &spcred->neg_mechs);
2689 #define SPNEGO_SASL_NAME "SPNEGO"
2690 #define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1)
2693 spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2694 const gss_buffer_t sasl_mech_name,
2697 if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2698 memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2699 SPNEGO_SASL_NAME_LEN) == 0) {
2700 if (mech_type != NULL)
2701 *mech_type = (gss_OID)gss_mech_spnego;
2702 return (GSS_S_COMPLETE);
2705 return (GSS_S_BAD_MECH);
2709 spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2710 const gss_OID desired_mech,
2711 gss_buffer_t sasl_mech_name,
2712 gss_buffer_t mech_name,
2713 gss_buffer_t mech_description)
2717 if (!g_OID_equal(desired_mech, gss_mech_spnego))
2718 return (GSS_S_BAD_MECH);
2720 if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2721 !g_make_string_buffer("spnego", mech_name) ||
2722 !g_make_string_buffer("Simple and Protected GSS-API "
2723 "Negotiation Mechanism", mech_description))
2726 return (GSS_S_COMPLETE);
2729 *minor_status = ENOMEM;
2730 return (GSS_S_FAILURE);
2734 spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2736 gss_OID_set *mech_attrs,
2737 gss_OID_set *known_mech_attrs)
2739 OM_uint32 major, tmpMinor;
2741 /* known_mech_attrs is handled by mechglue */
2744 if (mech_attrs == NULL)
2745 return (GSS_S_COMPLETE);
2747 major = gss_create_empty_oid_set(minor_status, mech_attrs);
2748 if (GSS_ERROR(major))
2751 #define MA_SUPPORTED(ma) do { \
2752 major = gss_add_oid_set_member(minor_status, \
2753 (gss_OID)ma, mech_attrs); \
2754 if (GSS_ERROR(major)) \
2758 MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2759 MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2762 if (GSS_ERROR(major))
2763 gss_release_oid_set(&tmpMinor, mech_attrs);
2769 * We will release everything but the ctx_handle so that it
2770 * can be passed back to init/accept context. This routine should
2771 * not be called until after the ctx_handle memory is assigned to
2772 * the supplied context handle from init/accept context.
2775 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2777 spnego_gss_ctx_id_t context;
2778 OM_uint32 minor_stat;
2781 if (context != NULL) {
2782 (void) gss_release_buffer(&minor_stat,
2783 &context->DER_mechTypes);
2785 (void) generic_gss_release_oid(&minor_stat,
2786 &context->internal_mech);
2788 (void) gss_release_name(&minor_stat, &context->internal_name);
2790 if (context->optionStr != NULL) {
2791 free(context->optionStr);
2792 context->optionStr = NULL;
2800 * Can't use gss_indicate_mechs by itself to get available mechs for
2801 * SPNEGO because it will also return the SPNEGO mech and we do not
2802 * want to consider SPNEGO as an available security mech for
2803 * negotiation. For this reason, get_available_mechs will return
2804 * all available mechs except SPNEGO.
2806 * If a ptr to a creds list is given, this function will attempt
2807 * to acquire creds for the creds given and trim the list of
2808 * returned mechanisms to only those for which creds are valid.
2812 get_available_mechs(OM_uint32 *minor_status,
2813 gss_name_t name, gss_cred_usage_t usage,
2814 gss_cred_id_t *creds, gss_OID_set *rmechs)
2818 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2819 gss_OID_set mechs, goodmechs;
2821 major_status = gss_indicate_mechs(minor_status, &mechs);
2823 if (major_status != GSS_S_COMPLETE) {
2824 return (major_status);
2827 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2829 if (major_status != GSS_S_COMPLETE) {
2830 (void) gss_release_oid_set(minor_status, &mechs);
2831 return (major_status);
2834 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2835 if ((mechs->elements[i].length
2836 != spnego_mechanism.mech_type.length) ||
2837 memcmp(mechs->elements[i].elements,
2838 spnego_mechanism.mech_type.elements,
2839 spnego_mechanism.mech_type.length)) {
2841 major_status = gss_add_oid_set_member(minor_status,
2842 &mechs->elements[i],
2844 if (major_status == GSS_S_COMPLETE)
2850 * If the caller wanted a list of creds returned,
2851 * trim the list of mechanisms down to only those
2852 * for which the creds are valid.
2854 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2855 major_status = gss_acquire_cred(minor_status,
2856 name, GSS_C_INDEFINITE,
2857 *rmechs, usage, creds,
2861 * Drop the old list in favor of the new
2864 (void) gss_release_oid_set(&tmpmin, rmechs);
2865 if (major_status == GSS_S_COMPLETE) {
2866 (void) gssint_copy_oid_set(&tmpmin,
2868 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2872 (void) gss_release_oid_set(&tmpmin, &mechs);
2873 if (found == 0 || major_status != GSS_S_COMPLETE) {
2874 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2875 map_errcode(minor_status);
2876 if (major_status == GSS_S_COMPLETE)
2877 major_status = GSS_S_FAILURE;
2880 return (major_status);
2884 * Return a list of mechanisms we are willing to negotiate for a credential,
2885 * taking into account the mech set provided with gss_set_neg_mechs if it
2889 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
2890 gss_cred_usage_t usage, gss_OID_set *rmechs)
2892 OM_uint32 ret, tmpmin;
2893 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
2894 gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
2895 gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
2899 if (spcred == NULL) {
2901 * The default credentials were supplied. Return a list of all
2902 * available mechs except SPNEGO. When initiating, trim this
2903 * list to mechs we can acquire credentials for.
2905 credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
2906 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
2908 gss_release_cred(&tmpmin, &creds);
2912 /* Get the list of mechs in the mechglue cred. */
2913 ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
2915 if (ret != GSS_S_COMPLETE)
2918 if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
2919 /* gss_set_neg_mechs was never called; return cred_mechs. */
2920 *rmechs = cred_mechs;
2922 return (GSS_S_COMPLETE);
2925 /* Compute the intersection of cred_mechs and spcred->neg_mechs,
2926 * preserving the order in spcred->neg_mechs. */
2927 ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
2928 if (ret != GSS_S_COMPLETE) {
2929 gss_release_oid_set(&tmpmin, &cred_mechs);
2933 for (i = 0; i < spcred->neg_mechs->count; i++) {
2934 gss_test_oid_set_member(&tmpmin,
2935 &spcred->neg_mechs->elements[i],
2936 cred_mechs, &present);
2939 ret = gss_add_oid_set_member(minor_status,
2940 &spcred->neg_mechs->elements[i],
2942 if (ret != GSS_S_COMPLETE)
2946 gss_release_oid_set(&tmpmin, &cred_mechs);
2947 if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
2948 gss_release_oid_set(&tmpmin, &intersect_mechs);
2949 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2950 map_errcode(minor_status);
2951 return (GSS_S_FAILURE);
2954 *rmechs = intersect_mechs;
2956 return (GSS_S_COMPLETE);
2959 /* following are token creation and reading routines */
2962 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2963 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2967 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2971 gss_OID mech_out = NULL;
2972 unsigned char *start, *end;
2974 if (length < 1 || **buff_in != MECH_OID)
2978 end = start + length;
2981 toid.length = *(*buff_in)++;
2983 if ((*buff_in + toid.length) > end)
2986 toid.elements = *buff_in;
2987 *buff_in += toid.length;
2989 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2991 if (status != GSS_S_COMPLETE) {
2992 map_errcode(minor_status);
3000 * der encode the given mechanism oid into buf_out, advancing the
3005 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
3007 if (buflen < mech->length + 2)
3009 *(*buf_out)++ = MECH_OID;
3010 *(*buf_out)++ = (unsigned char) mech->length;
3011 memcpy(*buf_out, mech->elements, mech->length);
3012 *buf_out += mech->length;
3017 * verify that buff_in points to an octet string, if it does not,
3018 * return NULL and don't advance the pointer. If it is an octet string
3019 * decode buff_in into a gss_buffer_t and return it, advancing the
3023 get_input_token(unsigned char **buff_in, unsigned int buff_length)
3025 gss_buffer_t input_token;
3028 if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
3031 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3032 if (input_token == NULL)
3035 input_token->length = len;
3036 input_token->value = malloc(input_token->length);
3038 if (input_token->value == NULL) {
3043 (void) memcpy(input_token->value, *buff_in, input_token->length);
3044 *buff_in += input_token->length;
3045 return (input_token);
3049 * verify that the input token length is not 0. If it is, just return.
3050 * If the token length is greater than 0, der encode as an octet string
3051 * and place in buf_out, advancing buf_out.
3055 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
3056 unsigned int buflen)
3060 /* if token length is 0, we do not want to send */
3061 if (input_token->length == 0)
3064 if (input_token->length > buflen)
3067 *(*buf_out)++ = OCTET_STRING;
3068 if ((ret = gssint_put_der_length(input_token->length, buf_out,
3069 input_token->length)))
3071 TWRITE_STR(*buf_out, input_token->value, input_token->length);
3076 * verify that buff_in points to a sequence of der encoding. The mech
3077 * set is the only sequence of encoded object in the token, so if it is
3078 * a sequence of encoding, decode the mechset into a gss_OID_set and
3079 * return it, advancing the buffer pointer.
3082 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
3083 unsigned int buff_length)
3085 gss_OID_set returned_mechSet;
3086 OM_uint32 major_status;
3089 OM_uint32 set_length;
3090 unsigned char *start;
3093 if (**buff_in != SEQUENCE_OF)
3099 length = gssint_get_der_length(buff_in, buff_length, &bytes);
3100 if (length < 0 || buff_length - bytes < (unsigned int)length)
3103 major_status = gss_create_empty_oid_set(minor_status,
3105 if (major_status != GSS_S_COMPLETE)
3108 for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
3109 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
3110 buff_length - (*buff_in - start));
3114 major_status = gss_add_oid_set_member(minor_status,
3115 temp, &returned_mechSet);
3116 if (major_status == GSS_S_COMPLETE) {
3117 set_length += returned_mechSet->elements[i].length +2;
3118 if (generic_gss_release_oid(minor_status, &temp))
3119 map_errcode(minor_status);
3123 return (returned_mechSet);
3127 * Encode mechSet into buf.
3130 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3134 unsigned int tlen, ilen;
3137 for (i = 0; i < mechSet->count; i++) {
3139 * 0x06 [DER LEN] [OID]
3142 gssint_der_length_size(mechSet->elements[i].length) +
3143 mechSet->elements[i].length;
3148 tlen = 1 + gssint_der_length_size(ilen) + ilen;
3155 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3157 *ptr++ = SEQUENCE_OF;
3158 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3160 for (i = 0; i < mechSet->count; i++) {
3161 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3170 * Verify that buff_in is pointing to a BIT_STRING with the correct
3171 * length and padding for the req_flags. If it is, decode req_flags
3172 * and return them, otherwise, return NULL.
3175 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3176 OM_uint32 *req_flags)
3180 if (**buff_in != (CONTEXT | 0x01))
3183 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3184 bodysize, &len) < 0)
3185 return GSS_S_DEFECTIVE_TOKEN;
3187 if (*(*buff_in)++ != BIT_STRING)
3188 return GSS_S_DEFECTIVE_TOKEN;
3190 if (*(*buff_in)++ != BIT_STRING_LENGTH)
3191 return GSS_S_DEFECTIVE_TOKEN;
3193 if (*(*buff_in)++ != BIT_STRING_PADDING)
3194 return GSS_S_DEFECTIVE_TOKEN;
3196 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3201 get_negTokenInit(OM_uint32 *minor_status,
3203 gss_buffer_t der_mechSet,
3204 gss_OID_set *mechSet,
3205 OM_uint32 *req_flags,
3206 gss_buffer_t *mechtok,
3207 gss_buffer_t *mechListMIC)
3210 unsigned char *ptr, *bufstart;
3212 gss_buffer_desc tmpbuf;
3215 der_mechSet->length = 0;
3216 der_mechSet->value = NULL;
3217 *mechSet = GSS_C_NO_OID_SET;
3219 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3221 ptr = bufstart = buf->value;
3222 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3223 return GSS_S_FAILURE;
3224 #define REMAIN (buf->length - (ptr - bufstart))
3226 err = g_verify_token_header(gss_mech_spnego,
3227 &len, &ptr, 0, REMAIN);
3229 *minor_status = err;
3230 map_errcode(minor_status);
3231 return GSS_S_FAILURE;
3233 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3234 if (*minor_status) {
3235 map_errcode(minor_status);
3236 return GSS_S_FAILURE;
3239 /* alias into input_token */
3241 tmpbuf.length = REMAIN;
3242 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3243 if (*mechSet == NULL)
3244 return GSS_S_FAILURE;
3246 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3247 der_mechSet->value = malloc(tmpbuf.length);
3248 if (der_mechSet->value == NULL)
3249 return GSS_S_FAILURE;
3250 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3251 der_mechSet->length = tmpbuf.length;
3253 err = get_req_flags(&ptr, REMAIN, req_flags);
3254 if (err != GSS_S_COMPLETE) {
3257 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3258 REMAIN, &len) >= 0) {
3259 *mechtok = get_input_token(&ptr, len);
3260 if (*mechtok == GSS_C_NO_BUFFER) {
3261 return GSS_S_FAILURE;
3264 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3265 REMAIN, &len) >= 0) {
3266 *mechListMIC = get_input_token(&ptr, len);
3267 if (*mechListMIC == GSS_C_NO_BUFFER) {
3268 return GSS_S_FAILURE;
3271 return GSS_S_COMPLETE;
3276 get_negTokenResp(OM_uint32 *minor_status,
3277 unsigned char *buf, unsigned int buflen,
3278 OM_uint32 *negState,
3279 gss_OID *supportedMech,
3280 gss_buffer_t *responseToken,
3281 gss_buffer_t *mechListMIC)
3283 unsigned char *ptr, *bufstart;
3286 unsigned int tag, bytes;
3288 *negState = ACCEPT_DEFECTIVE_TOKEN;
3289 *supportedMech = GSS_C_NO_OID;
3290 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3291 ptr = bufstart = buf;
3292 #define REMAIN (buflen - (ptr - bufstart))
3294 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3295 return GSS_S_DEFECTIVE_TOKEN;
3296 if (*ptr++ == SEQUENCE) {
3297 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3298 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3299 return GSS_S_DEFECTIVE_TOKEN;
3306 if (tag == CONTEXT) {
3307 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3308 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3309 return GSS_S_DEFECTIVE_TOKEN;
3311 if (g_get_tag_and_length(&ptr, ENUMERATED,
3313 return GSS_S_DEFECTIVE_TOKEN;
3315 if (len != ENUMERATION_LENGTH)
3316 return GSS_S_DEFECTIVE_TOKEN;
3319 return GSS_S_DEFECTIVE_TOKEN;
3327 if (tag == (CONTEXT | 0x01)) {
3328 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3329 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3330 return GSS_S_DEFECTIVE_TOKEN;
3332 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3333 if (*supportedMech == GSS_C_NO_OID)
3334 return GSS_S_DEFECTIVE_TOKEN;
3341 if (tag == (CONTEXT | 0x02)) {
3342 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3343 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3344 return GSS_S_DEFECTIVE_TOKEN;
3346 *responseToken = get_input_token(&ptr, REMAIN);
3347 if (*responseToken == GSS_C_NO_BUFFER)
3348 return GSS_S_DEFECTIVE_TOKEN;
3355 if (tag == (CONTEXT | 0x03)) {
3356 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3357 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3358 return GSS_S_DEFECTIVE_TOKEN;
3360 *mechListMIC = get_input_token(&ptr, REMAIN);
3361 if (*mechListMIC == GSS_C_NO_BUFFER)
3362 return GSS_S_DEFECTIVE_TOKEN;
3364 /* Handle Windows 2000 duplicate response token */
3365 if (*responseToken &&
3366 ((*responseToken)->length == (*mechListMIC)->length) &&
3367 !memcmp((*responseToken)->value, (*mechListMIC)->value,
3368 (*responseToken)->length)) {
3371 gss_release_buffer(&tmpmin, *mechListMIC);
3373 *mechListMIC = NULL;
3376 return GSS_S_COMPLETE;
3381 * der encode the passed negResults as an ENUMERATED type and
3382 * place it in buf_out, advancing the buffer.
3386 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3387 unsigned int buflen)
3391 *(*buf_out)++ = ENUMERATED;
3392 *(*buf_out)++ = ENUMERATION_LENGTH;
3393 *(*buf_out)++ = (unsigned char) negResult;
3398 * This routine compares the recieved mechset to the mechset that
3399 * this server can support. It looks sequentially through the mechset
3400 * and the first one that matches what the server can support is
3401 * chosen as the negotiated mechanism. If one is found, negResult
3402 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3403 * it's not the first mech, otherwise we return NULL and negResult
3406 * NOTE: There is currently no way to specify a preference order of
3407 * mechanisms supported by the acceptor.
3410 negotiate_mech_type(OM_uint32 *minor_status,
3411 gss_OID_set supported_mechSet,
3412 gss_OID_set mechset,
3413 OM_uint32 *negResult)
3415 gss_OID returned_mech;
3420 for (i = 0; i < mechset->count; i++) {
3421 gss_OID mech_oid = &mechset->elements[i];
3423 /* Accept wrong mechanism OID from MS clients */
3424 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3425 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3426 mech_oid = (gss_OID)&gss_mech_krb5_oid;;
3428 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3433 *negResult = ACCEPT_INCOMPLETE;
3435 *negResult = REQUEST_MIC;
3437 status = generic_gss_copy_oid(minor_status,
3438 &mechset->elements[i],
3440 if (status != GSS_S_COMPLETE) {
3441 *negResult = REJECT;
3442 map_errcode(minor_status);
3445 return (returned_mech);
3447 *negResult = REJECT;
3452 * the next two routines make a token buffer suitable for
3453 * spnego_gss_display_status. These currently take the string
3454 * in name and place it in the token. Eventually, if
3455 * spnego_gss_display_status returns valid error messages,
3456 * these routines will be changes to return the error string.
3458 static spnego_token_t
3459 make_spnego_token(char *name)
3461 return (spnego_token_t)strdup(name);
3464 static gss_buffer_desc
3465 make_err_msg(char *name)
3467 gss_buffer_desc buffer;
3471 buffer.value = NULL;
3473 buffer.length = strlen(name)+1;
3474 buffer.value = make_spnego_token(name);
3481 * Create the client side spnego token passed back to gss_init_sec_context
3482 * and eventually up to the application program and over to the server.
3484 * Use DER rules, definite length method per RFC 2478
3487 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3489 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3490 gss_buffer_t data, send_token_flag sendtoken,
3491 gss_buffer_t outbuf)
3494 unsigned int tlen, dataLen = 0;
3495 unsigned int negTokenInitSize = 0;
3496 unsigned int negTokenInitSeqSize = 0;
3497 unsigned int negTokenInitContSize = 0;
3498 unsigned int rspTokenSize = 0;
3499 unsigned int mechListTokenSize = 0;
3500 unsigned int micTokenSize = 0;
3504 if (outbuf == GSS_C_NO_BUFFER)
3508 outbuf->value = NULL;
3510 /* calculate the data length */
3513 * 0xa0 [DER LEN] [mechTypes]
3515 mechListTokenSize = 1 +
3516 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3517 spnego_ctx->DER_mechTypes.length;
3518 dataLen += mechListTokenSize;
3521 * If a token from gss_init_sec_context exists,
3522 * add the length of the token + the ASN.1 overhead
3526 * Encoded in final output as:
3527 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3528 * -----s--------|--------s2----------
3531 gssint_der_length_size(data->length) +
3533 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3539 * Encoded in final output as:
3540 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3541 * --s-- -----tlen------------
3544 gssint_der_length_size(mechListMIC->length) +
3545 mechListMIC->length;
3547 gssint_der_length_size(micTokenSize) +
3552 * Add size of DER encoding
3553 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3554 * 0x30 [DER_LEN] [data]
3557 negTokenInitContSize = dataLen;
3558 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3559 dataLen = negTokenInitSeqSize;
3562 * negTokenInitSize indicates the bytes needed to
3563 * hold the ASN.1 encoding of the entire NegTokenInit
3565 * 0xa0 [DER_LEN] + data
3568 negTokenInitSize = 1 +
3569 gssint_der_length_size(negTokenInitSeqSize) +
3570 negTokenInitSeqSize;
3572 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3574 t = (unsigned char *) malloc(tlen);
3582 /* create the message */
3583 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3587 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3588 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3592 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3593 tlen - (int)(ptr-t))))
3596 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3597 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3598 &ptr, tlen - (int)(ptr-t))))
3601 /* We already encoded the MechSetList */
3602 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3603 spnego_ctx->DER_mechTypes.length);
3605 ptr += spnego_ctx->DER_mechTypes.length;
3608 *ptr++ = CONTEXT | 0x02;
3609 if ((ret = gssint_put_der_length(rspTokenSize,
3610 &ptr, tlen - (int)(ptr - t))))
3613 if ((ret = put_input_token(&ptr, data,
3614 tlen - (int)(ptr - t))))
3618 if (mechListMIC != GSS_C_NO_BUFFER) {
3619 *ptr++ = CONTEXT | 0x03;
3620 if ((ret = gssint_put_der_length(micTokenSize,
3621 &ptr, tlen - (int)(ptr - t))))
3624 if (negHintsCompat) {
3625 ret = put_neg_hints(&ptr, mechListMIC,
3626 tlen - (int)(ptr - t));
3629 } else if ((ret = put_input_token(&ptr, mechListMIC,
3630 tlen - (int)(ptr - t))))
3641 outbuf->length = tlen;
3642 outbuf->value = (void *) t;
3648 * create the server side spnego token passed back to
3649 * gss_accept_sec_context and eventually up to the application program
3650 * and over to the client.
3653 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3654 gss_buffer_t data, gss_buffer_t mechListMIC,
3655 send_token_flag sendtoken,
3656 gss_buffer_t outbuf)
3658 unsigned int tlen = 0;
3659 unsigned int ret = 0;
3660 unsigned int NegTokenTargSize = 0;
3661 unsigned int NegTokenSize = 0;
3662 unsigned int rspTokenSize = 0;
3663 unsigned int micTokenSize = 0;
3664 unsigned int dataLen = 0;
3668 if (outbuf == GSS_C_NO_BUFFER)
3669 return (GSS_S_DEFECTIVE_TOKEN);
3670 if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3671 return (GSS_S_DEFECTIVE_TOKEN);
3674 outbuf->value = NULL;
3677 * ASN.1 encoding of the negResult
3678 * ENUMERATED type is 3 bytes
3679 * ENUMERATED TAG, Length, Value,
3680 * Plus 2 bytes for the CONTEXT id and length.
3685 * calculate data length
3687 * If this is the initial token, include length of
3688 * mech_type and the negotiation result fields.
3690 if (sendtoken == INIT_TOKEN_SEND) {
3691 int mechlistTokenSize;
3693 * 1 byte for the CONTEXT ID(0xa0),
3694 * 1 byte for the OID ID(0x06)
3695 * 1 byte for OID Length field
3696 * Plus the rest... (OID Length, OID value)
3698 mechlistTokenSize = 3 + mech_wanted->length +
3699 gssint_der_length_size(mech_wanted->length);
3701 dataLen += mechlistTokenSize;
3703 if (data != NULL && data->length > 0) {
3704 /* Length of the inner token */
3705 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3708 dataLen += rspTokenSize;
3710 /* Length of the outer token */
3711 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3713 if (mechListMIC != NULL) {
3715 /* Length of the inner token */
3716 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3717 mechListMIC->length;
3719 dataLen += micTokenSize;
3721 /* Length of the outer token */
3722 dataLen += 1 + gssint_der_length_size(micTokenSize);
3725 * Add size of DER encoded:
3726 * NegTokenTarg [ SEQUENCE ] of
3727 * NegResult[0] ENUMERATED {
3728 * accept_completed(0),
3729 * accept_incomplete(1),
3731 * supportedMech [1] MechType OPTIONAL,
3732 * responseToken [2] OCTET STRING OPTIONAL,
3733 * mechListMIC [3] OCTET STRING OPTIONAL
3735 * size = data->length + MechListMic + SupportedMech len +
3736 * Result Length + ASN.1 overhead
3738 NegTokenTargSize = dataLen;
3739 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3742 * NegotiationToken [ CHOICE ]{
3743 * negTokenInit [0] NegTokenInit,
3744 * negTokenTarg [1] NegTokenTarg }
3746 NegTokenSize = dataLen;
3747 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3750 t = (unsigned char *) malloc(tlen);
3753 ret = GSS_S_DEFECTIVE_TOKEN;
3760 * Indicate that we are sending CHOICE 1
3763 *ptr++ = CONTEXT | 0x01;
3764 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3765 ret = GSS_S_DEFECTIVE_TOKEN;
3769 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3770 tlen - (int)(ptr-t)) < 0) {
3771 ret = GSS_S_DEFECTIVE_TOKEN;
3776 * First field of the NegTokenTarg SEQUENCE
3777 * is the ENUMERATED NegResult.
3780 if (gssint_put_der_length(3, &ptr,
3781 tlen - (int)(ptr-t)) < 0) {
3782 ret = GSS_S_DEFECTIVE_TOKEN;
3785 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3786 ret = GSS_S_DEFECTIVE_TOKEN;
3789 if (sendtoken == INIT_TOKEN_SEND) {
3791 * Next, is the Supported MechType
3793 *ptr++ = CONTEXT | 0x01;
3794 if (gssint_put_der_length(mech_wanted->length + 2,
3796 tlen - (int)(ptr - t)) < 0) {
3797 ret = GSS_S_DEFECTIVE_TOKEN;
3800 if (put_mech_oid(&ptr, mech_wanted,
3801 tlen - (int)(ptr - t)) < 0) {
3802 ret = GSS_S_DEFECTIVE_TOKEN;
3806 if (data != NULL && data->length > 0) {
3807 *ptr++ = CONTEXT | 0x02;
3808 if (gssint_put_der_length(rspTokenSize, &ptr,
3809 tlen - (int)(ptr - t)) < 0) {
3810 ret = GSS_S_DEFECTIVE_TOKEN;
3813 if (put_input_token(&ptr, data,
3814 tlen - (int)(ptr - t)) < 0) {
3815 ret = GSS_S_DEFECTIVE_TOKEN;
3819 if (mechListMIC != NULL) {
3820 *ptr++ = CONTEXT | 0x03;
3821 if (gssint_put_der_length(micTokenSize, &ptr,
3822 tlen - (int)(ptr - t)) < 0) {
3823 ret = GSS_S_DEFECTIVE_TOKEN;
3826 if (put_input_token(&ptr, mechListMIC,
3827 tlen - (int)(ptr - t)) < 0) {
3828 ret = GSS_S_DEFECTIVE_TOKEN;
3832 ret = GSS_S_COMPLETE;
3834 if (ret != GSS_S_COMPLETE) {
3838 outbuf->length = ptr - t;
3839 outbuf->value = (void *) t;
3845 /* determine size of token */
3847 g_token_size(gss_OID_const mech, unsigned int body_size)
3852 * Initialize the header size to the
3853 * MECH_OID byte + the bytes needed to indicate the
3854 * length of the OID + the OID itself.
3856 * 0x06 [MECHLENFIELD] MECHDATA
3858 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3861 * Now add the bytes needed for the initial header
3863 * 0x60 + [DER_LEN] + HDRSIZE
3865 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3867 return (hdrsize + body_size);
3871 * generate token header.
3873 * Use DER Definite Length method per RFC2478
3874 * Use of indefinite length encoding will not be compatible
3875 * with Microsoft or others that actually follow the spec.
3878 g_make_token_header(gss_OID_const mech,
3879 unsigned int body_size,
3880 unsigned char **buf,
3881 unsigned int totallen)
3884 unsigned int hdrsize;
3885 unsigned char *p = *buf;
3887 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3889 *(*buf)++ = HEADER_ID;
3890 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3893 *(*buf)++ = MECH_OID;
3894 if ((ret = gssint_put_der_length(mech->length, buf,
3895 totallen - (int)(p - *buf))))
3897 TWRITE_STR(*buf, mech->elements, mech->length);
3902 * NOTE: This checks that the length returned by
3903 * gssint_get_der_length() is not greater than the number of octets
3904 * remaining, even though gssint_get_der_length() already checks, in
3908 g_get_tag_and_length(unsigned char **buf, int tag,
3909 unsigned int buflen, unsigned int *outlen)
3911 unsigned char *ptr = *buf;
3912 int ret = -1; /* pessimists, assume failure ! */
3913 unsigned int encoded_len;
3917 if (buflen > 1 && *ptr == tag) {
3919 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3923 } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
3934 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3936 unsigned char *buf = *buf_in;
3937 unsigned char *endptr = buf + cur_size;
3938 unsigned int seqsize;
3943 * Verify this is a NegotiationToken type token
3944 * - check for a0(context specific identifier)
3945 * - get length and verify that enoughd ata exists
3947 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3948 return (G_BAD_TOK_HEADER);
3950 cur_size = seqsize; /* should indicate bytes remaining */
3953 * Verify the next piece, it should identify this as
3954 * a strucure of type NegTokenInit.
3956 if (*buf++ == SEQUENCE) {
3957 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3958 return (G_BAD_TOK_HEADER);
3960 * Make sure we have the entire buffer as described
3962 if (buf + seqsize > endptr)
3963 return (G_BAD_TOK_HEADER);
3965 return (G_BAD_TOK_HEADER);
3968 cur_size = seqsize; /* should indicate bytes remaining */
3971 * Verify that the first blob is a sequence of mechTypes
3973 if (*buf++ == CONTEXT) {
3974 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3975 return (G_BAD_TOK_HEADER);
3977 * Make sure we have the entire buffer as described
3979 if (buf + bytes > endptr)
3980 return (G_BAD_TOK_HEADER);
3982 return (G_BAD_TOK_HEADER);
3986 * At this point, *buf should be at the beginning of the
3987 * DER encoded list of mech types that are to be negotiated.
3995 /* verify token header. */
3997 g_verify_token_header(gss_OID_const mech,
3998 unsigned int *body_size,
3999 unsigned char **buf_in,
4001 unsigned int toksize)
4003 unsigned char *buf = *buf_in;
4010 return (G_BAD_TOK_HEADER);
4012 if (*buf++ != HEADER_ID)
4013 return (G_BAD_TOK_HEADER);
4015 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
4016 return (G_BAD_TOK_HEADER);
4018 if ((seqsize + bytes) != toksize)
4019 return (G_BAD_TOK_HEADER);
4022 return (G_BAD_TOK_HEADER);
4025 if (*buf++ != MECH_OID)
4026 return (G_BAD_TOK_HEADER);
4029 return (G_BAD_TOK_HEADER);
4031 toid.length = *buf++;
4033 if (toksize < toid.length)
4034 return (G_BAD_TOK_HEADER);
4036 toksize -= toid.length;
4038 toid.elements = buf;
4041 if (!g_OID_equal(&toid, mech))
4045 * G_WRONG_MECH is not returned immediately because it's more important
4046 * to return G_BAD_TOK_HEADER if the token header is in fact bad
4049 return (G_BAD_TOK_HEADER);
4055 *body_size = toksize;
4062 * Return non-zero if the oid is one of the kerberos mech oids,
4063 * otherwise return zero.
4065 * N.B. There are 3 oids that represent the kerberos mech:
4066 * RFC-specified GSS_MECH_KRB5_OID,
4067 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
4068 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
4072 is_kerb_mech(gss_OID oid)
4076 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
4078 (void) gss_test_oid_set_member(&minor,
4079 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);