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.
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
28 * A module that implements the spnego security mechanism.
29 * It is used to negotiate the security mechanism between
30 * peers using the GSS-API. SPNEGO is specified in RFC 4178.
34 * Copyright (c) 2006-2008, Novell, Inc.
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions are met:
40 * * Redistributions of source code must retain the above copyright notice,
41 * this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * * The copyright holder's name is not used to endorse or promote products
46 * derived from this software without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
52 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 * POSSIBILITY OF SUCH DAMAGE.
60 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
69 #include "gssapiP_spnego.h"
70 #include <gssapi_err_generic.h>
74 #undef g_verify_token_header
75 #undef g_make_token_header
77 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
78 typedef const gss_OID_desc *gss_OID_const;
80 /* der routines defined in libgss */
81 extern unsigned int gssint_der_length_size(OM_uint32);
82 extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
83 extern int gssint_put_der_length(OM_uint32, unsigned char **, unsigned int);
86 /* private routines for spnego_mechanism */
87 static spnego_token_t make_spnego_token(char *);
88 static gss_buffer_desc make_err_msg(char *);
89 static int g_token_size(gss_OID_const, unsigned int);
90 static int g_make_token_header(gss_OID_const, unsigned int,
91 unsigned char **, unsigned int);
92 static int g_verify_token_header(gss_OID_const, unsigned int *,
95 static int g_verify_neg_token_init(unsigned char **, unsigned int);
96 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
97 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
98 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
99 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
100 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
101 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
102 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
103 gss_cred_usage_t, gss_OID_set *);
104 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
105 static void check_spnego_options(spnego_gss_ctx_id_t);
106 static spnego_gss_ctx_id_t create_spnego_ctx(void);
107 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
108 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
109 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
110 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
113 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
114 gss_buffer_t *, OM_uint32 *, send_token_flag *);
116 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
117 gss_buffer_t *, OM_uint32 *, send_token_flag *);
120 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
123 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
124 gss_buffer_t *, gss_buffer_t *,
125 OM_uint32 *, send_token_flag *);
127 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
128 gss_buffer_t *, gss_buffer_t *,
129 OM_uint32 *, send_token_flag *);
131 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
132 gss_OID, gss_buffer_t *, gss_buffer_t *,
133 OM_uint32 *, send_token_flag *);
135 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
136 gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
137 gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
138 OM_uint32 *, send_token_flag *);
141 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
142 spnego_gss_cred_id_t, gss_buffer_t *,
143 gss_buffer_t *, OM_uint32 *, send_token_flag *);
145 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
146 gss_buffer_t *, gss_buffer_t *,
147 OM_uint32 *, send_token_flag *);
149 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
150 OM_uint32 *, send_token_flag *);
152 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
153 gss_buffer_t, gss_OID *, gss_buffer_t,
154 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
155 OM_uint32 *, send_token_flag *);
158 negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *);
160 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
163 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
166 OM_uint32, gss_buffer_t, send_token_flag,
169 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
170 gss_buffer_t, send_token_flag,
174 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
175 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
178 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
179 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
182 is_kerb_mech(gss_OID oid);
184 /* SPNEGO oid structure */
185 static const gss_OID_desc spnego_oids[] = {
186 {SPNEGO_OID_LENGTH, SPNEGO_OID},
189 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
190 static const gss_OID_set_desc spnego_oidsets[] = {
191 {1, (gss_OID) spnego_oids+0},
193 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
195 static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
196 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
198 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
199 gss_buffer_t *, OM_uint32 *, send_token_flag *);
202 * The Mech OID for SPNEGO:
203 * { iso(1) org(3) dod(6) internet(1) security(5)
204 * mechanism(5) spnego(2) }
206 static struct gss_config spnego_mechanism =
208 {SPNEGO_OID_LENGTH, SPNEGO_OID},
210 spnego_gss_acquire_cred,
211 spnego_gss_release_cred,
212 spnego_gss_init_sec_context,
214 spnego_gss_accept_sec_context,
217 #endif /* LEAN_CLIENT */
218 NULL, /* gss_process_context_token */
219 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
220 spnego_gss_context_time, /* gss_context_time */
221 spnego_gss_get_mic, /* gss_get_mic */
222 spnego_gss_verify_mic, /* gss_verify_mic */
223 spnego_gss_wrap, /* gss_wrap */
224 spnego_gss_unwrap, /* gss_unwrap */
225 spnego_gss_display_status,
226 NULL, /* gss_indicate_mechs */
227 spnego_gss_compare_name,
228 spnego_gss_display_name,
229 spnego_gss_import_name,
230 spnego_gss_release_name,
231 spnego_gss_inquire_cred, /* gss_inquire_cred */
232 NULL, /* gss_add_cred */
234 spnego_gss_export_sec_context, /* gss_export_sec_context */
235 spnego_gss_import_sec_context, /* gss_import_sec_context */
237 NULL, /* gss_export_sec_context */
238 NULL, /* gss_import_sec_context */
239 #endif /* LEAN_CLIENT */
240 NULL, /* gss_inquire_cred_by_mech */
241 spnego_gss_inquire_names_for_mech,
242 spnego_gss_inquire_context, /* gss_inquire_context */
243 NULL, /* gss_internal_release_oid */
244 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
245 NULL, /* gssd_pname_to_uid */
246 NULL, /* gss_userok */
247 NULL, /* gss_export_name */
248 spnego_gss_duplicate_name, /* gss_duplicate_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)
324 OM_uint32 KRB5_CALLCONV
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");
395 OM_uint32 KRB5_CALLCONV
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->mech_set = NULL;
442 spnego_ctx->internal_mech = NULL;
443 spnego_ctx->optionStr = NULL;
444 spnego_ctx->DER_mechTypes.length = 0;
445 spnego_ctx->DER_mechTypes.value = NULL;
446 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
447 spnego_ctx->mic_reqd = 0;
448 spnego_ctx->mic_sent = 0;
449 spnego_ctx->mic_rcvd = 0;
450 spnego_ctx->mech_complete = 0;
451 spnego_ctx->nego_done = 0;
452 spnego_ctx->internal_name = GSS_C_NO_NAME;
453 spnego_ctx->actual_mech = GSS_C_NO_OID;
455 check_spnego_options(spnego_ctx);
461 * Both initiator and acceptor call here to verify and/or create mechListMIC,
462 * and to consistency-check the MIC state. handle_mic is invoked only if the
463 * negotiated mech has completed and supports MICs.
466 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
467 int send_mechtok, spnego_gss_ctx_id_t sc,
468 gss_buffer_t *mic_out,
469 OM_uint32 *negState, send_token_flag *tokflag)
474 *mic_out = GSS_C_NO_BUFFER;
475 if (mic_in != GSS_C_NO_BUFFER) {
477 /* Reject MIC if we've already received a MIC. */
479 *tokflag = ERROR_TOKEN_SEND;
480 return GSS_S_DEFECTIVE_TOKEN;
482 } else if (sc->mic_reqd && !send_mechtok) {
484 * If the peer sends the final mechanism token, it
485 * must send the MIC with that token if the
486 * negotiation requires MICs.
489 *tokflag = ERROR_TOKEN_SEND;
490 return GSS_S_DEFECTIVE_TOKEN;
492 ret = process_mic(minor_status, mic_in, sc, mic_out,
494 if (ret != GSS_S_COMPLETE) {
498 assert(sc->mic_sent || sc->mic_rcvd);
500 if (sc->mic_sent && sc->mic_rcvd) {
501 ret = GSS_S_COMPLETE;
502 *negState = ACCEPT_COMPLETE;
503 if (*mic_out == GSS_C_NO_BUFFER) {
505 * We sent a MIC on the previous pass; we
506 * shouldn't be sending a mechanism token.
508 assert(!send_mechtok);
509 *tokflag = NO_TOKEN_SEND;
511 *tokflag = CONT_TOKEN_SEND;
513 } else if (sc->mic_reqd) {
514 *negState = ACCEPT_INCOMPLETE;
515 ret = GSS_S_CONTINUE_NEEDED;
516 } else if (*negState == ACCEPT_COMPLETE) {
517 ret = GSS_S_COMPLETE;
519 ret = GSS_S_CONTINUE_NEEDED;
525 * Perform the actual verification and/or generation of mechListMIC.
528 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
529 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
530 OM_uint32 *negState, send_token_flag *tokflag)
532 OM_uint32 ret, tmpmin;
534 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
537 if (mic_in != GSS_C_NO_BUFFER) {
538 ret = gss_verify_mic(minor_status, sc->ctx_handle,
541 if (ret != GSS_S_COMPLETE) {
543 *tokflag = ERROR_TOKEN_SEND;
546 /* If we got a MIC, we must send a MIC. */
550 if (sc->mic_reqd && !sc->mic_sent) {
551 ret = gss_get_mic(minor_status, sc->ctx_handle,
555 if (ret != GSS_S_COMPLETE) {
556 gss_release_buffer(&tmpmin, &tmpmic);
557 *tokflag = NO_TOKEN_SEND;
560 *mic_out = malloc(sizeof(gss_buffer_desc));
561 if (*mic_out == GSS_C_NO_BUFFER) {
562 gss_release_buffer(&tmpmin, &tmpmic);
563 *tokflag = NO_TOKEN_SEND;
564 return GSS_S_FAILURE;
569 return GSS_S_COMPLETE;
573 * Initial call to spnego_gss_init_sec_context().
576 init_ctx_new(OM_uint32 *minor_status,
577 spnego_gss_cred_id_t spcred,
579 send_token_flag *tokflag)
582 spnego_gss_ctx_id_t sc = NULL;
584 sc = create_spnego_ctx();
586 return GSS_S_FAILURE;
588 /* determine negotiation mech set */
589 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
591 if (ret != GSS_S_COMPLETE)
594 /* Set an initial internal mech to make the first context token. */
595 sc->internal_mech = &sc->mech_set->elements[0];
597 if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
602 * The actual context is not yet determined, set the output
603 * context handle to refer to the spnego context itself.
605 sc->ctx_handle = GSS_C_NO_CONTEXT;
606 *ctx = (gss_ctx_id_t)sc;
608 *tokflag = INIT_TOKEN_SEND;
609 ret = GSS_S_CONTINUE_NEEDED;
612 release_spnego_ctx(&sc);
617 * Called by second and later calls to spnego_gss_init_sec_context()
618 * to decode reply and update state.
621 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
622 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
623 OM_uint32 *negState, send_token_flag *tokflag)
625 OM_uint32 ret, tmpmin, acc_negState;
627 spnego_gss_ctx_id_t sc;
628 gss_OID supportedMech = GSS_C_NO_OID;
630 sc = (spnego_gss_ctx_id_t)*ctx;
632 *tokflag = ERROR_TOKEN_SEND;
635 ret = get_negTokenResp(minor_status, ptr, buf->length,
636 &acc_negState, &supportedMech,
637 responseToken, mechListMIC);
638 if (ret != GSS_S_COMPLETE)
640 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
641 supportedMech == GSS_C_NO_OID &&
642 *responseToken == GSS_C_NO_BUFFER &&
643 *mechListMIC == GSS_C_NO_BUFFER) {
644 /* Reject "empty" token. */
645 ret = GSS_S_DEFECTIVE_TOKEN;
647 if (acc_negState == REJECT) {
648 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
649 map_errcode(minor_status);
650 *tokflag = NO_TOKEN_SEND;
655 * nego_done is false for the first call to init_ctx_cont()
657 if (!sc->nego_done) {
658 ret = init_ctx_nego(minor_status, sc,
660 supportedMech, responseToken,
663 } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
664 (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
665 /* Missing or spurious token from acceptor. */
666 ret = GSS_S_DEFECTIVE_TOKEN;
667 } else if (!sc->mech_complete ||
669 (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
670 /* Not obviously done; we may decide we're done later in
671 * init_ctx_call_init or handle_mic. */
672 *negState = ACCEPT_INCOMPLETE;
673 *tokflag = CONT_TOKEN_SEND;
674 ret = GSS_S_CONTINUE_NEEDED;
676 /* mech finished on last pass and no MIC required, so done. */
677 *negState = ACCEPT_COMPLETE;
678 *tokflag = NO_TOKEN_SEND;
679 ret = GSS_S_COMPLETE;
682 if (supportedMech != GSS_C_NO_OID)
683 generic_gss_release_oid(&tmpmin, &supportedMech);
688 * Consistency checking and mechanism negotiation handling for second
689 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
690 * update internal state if acceptor has counter-proposed.
693 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
694 OM_uint32 acc_negState, gss_OID supportedMech,
695 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
696 OM_uint32 *negState, send_token_flag *tokflag)
701 *tokflag = ERROR_TOKEN_SEND;
702 ret = GSS_S_DEFECTIVE_TOKEN;
704 * Both supportedMech and negState must be present in first
707 if (supportedMech == GSS_C_NO_OID) {
708 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
709 map_errcode(minor_status);
710 return GSS_S_DEFECTIVE_TOKEN;
712 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
713 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
714 map_errcode(minor_status);
715 return GSS_S_DEFECTIVE_TOKEN;
719 * If the mechanism we sent is not the mechanism returned from
720 * the server, we need to handle the server's counter
721 * proposal. There is a bug in SAMBA servers that always send
722 * the old Kerberos mech OID, even though we sent the new one.
723 * So we will treat all the Kerberos mech OIDS as the same.
725 if (!(is_kerb_mech(supportedMech) &&
726 is_kerb_mech(sc->internal_mech)) &&
727 !g_OID_equal(supportedMech, sc->internal_mech)) {
728 ret = init_ctx_reselect(minor_status, sc,
729 acc_negState, supportedMech,
730 responseToken, mechListMIC,
733 } else if (*responseToken == GSS_C_NO_BUFFER) {
734 if (sc->mech_complete) {
736 * Mech completed on first call to its
737 * init_sec_context(). Acceptor sends no mech
740 *negState = ACCEPT_COMPLETE;
741 *tokflag = NO_TOKEN_SEND;
742 ret = GSS_S_COMPLETE;
745 * Reject missing mech token when optimistic
748 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
749 map_errcode(minor_status);
750 ret = GSS_S_DEFECTIVE_TOKEN;
752 } else if (sc->mech_complete) {
753 /* Reject spurious mech token. */
754 ret = GSS_S_DEFECTIVE_TOKEN;
756 *negState = ACCEPT_INCOMPLETE;
757 *tokflag = CONT_TOKEN_SEND;
758 ret = GSS_S_CONTINUE_NEEDED;
765 * Handle acceptor's counter-proposal of an alternative mechanism.
768 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
769 OM_uint32 acc_negState, gss_OID supportedMech,
770 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
771 OM_uint32 *negState, send_token_flag *tokflag)
776 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
777 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
780 /* Find supportedMech in sc->mech_set. */
781 for (i = 0; i < sc->mech_set->count; i++) {
782 if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
785 if (i == sc->mech_set->count)
786 return GSS_S_DEFECTIVE_TOKEN;
787 sc->internal_mech = &sc->mech_set->elements[i];
790 * Windows 2003 and earlier don't correctly send a
791 * negState of request-mic when counter-proposing a
792 * mechanism. They probably don't handle mechListMICs
795 if (acc_negState != REQUEST_MIC)
796 return GSS_S_DEFECTIVE_TOKEN;
798 sc->mech_complete = 0;
800 *negState = REQUEST_MIC;
801 *tokflag = CONT_TOKEN_SEND;
802 return GSS_S_CONTINUE_NEEDED;
806 * Wrap call to mechanism gss_init_sec_context() and update state
810 init_ctx_call_init(OM_uint32 *minor_status,
811 spnego_gss_ctx_id_t sc,
812 spnego_gss_cred_id_t spcred,
813 gss_name_t target_name,
816 gss_buffer_t mechtok_in,
817 gss_OID *actual_mech,
818 gss_buffer_t mechtok_out,
819 OM_uint32 *ret_flags,
822 send_token_flag *send_token)
827 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
828 ret = gss_init_sec_context(minor_status,
833 (req_flags | GSS_C_INTEG_FLAG),
835 GSS_C_NO_CHANNEL_BINDINGS,
841 if (ret == GSS_S_COMPLETE) {
842 sc->mech_complete = 1;
843 if (ret_flags != NULL)
844 *ret_flags = sc->ctx_flags;
846 * Microsoft SPNEGO implementations expect an even number of
847 * token exchanges. So if we're sending a final token, ask for
848 * a zero-length token back from the server. Also ask for a
849 * token back if this is the first token or if a MIC exchange
852 if (*send_token == CONT_TOKEN_SEND &&
853 mechtok_out->length == 0 &&
855 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
856 /* The exchange is complete. */
857 *negState = ACCEPT_COMPLETE;
858 ret = GSS_S_COMPLETE;
859 *send_token = NO_TOKEN_SEND;
861 /* Ask for one more hop. */
862 *negState = ACCEPT_INCOMPLETE;
863 ret = GSS_S_CONTINUE_NEEDED;
865 } else if (ret != GSS_S_CONTINUE_NEEDED) {
866 if (*send_token == INIT_TOKEN_SEND) {
867 /* Don't output token on error if first call. */
868 *send_token = NO_TOKEN_SEND;
870 *send_token = ERROR_TOKEN_SEND;
878 OM_uint32 KRB5_CALLCONV
879 spnego_gss_init_sec_context(
880 OM_uint32 *minor_status,
881 gss_cred_id_t claimant_cred_handle,
882 gss_ctx_id_t *context_handle,
883 gss_name_t target_name,
887 gss_channel_bindings_t input_chan_bindings,
888 gss_buffer_t input_token,
889 gss_OID *actual_mech,
890 gss_buffer_t output_token,
891 OM_uint32 *ret_flags,
894 send_token_flag send_token = NO_TOKEN_SEND;
895 OM_uint32 tmpmin, ret, negState;
896 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
897 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
898 spnego_gss_cred_id_t spcred = NULL;
899 spnego_gss_ctx_id_t spnego_ctx = NULL;
901 dsyslog("Entering init_sec_context\n");
903 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
907 * This function works in three steps:
909 * 1. Perform mechanism negotiation.
910 * 2. Invoke the negotiated or optimistic mech's gss_init_sec_context
911 * function and examine the results.
912 * 3. Process or generate MICs if necessary.
914 * The three steps share responsibility for determining when the
915 * exchange is complete. If the selected mech completed in a previous
916 * call and no MIC exchange is expected, then step 1 will decide. If
917 * the selected mech completes in this call and no MIC exchange is
918 * expected, then step 2 will decide. If a MIC exchange is expected,
919 * then step 3 will decide. If an error occurs in any step, the
920 * exchange will be aborted, possibly with an error token.
922 * negState determines the state of the negotiation, and is
923 * communicated to the acceptor if a continuing token is sent.
924 * send_token is used to indicate what type of token, if any, should be
928 /* Validate arguments. */
929 if (minor_status != NULL)
931 if (output_token != GSS_C_NO_BUFFER) {
932 output_token->length = 0;
933 output_token->value = NULL;
935 if (minor_status == NULL ||
936 output_token == GSS_C_NO_BUFFER ||
937 context_handle == NULL)
938 return GSS_S_CALL_INACCESSIBLE_WRITE;
940 if (actual_mech != NULL)
941 *actual_mech = GSS_C_NO_OID;
943 /* Step 1: perform mechanism negotiation. */
944 spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
945 if (*context_handle == GSS_C_NO_CONTEXT) {
946 ret = init_ctx_new(minor_status, spcred,
947 context_handle, &send_token);
948 if (ret != GSS_S_CONTINUE_NEEDED) {
952 ret = init_ctx_cont(minor_status, context_handle,
953 input_token, &mechtok_in,
954 &mechListMIC_in, &negState, &send_token);
955 if (HARD_ERROR(ret)) {
960 /* Step 2: invoke the selected or optimistic mechanism's
961 * gss_init_sec_context function, if it didn't complete previously. */
962 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
963 if (!spnego_ctx->mech_complete) {
964 ret = init_ctx_call_init(
965 minor_status, spnego_ctx, spcred,
966 target_name, req_flags,
967 time_req, mechtok_in,
968 actual_mech, &mechtok_out,
970 &negState, &send_token);
973 /* Step 3: process or generate the MIC, if the negotiated mech is
974 * complete and supports MICs. */
975 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
976 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
978 ret = handle_mic(minor_status,
980 (mechtok_out.length != 0),
981 spnego_ctx, &mechListMIC_out,
982 &negState, &send_token);
985 if (send_token == INIT_TOKEN_SEND) {
986 if (make_spnego_tokenInit_msg(spnego_ctx,
990 &mechtok_out, send_token,
994 } else if (send_token != NO_TOKEN_SEND) {
995 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
996 &mechtok_out, mechListMIC_out,
1002 gss_release_buffer(&tmpmin, &mechtok_out);
1003 if (ret == GSS_S_COMPLETE) {
1005 * Now, switch the output context to refer to the
1006 * negotiated mechanism's context.
1008 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1009 if (actual_mech != NULL)
1010 *actual_mech = spnego_ctx->actual_mech;
1011 if (ret_flags != NULL)
1012 *ret_flags = spnego_ctx->ctx_flags;
1013 release_spnego_ctx(&spnego_ctx);
1014 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1015 if (spnego_ctx != NULL) {
1016 gss_delete_sec_context(&tmpmin,
1017 &spnego_ctx->ctx_handle,
1019 release_spnego_ctx(&spnego_ctx);
1021 *context_handle = GSS_C_NO_CONTEXT;
1023 if (mechtok_in != GSS_C_NO_BUFFER) {
1024 gss_release_buffer(&tmpmin, mechtok_in);
1027 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1028 gss_release_buffer(&tmpmin, mechListMIC_in);
1029 free(mechListMIC_in);
1031 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1032 gss_release_buffer(&tmpmin, mechListMIC_out);
1033 free(mechListMIC_out);
1036 } /* init_sec_context */
1038 /* We don't want to import KRB5 headers here */
1039 static const gss_OID_desc gss_mech_krb5_oid =
1040 { 9, "\052\206\110\206\367\022\001\002\002" };
1041 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1042 { 9, "\052\206\110\202\367\022\001\002\002" };
1045 * verify that the input token length is not 0. If it is, just return.
1046 * If the token length is greater than 0, der encode as a sequence
1047 * and place in buf_out, advancing buf_out.
1051 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1052 unsigned int buflen)
1056 /* if token length is 0, we do not want to send */
1057 if (input_token->length == 0)
1060 if (input_token->length > buflen)
1063 *(*buf_out)++ = SEQUENCE;
1064 if ((ret = gssint_put_der_length(input_token->length, buf_out,
1065 input_token->length)))
1067 TWRITE_STR(*buf_out, input_token->value, input_token->length);
1072 * NegHints ::= SEQUENCE {
1073 * hintName [0] GeneralString OPTIONAL,
1074 * hintAddress [1] OCTET STRING OPTIONAL
1078 #define HOST_PREFIX "host@"
1079 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1082 make_NegHints(OM_uint32 *minor_status,
1083 spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
1085 gss_buffer_desc hintNameBuf;
1086 gss_name_t hintName = GSS_C_NO_NAME;
1087 gss_name_t hintKerberosName;
1088 gss_OID hintNameType;
1089 OM_uint32 major_status;
1091 unsigned int tlen = 0;
1092 unsigned int hintNameSize = 0;
1093 unsigned int negHintsSize = 0;
1097 *outbuf = GSS_C_NO_BUFFER;
1099 if (spcred != NULL) {
1100 major_status = gss_inquire_cred(minor_status,
1106 if (major_status != GSS_S_COMPLETE)
1107 return (major_status);
1110 if (hintName == GSS_C_NO_NAME) {
1111 krb5_error_code code;
1112 krb5int_access kaccess;
1113 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1115 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1117 *minor_status = code;
1118 return (GSS_S_FAILURE);
1121 /* this breaks mutual authentication but Samba relies on it */
1122 code = (*kaccess.clean_hostname)(NULL, NULL,
1123 &hostname[HOST_PREFIX_LEN],
1126 *minor_status = code;
1127 return (GSS_S_FAILURE);
1130 hintNameBuf.value = hostname;
1131 hintNameBuf.length = strlen(hostname);
1133 major_status = gss_import_name(minor_status,
1135 GSS_C_NT_HOSTBASED_SERVICE,
1137 if (major_status != GSS_S_COMPLETE) {
1138 return (major_status);
1142 hintNameBuf.value = NULL;
1143 hintNameBuf.length = 0;
1145 major_status = gss_canonicalize_name(minor_status,
1147 (gss_OID)&gss_mech_krb5_oid,
1149 if (major_status != GSS_S_COMPLETE) {
1150 gss_release_name(&minor, &hintName);
1151 return (major_status);
1153 gss_release_name(&minor, &hintName);
1155 major_status = gss_display_name(minor_status,
1159 if (major_status != GSS_S_COMPLETE) {
1160 gss_release_name(&minor, &hintName);
1161 return (major_status);
1163 gss_release_name(&minor, &hintKerberosName);
1166 * Now encode the name hint into a NegHints ASN.1 type
1168 major_status = GSS_S_FAILURE;
1170 /* Length of DER encoded GeneralString */
1171 tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1173 hintNameSize = tlen;
1175 /* Length of DER encoded hintName */
1176 tlen += 1 + gssint_der_length_size(hintNameSize);
1177 negHintsSize = tlen;
1179 t = (unsigned char *)malloc(tlen);
1181 *minor_status = ENOMEM;
1187 *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1188 if (gssint_put_der_length(hintNameSize,
1189 &ptr, tlen - (int)(ptr-t)))
1192 *ptr++ = GENERAL_STRING;
1193 if (gssint_put_der_length(hintNameBuf.length,
1194 &ptr, tlen - (int)(ptr-t)))
1197 memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1198 ptr += hintNameBuf.length;
1200 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1201 if (*outbuf == NULL) {
1202 *minor_status = ENOMEM;
1205 (*outbuf)->value = (void *)t;
1206 (*outbuf)->length = ptr - t;
1208 t = NULL; /* don't free */
1211 major_status = GSS_S_COMPLETE;
1218 gss_release_buffer(&minor, &hintNameBuf);
1220 return (major_status);
1224 * Support the Microsoft NegHints extension to SPNEGO for compatibility with
1225 * some versions of Samba. See:
1226 * http://msdn.microsoft.com/en-us/library/cc247039(PROT.10).aspx
1229 acc_ctx_hints(OM_uint32 *minor_status,
1231 spnego_gss_cred_id_t spcred,
1232 gss_buffer_t *mechListMIC,
1233 OM_uint32 *negState,
1234 send_token_flag *return_token)
1236 OM_uint32 tmpmin, ret;
1237 gss_OID_set supported_mechSet;
1238 spnego_gss_ctx_id_t sc = NULL;
1240 *mechListMIC = GSS_C_NO_BUFFER;
1241 supported_mechSet = GSS_C_NO_OID_SET;
1242 *return_token = NO_TOKEN_SEND;
1246 /* A hint request must be the first token received. */
1247 if (*ctx != GSS_C_NO_CONTEXT)
1248 return GSS_S_DEFECTIVE_TOKEN;
1250 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1251 &supported_mechSet);
1252 if (ret != GSS_S_COMPLETE)
1255 ret = make_NegHints(minor_status, spcred, mechListMIC);
1256 if (ret != GSS_S_COMPLETE)
1259 sc = create_spnego_ctx();
1261 ret = GSS_S_FAILURE;
1264 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1265 ret = GSS_S_FAILURE;
1268 sc->internal_mech = GSS_C_NO_OID;
1270 *negState = ACCEPT_INCOMPLETE;
1271 *return_token = INIT_TOKEN_SEND;
1273 *ctx = (gss_ctx_id_t)sc;
1275 ret = GSS_S_COMPLETE;
1278 release_spnego_ctx(&sc);
1279 gss_release_oid_set(&tmpmin, &supported_mechSet);
1285 * Set negState to REJECT if the token is defective, else
1286 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1287 * preferred mechanism is supported.
1290 acc_ctx_new(OM_uint32 *minor_status,
1293 spnego_gss_cred_id_t spcred,
1294 gss_buffer_t *mechToken,
1295 gss_buffer_t *mechListMIC,
1296 OM_uint32 *negState,
1297 send_token_flag *return_token)
1299 OM_uint32 tmpmin, ret, req_flags;
1300 gss_OID_set supported_mechSet, mechTypes;
1301 gss_buffer_desc der_mechTypes;
1302 gss_OID mech_wanted;
1303 spnego_gss_ctx_id_t sc = NULL;
1305 ret = GSS_S_DEFECTIVE_TOKEN;
1306 der_mechTypes.length = 0;
1307 der_mechTypes.value = NULL;
1308 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1309 supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1310 *return_token = ERROR_TOKEN_SEND;
1314 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1315 &mechTypes, &req_flags,
1316 mechToken, mechListMIC);
1317 if (ret != GSS_S_COMPLETE) {
1320 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1321 &supported_mechSet);
1322 if (ret != GSS_S_COMPLETE) {
1323 *return_token = NO_TOKEN_SEND;
1327 * Select the best match between the list of mechs
1328 * that the initiator requested and the list that
1329 * the acceptor will support.
1331 mech_wanted = negotiate_mech(supported_mechSet, mechTypes, negState);
1332 if (*negState == REJECT) {
1333 ret = GSS_S_BAD_MECH;
1336 sc = (spnego_gss_ctx_id_t)*ctx;
1338 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1339 assert(mech_wanted != GSS_C_NO_OID);
1341 sc = create_spnego_ctx();
1343 ret = GSS_S_FAILURE;
1344 *return_token = NO_TOKEN_SEND;
1347 sc->mech_set = supported_mechSet;
1348 supported_mechSet = GSS_C_NO_OID_SET;
1349 sc->internal_mech = mech_wanted;
1350 sc->DER_mechTypes = der_mechTypes;
1351 der_mechTypes.length = 0;
1352 der_mechTypes.value = NULL;
1354 if (*negState == REQUEST_MIC)
1357 *return_token = INIT_TOKEN_SEND;
1359 *ctx = (gss_ctx_id_t)sc;
1360 ret = GSS_S_COMPLETE;
1362 gss_release_oid_set(&tmpmin, &mechTypes);
1363 gss_release_oid_set(&tmpmin, &supported_mechSet);
1364 if (der_mechTypes.length != 0)
1365 gss_release_buffer(&tmpmin, &der_mechTypes);
1371 acc_ctx_cont(OM_uint32 *minstat,
1374 gss_buffer_t *responseToken,
1375 gss_buffer_t *mechListMIC,
1376 OM_uint32 *negState,
1377 send_token_flag *return_token)
1379 OM_uint32 ret, tmpmin;
1380 gss_OID supportedMech;
1381 spnego_gss_ctx_id_t sc;
1383 unsigned char *ptr, *bufstart;
1385 sc = (spnego_gss_ctx_id_t)*ctx;
1386 ret = GSS_S_DEFECTIVE_TOKEN;
1389 supportedMech = GSS_C_NO_OID;
1390 *return_token = ERROR_TOKEN_SEND;
1391 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1393 ptr = bufstart = buf->value;
1394 #define REMAIN (buf->length - (ptr - bufstart))
1395 if (REMAIN > INT_MAX)
1396 return GSS_S_DEFECTIVE_TOKEN;
1399 * Attempt to work with old Sun SPNEGO.
1401 if (*ptr == HEADER_ID) {
1402 ret = g_verify_token_header(gss_mech_spnego,
1403 &len, &ptr, 0, REMAIN);
1406 return GSS_S_DEFECTIVE_TOKEN;
1409 if (*ptr != (CONTEXT | 0x01)) {
1410 return GSS_S_DEFECTIVE_TOKEN;
1412 ret = get_negTokenResp(minstat, ptr, REMAIN,
1413 negState, &supportedMech,
1414 responseToken, mechListMIC);
1415 if (ret != GSS_S_COMPLETE)
1418 if (*responseToken == GSS_C_NO_BUFFER &&
1419 *mechListMIC == GSS_C_NO_BUFFER) {
1421 ret = GSS_S_DEFECTIVE_TOKEN;
1424 if (supportedMech != GSS_C_NO_OID) {
1425 ret = GSS_S_DEFECTIVE_TOKEN;
1429 *negState = ACCEPT_INCOMPLETE;
1430 *return_token = CONT_TOKEN_SEND;
1432 if (supportedMech != GSS_C_NO_OID) {
1433 generic_gss_release_oid(&tmpmin, &supportedMech);
1440 * Verify that mech OID is either exactly the same as the negotiated
1441 * mech OID, or is a mech OID supported by the negotiated mech. MS
1442 * implementations can list a most preferred mech using an incorrect
1443 * krb5 OID while emitting a krb5 initiator mech token having the
1444 * correct krb5 mech OID.
1447 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1448 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1449 OM_uint32 *negState, send_token_flag *tokflag)
1451 OM_uint32 ret, tmpmin;
1452 gss_mechanism mech = NULL;
1453 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1456 if (g_OID_equal(sc->internal_mech, mechoid))
1457 return GSS_S_COMPLETE;
1459 mech = gssint_get_mechanism(sc->internal_mech);
1460 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1461 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1462 map_errcode(minor_status);
1464 *tokflag = ERROR_TOKEN_SEND;
1465 return GSS_S_BAD_MECH;
1467 ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1468 if (ret != GSS_S_COMPLETE) {
1469 *tokflag = NO_TOKEN_SEND;
1470 map_error(minor_status, mech);
1473 ret = gss_test_oid_set_member(minor_status, mechoid,
1474 mech_set, &present);
1475 if (ret != GSS_S_COMPLETE)
1478 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1479 map_errcode(minor_status);
1481 *tokflag = ERROR_TOKEN_SEND;
1482 ret = GSS_S_BAD_MECH;
1485 gss_release_oid_set(&tmpmin, &mech_set);
1490 * Wrap call to gss_accept_sec_context() and update state
1494 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1495 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1496 gss_OID *mech_type, gss_buffer_t mechtok_out,
1497 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1498 gss_cred_id_t *delegated_cred_handle,
1499 OM_uint32 *negState, send_token_flag *tokflag)
1502 gss_OID_desc mechoid;
1503 gss_cred_id_t mcred;
1505 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1507 * mechoid is an alias; don't free it.
1509 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1510 if (ret != GSS_S_COMPLETE) {
1511 *tokflag = NO_TOKEN_SEND;
1514 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1516 if (ret != GSS_S_COMPLETE)
1520 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1521 ret = gss_accept_sec_context(minor_status,
1525 GSS_C_NO_CHANNEL_BINDINGS,
1531 delegated_cred_handle);
1532 if (ret == GSS_S_COMPLETE) {
1535 * Force MIC to be not required even if we previously
1538 char *envstr = getenv("MS_FORCE_NO_MIC");
1540 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1541 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1547 sc->mech_complete = 1;
1548 if (ret_flags != NULL)
1549 *ret_flags = sc->ctx_flags;
1551 if (!sc->mic_reqd ||
1552 !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1553 /* No MIC exchange required, so we're done. */
1554 *negState = ACCEPT_COMPLETE;
1555 ret = GSS_S_COMPLETE;
1557 /* handle_mic will decide if we're done. */
1558 ret = GSS_S_CONTINUE_NEEDED;
1560 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1562 *tokflag = ERROR_TOKEN_SEND;
1568 OM_uint32 KRB5_CALLCONV
1569 spnego_gss_accept_sec_context(
1570 OM_uint32 *minor_status,
1571 gss_ctx_id_t *context_handle,
1572 gss_cred_id_t verifier_cred_handle,
1573 gss_buffer_t input_token,
1574 gss_channel_bindings_t input_chan_bindings,
1575 gss_name_t *src_name,
1577 gss_buffer_t output_token,
1578 OM_uint32 *ret_flags,
1579 OM_uint32 *time_rec,
1580 gss_cred_id_t *delegated_cred_handle)
1582 OM_uint32 ret, tmpmin, negState;
1583 send_token_flag return_token;
1584 gss_buffer_t mechtok_in, mic_in, mic_out;
1585 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1586 spnego_gss_ctx_id_t sc = NULL;
1587 spnego_gss_cred_id_t spcred = NULL;
1588 OM_uint32 mechstat = GSS_S_FAILURE;
1589 int sendTokenInit = 0, tmpret;
1591 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1594 * This function works in three steps:
1596 * 1. Perform mechanism negotiation.
1597 * 2. Invoke the negotiated mech's gss_accept_sec_context function
1598 * and examine the results.
1599 * 3. Process or generate MICs if necessary.
1601 * Step one determines whether the negotiation requires a MIC exchange,
1602 * while steps two and three share responsibility for determining when
1603 * the exchange is complete. If the selected mech completes in this
1604 * call and no MIC exchange is expected, then step 2 will decide. If a
1605 * MIC exchange is expected, then step 3 will decide. If an error
1606 * occurs in any step, the exchange will be aborted, possibly with an
1609 * negState determines the state of the negotiation, and is
1610 * communicated to the acceptor if a continuing token is sent.
1611 * return_token is used to indicate what type of token, if any, should
1615 /* Validate arguments. */
1616 if (minor_status != NULL)
1618 if (output_token != GSS_C_NO_BUFFER) {
1619 output_token->length = 0;
1620 output_token->value = NULL;
1623 if (minor_status == NULL ||
1624 output_token == GSS_C_NO_BUFFER ||
1625 context_handle == NULL)
1626 return GSS_S_CALL_INACCESSIBLE_WRITE;
1628 if (input_token == GSS_C_NO_BUFFER)
1629 return GSS_S_CALL_INACCESSIBLE_READ;
1631 /* Step 1: Perform mechanism negotiation. */
1632 sc = (spnego_gss_ctx_id_t)*context_handle;
1633 spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1634 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1635 /* Process an initial token or request for NegHints. */
1636 if (src_name != NULL)
1637 *src_name = GSS_C_NO_NAME;
1638 if (mech_type != NULL)
1639 *mech_type = GSS_C_NO_OID;
1640 if (time_rec != NULL)
1642 if (ret_flags != NULL)
1644 if (delegated_cred_handle != NULL)
1645 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1646 if (input_token->length == 0) {
1647 ret = acc_ctx_hints(minor_status,
1648 context_handle, spcred,
1652 if (ret != GSS_S_COMPLETE)
1655 ret = GSS_S_CONTINUE_NEEDED;
1657 /* Can set negState to REQUEST_MIC */
1658 ret = acc_ctx_new(minor_status, input_token,
1659 context_handle, spcred,
1660 &mechtok_in, &mic_in,
1661 &negState, &return_token);
1662 if (ret != GSS_S_COMPLETE)
1664 ret = GSS_S_CONTINUE_NEEDED;
1667 /* Process a response token. Can set negState to
1668 * ACCEPT_INCOMPLETE. */
1669 ret = acc_ctx_cont(minor_status, input_token,
1670 context_handle, &mechtok_in,
1671 &mic_in, &negState, &return_token);
1672 if (ret != GSS_S_COMPLETE)
1674 ret = GSS_S_CONTINUE_NEEDED;
1677 /* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1679 sc = (spnego_gss_ctx_id_t)*context_handle;
1681 * Handle mechtok_in and mic_in only if they are
1682 * present in input_token. If neither is present, whether
1683 * this is an error depends on whether this is the first
1684 * round-trip. RET is set to a default value according to
1685 * whether it is the first round-trip.
1687 mechstat = GSS_S_FAILURE;
1688 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1689 ret = acc_ctx_call_acc(minor_status, sc, spcred,
1690 mechtok_in, mech_type, &mechtok_out,
1691 ret_flags, time_rec,
1692 delegated_cred_handle,
1693 &negState, &return_token);
1694 } else if (negState == REQUEST_MIC) {
1695 mechstat = GSS_S_CONTINUE_NEEDED;
1698 /* Step 3: process or generate the MIC, if the negotiated mech is
1699 * complete and supports MICs. */
1700 if (!HARD_ERROR(ret) && sc->mech_complete &&
1701 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1703 ret = handle_mic(minor_status, mic_in,
1704 (mechtok_out.length != 0),
1706 &negState, &return_token);
1709 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1711 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1713 return_token, output_token);
1715 ret = GSS_S_FAILURE;
1716 } else if (return_token != NO_TOKEN_SEND &&
1717 return_token != CHECK_MIC) {
1718 tmpret = make_spnego_tokenTarg_msg(negState,
1719 sc ? sc->internal_mech :
1721 &mechtok_out, mic_out,
1725 ret = GSS_S_FAILURE;
1727 if (ret == GSS_S_COMPLETE) {
1728 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1729 if (sc->internal_name != GSS_C_NO_NAME &&
1731 *src_name = sc->internal_name;
1732 sc->internal_name = GSS_C_NO_NAME;
1734 release_spnego_ctx(&sc);
1735 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1737 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1739 release_spnego_ctx(&sc);
1741 *context_handle = GSS_C_NO_CONTEXT;
1743 gss_release_buffer(&tmpmin, &mechtok_out);
1744 if (mechtok_in != GSS_C_NO_BUFFER) {
1745 gss_release_buffer(&tmpmin, mechtok_in);
1748 if (mic_in != GSS_C_NO_BUFFER) {
1749 gss_release_buffer(&tmpmin, mic_in);
1752 if (mic_out != GSS_C_NO_BUFFER) {
1753 gss_release_buffer(&tmpmin, mic_out);
1758 #endif /* LEAN_CLIENT */
1762 OM_uint32 KRB5_CALLCONV
1763 spnego_gss_display_status(
1764 OM_uint32 *minor_status,
1765 OM_uint32 status_value,
1768 OM_uint32 *message_context,
1769 gss_buffer_t status_string)
1771 dsyslog("Entering display_status\n");
1773 *message_context = 0;
1774 switch (status_value) {
1775 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1777 *status_string = make_err_msg(_("SPNEGO cannot find "
1778 "mechanisms to negotiate"));
1780 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1782 *status_string = make_err_msg(_("SPNEGO failed to acquire "
1785 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1787 *status_string = make_err_msg(_("SPNEGO acceptor did not "
1788 "select a mechanism"));
1790 case ERR_SPNEGO_NEGOTIATION_FAILED:
1792 *status_string = make_err_msg(_("SPNEGO failed to negotiate a "
1795 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1797 *status_string = make_err_msg(_("SPNEGO acceptor did not "
1798 "return a valid token"));
1801 status_string->length = 0;
1802 status_string->value = "";
1806 dsyslog("Leaving display_status\n");
1807 return (GSS_S_COMPLETE);
1812 OM_uint32 KRB5_CALLCONV
1813 spnego_gss_import_name(
1814 OM_uint32 *minor_status,
1815 gss_buffer_t input_name_buffer,
1816 gss_OID input_name_type,
1817 gss_name_t *output_name)
1821 dsyslog("Entering import_name\n");
1823 status = gss_import_name(minor_status, input_name_buffer,
1824 input_name_type, output_name);
1826 dsyslog("Leaving import_name\n");
1831 OM_uint32 KRB5_CALLCONV
1832 spnego_gss_release_name(
1833 OM_uint32 *minor_status,
1834 gss_name_t *input_name)
1838 dsyslog("Entering release_name\n");
1840 status = gss_release_name(minor_status, input_name);
1842 dsyslog("Leaving release_name\n");
1847 OM_uint32 KRB5_CALLCONV
1848 spnego_gss_duplicate_name(
1849 OM_uint32 *minor_status,
1850 const gss_name_t input_name,
1851 gss_name_t *output_name)
1855 dsyslog("Entering duplicate_name\n");
1857 status = gss_duplicate_name(minor_status, input_name, output_name);
1859 dsyslog("Leaving duplicate_name\n");
1863 OM_uint32 KRB5_CALLCONV
1864 spnego_gss_inquire_cred(
1865 OM_uint32 *minor_status,
1866 gss_cred_id_t cred_handle,
1868 OM_uint32 *lifetime,
1870 gss_OID_set *mechanisms)
1873 spnego_gss_cred_id_t spcred = NULL;
1874 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1875 OM_uint32 tmp_minor_status;
1876 OM_uint32 initiator_lifetime, acceptor_lifetime;
1878 dsyslog("Entering inquire_cred\n");
1881 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1882 * supplied we call gss_inquire_cred_by_mech() on the
1883 * first non-SPNEGO mechanism.
1885 spcred = (spnego_gss_cred_id_t)cred_handle;
1886 if (spcred == NULL) {
1887 status = get_available_mechs(minor_status,
1892 if (status != GSS_S_COMPLETE) {
1893 dsyslog("Leaving inquire_cred\n");
1897 if ((*mechanisms)->count == 0) {
1898 gss_release_cred(&tmp_minor_status, &creds);
1899 gss_release_oid_set(&tmp_minor_status, mechanisms);
1900 dsyslog("Leaving inquire_cred\n");
1901 return (GSS_S_DEFECTIVE_CREDENTIAL);
1904 assert((*mechanisms)->elements != NULL);
1906 status = gss_inquire_cred_by_mech(minor_status,
1908 &(*mechanisms)->elements[0],
1910 &initiator_lifetime,
1913 if (status != GSS_S_COMPLETE) {
1914 gss_release_cred(&tmp_minor_status, &creds);
1915 dsyslog("Leaving inquire_cred\n");
1919 if (lifetime != NULL)
1920 *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1921 acceptor_lifetime : initiator_lifetime;
1923 gss_release_cred(&tmp_minor_status, &creds);
1925 status = gss_inquire_cred(minor_status, spcred->mcred,
1927 cred_usage, mechanisms);
1930 dsyslog("Leaving inquire_cred\n");
1936 OM_uint32 KRB5_CALLCONV
1937 spnego_gss_compare_name(
1938 OM_uint32 *minor_status,
1939 const gss_name_t name1,
1940 const gss_name_t name2,
1943 OM_uint32 status = GSS_S_COMPLETE;
1944 dsyslog("Entering compare_name\n");
1946 status = gss_compare_name(minor_status, name1, name2, name_equal);
1948 dsyslog("Leaving compare_name\n");
1954 OM_uint32 KRB5_CALLCONV
1955 spnego_gss_display_name(
1956 OM_uint32 *minor_status,
1957 gss_name_t input_name,
1958 gss_buffer_t output_name_buffer,
1959 gss_OID *output_name_type)
1961 OM_uint32 status = GSS_S_COMPLETE;
1962 dsyslog("Entering display_name\n");
1964 status = gss_display_name(minor_status, input_name,
1965 output_name_buffer, output_name_type);
1967 dsyslog("Leaving display_name\n");
1973 OM_uint32 KRB5_CALLCONV
1974 spnego_gss_inquire_names_for_mech(
1975 OM_uint32 *minor_status,
1977 gss_OID_set *name_types)
1979 OM_uint32 major, minor;
1981 dsyslog("Entering inquire_names_for_mech\n");
1983 * We only know how to handle our own mechanism.
1985 if ((mechanism != GSS_C_NULL_OID) &&
1986 !g_OID_equal(gss_mech_spnego, mechanism)) {
1988 return (GSS_S_FAILURE);
1991 major = gss_create_empty_oid_set(minor_status, name_types);
1992 if (major == GSS_S_COMPLETE) {
1993 /* Now add our members. */
1994 if (((major = gss_add_oid_set_member(minor_status,
1995 (gss_OID) GSS_C_NT_USER_NAME,
1996 name_types)) == GSS_S_COMPLETE) &&
1997 ((major = gss_add_oid_set_member(minor_status,
1998 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1999 name_types)) == GSS_S_COMPLETE) &&
2000 ((major = gss_add_oid_set_member(minor_status,
2001 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2002 name_types)) == GSS_S_COMPLETE)) {
2003 major = gss_add_oid_set_member(minor_status,
2004 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2008 if (major != GSS_S_COMPLETE)
2009 (void) gss_release_oid_set(&minor, name_types);
2012 dsyslog("Leaving inquire_names_for_mech\n");
2016 OM_uint32 KRB5_CALLCONV
2018 OM_uint32 *minor_status,
2019 gss_ctx_id_t context_handle,
2020 gss_buffer_t input_message_buffer,
2021 gss_buffer_t output_message_buffer,
2023 gss_qop_t *qop_state)
2026 ret = gss_unwrap(minor_status,
2028 input_message_buffer,
2029 output_message_buffer,
2036 OM_uint32 KRB5_CALLCONV
2038 OM_uint32 *minor_status,
2039 gss_ctx_id_t context_handle,
2042 gss_buffer_t input_message_buffer,
2044 gss_buffer_t output_message_buffer)
2047 ret = gss_wrap(minor_status,
2051 input_message_buffer,
2053 output_message_buffer);
2058 OM_uint32 KRB5_CALLCONV
2059 spnego_gss_process_context_token(
2060 OM_uint32 *minor_status,
2061 const gss_ctx_id_t context_handle,
2062 const gss_buffer_t token_buffer)
2065 ret = gss_process_context_token(minor_status,
2072 OM_uint32 KRB5_CALLCONV
2073 spnego_gss_delete_sec_context(
2074 OM_uint32 *minor_status,
2075 gss_ctx_id_t *context_handle,
2076 gss_buffer_t output_token)
2078 OM_uint32 ret = GSS_S_COMPLETE;
2079 spnego_gss_ctx_id_t *ctx =
2080 (spnego_gss_ctx_id_t *)context_handle;
2084 if (context_handle == NULL)
2085 return (GSS_S_FAILURE);
2088 return (GSS_S_COMPLETE);
2091 * If this is still an SPNEGO mech, release it locally.
2093 if ((*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2094 (void) gss_delete_sec_context(minor_status,
2095 &(*ctx)->ctx_handle,
2097 (void) release_spnego_ctx(ctx);
2099 ret = gss_delete_sec_context(minor_status,
2107 OM_uint32 KRB5_CALLCONV
2108 spnego_gss_context_time(
2109 OM_uint32 *minor_status,
2110 const gss_ctx_id_t context_handle,
2111 OM_uint32 *time_rec)
2114 ret = gss_context_time(minor_status,
2120 OM_uint32 KRB5_CALLCONV
2121 spnego_gss_export_sec_context(
2122 OM_uint32 *minor_status,
2123 gss_ctx_id_t *context_handle,
2124 gss_buffer_t interprocess_token)
2127 ret = gss_export_sec_context(minor_status,
2129 interprocess_token);
2133 OM_uint32 KRB5_CALLCONV
2134 spnego_gss_import_sec_context(
2135 OM_uint32 *minor_status,
2136 const gss_buffer_t interprocess_token,
2137 gss_ctx_id_t *context_handle)
2140 ret = gss_import_sec_context(minor_status,
2145 #endif /* LEAN_CLIENT */
2147 OM_uint32 KRB5_CALLCONV
2148 spnego_gss_inquire_context(
2149 OM_uint32 *minor_status,
2150 const gss_ctx_id_t context_handle,
2151 gss_name_t *src_name,
2152 gss_name_t *targ_name,
2153 OM_uint32 *lifetime_rec,
2155 OM_uint32 *ctx_flags,
2156 int *locally_initiated,
2159 OM_uint32 ret = GSS_S_COMPLETE;
2161 ret = gss_inquire_context(minor_status,
2174 OM_uint32 KRB5_CALLCONV
2175 spnego_gss_wrap_size_limit(
2176 OM_uint32 *minor_status,
2177 const gss_ctx_id_t context_handle,
2180 OM_uint32 req_output_size,
2181 OM_uint32 *max_input_size)
2184 ret = gss_wrap_size_limit(minor_status,
2193 OM_uint32 KRB5_CALLCONV
2195 OM_uint32 *minor_status,
2196 const gss_ctx_id_t context_handle,
2198 const gss_buffer_t message_buffer,
2199 gss_buffer_t message_token)
2202 ret = gss_get_mic(minor_status,
2210 OM_uint32 KRB5_CALLCONV
2211 spnego_gss_verify_mic(
2212 OM_uint32 *minor_status,
2213 const gss_ctx_id_t context_handle,
2214 const gss_buffer_t msg_buffer,
2215 const gss_buffer_t token_buffer,
2216 gss_qop_t *qop_state)
2219 ret = gss_verify_mic(minor_status,
2227 OM_uint32 KRB5_CALLCONV
2228 spnego_gss_inquire_sec_context_by_oid(
2229 OM_uint32 *minor_status,
2230 const gss_ctx_id_t context_handle,
2231 const gss_OID desired_object,
2232 gss_buffer_set_t *data_set)
2235 ret = gss_inquire_sec_context_by_oid(minor_status,
2242 OM_uint32 KRB5_CALLCONV
2243 spnego_gss_inquire_cred_by_oid(
2244 OM_uint32 *minor_status,
2245 const gss_cred_id_t cred_handle,
2246 const gss_OID desired_object,
2247 gss_buffer_set_t *data_set)
2250 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2251 gss_cred_id_t mcred;
2252 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2253 ret = gss_inquire_cred_by_oid(minor_status,
2260 OM_uint32 KRB5_CALLCONV
2261 spnego_gss_set_cred_option(
2262 OM_uint32 *minor_status,
2263 gss_cred_id_t *cred_handle,
2264 const gss_OID desired_object,
2265 const gss_buffer_t value)
2268 OM_uint32 tmp_minor_status;
2269 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
2270 gss_cred_id_t mcred;
2272 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2273 ret = gss_set_cred_option(minor_status,
2277 if (ret == GSS_S_COMPLETE && spcred == NULL) {
2279 * If the mechanism allocated a new credential handle, then
2280 * we need to wrap it up in an SPNEGO credential handle.
2283 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2284 if (spcred == NULL) {
2285 gss_release_cred(&tmp_minor_status, &mcred);
2286 *minor_status = ENOMEM;
2287 return (GSS_S_FAILURE);
2289 spcred->mcred = mcred;
2290 spcred->neg_mechs = GSS_C_NULL_OID_SET;
2291 *cred_handle = (gss_cred_id_t)spcred;
2297 OM_uint32 KRB5_CALLCONV
2298 spnego_gss_set_sec_context_option(
2299 OM_uint32 *minor_status,
2300 gss_ctx_id_t *context_handle,
2301 const gss_OID desired_object,
2302 const gss_buffer_t value)
2305 ret = gss_set_sec_context_option(minor_status,
2312 OM_uint32 KRB5_CALLCONV
2313 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2314 gss_ctx_id_t context_handle,
2317 gss_buffer_t input_assoc_buffer,
2318 gss_buffer_t input_payload_buffer,
2320 gss_buffer_t output_message_buffer)
2323 ret = gss_wrap_aead(minor_status,
2328 input_payload_buffer,
2330 output_message_buffer);
2335 OM_uint32 KRB5_CALLCONV
2336 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2337 gss_ctx_id_t context_handle,
2338 gss_buffer_t input_message_buffer,
2339 gss_buffer_t input_assoc_buffer,
2340 gss_buffer_t output_payload_buffer,
2342 gss_qop_t *qop_state)
2345 ret = gss_unwrap_aead(minor_status,
2347 input_message_buffer,
2349 output_payload_buffer,
2355 OM_uint32 KRB5_CALLCONV
2356 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2357 gss_ctx_id_t context_handle,
2361 gss_iov_buffer_desc *iov,
2365 ret = gss_wrap_iov(minor_status,
2375 OM_uint32 KRB5_CALLCONV
2376 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2377 gss_ctx_id_t context_handle,
2379 gss_qop_t *qop_state,
2380 gss_iov_buffer_desc *iov,
2384 ret = gss_unwrap_iov(minor_status,
2393 OM_uint32 KRB5_CALLCONV
2394 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2395 gss_ctx_id_t context_handle,
2399 gss_iov_buffer_desc *iov,
2403 ret = gss_wrap_iov_length(minor_status,
2414 OM_uint32 KRB5_CALLCONV
2415 spnego_gss_complete_auth_token(
2416 OM_uint32 *minor_status,
2417 const gss_ctx_id_t context_handle,
2418 gss_buffer_t input_message_buffer)
2421 ret = gss_complete_auth_token(minor_status,
2423 input_message_buffer);
2427 OM_uint32 KRB5_CALLCONV
2428 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2429 const gss_cred_id_t impersonator_cred_handle,
2430 const gss_name_t desired_name,
2432 gss_OID_set desired_mechs,
2433 gss_cred_usage_t cred_usage,
2434 gss_cred_id_t *output_cred_handle,
2435 gss_OID_set *actual_mechs,
2436 OM_uint32 *time_rec)
2439 gss_OID_set amechs = GSS_C_NULL_OID_SET;
2440 spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2441 gss_cred_id_t imp_mcred, out_mcred;
2443 dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2446 *actual_mechs = NULL;
2451 imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2452 imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
2453 if (desired_mechs == GSS_C_NO_OID_SET) {
2454 status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
2456 if (status != GSS_S_COMPLETE)
2459 desired_mechs = amechs;
2462 status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
2463 desired_name, time_req,
2464 desired_mechs, cred_usage,
2465 &out_mcred, actual_mechs,
2468 if (amechs != GSS_C_NULL_OID_SET)
2469 (void) gss_release_oid_set(minor_status, &amechs);
2471 out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2472 if (out_spcred == NULL) {
2473 gss_release_cred(minor_status, &out_mcred);
2474 *minor_status = ENOMEM;
2475 return (GSS_S_FAILURE);
2477 out_spcred->mcred = out_mcred;
2478 out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
2479 *output_cred_handle = (gss_cred_id_t)out_spcred;
2481 dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2485 OM_uint32 KRB5_CALLCONV
2486 spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2487 const gss_name_t desired_name,
2488 const gss_buffer_t password,
2490 const gss_OID_set desired_mechs,
2491 gss_cred_usage_t cred_usage,
2492 gss_cred_id_t *output_cred_handle,
2493 gss_OID_set *actual_mechs,
2494 OM_uint32 *time_rec)
2496 OM_uint32 status, tmpmin;
2497 gss_OID_set amechs = GSS_C_NULL_OID_SET, dmechs;
2498 gss_cred_id_t mcred = NULL;
2499 spnego_gss_cred_id_t spcred = NULL;
2501 dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2504 *actual_mechs = NULL;
2509 dmechs = desired_mechs;
2510 if (desired_mechs == GSS_C_NULL_OID_SET) {
2511 status = get_available_mechs(minor_status, desired_name,
2512 cred_usage, NULL, &amechs);
2516 status = gss_acquire_cred_with_password(minor_status, desired_name,
2517 password, time_req, dmechs,
2519 actual_mechs, time_rec);
2520 if (status != GSS_S_COMPLETE)
2523 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2524 if (spcred == NULL) {
2525 *minor_status = ENOMEM;
2526 status = GSS_S_FAILURE;
2529 spcred->neg_mechs = GSS_C_NULL_OID_SET;
2530 spcred->mcred = mcred;
2531 mcred = GSS_C_NO_CREDENTIAL;
2532 *output_cred_handle = (gss_cred_id_t)spcred;
2536 (void) gss_release_oid_set(&tmpmin, &amechs);
2537 (void) gss_release_cred(&tmpmin, &mcred);
2539 dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2543 OM_uint32 KRB5_CALLCONV
2544 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2546 gss_OID display_as_name_type,
2547 gss_buffer_t display_name)
2550 ret = gss_display_name_ext(minor_status,
2552 display_as_name_type,
2558 OM_uint32 KRB5_CALLCONV
2559 spnego_gss_inquire_name(OM_uint32 *minor_status,
2563 gss_buffer_set_t *attrs)
2566 ret = gss_inquire_name(minor_status,
2574 OM_uint32 KRB5_CALLCONV
2575 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2581 gss_buffer_t display_value,
2585 ret = gss_get_name_attribute(minor_status,
2596 OM_uint32 KRB5_CALLCONV
2597 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2604 ret = gss_set_name_attribute(minor_status,
2612 OM_uint32 KRB5_CALLCONV
2613 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2618 ret = gss_delete_name_attribute(minor_status,
2624 OM_uint32 KRB5_CALLCONV
2625 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2627 gss_buffer_t exp_composite_name)
2630 ret = gss_export_name_composite(minor_status,
2632 exp_composite_name);
2636 OM_uint32 KRB5_CALLCONV
2637 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2640 gss_buffer_t type_id,
2644 ret = gss_map_name_to_any(minor_status,
2652 OM_uint32 KRB5_CALLCONV
2653 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2655 gss_buffer_t type_id,
2659 ret = gss_release_any_name_mapping(minor_status,
2666 OM_uint32 KRB5_CALLCONV
2667 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2668 gss_ctx_id_t context,
2670 const gss_buffer_t prf_in,
2671 ssize_t desired_output_len,
2672 gss_buffer_t prf_out)
2675 ret = gss_pseudo_random(minor_status,
2684 OM_uint32 KRB5_CALLCONV
2685 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2686 gss_cred_id_t cred_handle,
2687 const gss_OID_set mech_list)
2690 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2692 /* Store mech_list in spcred for use in negotiation logic. */
2693 gss_release_oid_set(minor_status, &spcred->neg_mechs);
2694 ret = generic_gss_copy_oid_set(minor_status, mech_list,
2695 &spcred->neg_mechs);
2699 #define SPNEGO_SASL_NAME "SPNEGO"
2700 #define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1)
2702 OM_uint32 KRB5_CALLCONV
2703 spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2704 const gss_buffer_t sasl_mech_name,
2707 if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2708 memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2709 SPNEGO_SASL_NAME_LEN) == 0) {
2710 if (mech_type != NULL)
2711 *mech_type = (gss_OID)gss_mech_spnego;
2712 return (GSS_S_COMPLETE);
2715 return (GSS_S_BAD_MECH);
2718 OM_uint32 KRB5_CALLCONV
2719 spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2720 const gss_OID desired_mech,
2721 gss_buffer_t sasl_mech_name,
2722 gss_buffer_t mech_name,
2723 gss_buffer_t mech_description)
2727 if (!g_OID_equal(desired_mech, gss_mech_spnego))
2728 return (GSS_S_BAD_MECH);
2730 if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2731 !g_make_string_buffer("spnego", mech_name) ||
2732 !g_make_string_buffer("Simple and Protected GSS-API "
2733 "Negotiation Mechanism", mech_description))
2736 return (GSS_S_COMPLETE);
2739 *minor_status = ENOMEM;
2740 return (GSS_S_FAILURE);
2743 OM_uint32 KRB5_CALLCONV
2744 spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2746 gss_OID_set *mech_attrs,
2747 gss_OID_set *known_mech_attrs)
2749 OM_uint32 major, tmpMinor;
2751 /* known_mech_attrs is handled by mechglue */
2754 if (mech_attrs == NULL)
2755 return (GSS_S_COMPLETE);
2757 major = gss_create_empty_oid_set(minor_status, mech_attrs);
2758 if (GSS_ERROR(major))
2761 #define MA_SUPPORTED(ma) do { \
2762 major = gss_add_oid_set_member(minor_status, \
2763 (gss_OID)ma, mech_attrs); \
2764 if (GSS_ERROR(major)) \
2768 MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2769 MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2772 if (GSS_ERROR(major))
2773 gss_release_oid_set(&tmpMinor, mech_attrs);
2779 * We will release everything but the ctx_handle so that it
2780 * can be passed back to init/accept context. This routine should
2781 * not be called until after the ctx_handle memory is assigned to
2782 * the supplied context handle from init/accept context.
2785 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2787 spnego_gss_ctx_id_t context;
2788 OM_uint32 minor_stat;
2791 if (context != NULL) {
2792 (void) gss_release_buffer(&minor_stat,
2793 &context->DER_mechTypes);
2795 (void) gss_release_oid_set(&minor_stat, &context->mech_set);
2797 (void) gss_release_name(&minor_stat, &context->internal_name);
2799 if (context->optionStr != NULL) {
2800 free(context->optionStr);
2801 context->optionStr = NULL;
2809 * Can't use gss_indicate_mechs by itself to get available mechs for
2810 * SPNEGO because it will also return the SPNEGO mech and we do not
2811 * want to consider SPNEGO as an available security mech for
2812 * negotiation. For this reason, get_available_mechs will return
2813 * all available mechs except SPNEGO.
2815 * If a ptr to a creds list is given, this function will attempt
2816 * to acquire creds for the creds given and trim the list of
2817 * returned mechanisms to only those for which creds are valid.
2821 get_available_mechs(OM_uint32 *minor_status,
2822 gss_name_t name, gss_cred_usage_t usage,
2823 gss_cred_id_t *creds, gss_OID_set *rmechs)
2827 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2828 gss_OID_set mechs, goodmechs;
2830 major_status = gss_indicate_mechs(minor_status, &mechs);
2832 if (major_status != GSS_S_COMPLETE) {
2833 return (major_status);
2836 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2838 if (major_status != GSS_S_COMPLETE) {
2839 (void) gss_release_oid_set(minor_status, &mechs);
2840 return (major_status);
2843 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2844 if ((mechs->elements[i].length
2845 != spnego_mechanism.mech_type.length) ||
2846 memcmp(mechs->elements[i].elements,
2847 spnego_mechanism.mech_type.elements,
2848 spnego_mechanism.mech_type.length)) {
2850 major_status = gss_add_oid_set_member(minor_status,
2851 &mechs->elements[i],
2853 if (major_status == GSS_S_COMPLETE)
2859 * If the caller wanted a list of creds returned,
2860 * trim the list of mechanisms down to only those
2861 * for which the creds are valid.
2863 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2864 major_status = gss_acquire_cred(minor_status,
2865 name, GSS_C_INDEFINITE,
2866 *rmechs, usage, creds,
2870 * Drop the old list in favor of the new
2873 (void) gss_release_oid_set(&tmpmin, rmechs);
2874 if (major_status == GSS_S_COMPLETE) {
2875 (void) gssint_copy_oid_set(&tmpmin,
2877 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2881 (void) gss_release_oid_set(&tmpmin, &mechs);
2882 if (found == 0 || major_status != GSS_S_COMPLETE) {
2883 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2884 map_errcode(minor_status);
2885 if (major_status == GSS_S_COMPLETE)
2886 major_status = GSS_S_FAILURE;
2889 return (major_status);
2893 * Return a list of mechanisms we are willing to negotiate for a credential,
2894 * taking into account the mech set provided with gss_set_neg_mechs if it
2898 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
2899 gss_cred_usage_t usage, gss_OID_set *rmechs)
2901 OM_uint32 ret, tmpmin;
2902 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
2903 gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
2904 gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
2908 if (spcred == NULL) {
2910 * The default credentials were supplied. Return a list of all
2911 * available mechs except SPNEGO. When initiating, trim this
2912 * list to mechs we can acquire credentials for.
2914 credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
2915 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
2917 gss_release_cred(&tmpmin, &creds);
2921 /* Get the list of mechs in the mechglue cred. */
2922 ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
2924 if (ret != GSS_S_COMPLETE)
2927 if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
2928 /* gss_set_neg_mechs was never called; return cred_mechs. */
2929 *rmechs = cred_mechs;
2931 return (GSS_S_COMPLETE);
2934 /* Compute the intersection of cred_mechs and spcred->neg_mechs,
2935 * preserving the order in spcred->neg_mechs. */
2936 ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
2937 if (ret != GSS_S_COMPLETE) {
2938 gss_release_oid_set(&tmpmin, &cred_mechs);
2942 for (i = 0; i < spcred->neg_mechs->count; i++) {
2943 gss_test_oid_set_member(&tmpmin,
2944 &spcred->neg_mechs->elements[i],
2945 cred_mechs, &present);
2948 ret = gss_add_oid_set_member(minor_status,
2949 &spcred->neg_mechs->elements[i],
2951 if (ret != GSS_S_COMPLETE)
2955 gss_release_oid_set(&tmpmin, &cred_mechs);
2956 if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
2957 gss_release_oid_set(&tmpmin, &intersect_mechs);
2958 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2959 map_errcode(minor_status);
2960 return (GSS_S_FAILURE);
2963 *rmechs = intersect_mechs;
2965 return (GSS_S_COMPLETE);
2968 /* following are token creation and reading routines */
2971 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2972 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2976 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2980 gss_OID mech_out = NULL;
2981 unsigned char *start, *end;
2983 if (length < 1 || **buff_in != MECH_OID)
2987 end = start + length;
2990 toid.length = *(*buff_in)++;
2992 if ((*buff_in + toid.length) > end)
2995 toid.elements = *buff_in;
2996 *buff_in += toid.length;
2998 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
3000 if (status != GSS_S_COMPLETE) {
3001 map_errcode(minor_status);
3009 * der encode the given mechanism oid into buf_out, advancing the
3014 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
3016 if (buflen < mech->length + 2)
3018 *(*buf_out)++ = MECH_OID;
3019 *(*buf_out)++ = (unsigned char) mech->length;
3020 memcpy(*buf_out, mech->elements, mech->length);
3021 *buf_out += mech->length;
3026 * verify that buff_in points to an octet string, if it does not,
3027 * return NULL and don't advance the pointer. If it is an octet string
3028 * decode buff_in into a gss_buffer_t and return it, advancing the
3032 get_input_token(unsigned char **buff_in, unsigned int buff_length)
3034 gss_buffer_t input_token;
3037 if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
3040 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3041 if (input_token == NULL)
3044 input_token->length = len;
3045 input_token->value = malloc(input_token->length);
3047 if (input_token->value == NULL) {
3052 (void) memcpy(input_token->value, *buff_in, input_token->length);
3053 *buff_in += input_token->length;
3054 return (input_token);
3058 * verify that the input token length is not 0. If it is, just return.
3059 * If the token length is greater than 0, der encode as an octet string
3060 * and place in buf_out, advancing buf_out.
3064 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
3065 unsigned int buflen)
3069 /* if token length is 0, we do not want to send */
3070 if (input_token->length == 0)
3073 if (input_token->length > buflen)
3076 *(*buf_out)++ = OCTET_STRING;
3077 if ((ret = gssint_put_der_length(input_token->length, buf_out,
3078 input_token->length)))
3080 TWRITE_STR(*buf_out, input_token->value, input_token->length);
3085 * verify that buff_in points to a sequence of der encoding. The mech
3086 * set is the only sequence of encoded object in the token, so if it is
3087 * a sequence of encoding, decode the mechset into a gss_OID_set and
3088 * return it, advancing the buffer pointer.
3091 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
3092 unsigned int buff_length)
3094 gss_OID_set returned_mechSet;
3095 OM_uint32 major_status;
3098 OM_uint32 set_length;
3099 unsigned char *start;
3102 if (**buff_in != SEQUENCE_OF)
3108 length = gssint_get_der_length(buff_in, buff_length, &bytes);
3109 if (length < 0 || buff_length - bytes < (unsigned int)length)
3112 major_status = gss_create_empty_oid_set(minor_status,
3114 if (major_status != GSS_S_COMPLETE)
3117 for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
3118 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
3119 buff_length - (*buff_in - start));
3123 major_status = gss_add_oid_set_member(minor_status,
3124 temp, &returned_mechSet);
3125 if (major_status == GSS_S_COMPLETE) {
3126 set_length += returned_mechSet->elements[i].length +2;
3127 if (generic_gss_release_oid(minor_status, &temp))
3128 map_errcode(minor_status);
3132 return (returned_mechSet);
3136 * Encode mechSet into buf.
3139 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3143 unsigned int tlen, ilen;
3146 for (i = 0; i < mechSet->count; i++) {
3148 * 0x06 [DER LEN] [OID]
3151 gssint_der_length_size(mechSet->elements[i].length) +
3152 mechSet->elements[i].length;
3157 tlen = 1 + gssint_der_length_size(ilen) + ilen;
3164 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3166 *ptr++ = SEQUENCE_OF;
3167 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3169 for (i = 0; i < mechSet->count; i++) {
3170 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3179 * Verify that buff_in is pointing to a BIT_STRING with the correct
3180 * length and padding for the req_flags. If it is, decode req_flags
3181 * and return them, otherwise, return NULL.
3184 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3185 OM_uint32 *req_flags)
3189 if (**buff_in != (CONTEXT | 0x01))
3192 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3193 bodysize, &len) < 0)
3194 return GSS_S_DEFECTIVE_TOKEN;
3196 if (*(*buff_in)++ != BIT_STRING)
3197 return GSS_S_DEFECTIVE_TOKEN;
3199 if (*(*buff_in)++ != BIT_STRING_LENGTH)
3200 return GSS_S_DEFECTIVE_TOKEN;
3202 if (*(*buff_in)++ != BIT_STRING_PADDING)
3203 return GSS_S_DEFECTIVE_TOKEN;
3205 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3210 get_negTokenInit(OM_uint32 *minor_status,
3212 gss_buffer_t der_mechSet,
3213 gss_OID_set *mechSet,
3214 OM_uint32 *req_flags,
3215 gss_buffer_t *mechtok,
3216 gss_buffer_t *mechListMIC)
3219 unsigned char *ptr, *bufstart;
3221 gss_buffer_desc tmpbuf;
3224 der_mechSet->length = 0;
3225 der_mechSet->value = NULL;
3226 *mechSet = GSS_C_NO_OID_SET;
3228 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3230 ptr = bufstart = buf->value;
3231 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3232 return GSS_S_FAILURE;
3233 #define REMAIN (buf->length - (ptr - bufstart))
3235 err = g_verify_token_header(gss_mech_spnego,
3236 &len, &ptr, 0, REMAIN);
3238 *minor_status = err;
3239 map_errcode(minor_status);
3240 return GSS_S_FAILURE;
3242 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3243 if (*minor_status) {
3244 map_errcode(minor_status);
3245 return GSS_S_FAILURE;
3248 /* alias into input_token */
3250 tmpbuf.length = REMAIN;
3251 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3252 if (*mechSet == NULL)
3253 return GSS_S_FAILURE;
3255 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3256 der_mechSet->value = malloc(tmpbuf.length);
3257 if (der_mechSet->value == NULL)
3258 return GSS_S_FAILURE;
3259 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3260 der_mechSet->length = tmpbuf.length;
3262 err = get_req_flags(&ptr, REMAIN, req_flags);
3263 if (err != GSS_S_COMPLETE) {
3266 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3267 REMAIN, &len) >= 0) {
3268 *mechtok = get_input_token(&ptr, len);
3269 if (*mechtok == GSS_C_NO_BUFFER) {
3270 return GSS_S_FAILURE;
3273 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3274 REMAIN, &len) >= 0) {
3275 *mechListMIC = get_input_token(&ptr, len);
3276 if (*mechListMIC == GSS_C_NO_BUFFER) {
3277 return GSS_S_FAILURE;
3280 return GSS_S_COMPLETE;
3285 get_negTokenResp(OM_uint32 *minor_status,
3286 unsigned char *buf, unsigned int buflen,
3287 OM_uint32 *negState,
3288 gss_OID *supportedMech,
3289 gss_buffer_t *responseToken,
3290 gss_buffer_t *mechListMIC)
3292 unsigned char *ptr, *bufstart;
3295 unsigned int tag, bytes;
3297 *negState = ACCEPT_DEFECTIVE_TOKEN;
3298 *supportedMech = GSS_C_NO_OID;
3299 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3300 ptr = bufstart = buf;
3301 #define REMAIN (buflen - (ptr - bufstart))
3303 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3304 return GSS_S_DEFECTIVE_TOKEN;
3305 if (*ptr++ == SEQUENCE) {
3306 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3307 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3308 return GSS_S_DEFECTIVE_TOKEN;
3315 if (tag == CONTEXT) {
3316 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3317 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3318 return GSS_S_DEFECTIVE_TOKEN;
3320 if (g_get_tag_and_length(&ptr, ENUMERATED,
3322 return GSS_S_DEFECTIVE_TOKEN;
3324 if (len != ENUMERATION_LENGTH)
3325 return GSS_S_DEFECTIVE_TOKEN;
3328 return GSS_S_DEFECTIVE_TOKEN;
3336 if (tag == (CONTEXT | 0x01)) {
3337 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3338 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3339 return GSS_S_DEFECTIVE_TOKEN;
3341 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3342 if (*supportedMech == GSS_C_NO_OID)
3343 return GSS_S_DEFECTIVE_TOKEN;
3350 if (tag == (CONTEXT | 0x02)) {
3351 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3352 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3353 return GSS_S_DEFECTIVE_TOKEN;
3355 *responseToken = get_input_token(&ptr, REMAIN);
3356 if (*responseToken == GSS_C_NO_BUFFER)
3357 return GSS_S_DEFECTIVE_TOKEN;
3364 if (tag == (CONTEXT | 0x03)) {
3365 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3366 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3367 return GSS_S_DEFECTIVE_TOKEN;
3369 *mechListMIC = get_input_token(&ptr, REMAIN);
3370 if (*mechListMIC == GSS_C_NO_BUFFER)
3371 return GSS_S_DEFECTIVE_TOKEN;
3373 /* Handle Windows 2000 duplicate response token */
3374 if (*responseToken &&
3375 ((*responseToken)->length == (*mechListMIC)->length) &&
3376 !memcmp((*responseToken)->value, (*mechListMIC)->value,
3377 (*responseToken)->length)) {
3380 gss_release_buffer(&tmpmin, *mechListMIC);
3382 *mechListMIC = NULL;
3385 return GSS_S_COMPLETE;
3390 * der encode the passed negResults as an ENUMERATED type and
3391 * place it in buf_out, advancing the buffer.
3395 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3396 unsigned int buflen)
3400 *(*buf_out)++ = ENUMERATED;
3401 *(*buf_out)++ = ENUMERATION_LENGTH;
3402 *(*buf_out)++ = (unsigned char) negResult;
3407 * This routine compares the recieved mechset to the mechset that
3408 * this server can support. It looks sequentially through the mechset
3409 * and the first one that matches what the server can support is
3410 * chosen as the negotiated mechanism. If one is found, negResult
3411 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3412 * it's not the first mech, otherwise we return NULL and negResult
3413 * is set to REJECT. The returned pointer is an alias into
3414 * supported->elements and should not be freed.
3416 * NOTE: There is currently no way to specify a preference order of
3417 * mechanisms supported by the acceptor.
3420 negotiate_mech(gss_OID_set supported, gss_OID_set received,
3421 OM_uint32 *negResult)
3425 for (i = 0; i < received->count; i++) {
3426 gss_OID mech_oid = &received->elements[i];
3428 /* Accept wrong mechanism OID from MS clients */
3429 if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid))
3430 mech_oid = (gss_OID)&gss_mech_krb5_oid;
3432 for (j = 0; j < supported->count; j++) {
3433 if (g_OID_equal(mech_oid, &supported->elements[j])) {
3434 *negResult = (i == 0) ? ACCEPT_INCOMPLETE :
3436 return &supported->elements[j];
3440 *negResult = REJECT;
3445 * the next two routines make a token buffer suitable for
3446 * spnego_gss_display_status. These currently take the string
3447 * in name and place it in the token. Eventually, if
3448 * spnego_gss_display_status returns valid error messages,
3449 * these routines will be changes to return the error string.
3451 static spnego_token_t
3452 make_spnego_token(char *name)
3454 return (spnego_token_t)strdup(name);
3457 static gss_buffer_desc
3458 make_err_msg(char *name)
3460 gss_buffer_desc buffer;
3464 buffer.value = NULL;
3466 buffer.length = strlen(name)+1;
3467 buffer.value = make_spnego_token(name);
3474 * Create the client side spnego token passed back to gss_init_sec_context
3475 * and eventually up to the application program and over to the server.
3477 * Use DER rules, definite length method per RFC 2478
3480 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3482 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3483 gss_buffer_t data, send_token_flag sendtoken,
3484 gss_buffer_t outbuf)
3487 unsigned int tlen, dataLen = 0;
3488 unsigned int negTokenInitSize = 0;
3489 unsigned int negTokenInitSeqSize = 0;
3490 unsigned int negTokenInitContSize = 0;
3491 unsigned int rspTokenSize = 0;
3492 unsigned int mechListTokenSize = 0;
3493 unsigned int micTokenSize = 0;
3497 if (outbuf == GSS_C_NO_BUFFER)
3501 outbuf->value = NULL;
3503 /* calculate the data length */
3506 * 0xa0 [DER LEN] [mechTypes]
3508 mechListTokenSize = 1 +
3509 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3510 spnego_ctx->DER_mechTypes.length;
3511 dataLen += mechListTokenSize;
3514 * If a token from gss_init_sec_context exists,
3515 * add the length of the token + the ASN.1 overhead
3519 * Encoded in final output as:
3520 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3521 * -----s--------|--------s2----------
3524 gssint_der_length_size(data->length) +
3526 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3532 * Encoded in final output as:
3533 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3534 * --s-- -----tlen------------
3537 gssint_der_length_size(mechListMIC->length) +
3538 mechListMIC->length;
3540 gssint_der_length_size(micTokenSize) +
3545 * Add size of DER encoding
3546 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3547 * 0x30 [DER_LEN] [data]
3550 negTokenInitContSize = dataLen;
3551 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3552 dataLen = negTokenInitSeqSize;
3555 * negTokenInitSize indicates the bytes needed to
3556 * hold the ASN.1 encoding of the entire NegTokenInit
3558 * 0xa0 [DER_LEN] + data
3561 negTokenInitSize = 1 +
3562 gssint_der_length_size(negTokenInitSeqSize) +
3563 negTokenInitSeqSize;
3565 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3567 t = (unsigned char *) malloc(tlen);
3575 /* create the message */
3576 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3580 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3581 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3585 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3586 tlen - (int)(ptr-t))))
3589 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3590 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3591 &ptr, tlen - (int)(ptr-t))))
3594 /* We already encoded the MechSetList */
3595 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3596 spnego_ctx->DER_mechTypes.length);
3598 ptr += spnego_ctx->DER_mechTypes.length;
3601 *ptr++ = CONTEXT | 0x02;
3602 if ((ret = gssint_put_der_length(rspTokenSize,
3603 &ptr, tlen - (int)(ptr - t))))
3606 if ((ret = put_input_token(&ptr, data,
3607 tlen - (int)(ptr - t))))
3611 if (mechListMIC != GSS_C_NO_BUFFER) {
3612 *ptr++ = CONTEXT | 0x03;
3613 if ((ret = gssint_put_der_length(micTokenSize,
3614 &ptr, tlen - (int)(ptr - t))))
3617 if (negHintsCompat) {
3618 ret = put_neg_hints(&ptr, mechListMIC,
3619 tlen - (int)(ptr - t));
3622 } else if ((ret = put_input_token(&ptr, mechListMIC,
3623 tlen - (int)(ptr - t))))
3634 outbuf->length = tlen;
3635 outbuf->value = (void *) t;
3641 * create the server side spnego token passed back to
3642 * gss_accept_sec_context and eventually up to the application program
3643 * and over to the client.
3646 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3647 gss_buffer_t data, gss_buffer_t mechListMIC,
3648 send_token_flag sendtoken,
3649 gss_buffer_t outbuf)
3651 unsigned int tlen = 0;
3652 unsigned int ret = 0;
3653 unsigned int NegTokenTargSize = 0;
3654 unsigned int NegTokenSize = 0;
3655 unsigned int rspTokenSize = 0;
3656 unsigned int micTokenSize = 0;
3657 unsigned int dataLen = 0;
3661 if (outbuf == GSS_C_NO_BUFFER)
3662 return (GSS_S_DEFECTIVE_TOKEN);
3663 if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3664 return (GSS_S_DEFECTIVE_TOKEN);
3667 outbuf->value = NULL;
3670 * ASN.1 encoding of the negResult
3671 * ENUMERATED type is 3 bytes
3672 * ENUMERATED TAG, Length, Value,
3673 * Plus 2 bytes for the CONTEXT id and length.
3678 * calculate data length
3680 * If this is the initial token, include length of
3681 * mech_type and the negotiation result fields.
3683 if (sendtoken == INIT_TOKEN_SEND) {
3684 int mechlistTokenSize;
3686 * 1 byte for the CONTEXT ID(0xa0),
3687 * 1 byte for the OID ID(0x06)
3688 * 1 byte for OID Length field
3689 * Plus the rest... (OID Length, OID value)
3691 mechlistTokenSize = 3 + mech_wanted->length +
3692 gssint_der_length_size(mech_wanted->length);
3694 dataLen += mechlistTokenSize;
3696 if (data != NULL && data->length > 0) {
3697 /* Length of the inner token */
3698 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3701 dataLen += rspTokenSize;
3703 /* Length of the outer token */
3704 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3706 if (mechListMIC != NULL) {
3708 /* Length of the inner token */
3709 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3710 mechListMIC->length;
3712 dataLen += micTokenSize;
3714 /* Length of the outer token */
3715 dataLen += 1 + gssint_der_length_size(micTokenSize);
3718 * Add size of DER encoded:
3719 * NegTokenTarg [ SEQUENCE ] of
3720 * NegResult[0] ENUMERATED {
3721 * accept_completed(0),
3722 * accept_incomplete(1),
3724 * supportedMech [1] MechType OPTIONAL,
3725 * responseToken [2] OCTET STRING OPTIONAL,
3726 * mechListMIC [3] OCTET STRING OPTIONAL
3728 * size = data->length + MechListMic + SupportedMech len +
3729 * Result Length + ASN.1 overhead
3731 NegTokenTargSize = dataLen;
3732 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3735 * NegotiationToken [ CHOICE ]{
3736 * negTokenInit [0] NegTokenInit,
3737 * negTokenTarg [1] NegTokenTarg }
3739 NegTokenSize = dataLen;
3740 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3743 t = (unsigned char *) malloc(tlen);
3746 ret = GSS_S_DEFECTIVE_TOKEN;
3753 * Indicate that we are sending CHOICE 1
3756 *ptr++ = CONTEXT | 0x01;
3757 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3758 ret = GSS_S_DEFECTIVE_TOKEN;
3762 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3763 tlen - (int)(ptr-t)) < 0) {
3764 ret = GSS_S_DEFECTIVE_TOKEN;
3769 * First field of the NegTokenTarg SEQUENCE
3770 * is the ENUMERATED NegResult.
3773 if (gssint_put_der_length(3, &ptr,
3774 tlen - (int)(ptr-t)) < 0) {
3775 ret = GSS_S_DEFECTIVE_TOKEN;
3778 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3779 ret = GSS_S_DEFECTIVE_TOKEN;
3782 if (sendtoken == INIT_TOKEN_SEND) {
3784 * Next, is the Supported MechType
3786 *ptr++ = CONTEXT | 0x01;
3787 if (gssint_put_der_length(mech_wanted->length + 2,
3789 tlen - (int)(ptr - t)) < 0) {
3790 ret = GSS_S_DEFECTIVE_TOKEN;
3793 if (put_mech_oid(&ptr, mech_wanted,
3794 tlen - (int)(ptr - t)) < 0) {
3795 ret = GSS_S_DEFECTIVE_TOKEN;
3799 if (data != NULL && data->length > 0) {
3800 *ptr++ = CONTEXT | 0x02;
3801 if (gssint_put_der_length(rspTokenSize, &ptr,
3802 tlen - (int)(ptr - t)) < 0) {
3803 ret = GSS_S_DEFECTIVE_TOKEN;
3806 if (put_input_token(&ptr, data,
3807 tlen - (int)(ptr - t)) < 0) {
3808 ret = GSS_S_DEFECTIVE_TOKEN;
3812 if (mechListMIC != NULL) {
3813 *ptr++ = CONTEXT | 0x03;
3814 if (gssint_put_der_length(micTokenSize, &ptr,
3815 tlen - (int)(ptr - t)) < 0) {
3816 ret = GSS_S_DEFECTIVE_TOKEN;
3819 if (put_input_token(&ptr, mechListMIC,
3820 tlen - (int)(ptr - t)) < 0) {
3821 ret = GSS_S_DEFECTIVE_TOKEN;
3825 ret = GSS_S_COMPLETE;
3827 if (ret != GSS_S_COMPLETE) {
3831 outbuf->length = ptr - t;
3832 outbuf->value = (void *) t;
3838 /* determine size of token */
3840 g_token_size(gss_OID_const mech, unsigned int body_size)
3845 * Initialize the header size to the
3846 * MECH_OID byte + the bytes needed to indicate the
3847 * length of the OID + the OID itself.
3849 * 0x06 [MECHLENFIELD] MECHDATA
3851 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3854 * Now add the bytes needed for the initial header
3856 * 0x60 + [DER_LEN] + HDRSIZE
3858 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3860 return (hdrsize + body_size);
3864 * generate token header.
3866 * Use DER Definite Length method per RFC2478
3867 * Use of indefinite length encoding will not be compatible
3868 * with Microsoft or others that actually follow the spec.
3871 g_make_token_header(gss_OID_const mech,
3872 unsigned int body_size,
3873 unsigned char **buf,
3874 unsigned int totallen)
3877 unsigned int hdrsize;
3878 unsigned char *p = *buf;
3880 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3882 *(*buf)++ = HEADER_ID;
3883 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3886 *(*buf)++ = MECH_OID;
3887 if ((ret = gssint_put_der_length(mech->length, buf,
3888 totallen - (int)(p - *buf))))
3890 TWRITE_STR(*buf, mech->elements, mech->length);
3895 * NOTE: This checks that the length returned by
3896 * gssint_get_der_length() is not greater than the number of octets
3897 * remaining, even though gssint_get_der_length() already checks, in
3901 g_get_tag_and_length(unsigned char **buf, int tag,
3902 unsigned int buflen, unsigned int *outlen)
3904 unsigned char *ptr = *buf;
3905 int ret = -1; /* pessimists, assume failure ! */
3906 unsigned int encoded_len;
3910 if (buflen > 1 && *ptr == tag) {
3912 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3916 } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
3927 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3929 unsigned char *buf = *buf_in;
3930 unsigned char *endptr = buf + cur_size;
3931 unsigned int seqsize;
3936 * Verify this is a NegotiationToken type token
3937 * - check for a0(context specific identifier)
3938 * - get length and verify that enoughd ata exists
3940 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3941 return (G_BAD_TOK_HEADER);
3943 cur_size = seqsize; /* should indicate bytes remaining */
3946 * Verify the next piece, it should identify this as
3947 * a strucure of type NegTokenInit.
3949 if (*buf++ == SEQUENCE) {
3950 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3951 return (G_BAD_TOK_HEADER);
3953 * Make sure we have the entire buffer as described
3955 if (buf + seqsize > endptr)
3956 return (G_BAD_TOK_HEADER);
3958 return (G_BAD_TOK_HEADER);
3961 cur_size = seqsize; /* should indicate bytes remaining */
3964 * Verify that the first blob is a sequence of mechTypes
3966 if (*buf++ == CONTEXT) {
3967 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3968 return (G_BAD_TOK_HEADER);
3970 * Make sure we have the entire buffer as described
3972 if (buf + bytes > endptr)
3973 return (G_BAD_TOK_HEADER);
3975 return (G_BAD_TOK_HEADER);
3979 * At this point, *buf should be at the beginning of the
3980 * DER encoded list of mech types that are to be negotiated.
3988 /* verify token header. */
3990 g_verify_token_header(gss_OID_const mech,
3991 unsigned int *body_size,
3992 unsigned char **buf_in,
3994 unsigned int toksize)
3996 unsigned char *buf = *buf_in;
4003 return (G_BAD_TOK_HEADER);
4005 if (*buf++ != HEADER_ID)
4006 return (G_BAD_TOK_HEADER);
4008 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
4009 return (G_BAD_TOK_HEADER);
4011 if ((seqsize + bytes) != toksize)
4012 return (G_BAD_TOK_HEADER);
4015 return (G_BAD_TOK_HEADER);
4018 if (*buf++ != MECH_OID)
4019 return (G_BAD_TOK_HEADER);
4022 return (G_BAD_TOK_HEADER);
4024 toid.length = *buf++;
4026 if (toksize < toid.length)
4027 return (G_BAD_TOK_HEADER);
4029 toksize -= toid.length;
4031 toid.elements = buf;
4034 if (!g_OID_equal(&toid, mech))
4038 * G_WRONG_MECH is not returned immediately because it's more important
4039 * to return G_BAD_TOK_HEADER if the token header is in fact bad
4042 return (G_BAD_TOK_HEADER);
4048 *body_size = toksize;
4055 * Return non-zero if the oid is one of the kerberos mech oids,
4056 * otherwise return zero.
4058 * N.B. There are 3 oids that represent the kerberos mech:
4059 * RFC-specified GSS_MECH_KRB5_OID,
4060 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
4061 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
4065 is_kerb_mech(gss_OID oid)
4069 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
4071 (void) gss_test_oid_set_member(&minor,
4072 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);