2 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
5 * Export of this software from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11 * distribute this software and its documentation for any purpose and
12 * without fee is hereby granted, provided that the above copyright
13 * notice appear in all copies and that both that copyright notice and
14 * this permission notice appear in supporting documentation, and that
15 * the name of M.I.T. not be used in advertising or publicity pertaining
16 * to distribution of the software without specific, written prior
17 * permission. Furthermore if you modify this software you must label
18 * your software as modified software and not distribute it in such a
19 * fashion that it might be confused with the original M.I.T. software.
20 * M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
30 * A module that implements the spnego security mechanism.
31 * It is used to negotiate the security mechanism between
32 * peers using the GSS-API.
36 * Copyright (c) 2006-2008, Novell, Inc.
37 * All rights reserved.
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions are met:
42 * * Redistributions of source code must retain the above copyright notice,
43 * this list of conditions and the following disclaimer.
44 * * Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * * The copyright holder's name is not used to endorse or promote products
48 * derived from this software without specific prior written permission.
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
62 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
64 #include <sys/param.h>
73 #include "gssapiP_spnego.h"
74 #include <gssapi_err_generic.h>
78 #undef g_verify_token_header
79 #undef g_make_token_header
81 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
82 typedef const gss_OID_desc *gss_OID_const;
84 /* der routines defined in libgss */
85 extern unsigned int gssint_der_length_size(OM_uint32);
86 extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
87 extern int gssint_put_der_length(OM_uint32, unsigned char **, unsigned int);
90 /* private routines for spnego_mechanism */
91 static spnego_token_t make_spnego_token(char *);
92 static gss_buffer_desc make_err_msg(char *);
93 static int g_token_size(gss_OID_const, unsigned int);
94 static int g_make_token_header(gss_OID_const, unsigned int,
95 unsigned char **, unsigned int);
96 static int g_verify_token_header(gss_OID_const, unsigned int *,
99 static int g_verify_neg_token_init(unsigned char **, unsigned int);
100 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
101 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
102 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
103 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
104 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
105 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
106 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
107 gss_cred_usage_t, gss_OID_set *);
108 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
109 static void check_spnego_options(spnego_gss_ctx_id_t);
110 static spnego_gss_ctx_id_t create_spnego_ctx(void);
111 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
112 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
113 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
114 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
117 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
118 gss_buffer_t *, OM_uint32 *, send_token_flag *);
120 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
121 gss_buffer_t *, OM_uint32 *, send_token_flag *);
124 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
125 gss_OID_set *, send_token_flag *);
127 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
128 gss_buffer_t *, gss_buffer_t *,
129 OM_uint32 *, send_token_flag *);
131 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
132 gss_buffer_t *, gss_buffer_t *,
133 OM_uint32 *, send_token_flag *);
135 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
136 gss_OID, gss_buffer_t *, gss_buffer_t *,
137 OM_uint32 *, send_token_flag *);
139 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
140 gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
141 gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
142 OM_uint32 *, send_token_flag *);
145 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
146 spnego_gss_cred_id_t, gss_buffer_t *,
147 gss_buffer_t *, OM_uint32 *, send_token_flag *);
149 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
150 gss_buffer_t *, gss_buffer_t *,
151 OM_uint32 *, send_token_flag *);
153 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
154 OM_uint32 *, send_token_flag *);
156 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
157 gss_buffer_t, gss_OID *, gss_buffer_t,
158 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
159 OM_uint32 *, send_token_flag *);
162 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
165 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
168 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
171 OM_uint32, gss_buffer_t, send_token_flag,
174 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
175 gss_buffer_t, send_token_flag,
179 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
180 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
183 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
184 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
187 is_kerb_mech(gss_OID oid);
189 /* SPNEGO oid structure */
190 static const gss_OID_desc spnego_oids[] = {
191 {SPNEGO_OID_LENGTH, SPNEGO_OID},
194 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
195 static const gss_OID_set_desc spnego_oidsets[] = {
196 {1, (gss_OID) spnego_oids+0},
198 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
200 static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
201 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
203 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
204 gss_buffer_t *, OM_uint32 *, send_token_flag *);
207 * The Mech OID for SPNEGO:
208 * { iso(1) org(3) dod(6) internet(1) security(5)
209 * mechanism(5) spnego(2) }
211 static struct gss_config spnego_mechanism =
213 {SPNEGO_OID_LENGTH, SPNEGO_OID},
215 spnego_gss_acquire_cred,
216 spnego_gss_release_cred,
217 spnego_gss_init_sec_context,
219 spnego_gss_accept_sec_context,
222 #endif /* LEAN_CLIENT */
223 NULL, /* gss_process_context_token */
224 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
225 spnego_gss_context_time, /* gss_context_time */
226 spnego_gss_get_mic, /* gss_get_mic */
227 spnego_gss_verify_mic, /* gss_verify_mic */
228 spnego_gss_wrap, /* gss_wrap */
229 spnego_gss_unwrap, /* gss_unwrap */
230 spnego_gss_display_status,
231 NULL, /* gss_indicate_mechs */
232 spnego_gss_compare_name,
233 spnego_gss_display_name,
234 spnego_gss_import_name,
235 spnego_gss_release_name,
236 spnego_gss_inquire_cred, /* gss_inquire_cred */
237 NULL, /* gss_add_cred */
239 spnego_gss_export_sec_context, /* gss_export_sec_context */
240 spnego_gss_import_sec_context, /* gss_import_sec_context */
242 NULL, /* gss_export_sec_context */
243 NULL, /* gss_import_sec_context */
244 #endif /* LEAN_CLIENT */
245 NULL, /* gss_inquire_cred_by_mech */
246 spnego_gss_inquire_names_for_mech,
247 spnego_gss_inquire_context, /* gss_inquire_context */
248 NULL, /* gss_internal_release_oid */
249 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
250 NULL, /* gss_export_name */
251 NULL, /* gss_store_cred */
252 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
253 spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
254 spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
255 spnego_gss_set_cred_option, /* gssspi_set_cred_option */
256 NULL, /* gssspi_mech_invoke */
257 spnego_gss_wrap_aead,
258 spnego_gss_unwrap_aead,
260 spnego_gss_unwrap_iov,
261 spnego_gss_wrap_iov_length,
262 spnego_gss_complete_auth_token,
263 spnego_gss_acquire_cred_impersonate_name,
264 NULL, /* gss_add_cred_impersonate_name */
265 spnego_gss_display_name_ext,
266 spnego_gss_inquire_name,
267 spnego_gss_get_name_attribute,
268 spnego_gss_set_name_attribute,
269 spnego_gss_delete_name_attribute,
270 spnego_gss_export_name_composite,
271 spnego_gss_map_name_to_any,
272 spnego_gss_release_any_name_mapping,
273 spnego_gss_pseudo_random,
274 spnego_gss_set_neg_mechs,
277 #ifdef _GSS_STATIC_LINK
280 static int gss_spnegomechglue_init(void)
282 struct gss_mech_config mech_spnego;
284 memset(&mech_spnego, 0, sizeof(mech_spnego));
285 mech_spnego.mech = &spnego_mechanism;
286 mech_spnego.mechNameStr = "spnego";
287 mech_spnego.mech_type = GSS_C_NO_OID;
289 return gssint_register_mechinfo(&mech_spnego);
292 gss_mechanism KRB5_CALLCONV
293 gss_mech_initialize(void)
295 return (&spnego_mechanism);
298 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
299 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
300 int gss_krb5int_lib_init(void);
301 #endif /* _GSS_STATIC_LINK */
303 int gss_spnegoint_lib_init(void)
305 #ifdef _GSS_STATIC_LINK
306 return gss_spnegomechglue_init();
312 void gss_spnegoint_lib_fini(void)
318 spnego_gss_acquire_cred(OM_uint32 *minor_status,
319 gss_name_t desired_name,
321 gss_OID_set desired_mechs,
322 gss_cred_usage_t cred_usage,
323 gss_cred_id_t *output_cred_handle,
324 gss_OID_set *actual_mechs,
329 gss_cred_id_t mcred = NULL;
330 spnego_gss_cred_id_t spcred = NULL;
331 dsyslog("Entering spnego_gss_acquire_cred\n");
334 *actual_mechs = NULL;
339 spcred = malloc(sizeof(spnego_gss_cred_id_rec));
340 if (spcred == NULL) {
341 *minor_status = ENOMEM;
342 return (GSS_S_FAILURE);
344 spcred->neg_mechs = GSS_C_NULL_OID_SET;
347 * If the user did not specify a list of mechs,
348 * use get_available_mechs to collect a list of
349 * mechs for which creds are available.
351 if (desired_mechs == GSS_C_NULL_OID_SET) {
352 status = get_available_mechs(minor_status,
353 desired_name, cred_usage,
357 * The caller gave a specific list of mechanisms,
358 * so just get whatever creds are available.
359 * gss_acquire_creds will return the subset of mechs for
360 * which the given 'output_cred_handle' is valid.
362 status = gss_acquire_cred(minor_status,
363 desired_name, time_req,
364 desired_mechs, cred_usage,
365 &mcred, &amechs, time_rec);
368 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
369 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
371 (void) gss_release_oid_set(minor_status, &amechs);
373 if (status == GSS_S_COMPLETE) {
374 spcred->mcred = mcred;
375 *output_cred_handle = (gss_cred_id_t)spcred;
378 *output_cred_handle = GSS_C_NO_CREDENTIAL;
381 dsyslog("Leaving spnego_gss_acquire_cred\n");
387 spnego_gss_release_cred(OM_uint32 *minor_status,
388 gss_cred_id_t *cred_handle)
390 spnego_gss_cred_id_t spcred = NULL;
392 dsyslog("Entering spnego_gss_release_cred\n");
394 if (minor_status == NULL || cred_handle == NULL)
395 return (GSS_S_CALL_INACCESSIBLE_WRITE);
399 if (*cred_handle == GSS_C_NO_CREDENTIAL)
400 return (GSS_S_COMPLETE);
402 spcred = (spnego_gss_cred_id_t)*cred_handle;
403 *cred_handle = GSS_C_NO_CREDENTIAL;
404 gss_release_oid_set(minor_status, &spcred->neg_mechs);
405 gss_release_cred(minor_status, &spcred->mcred);
408 dsyslog("Leaving spnego_gss_release_cred\n");
409 return (GSS_S_COMPLETE);
413 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
415 spnego_ctx->optionStr = gssint_get_modOptions(
416 (const gss_OID)&spnego_oids[0]);
419 static spnego_gss_ctx_id_t
420 create_spnego_ctx(void)
422 spnego_gss_ctx_id_t spnego_ctx = NULL;
423 spnego_ctx = (spnego_gss_ctx_id_t)
424 malloc(sizeof (spnego_gss_ctx_id_rec));
426 if (spnego_ctx == NULL) {
430 spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
431 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
432 spnego_ctx->internal_mech = NULL;
433 spnego_ctx->optionStr = NULL;
434 spnego_ctx->DER_mechTypes.length = 0;
435 spnego_ctx->DER_mechTypes.value = NULL;
436 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
437 spnego_ctx->mic_reqd = 0;
438 spnego_ctx->mic_sent = 0;
439 spnego_ctx->mic_rcvd = 0;
440 spnego_ctx->mech_complete = 0;
441 spnego_ctx->nego_done = 0;
442 spnego_ctx->internal_name = GSS_C_NO_NAME;
443 spnego_ctx->actual_mech = GSS_C_NO_OID;
445 check_spnego_options(spnego_ctx);
451 * Both initiator and acceptor call here to verify and/or create mechListMIC,
452 * and to consistency-check the MIC state. handle_mic is invoked only if the
453 * negotiated mech has completed and supports MICs.
456 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
457 int send_mechtok, spnego_gss_ctx_id_t sc,
458 gss_buffer_t *mic_out,
459 OM_uint32 *negState, send_token_flag *tokflag)
464 *mic_out = GSS_C_NO_BUFFER;
465 if (mic_in != GSS_C_NO_BUFFER) {
467 /* Reject MIC if we've already received a MIC. */
469 *tokflag = ERROR_TOKEN_SEND;
470 return GSS_S_DEFECTIVE_TOKEN;
472 } else if (sc->mic_reqd && !send_mechtok) {
474 * If the peer sends the final mechanism token, it
475 * must send the MIC with that token if the
476 * negotiation requires MICs.
479 *tokflag = ERROR_TOKEN_SEND;
480 return GSS_S_DEFECTIVE_TOKEN;
482 ret = process_mic(minor_status, mic_in, sc, mic_out,
484 if (ret != GSS_S_COMPLETE) {
488 assert(sc->mic_sent || sc->mic_rcvd);
490 if (sc->mic_sent && sc->mic_rcvd) {
491 ret = GSS_S_COMPLETE;
492 *negState = ACCEPT_COMPLETE;
493 if (*mic_out == GSS_C_NO_BUFFER) {
495 * We sent a MIC on the previous pass; we
496 * shouldn't be sending a mechanism token.
498 assert(!send_mechtok);
499 *tokflag = NO_TOKEN_SEND;
501 *tokflag = CONT_TOKEN_SEND;
503 } else if (sc->mic_reqd) {
504 *negState = ACCEPT_INCOMPLETE;
505 ret = GSS_S_CONTINUE_NEEDED;
506 } else if (*negState == ACCEPT_COMPLETE) {
507 ret = GSS_S_COMPLETE;
509 ret = GSS_S_CONTINUE_NEEDED;
515 * Perform the actual verification and/or generation of mechListMIC.
518 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
519 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
520 OM_uint32 *negState, send_token_flag *tokflag)
522 OM_uint32 ret, tmpmin;
524 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
527 if (mic_in != GSS_C_NO_BUFFER) {
528 ret = gss_verify_mic(minor_status, sc->ctx_handle,
531 if (ret != GSS_S_COMPLETE) {
533 *tokflag = ERROR_TOKEN_SEND;
536 /* If we got a MIC, we must send a MIC. */
540 if (sc->mic_reqd && !sc->mic_sent) {
541 ret = gss_get_mic(minor_status, sc->ctx_handle,
545 if (ret != GSS_S_COMPLETE) {
546 gss_release_buffer(&tmpmin, &tmpmic);
547 *tokflag = NO_TOKEN_SEND;
550 *mic_out = malloc(sizeof(gss_buffer_desc));
551 if (*mic_out == GSS_C_NO_BUFFER) {
552 gss_release_buffer(&tmpmin, &tmpmic);
553 *tokflag = NO_TOKEN_SEND;
554 return GSS_S_FAILURE;
559 return GSS_S_COMPLETE;
563 * Initial call to spnego_gss_init_sec_context().
566 init_ctx_new(OM_uint32 *minor_status,
567 spnego_gss_cred_id_t spcred,
569 gss_OID_set *mechSet,
570 send_token_flag *tokflag)
572 OM_uint32 ret, tmpmin;
573 spnego_gss_ctx_id_t sc = NULL;
575 /* determine negotiation mech set */
576 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
578 if (ret != GSS_S_COMPLETE)
581 sc = create_spnego_ctx();
583 return GSS_S_FAILURE;
586 * need to pull the first mech from mechSet to do first
587 * gss_init_sec_context()
589 ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
591 if (ret != GSS_S_COMPLETE) {
592 map_errcode(minor_status);
596 if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
597 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
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;
607 *tokflag = INIT_TOKEN_SEND;
608 ret = GSS_S_CONTINUE_NEEDED;
611 gss_release_oid_set(&tmpmin, mechSet);
616 * Called by second and later calls to spnego_gss_init_sec_context()
617 * to decode reply and update state.
620 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
621 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
622 OM_uint32 *negState, send_token_flag *tokflag)
624 OM_uint32 ret, tmpmin, acc_negState;
626 spnego_gss_ctx_id_t sc;
627 gss_OID supportedMech = GSS_C_NO_OID;
629 sc = (spnego_gss_ctx_id_t)*ctx;
631 *tokflag = ERROR_TOKEN_SEND;
634 ret = get_negTokenResp(minor_status, ptr, buf->length,
635 &acc_negState, &supportedMech,
636 responseToken, mechListMIC);
637 if (ret != GSS_S_COMPLETE)
639 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
640 supportedMech == GSS_C_NO_OID &&
641 *responseToken == GSS_C_NO_BUFFER &&
642 *mechListMIC == GSS_C_NO_BUFFER) {
643 /* Reject "empty" token. */
644 ret = GSS_S_DEFECTIVE_TOKEN;
646 if (acc_negState == REJECT) {
647 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
648 map_errcode(minor_status);
649 *tokflag = NO_TOKEN_SEND;
654 * nego_done is false for the first call to init_ctx_cont()
656 if (!sc->nego_done) {
657 ret = init_ctx_nego(minor_status, sc,
659 supportedMech, responseToken,
662 } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
663 (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
664 /* Missing or spurious token from acceptor. */
665 ret = GSS_S_DEFECTIVE_TOKEN;
666 } else if (!sc->mech_complete ||
668 (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
669 /* Not obviously done; we may decide we're done later in
670 * init_ctx_call_init or handle_mic. */
671 *negState = ACCEPT_INCOMPLETE;
672 *tokflag = CONT_TOKEN_SEND;
673 ret = GSS_S_CONTINUE_NEEDED;
675 /* mech finished on last pass and no MIC required, so done. */
676 *negState = ACCEPT_COMPLETE;
677 *tokflag = NO_TOKEN_SEND;
678 ret = GSS_S_COMPLETE;
681 if (supportedMech != GSS_C_NO_OID)
682 generic_gss_release_oid(&tmpmin, &supportedMech);
687 * Consistency checking and mechanism negotiation handling for second
688 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
689 * update internal state if acceptor has counter-proposed.
692 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
693 OM_uint32 acc_negState, gss_OID supportedMech,
694 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
695 OM_uint32 *negState, send_token_flag *tokflag)
700 *tokflag = ERROR_TOKEN_SEND;
701 ret = GSS_S_DEFECTIVE_TOKEN;
703 * Both supportedMech and negState must be present in first
706 if (supportedMech == GSS_C_NO_OID) {
707 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
708 map_errcode(minor_status);
709 return GSS_S_DEFECTIVE_TOKEN;
711 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
712 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
713 map_errcode(minor_status);
714 return GSS_S_DEFECTIVE_TOKEN;
718 * If the mechanism we sent is not the mechanism returned from
719 * the server, we need to handle the server's counter
720 * proposal. There is a bug in SAMBA servers that always send
721 * the old Kerberos mech OID, even though we sent the new one.
722 * So we will treat all the Kerberos mech OIDS as the same.
724 if (!(is_kerb_mech(supportedMech) &&
725 is_kerb_mech(sc->internal_mech)) &&
726 !g_OID_equal(supportedMech, sc->internal_mech)) {
727 ret = init_ctx_reselect(minor_status, sc,
728 acc_negState, supportedMech,
729 responseToken, mechListMIC,
732 } else if (*responseToken == GSS_C_NO_BUFFER) {
733 if (sc->mech_complete) {
735 * Mech completed on first call to its
736 * init_sec_context(). Acceptor sends no mech
739 *negState = ACCEPT_COMPLETE;
740 *tokflag = NO_TOKEN_SEND;
741 ret = GSS_S_COMPLETE;
744 * Reject missing mech token when optimistic
747 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
748 map_errcode(minor_status);
749 ret = GSS_S_DEFECTIVE_TOKEN;
751 } else if (sc->mech_complete) {
752 /* Reject spurious mech token. */
753 ret = GSS_S_DEFECTIVE_TOKEN;
755 *negState = ACCEPT_INCOMPLETE;
756 *tokflag = CONT_TOKEN_SEND;
757 ret = GSS_S_CONTINUE_NEEDED;
764 * Handle acceptor's counter-proposal of an alternative mechanism.
767 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
768 OM_uint32 acc_negState, gss_OID supportedMech,
769 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
770 OM_uint32 *negState, send_token_flag *tokflag)
772 OM_uint32 ret, tmpmin;
774 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
775 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
778 ret = generic_gss_copy_oid(minor_status, supportedMech,
780 if (ret != GSS_S_COMPLETE) {
781 map_errcode(minor_status);
782 sc->internal_mech = GSS_C_NO_OID;
783 *tokflag = NO_TOKEN_SEND;
786 if (*responseToken != GSS_C_NO_BUFFER) {
787 /* Reject spurious mech token. */
788 return GSS_S_DEFECTIVE_TOKEN;
791 * Windows 2003 and earlier don't correctly send a
792 * negState of request-mic when counter-proposing a
793 * mechanism. They probably don't handle mechListMICs
796 if (acc_negState != REQUEST_MIC)
797 return GSS_S_DEFECTIVE_TOKEN;
799 sc->mech_complete = 0;
801 *negState = REQUEST_MIC;
802 *tokflag = CONT_TOKEN_SEND;
803 return GSS_S_CONTINUE_NEEDED;
807 * Wrap call to mechanism gss_init_sec_context() and update state
811 init_ctx_call_init(OM_uint32 *minor_status,
812 spnego_gss_ctx_id_t sc,
813 spnego_gss_cred_id_t spcred,
814 gss_name_t target_name,
817 gss_buffer_t mechtok_in,
818 gss_OID *actual_mech,
819 gss_buffer_t mechtok_out,
820 OM_uint32 *ret_flags,
823 send_token_flag *send_token)
828 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
829 ret = gss_init_sec_context(minor_status,
834 (req_flags | GSS_C_INTEG_FLAG),
836 GSS_C_NO_CHANNEL_BINDINGS,
842 if (ret == GSS_S_COMPLETE) {
843 sc->mech_complete = 1;
844 if (ret_flags != NULL)
845 *ret_flags = sc->ctx_flags;
847 * If this isn't the first time we've been called,
848 * we're done unless a MIC needs to be
851 if (*send_token == CONT_TOKEN_SEND &&
852 mechtok_out->length == 0 &&
854 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
856 *negState = ACCEPT_COMPLETE;
857 ret = GSS_S_COMPLETE;
858 if (mechtok_out->length == 0) {
859 *send_token = NO_TOKEN_SEND;
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;
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,
895 * send_token is used to indicate in later steps
896 * what type of token, if any should be sent or processed.
897 * NO_TOKEN_SEND = no token should be sent
898 * INIT_TOKEN_SEND = initial token will be sent
899 * CONT_TOKEN_SEND = continuing tokens to be sent
900 * CHECK_MIC = no token to be sent, but have a MIC to check.
902 send_token_flag send_token = NO_TOKEN_SEND;
903 OM_uint32 tmpmin, ret, negState;
904 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
905 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
906 gss_OID_set mechSet = GSS_C_NO_OID_SET;
907 spnego_gss_cred_id_t spcred = NULL;
908 spnego_gss_ctx_id_t spnego_ctx = NULL;
910 dsyslog("Entering init_sec_context\n");
912 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
915 if (minor_status != NULL)
917 if (output_token != GSS_C_NO_BUFFER) {
918 output_token->length = 0;
919 output_token->value = NULL;
921 if (minor_status == NULL ||
922 output_token == GSS_C_NO_BUFFER ||
923 context_handle == NULL)
924 return GSS_S_CALL_INACCESSIBLE_WRITE;
926 if (actual_mech != NULL)
927 *actual_mech = GSS_C_NO_OID;
929 spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
930 if (*context_handle == GSS_C_NO_CONTEXT) {
931 ret = init_ctx_new(minor_status, spcred,
932 context_handle, &mechSet, &send_token);
933 if (ret != GSS_S_CONTINUE_NEEDED) {
937 ret = init_ctx_cont(minor_status, context_handle,
938 input_token, &mechtok_in,
939 &mechListMIC_in, &negState, &send_token);
940 if (HARD_ERROR(ret)) {
944 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
945 if (!spnego_ctx->mech_complete) {
946 ret = init_ctx_call_init(
947 minor_status, spnego_ctx, spcred,
948 target_name, req_flags,
949 time_req, mechtok_in,
950 actual_mech, &mechtok_out,
952 &negState, &send_token);
954 /* create mic/check mic */
955 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
956 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
958 ret = handle_mic(minor_status,
960 (mechtok_out.length != 0),
961 spnego_ctx, &mechListMIC_out,
962 &negState, &send_token);
965 if (send_token == INIT_TOKEN_SEND) {
966 if (make_spnego_tokenInit_msg(spnego_ctx,
970 &mechtok_out, send_token,
974 } else if (send_token != NO_TOKEN_SEND) {
975 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
976 &mechtok_out, mechListMIC_out,
982 gss_release_buffer(&tmpmin, &mechtok_out);
983 if (ret == GSS_S_COMPLETE) {
985 * Now, switch the output context to refer to the
986 * negotiated mechanism's context.
988 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
989 if (actual_mech != NULL)
990 *actual_mech = spnego_ctx->actual_mech;
991 if (ret_flags != NULL)
992 *ret_flags = spnego_ctx->ctx_flags;
993 release_spnego_ctx(&spnego_ctx);
994 } else if (ret != GSS_S_CONTINUE_NEEDED) {
995 if (spnego_ctx != NULL) {
996 gss_delete_sec_context(&tmpmin,
997 &spnego_ctx->ctx_handle,
999 release_spnego_ctx(&spnego_ctx);
1001 *context_handle = GSS_C_NO_CONTEXT;
1003 if (mechtok_in != GSS_C_NO_BUFFER) {
1004 gss_release_buffer(&tmpmin, mechtok_in);
1007 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1008 gss_release_buffer(&tmpmin, mechListMIC_in);
1009 free(mechListMIC_in);
1011 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1012 gss_release_buffer(&tmpmin, mechListMIC_out);
1013 free(mechListMIC_out);
1015 if (mechSet != GSS_C_NO_OID_SET) {
1016 gss_release_oid_set(&tmpmin, &mechSet);
1019 } /* init_sec_context */
1021 /* We don't want to import KRB5 headers here */
1022 static const gss_OID_desc gss_mech_krb5_oid =
1023 { 9, "\052\206\110\206\367\022\001\002\002" };
1024 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1025 { 9, "\052\206\110\202\367\022\001\002\002" };
1028 * verify that the input token length is not 0. If it is, just return.
1029 * If the token length is greater than 0, der encode as a sequence
1030 * and place in buf_out, advancing buf_out.
1034 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1035 unsigned int buflen)
1039 /* if token length is 0, we do not want to send */
1040 if (input_token->length == 0)
1043 if (input_token->length > buflen)
1046 *(*buf_out)++ = SEQUENCE;
1047 if ((ret = gssint_put_der_length(input_token->length, buf_out,
1048 input_token->length)))
1050 TWRITE_STR(*buf_out, input_token->value, input_token->length);
1055 * NegHints ::= SEQUENCE {
1056 * hintName [0] GeneralString OPTIONAL,
1057 * hintAddress [1] OCTET STRING OPTIONAL
1061 #define HOST_PREFIX "host@"
1062 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1065 make_NegHints(OM_uint32 *minor_status,
1066 spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
1068 gss_buffer_desc hintNameBuf;
1069 gss_name_t hintName = GSS_C_NO_NAME;
1070 gss_name_t hintKerberosName;
1071 gss_OID hintNameType;
1072 OM_uint32 major_status;
1074 unsigned int tlen = 0;
1075 unsigned int hintNameSize = 0;
1076 unsigned int negHintsSize = 0;
1080 *outbuf = GSS_C_NO_BUFFER;
1082 if (spcred != NULL) {
1083 major_status = gss_inquire_cred(minor_status,
1089 if (major_status != GSS_S_COMPLETE)
1090 return (major_status);
1093 if (hintName == GSS_C_NO_NAME) {
1094 krb5_error_code code;
1095 krb5int_access kaccess;
1096 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1098 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1100 *minor_status = code;
1101 return (GSS_S_FAILURE);
1104 /* this breaks mutual authentication but Samba relies on it */
1105 code = (*kaccess.clean_hostname)(NULL, NULL,
1106 &hostname[HOST_PREFIX_LEN],
1109 *minor_status = code;
1110 return (GSS_S_FAILURE);
1113 hintNameBuf.value = hostname;
1114 hintNameBuf.length = strlen(hostname);
1116 major_status = gss_import_name(minor_status,
1118 GSS_C_NT_HOSTBASED_SERVICE,
1120 if (major_status != GSS_S_COMPLETE) {
1121 return (major_status);
1125 hintNameBuf.value = NULL;
1126 hintNameBuf.length = 0;
1128 major_status = gss_canonicalize_name(minor_status,
1130 (gss_OID)&gss_mech_krb5_oid,
1132 if (major_status != GSS_S_COMPLETE) {
1133 gss_release_name(&minor, &hintName);
1134 return (major_status);
1136 gss_release_name(&minor, &hintName);
1138 major_status = gss_display_name(minor_status,
1142 if (major_status != GSS_S_COMPLETE) {
1143 gss_release_name(&minor, &hintName);
1144 return (major_status);
1146 gss_release_name(&minor, &hintKerberosName);
1149 * Now encode the name hint into a NegHints ASN.1 type
1151 major_status = GSS_S_FAILURE;
1153 /* Length of DER encoded GeneralString */
1154 tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1156 hintNameSize = tlen;
1158 /* Length of DER encoded hintName */
1159 tlen += 1 + gssint_der_length_size(hintNameSize);
1160 negHintsSize = tlen;
1162 t = (unsigned char *)malloc(tlen);
1164 *minor_status = ENOMEM;
1170 *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1171 if (gssint_put_der_length(hintNameSize,
1172 &ptr, tlen - (int)(ptr-t)))
1175 *ptr++ = GENERAL_STRING;
1176 if (gssint_put_der_length(hintNameBuf.length,
1177 &ptr, tlen - (int)(ptr-t)))
1180 memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1181 ptr += hintNameBuf.length;
1183 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1184 if (*outbuf == NULL) {
1185 *minor_status = ENOMEM;
1188 (*outbuf)->value = (void *)t;
1189 (*outbuf)->length = ptr - t;
1191 t = NULL; /* don't free */
1194 major_status = GSS_S_COMPLETE;
1201 gss_release_buffer(&minor, &hintNameBuf);
1203 return (major_status);
1207 acc_ctx_hints(OM_uint32 *minor_status,
1209 spnego_gss_cred_id_t spcred,
1210 gss_buffer_t *mechListMIC,
1211 OM_uint32 *negState,
1212 send_token_flag *return_token)
1214 OM_uint32 tmpmin, ret;
1215 gss_OID_set supported_mechSet;
1216 spnego_gss_ctx_id_t sc = NULL;
1218 *mechListMIC = GSS_C_NO_BUFFER;
1219 supported_mechSet = GSS_C_NO_OID_SET;
1220 *return_token = ERROR_TOKEN_SEND;
1224 *ctx = GSS_C_NO_CONTEXT;
1225 ret = GSS_S_DEFECTIVE_TOKEN;
1227 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1228 &supported_mechSet);
1229 if (ret != GSS_S_COMPLETE) {
1230 *return_token = NO_TOKEN_SEND;
1234 ret = make_NegHints(minor_status, spcred, mechListMIC);
1235 if (ret != GSS_S_COMPLETE) {
1236 *return_token = NO_TOKEN_SEND;
1241 * Select the best match between the list of mechs
1242 * that the initiator requested and the list that
1243 * the acceptor will support.
1245 sc = create_spnego_ctx();
1247 ret = GSS_S_FAILURE;
1248 *return_token = NO_TOKEN_SEND;
1251 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1252 ret = GSS_S_FAILURE;
1253 *return_token = NO_TOKEN_SEND;
1256 sc->internal_mech = GSS_C_NO_OID;
1258 *negState = ACCEPT_INCOMPLETE;
1259 *return_token = INIT_TOKEN_SEND;
1261 *ctx = (gss_ctx_id_t)sc;
1262 ret = GSS_S_COMPLETE;
1265 gss_release_oid_set(&tmpmin, &supported_mechSet);
1271 * Set negState to REJECT if the token is defective, else
1272 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1273 * preferred mechanism is supported.
1276 acc_ctx_new(OM_uint32 *minor_status,
1279 spnego_gss_cred_id_t spcred,
1280 gss_buffer_t *mechToken,
1281 gss_buffer_t *mechListMIC,
1282 OM_uint32 *negState,
1283 send_token_flag *return_token)
1285 OM_uint32 tmpmin, ret, req_flags;
1286 gss_OID_set supported_mechSet, mechTypes;
1287 gss_buffer_desc der_mechTypes;
1288 gss_OID mech_wanted;
1289 spnego_gss_ctx_id_t sc = NULL;
1291 *ctx = GSS_C_NO_CONTEXT;
1293 ret = GSS_S_DEFECTIVE_TOKEN;
1294 der_mechTypes.length = 0;
1295 der_mechTypes.value = NULL;
1296 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1297 supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1298 *return_token = ERROR_TOKEN_SEND;
1302 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1303 &mechTypes, &req_flags,
1304 mechToken, mechListMIC);
1305 if (ret != GSS_S_COMPLETE) {
1308 ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1309 &supported_mechSet);
1310 if (ret != GSS_S_COMPLETE) {
1311 *return_token = NO_TOKEN_SEND;
1315 * Select the best match between the list of mechs
1316 * that the initiator requested and the list that
1317 * the acceptor will support.
1319 mech_wanted = negotiate_mech_type(minor_status,
1323 if (*negState == REJECT) {
1324 ret = GSS_S_BAD_MECH;
1327 sc = (spnego_gss_ctx_id_t)*ctx;
1329 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1330 assert(mech_wanted != GSS_C_NO_OID);
1332 sc = create_spnego_ctx();
1334 ret = GSS_S_FAILURE;
1335 *return_token = NO_TOKEN_SEND;
1336 generic_gss_release_oid(&tmpmin, &mech_wanted);
1339 sc->internal_mech = mech_wanted;
1340 sc->DER_mechTypes = der_mechTypes;
1341 der_mechTypes.length = 0;
1342 der_mechTypes.value = NULL;
1344 if (*negState == REQUEST_MIC)
1347 *return_token = INIT_TOKEN_SEND;
1349 *ctx = (gss_ctx_id_t)sc;
1350 ret = GSS_S_COMPLETE;
1352 gss_release_oid_set(&tmpmin, &mechTypes);
1353 gss_release_oid_set(&tmpmin, &supported_mechSet);
1354 if (der_mechTypes.length != 0)
1355 gss_release_buffer(&tmpmin, &der_mechTypes);
1361 acc_ctx_cont(OM_uint32 *minstat,
1364 gss_buffer_t *responseToken,
1365 gss_buffer_t *mechListMIC,
1366 OM_uint32 *negState,
1367 send_token_flag *return_token)
1369 OM_uint32 ret, tmpmin;
1370 gss_OID supportedMech;
1371 spnego_gss_ctx_id_t sc;
1373 unsigned char *ptr, *bufstart;
1375 sc = (spnego_gss_ctx_id_t)*ctx;
1376 ret = GSS_S_DEFECTIVE_TOKEN;
1379 supportedMech = GSS_C_NO_OID;
1380 *return_token = ERROR_TOKEN_SEND;
1381 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1383 ptr = bufstart = buf->value;
1384 #define REMAIN (buf->length - (ptr - bufstart))
1385 if (REMAIN > INT_MAX)
1386 return GSS_S_DEFECTIVE_TOKEN;
1389 * Attempt to work with old Sun SPNEGO.
1391 if (*ptr == HEADER_ID) {
1392 ret = g_verify_token_header(gss_mech_spnego,
1393 &len, &ptr, 0, REMAIN);
1396 return GSS_S_DEFECTIVE_TOKEN;
1399 if (*ptr != (CONTEXT | 0x01)) {
1400 return GSS_S_DEFECTIVE_TOKEN;
1402 ret = get_negTokenResp(minstat, ptr, REMAIN,
1403 negState, &supportedMech,
1404 responseToken, mechListMIC);
1405 if (ret != GSS_S_COMPLETE)
1408 if (*responseToken == GSS_C_NO_BUFFER &&
1409 *mechListMIC == GSS_C_NO_BUFFER) {
1411 ret = GSS_S_DEFECTIVE_TOKEN;
1414 if (supportedMech != GSS_C_NO_OID) {
1415 ret = GSS_S_DEFECTIVE_TOKEN;
1419 *negState = ACCEPT_INCOMPLETE;
1420 *return_token = CONT_TOKEN_SEND;
1422 if (supportedMech != GSS_C_NO_OID) {
1423 generic_gss_release_oid(&tmpmin, &supportedMech);
1430 * Verify that mech OID is either exactly the same as the negotiated
1431 * mech OID, or is a mech OID supported by the negotiated mech. MS
1432 * implementations can list a most preferred mech using an incorrect
1433 * krb5 OID while emitting a krb5 initiator mech token having the
1434 * correct krb5 mech OID.
1437 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1438 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1439 OM_uint32 *negState, send_token_flag *tokflag)
1441 OM_uint32 ret, tmpmin;
1442 gss_mechanism mech = NULL;
1443 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1446 if (g_OID_equal(sc->internal_mech, mechoid))
1447 return GSS_S_COMPLETE;
1449 mech = gssint_get_mechanism(sc->internal_mech);
1450 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1451 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1452 map_errcode(minor_status);
1454 *tokflag = ERROR_TOKEN_SEND;
1455 return GSS_S_BAD_MECH;
1457 ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1458 if (ret != GSS_S_COMPLETE) {
1459 *tokflag = NO_TOKEN_SEND;
1460 map_error(minor_status, mech);
1463 ret = gss_test_oid_set_member(minor_status, mechoid,
1464 mech_set, &present);
1465 if (ret != GSS_S_COMPLETE)
1468 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1469 map_errcode(minor_status);
1471 *tokflag = ERROR_TOKEN_SEND;
1472 ret = GSS_S_BAD_MECH;
1475 gss_release_oid_set(&tmpmin, &mech_set);
1480 * Wrap call to gss_accept_sec_context() and update state
1484 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1485 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1486 gss_OID *mech_type, gss_buffer_t mechtok_out,
1487 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1488 gss_cred_id_t *delegated_cred_handle,
1489 OM_uint32 *negState, send_token_flag *tokflag)
1492 gss_OID_desc mechoid;
1493 gss_cred_id_t mcred;
1495 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1497 * mechoid is an alias; don't free it.
1499 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1500 if (ret != GSS_S_COMPLETE) {
1501 *tokflag = NO_TOKEN_SEND;
1504 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1506 if (ret != GSS_S_COMPLETE)
1510 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1511 ret = gss_accept_sec_context(minor_status,
1515 GSS_C_NO_CHANNEL_BINDINGS,
1521 delegated_cred_handle);
1522 if (ret == GSS_S_COMPLETE) {
1525 * Force MIC to be not required even if we previously
1528 char *envstr = getenv("MS_FORCE_NO_MIC");
1530 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1531 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1537 sc->mech_complete = 1;
1538 if (ret_flags != NULL)
1539 *ret_flags = sc->ctx_flags;
1541 if (!sc->mic_reqd ||
1542 !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1543 /* No MIC exchange required, so we're done. */
1544 *negState = ACCEPT_COMPLETE;
1545 ret = GSS_S_COMPLETE;
1547 /* handle_mic will decide if we're done. */
1548 ret = GSS_S_CONTINUE_NEEDED;
1550 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1552 *tokflag = ERROR_TOKEN_SEND;
1559 spnego_gss_accept_sec_context(
1560 OM_uint32 *minor_status,
1561 gss_ctx_id_t *context_handle,
1562 gss_cred_id_t verifier_cred_handle,
1563 gss_buffer_t input_token,
1564 gss_channel_bindings_t input_chan_bindings,
1565 gss_name_t *src_name,
1567 gss_buffer_t output_token,
1568 OM_uint32 *ret_flags,
1569 OM_uint32 *time_rec,
1570 gss_cred_id_t *delegated_cred_handle)
1572 OM_uint32 ret, tmpmin, negState;
1573 send_token_flag return_token;
1574 gss_buffer_t mechtok_in, mic_in, mic_out;
1575 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1576 spnego_gss_ctx_id_t sc = NULL;
1577 spnego_gss_cred_id_t spcred = NULL;
1578 OM_uint32 mechstat = GSS_S_FAILURE;
1579 int sendTokenInit = 0, tmpret;
1581 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1583 if (minor_status != NULL)
1585 if (output_token != GSS_C_NO_BUFFER) {
1586 output_token->length = 0;
1587 output_token->value = NULL;
1590 if (minor_status == NULL ||
1591 output_token == GSS_C_NO_BUFFER ||
1592 context_handle == NULL)
1593 return GSS_S_CALL_INACCESSIBLE_WRITE;
1595 if (input_token == GSS_C_NO_BUFFER)
1596 return GSS_S_CALL_INACCESSIBLE_READ;
1598 sc = (spnego_gss_ctx_id_t)*context_handle;
1599 spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1600 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1601 if (src_name != NULL)
1602 *src_name = GSS_C_NO_NAME;
1603 if (mech_type != NULL)
1604 *mech_type = GSS_C_NO_OID;
1605 if (time_rec != NULL)
1607 if (ret_flags != NULL)
1609 if (delegated_cred_handle != NULL)
1610 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1611 if (input_token->length == 0) {
1612 ret = acc_ctx_hints(minor_status,
1613 context_handle, spcred,
1617 if (ret != GSS_S_COMPLETE)
1620 ret = GSS_S_CONTINUE_NEEDED;
1622 /* Can set negState to REQUEST_MIC */
1623 ret = acc_ctx_new(minor_status, input_token,
1624 context_handle, spcred,
1625 &mechtok_in, &mic_in,
1626 &negState, &return_token);
1627 if (ret != GSS_S_COMPLETE)
1629 ret = GSS_S_CONTINUE_NEEDED;
1632 /* Can set negState to ACCEPT_INCOMPLETE */
1633 ret = acc_ctx_cont(minor_status, input_token,
1634 context_handle, &mechtok_in,
1635 &mic_in, &negState, &return_token);
1636 if (ret != GSS_S_COMPLETE)
1638 ret = GSS_S_CONTINUE_NEEDED;
1640 sc = (spnego_gss_ctx_id_t)*context_handle;
1642 * Handle mechtok_in and mic_in only if they are
1643 * present in input_token. If neither is present, whether
1644 * this is an error depends on whether this is the first
1645 * round-trip. RET is set to a default value according to
1646 * whether it is the first round-trip.
1648 mechstat = GSS_S_FAILURE;
1649 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1650 ret = acc_ctx_call_acc(minor_status, sc, spcred,
1651 mechtok_in, mech_type, &mechtok_out,
1652 ret_flags, time_rec,
1653 delegated_cred_handle,
1654 &negState, &return_token);
1655 } else if (negState == REQUEST_MIC) {
1656 mechstat = GSS_S_CONTINUE_NEEDED;
1658 if (!HARD_ERROR(ret) && sc->mech_complete &&
1659 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1661 ret = handle_mic(minor_status, mic_in,
1662 (mechtok_out.length != 0),
1664 &negState, &return_token);
1667 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1669 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1671 return_token, output_token);
1673 ret = GSS_S_FAILURE;
1674 } else if (return_token != NO_TOKEN_SEND &&
1675 return_token != CHECK_MIC) {
1676 tmpret = make_spnego_tokenTarg_msg(negState,
1677 sc ? sc->internal_mech :
1679 &mechtok_out, mic_out,
1683 ret = GSS_S_FAILURE;
1685 if (ret == GSS_S_COMPLETE) {
1686 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1687 if (sc->internal_name != GSS_C_NO_NAME &&
1689 *src_name = sc->internal_name;
1690 sc->internal_name = GSS_C_NO_NAME;
1692 release_spnego_ctx(&sc);
1693 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1695 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1697 release_spnego_ctx(&sc);
1699 *context_handle = GSS_C_NO_CONTEXT;
1701 gss_release_buffer(&tmpmin, &mechtok_out);
1702 if (mechtok_in != GSS_C_NO_BUFFER) {
1703 gss_release_buffer(&tmpmin, mechtok_in);
1706 if (mic_in != GSS_C_NO_BUFFER) {
1707 gss_release_buffer(&tmpmin, mic_in);
1710 if (mic_out != GSS_C_NO_BUFFER) {
1711 gss_release_buffer(&tmpmin, mic_out);
1716 #endif /* LEAN_CLIENT */
1721 spnego_gss_display_status(
1722 OM_uint32 *minor_status,
1723 OM_uint32 status_value,
1726 OM_uint32 *message_context,
1727 gss_buffer_t status_string)
1729 dsyslog("Entering display_status\n");
1731 *message_context = 0;
1732 switch (status_value) {
1733 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1735 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
1737 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1739 *status_string = make_err_msg("SPNEGO failed to acquire creds");
1741 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1743 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
1745 case ERR_SPNEGO_NEGOTIATION_FAILED:
1747 *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
1749 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1751 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
1754 status_string->length = 0;
1755 status_string->value = "";
1759 dsyslog("Leaving display_status\n");
1760 return (GSS_S_COMPLETE);
1766 spnego_gss_import_name(
1767 OM_uint32 *minor_status,
1768 gss_buffer_t input_name_buffer,
1769 gss_OID input_name_type,
1770 gss_name_t *output_name)
1774 dsyslog("Entering import_name\n");
1776 status = gss_import_name(minor_status, input_name_buffer,
1777 input_name_type, output_name);
1779 dsyslog("Leaving import_name\n");
1785 spnego_gss_release_name(
1786 OM_uint32 *minor_status,
1787 gss_name_t *input_name)
1791 dsyslog("Entering release_name\n");
1793 status = gss_release_name(minor_status, input_name);
1795 dsyslog("Leaving release_name\n");
1800 spnego_gss_inquire_cred(
1801 OM_uint32 *minor_status,
1802 gss_cred_id_t cred_handle,
1804 OM_uint32 *lifetime,
1806 gss_OID_set *mechanisms)
1809 spnego_gss_cred_id_t spcred = NULL;
1810 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1811 OM_uint32 tmp_minor_status;
1812 OM_uint32 initiator_lifetime, acceptor_lifetime;
1814 dsyslog("Entering inquire_cred\n");
1817 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1818 * supplied we call gss_inquire_cred_by_mech() on the
1819 * first non-SPNEGO mechanism.
1821 spcred = (spnego_gss_cred_id_t)cred_handle;
1822 if (spcred == NULL) {
1823 status = get_available_mechs(minor_status,
1828 if (status != GSS_S_COMPLETE) {
1829 dsyslog("Leaving inquire_cred\n");
1833 if ((*mechanisms)->count == 0) {
1834 gss_release_cred(&tmp_minor_status, &creds);
1835 gss_release_oid_set(&tmp_minor_status, mechanisms);
1836 dsyslog("Leaving inquire_cred\n");
1837 return (GSS_S_DEFECTIVE_CREDENTIAL);
1840 assert((*mechanisms)->elements != NULL);
1842 status = gss_inquire_cred_by_mech(minor_status,
1844 &(*mechanisms)->elements[0],
1846 &initiator_lifetime,
1849 if (status != GSS_S_COMPLETE) {
1850 gss_release_cred(&tmp_minor_status, &creds);
1851 dsyslog("Leaving inquire_cred\n");
1855 if (lifetime != NULL)
1856 *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1857 acceptor_lifetime : initiator_lifetime;
1859 gss_release_cred(&tmp_minor_status, &creds);
1861 status = gss_inquire_cred(minor_status, spcred->mcred,
1863 cred_usage, mechanisms);
1866 dsyslog("Leaving inquire_cred\n");
1873 spnego_gss_compare_name(
1874 OM_uint32 *minor_status,
1875 const gss_name_t name1,
1876 const gss_name_t name2,
1879 OM_uint32 status = GSS_S_COMPLETE;
1880 dsyslog("Entering compare_name\n");
1882 status = gss_compare_name(minor_status, name1, name2, name_equal);
1884 dsyslog("Leaving compare_name\n");
1891 spnego_gss_display_name(
1892 OM_uint32 *minor_status,
1893 gss_name_t input_name,
1894 gss_buffer_t output_name_buffer,
1895 gss_OID *output_name_type)
1897 OM_uint32 status = GSS_S_COMPLETE;
1898 dsyslog("Entering display_name\n");
1900 status = gss_display_name(minor_status, input_name,
1901 output_name_buffer, output_name_type);
1903 dsyslog("Leaving display_name\n");
1910 spnego_gss_inquire_names_for_mech(
1911 OM_uint32 *minor_status,
1913 gss_OID_set *name_types)
1915 OM_uint32 major, minor;
1917 dsyslog("Entering inquire_names_for_mech\n");
1919 * We only know how to handle our own mechanism.
1921 if ((mechanism != GSS_C_NULL_OID) &&
1922 !g_OID_equal(gss_mech_spnego, mechanism)) {
1924 return (GSS_S_FAILURE);
1927 major = gss_create_empty_oid_set(minor_status, name_types);
1928 if (major == GSS_S_COMPLETE) {
1929 /* Now add our members. */
1930 if (((major = gss_add_oid_set_member(minor_status,
1931 (gss_OID) GSS_C_NT_USER_NAME,
1932 name_types)) == GSS_S_COMPLETE) &&
1933 ((major = gss_add_oid_set_member(minor_status,
1934 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1935 name_types)) == GSS_S_COMPLETE) &&
1936 ((major = gss_add_oid_set_member(minor_status,
1937 (gss_OID) GSS_C_NT_STRING_UID_NAME,
1938 name_types)) == GSS_S_COMPLETE)) {
1939 major = gss_add_oid_set_member(minor_status,
1940 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1944 if (major != GSS_S_COMPLETE)
1945 (void) gss_release_oid_set(&minor, name_types);
1948 dsyslog("Leaving inquire_names_for_mech\n");
1954 OM_uint32 *minor_status,
1955 gss_ctx_id_t context_handle,
1956 gss_buffer_t input_message_buffer,
1957 gss_buffer_t output_message_buffer,
1959 gss_qop_t *qop_state)
1962 ret = gss_unwrap(minor_status,
1964 input_message_buffer,
1965 output_message_buffer,
1974 OM_uint32 *minor_status,
1975 gss_ctx_id_t context_handle,
1978 gss_buffer_t input_message_buffer,
1980 gss_buffer_t output_message_buffer)
1983 ret = gss_wrap(minor_status,
1987 input_message_buffer,
1989 output_message_buffer);
1995 spnego_gss_process_context_token(
1996 OM_uint32 *minor_status,
1997 const gss_ctx_id_t context_handle,
1998 const gss_buffer_t token_buffer)
2001 ret = gss_process_context_token(minor_status,
2009 spnego_gss_delete_sec_context(
2010 OM_uint32 *minor_status,
2011 gss_ctx_id_t *context_handle,
2012 gss_buffer_t output_token)
2014 OM_uint32 ret = GSS_S_COMPLETE;
2015 spnego_gss_ctx_id_t *ctx =
2016 (spnego_gss_ctx_id_t *)context_handle;
2018 if (context_handle == NULL)
2019 return (GSS_S_FAILURE);
2022 * If this is still an SPNEGO mech, release it locally.
2025 (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2026 (void) gss_delete_sec_context(minor_status,
2027 &(*ctx)->ctx_handle,
2029 (void) release_spnego_ctx(ctx);
2031 ret = gss_delete_sec_context(minor_status,
2040 spnego_gss_context_time(
2041 OM_uint32 *minor_status,
2042 const gss_ctx_id_t context_handle,
2043 OM_uint32 *time_rec)
2046 ret = gss_context_time(minor_status,
2053 spnego_gss_export_sec_context(
2054 OM_uint32 *minor_status,
2055 gss_ctx_id_t *context_handle,
2056 gss_buffer_t interprocess_token)
2059 ret = gss_export_sec_context(minor_status,
2061 interprocess_token);
2066 spnego_gss_import_sec_context(
2067 OM_uint32 *minor_status,
2068 const gss_buffer_t interprocess_token,
2069 gss_ctx_id_t *context_handle)
2072 ret = gss_import_sec_context(minor_status,
2077 #endif /* LEAN_CLIENT */
2080 spnego_gss_inquire_context(
2081 OM_uint32 *minor_status,
2082 const gss_ctx_id_t context_handle,
2083 gss_name_t *src_name,
2084 gss_name_t *targ_name,
2085 OM_uint32 *lifetime_rec,
2087 OM_uint32 *ctx_flags,
2088 int *locally_initiated,
2091 OM_uint32 ret = GSS_S_COMPLETE;
2093 ret = gss_inquire_context(minor_status,
2107 spnego_gss_wrap_size_limit(
2108 OM_uint32 *minor_status,
2109 const gss_ctx_id_t context_handle,
2112 OM_uint32 req_output_size,
2113 OM_uint32 *max_input_size)
2116 ret = gss_wrap_size_limit(minor_status,
2127 OM_uint32 *minor_status,
2128 const gss_ctx_id_t context_handle,
2130 const gss_buffer_t message_buffer,
2131 gss_buffer_t message_token)
2134 ret = gss_get_mic(minor_status,
2143 spnego_gss_verify_mic(
2144 OM_uint32 *minor_status,
2145 const gss_ctx_id_t context_handle,
2146 const gss_buffer_t msg_buffer,
2147 const gss_buffer_t token_buffer,
2148 gss_qop_t *qop_state)
2151 ret = gss_verify_mic(minor_status,
2160 spnego_gss_inquire_sec_context_by_oid(
2161 OM_uint32 *minor_status,
2162 const gss_ctx_id_t context_handle,
2163 const gss_OID desired_object,
2164 gss_buffer_set_t *data_set)
2167 ret = gss_inquire_sec_context_by_oid(minor_status,
2175 spnego_gss_inquire_cred_by_oid(
2176 OM_uint32 *minor_status,
2177 const gss_cred_id_t cred_handle,
2178 const gss_OID desired_object,
2179 gss_buffer_set_t *data_set)
2182 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2183 gss_cred_id_t mcred;
2184 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2185 ret = gss_inquire_cred_by_oid(minor_status,
2193 spnego_gss_set_cred_option(
2194 OM_uint32 *minor_status,
2195 gss_cred_id_t cred_handle,
2196 const gss_OID desired_object,
2197 const gss_buffer_t value)
2200 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2201 gss_cred_id_t mcred;
2202 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2203 ret = gssspi_set_cred_option(minor_status,
2211 spnego_gss_set_sec_context_option(
2212 OM_uint32 *minor_status,
2213 gss_ctx_id_t *context_handle,
2214 const gss_OID desired_object,
2215 const gss_buffer_t value)
2218 ret = gss_set_sec_context_option(minor_status,
2226 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2227 gss_ctx_id_t context_handle,
2230 gss_buffer_t input_assoc_buffer,
2231 gss_buffer_t input_payload_buffer,
2233 gss_buffer_t output_message_buffer)
2236 ret = gss_wrap_aead(minor_status,
2241 input_payload_buffer,
2243 output_message_buffer);
2249 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2250 gss_ctx_id_t context_handle,
2251 gss_buffer_t input_message_buffer,
2252 gss_buffer_t input_assoc_buffer,
2253 gss_buffer_t output_payload_buffer,
2255 gss_qop_t *qop_state)
2258 ret = gss_unwrap_aead(minor_status,
2260 input_message_buffer,
2262 output_payload_buffer,
2269 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2270 gss_ctx_id_t context_handle,
2274 gss_iov_buffer_desc *iov,
2278 ret = gss_wrap_iov(minor_status,
2289 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2290 gss_ctx_id_t context_handle,
2292 gss_qop_t *qop_state,
2293 gss_iov_buffer_desc *iov,
2297 ret = gss_unwrap_iov(minor_status,
2307 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2308 gss_ctx_id_t context_handle,
2312 gss_iov_buffer_desc *iov,
2316 ret = gss_wrap_iov_length(minor_status,
2328 spnego_gss_complete_auth_token(
2329 OM_uint32 *minor_status,
2330 const gss_ctx_id_t context_handle,
2331 gss_buffer_t input_message_buffer)
2334 ret = gss_complete_auth_token(minor_status,
2336 input_message_buffer);
2341 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2342 const gss_cred_id_t impersonator_cred_handle,
2343 gss_name_t desired_name,
2345 gss_OID_set desired_mechs,
2346 gss_cred_usage_t cred_usage,
2347 gss_cred_id_t *output_cred_handle,
2348 gss_OID_set *actual_mechs,
2349 OM_uint32 *time_rec)
2352 gss_OID_set amechs = GSS_C_NULL_OID_SET;
2353 spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2354 gss_cred_id_t mcred;
2356 dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2359 *actual_mechs = NULL;
2364 if (desired_mechs == GSS_C_NO_OID_SET) {
2365 status = gss_inquire_cred(minor_status,
2366 impersonator_cred_handle,
2369 if (status != GSS_S_COMPLETE)
2372 desired_mechs = amechs;
2375 imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2376 status = gss_acquire_cred_impersonate_name(minor_status,
2377 imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL,
2378 desired_name, time_req,
2379 desired_mechs, cred_usage,
2380 &mcred, actual_mechs,
2383 if (amechs != GSS_C_NULL_OID_SET)
2384 (void) gss_release_oid_set(minor_status, &amechs);
2386 out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2387 if (out_spcred == NULL) {
2388 gss_release_cred(minor_status, &mcred);
2389 *minor_status = ENOMEM;
2390 return (GSS_S_FAILURE);
2392 out_spcred->mcred = mcred;
2393 out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
2394 *output_cred_handle = (gss_cred_id_t)out_spcred;
2396 dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2401 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2403 gss_OID display_as_name_type,
2404 gss_buffer_t display_name)
2407 ret = gss_display_name_ext(minor_status,
2409 display_as_name_type,
2416 spnego_gss_inquire_name(OM_uint32 *minor_status,
2420 gss_buffer_set_t *attrs)
2423 ret = gss_inquire_name(minor_status,
2432 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2438 gss_buffer_t display_value,
2442 ret = gss_get_name_attribute(minor_status,
2454 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2461 ret = gss_set_name_attribute(minor_status,
2470 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2475 ret = gss_delete_name_attribute(minor_status,
2482 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2484 gss_buffer_t exp_composite_name)
2487 ret = gss_export_name_composite(minor_status,
2489 exp_composite_name);
2494 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2497 gss_buffer_t type_id,
2501 ret = gss_map_name_to_any(minor_status,
2510 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2512 gss_buffer_t type_id,
2516 ret = gss_release_any_name_mapping(minor_status,
2524 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2525 gss_ctx_id_t context,
2527 const gss_buffer_t prf_in,
2528 ssize_t desired_output_len,
2529 gss_buffer_t prf_out)
2532 ret = gss_pseudo_random(minor_status,
2542 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2543 gss_cred_id_t cred_handle,
2544 const gss_OID_set mech_list)
2547 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2549 /* Store mech_list in spcred for use in negotiation logic. */
2550 gss_release_oid_set(minor_status, &spcred->neg_mechs);
2551 ret = generic_gss_copy_oid_set(minor_status, mech_list,
2552 &spcred->neg_mechs);
2557 * We will release everything but the ctx_handle so that it
2558 * can be passed back to init/accept context. This routine should
2559 * not be called until after the ctx_handle memory is assigned to
2560 * the supplied context handle from init/accept context.
2563 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2565 spnego_gss_ctx_id_t context;
2566 OM_uint32 minor_stat;
2569 if (context != NULL) {
2570 (void) gss_release_buffer(&minor_stat,
2571 &context->DER_mechTypes);
2573 (void) generic_gss_release_oid(&minor_stat,
2574 &context->internal_mech);
2576 (void) gss_release_name(&minor_stat, &context->internal_name);
2578 if (context->optionStr != NULL) {
2579 free(context->optionStr);
2580 context->optionStr = NULL;
2588 * Can't use gss_indicate_mechs by itself to get available mechs for
2589 * SPNEGO because it will also return the SPNEGO mech and we do not
2590 * want to consider SPNEGO as an available security mech for
2591 * negotiation. For this reason, get_available_mechs will return
2592 * all available mechs except SPNEGO.
2594 * If a ptr to a creds list is given, this function will attempt
2595 * to acquire creds for the creds given and trim the list of
2596 * returned mechanisms to only those for which creds are valid.
2600 get_available_mechs(OM_uint32 *minor_status,
2601 gss_name_t name, gss_cred_usage_t usage,
2602 gss_cred_id_t *creds, gss_OID_set *rmechs)
2606 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2607 gss_OID_set mechs, goodmechs;
2609 major_status = gss_indicate_mechs(minor_status, &mechs);
2611 if (major_status != GSS_S_COMPLETE) {
2612 return (major_status);
2615 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2617 if (major_status != GSS_S_COMPLETE) {
2618 (void) gss_release_oid_set(minor_status, &mechs);
2619 return (major_status);
2622 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2623 if ((mechs->elements[i].length
2624 != spnego_mechanism.mech_type.length) ||
2625 memcmp(mechs->elements[i].elements,
2626 spnego_mechanism.mech_type.elements,
2627 spnego_mechanism.mech_type.length)) {
2629 major_status = gss_add_oid_set_member(minor_status,
2630 &mechs->elements[i],
2632 if (major_status == GSS_S_COMPLETE)
2638 * If the caller wanted a list of creds returned,
2639 * trim the list of mechanisms down to only those
2640 * for which the creds are valid.
2642 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2643 major_status = gss_acquire_cred(minor_status,
2644 name, GSS_C_INDEFINITE,
2645 *rmechs, usage, creds,
2649 * Drop the old list in favor of the new
2652 (void) gss_release_oid_set(&tmpmin, rmechs);
2653 if (major_status == GSS_S_COMPLETE) {
2654 (void) gssint_copy_oid_set(&tmpmin,
2656 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2660 (void) gss_release_oid_set(&tmpmin, &mechs);
2661 if (found == 0 || major_status != GSS_S_COMPLETE) {
2662 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2663 map_errcode(minor_status);
2664 if (major_status == GSS_S_COMPLETE)
2665 major_status = GSS_S_FAILURE;
2668 return (major_status);
2672 * Return a list of mechanisms we are willing to negotiate for a credential,
2673 * taking into account the mech set provided with gss_set_neg_mechs if it
2677 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
2678 gss_cred_usage_t usage, gss_OID_set *rmechs)
2680 OM_uint32 ret, tmpmin;
2681 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
2682 gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
2683 gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
2686 if (spcred == NULL) {
2688 * The default credentials were supplied. Return a list of all
2689 * available mechs except SPNEGO. When initiating, trim this
2690 * list to mechs we can acquire credentials for.
2692 credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
2693 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
2695 gss_release_cred(&tmpmin, &creds);
2699 /* Get the list of mechs in the mechglue cred. */
2700 ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
2702 if (ret != GSS_S_COMPLETE)
2705 if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
2706 /* gss_set_neg_mechs was never called; return cred_mechs. */
2707 *rmechs = cred_mechs;
2709 return (GSS_S_COMPLETE);
2712 /* Compute the intersection of cred_mechs and spcred->neg_mechs,
2713 * preserving the order in spcred->neg_mechs. */
2714 ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
2715 if (ret != GSS_S_COMPLETE) {
2716 gss_release_oid_set(&tmpmin, &cred_mechs);
2720 for (i = 0; i < spcred->neg_mechs->count; i++) {
2721 for (j = 0; j < cred_mechs->count; j++) {
2722 if (!g_OID_equal(&spcred->neg_mechs->elements[i],
2723 &cred_mechs->elements[j]))
2726 if (j == cred_mechs->count)
2728 ret = gss_add_oid_set_member(minor_status,
2729 &spcred->neg_mechs->elements[i],
2731 if (ret != GSS_S_COMPLETE)
2735 gss_release_oid_set(&tmpmin, &cred_mechs);
2736 if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
2737 gss_release_oid_set(&tmpmin, &intersect_mechs);
2738 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2739 map_errcode(minor_status);
2740 return (GSS_S_FAILURE);
2743 *rmechs = intersect_mechs;
2745 return (GSS_S_COMPLETE);
2748 /* following are token creation and reading routines */
2751 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2752 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2756 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2760 gss_OID mech_out = NULL;
2761 unsigned char *start, *end;
2763 if (length < 1 || **buff_in != MECH_OID)
2767 end = start + length;
2770 toid.length = *(*buff_in)++;
2772 if ((*buff_in + toid.length) > end)
2775 toid.elements = *buff_in;
2776 *buff_in += toid.length;
2778 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2780 if (status != GSS_S_COMPLETE) {
2781 map_errcode(minor_status);
2789 * der encode the given mechanism oid into buf_out, advancing the
2794 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2796 if (buflen < mech->length + 2)
2798 *(*buf_out)++ = MECH_OID;
2799 *(*buf_out)++ = (unsigned char) mech->length;
2800 memcpy(*buf_out, mech->elements, mech->length);
2801 *buf_out += mech->length;
2806 * verify that buff_in points to an octet string, if it does not,
2807 * return NULL and don't advance the pointer. If it is an octet string
2808 * decode buff_in into a gss_buffer_t and return it, advancing the
2812 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2814 gss_buffer_t input_token;
2817 if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
2820 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2821 if (input_token == NULL)
2824 input_token->length = len;
2825 input_token->value = malloc(input_token->length);
2827 if (input_token->value == NULL) {
2832 (void) memcpy(input_token->value, *buff_in, input_token->length);
2833 *buff_in += input_token->length;
2834 return (input_token);
2838 * verify that the input token length is not 0. If it is, just return.
2839 * If the token length is greater than 0, der encode as an octet string
2840 * and place in buf_out, advancing buf_out.
2844 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2845 unsigned int buflen)
2849 /* if token length is 0, we do not want to send */
2850 if (input_token->length == 0)
2853 if (input_token->length > buflen)
2856 *(*buf_out)++ = OCTET_STRING;
2857 if ((ret = gssint_put_der_length(input_token->length, buf_out,
2858 input_token->length)))
2860 TWRITE_STR(*buf_out, input_token->value, input_token->length);
2865 * verify that buff_in points to a sequence of der encoding. The mech
2866 * set is the only sequence of encoded object in the token, so if it is
2867 * a sequence of encoding, decode the mechset into a gss_OID_set and
2868 * return it, advancing the buffer pointer.
2871 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2872 unsigned int buff_length)
2874 gss_OID_set returned_mechSet;
2875 OM_uint32 major_status;
2878 OM_uint32 set_length;
2879 unsigned char *start;
2882 if (**buff_in != SEQUENCE_OF)
2888 length = gssint_get_der_length(buff_in, buff_length, &bytes);
2889 if (length < 0 || buff_length - bytes < (unsigned int)length)
2892 major_status = gss_create_empty_oid_set(minor_status,
2894 if (major_status != GSS_S_COMPLETE)
2897 for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
2898 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2899 buff_length - (*buff_in - start));
2903 major_status = gss_add_oid_set_member(minor_status,
2904 temp, &returned_mechSet);
2905 if (major_status == GSS_S_COMPLETE) {
2906 set_length += returned_mechSet->elements[i].length +2;
2907 if (generic_gss_release_oid(minor_status, &temp))
2908 map_errcode(minor_status);
2912 return (returned_mechSet);
2916 * Encode mechSet into buf.
2919 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
2923 unsigned int tlen, ilen;
2926 for (i = 0; i < mechSet->count; i++) {
2928 * 0x06 [DER LEN] [OID]
2931 gssint_der_length_size(mechSet->elements[i].length) +
2932 mechSet->elements[i].length;
2937 tlen = 1 + gssint_der_length_size(ilen) + ilen;
2944 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
2946 *ptr++ = SEQUENCE_OF;
2947 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
2949 for (i = 0; i < mechSet->count; i++) {
2950 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
2959 * Verify that buff_in is pointing to a BIT_STRING with the correct
2960 * length and padding for the req_flags. If it is, decode req_flags
2961 * and return them, otherwise, return NULL.
2964 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
2965 OM_uint32 *req_flags)
2969 if (**buff_in != (CONTEXT | 0x01))
2972 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
2973 bodysize, &len) < 0)
2974 return GSS_S_DEFECTIVE_TOKEN;
2976 if (*(*buff_in)++ != BIT_STRING)
2977 return GSS_S_DEFECTIVE_TOKEN;
2979 if (*(*buff_in)++ != BIT_STRING_LENGTH)
2980 return GSS_S_DEFECTIVE_TOKEN;
2982 if (*(*buff_in)++ != BIT_STRING_PADDING)
2983 return GSS_S_DEFECTIVE_TOKEN;
2985 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
2990 get_negTokenInit(OM_uint32 *minor_status,
2992 gss_buffer_t der_mechSet,
2993 gss_OID_set *mechSet,
2994 OM_uint32 *req_flags,
2995 gss_buffer_t *mechtok,
2996 gss_buffer_t *mechListMIC)
2999 unsigned char *ptr, *bufstart;
3001 gss_buffer_desc tmpbuf;
3004 der_mechSet->length = 0;
3005 der_mechSet->value = NULL;
3006 *mechSet = GSS_C_NO_OID_SET;
3008 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3010 ptr = bufstart = buf->value;
3011 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3012 return GSS_S_FAILURE;
3013 #define REMAIN (buf->length - (ptr - bufstart))
3015 err = g_verify_token_header(gss_mech_spnego,
3016 &len, &ptr, 0, REMAIN);
3018 *minor_status = err;
3019 map_errcode(minor_status);
3020 return GSS_S_FAILURE;
3022 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3023 if (*minor_status) {
3024 map_errcode(minor_status);
3025 return GSS_S_FAILURE;
3028 /* alias into input_token */
3030 tmpbuf.length = REMAIN;
3031 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3032 if (*mechSet == NULL)
3033 return GSS_S_FAILURE;
3035 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3036 der_mechSet->value = malloc(tmpbuf.length);
3037 if (der_mechSet->value == NULL)
3038 return GSS_S_FAILURE;
3039 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3040 der_mechSet->length = tmpbuf.length;
3042 err = get_req_flags(&ptr, REMAIN, req_flags);
3043 if (err != GSS_S_COMPLETE) {
3046 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3047 REMAIN, &len) >= 0) {
3048 *mechtok = get_input_token(&ptr, len);
3049 if (*mechtok == GSS_C_NO_BUFFER) {
3050 return GSS_S_FAILURE;
3053 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3054 REMAIN, &len) >= 0) {
3055 *mechListMIC = get_input_token(&ptr, len);
3056 if (*mechListMIC == GSS_C_NO_BUFFER) {
3057 return GSS_S_FAILURE;
3060 return GSS_S_COMPLETE;
3065 get_negTokenResp(OM_uint32 *minor_status,
3066 unsigned char *buf, unsigned int buflen,
3067 OM_uint32 *negState,
3068 gss_OID *supportedMech,
3069 gss_buffer_t *responseToken,
3070 gss_buffer_t *mechListMIC)
3072 unsigned char *ptr, *bufstart;
3075 unsigned int tag, bytes;
3077 *negState = ACCEPT_DEFECTIVE_TOKEN;
3078 *supportedMech = GSS_C_NO_OID;
3079 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3080 ptr = bufstart = buf;
3081 #define REMAIN (buflen - (ptr - bufstart))
3083 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3084 return GSS_S_DEFECTIVE_TOKEN;
3085 if (*ptr++ == SEQUENCE) {
3086 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3087 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3088 return GSS_S_DEFECTIVE_TOKEN;
3095 if (tag == CONTEXT) {
3096 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3097 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3098 return GSS_S_DEFECTIVE_TOKEN;
3100 if (g_get_tag_and_length(&ptr, ENUMERATED,
3102 return GSS_S_DEFECTIVE_TOKEN;
3104 if (len != ENUMERATION_LENGTH)
3105 return GSS_S_DEFECTIVE_TOKEN;
3108 return GSS_S_DEFECTIVE_TOKEN;
3116 if (tag == (CONTEXT | 0x01)) {
3117 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3118 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3119 return GSS_S_DEFECTIVE_TOKEN;
3121 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3122 if (*supportedMech == GSS_C_NO_OID)
3123 return GSS_S_DEFECTIVE_TOKEN;
3130 if (tag == (CONTEXT | 0x02)) {
3131 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3132 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3133 return GSS_S_DEFECTIVE_TOKEN;
3135 *responseToken = get_input_token(&ptr, REMAIN);
3136 if (*responseToken == GSS_C_NO_BUFFER)
3137 return GSS_S_DEFECTIVE_TOKEN;
3144 if (tag == (CONTEXT | 0x03)) {
3145 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3146 if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3147 return GSS_S_DEFECTIVE_TOKEN;
3149 *mechListMIC = get_input_token(&ptr, REMAIN);
3150 if (*mechListMIC == GSS_C_NO_BUFFER)
3151 return GSS_S_DEFECTIVE_TOKEN;
3153 return GSS_S_COMPLETE;
3158 * der encode the passed negResults as an ENUMERATED type and
3159 * place it in buf_out, advancing the buffer.
3163 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3164 unsigned int buflen)
3168 *(*buf_out)++ = ENUMERATED;
3169 *(*buf_out)++ = ENUMERATION_LENGTH;
3170 *(*buf_out)++ = (unsigned char) negResult;
3175 * This routine compares the recieved mechset to the mechset that
3176 * this server can support. It looks sequentially through the mechset
3177 * and the first one that matches what the server can support is
3178 * chosen as the negotiated mechanism. If one is found, negResult
3179 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3180 * it's not the first mech, otherwise we return NULL and negResult
3183 * NOTE: There is currently no way to specify a preference order of
3184 * mechanisms supported by the acceptor.
3187 negotiate_mech_type(OM_uint32 *minor_status,
3188 gss_OID_set supported_mechSet,
3189 gss_OID_set mechset,
3190 OM_uint32 *negResult)
3192 gss_OID returned_mech;
3197 for (i = 0; i < mechset->count; i++) {
3198 gss_OID mech_oid = &mechset->elements[i];
3200 /* Accept wrong mechanism OID from MS clients */
3201 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3202 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3203 mech_oid = (gss_OID)&gss_mech_krb5_oid;;
3205 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3210 *negResult = ACCEPT_INCOMPLETE;
3212 *negResult = REQUEST_MIC;
3214 status = generic_gss_copy_oid(minor_status,
3215 &mechset->elements[i],
3217 if (status != GSS_S_COMPLETE) {
3218 *negResult = REJECT;
3219 map_errcode(minor_status);
3222 return (returned_mech);
3224 *negResult = REJECT;
3229 * the next two routines make a token buffer suitable for
3230 * spnego_gss_display_status. These currently take the string
3231 * in name and place it in the token. Eventually, if
3232 * spnego_gss_display_status returns valid error messages,
3233 * these routines will be changes to return the error string.
3235 static spnego_token_t
3236 make_spnego_token(char *name)
3238 return (spnego_token_t)strdup(name);
3241 static gss_buffer_desc
3242 make_err_msg(char *name)
3244 gss_buffer_desc buffer;
3248 buffer.value = NULL;
3250 buffer.length = strlen(name)+1;
3251 buffer.value = make_spnego_token(name);
3258 * Create the client side spnego token passed back to gss_init_sec_context
3259 * and eventually up to the application program and over to the server.
3261 * Use DER rules, definite length method per RFC 2478
3264 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3266 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3267 gss_buffer_t data, send_token_flag sendtoken,
3268 gss_buffer_t outbuf)
3271 unsigned int tlen, dataLen = 0;
3272 unsigned int negTokenInitSize = 0;
3273 unsigned int negTokenInitSeqSize = 0;
3274 unsigned int negTokenInitContSize = 0;
3275 unsigned int rspTokenSize = 0;
3276 unsigned int mechListTokenSize = 0;
3277 unsigned int micTokenSize = 0;
3281 if (outbuf == GSS_C_NO_BUFFER)
3285 outbuf->value = NULL;
3287 /* calculate the data length */
3290 * 0xa0 [DER LEN] [mechTypes]
3292 mechListTokenSize = 1 +
3293 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3294 spnego_ctx->DER_mechTypes.length;
3295 dataLen += mechListTokenSize;
3298 * If a token from gss_init_sec_context exists,
3299 * add the length of the token + the ASN.1 overhead
3303 * Encoded in final output as:
3304 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3305 * -----s--------|--------s2----------
3308 gssint_der_length_size(data->length) +
3310 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3316 * Encoded in final output as:
3317 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3318 * --s-- -----tlen------------
3321 gssint_der_length_size(mechListMIC->length) +
3322 mechListMIC->length;
3324 gssint_der_length_size(micTokenSize) +
3329 * Add size of DER encoding
3330 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3331 * 0x30 [DER_LEN] [data]
3334 negTokenInitContSize = dataLen;
3335 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3336 dataLen = negTokenInitSeqSize;
3339 * negTokenInitSize indicates the bytes needed to
3340 * hold the ASN.1 encoding of the entire NegTokenInit
3342 * 0xa0 [DER_LEN] + data
3345 negTokenInitSize = 1 +
3346 gssint_der_length_size(negTokenInitSeqSize) +
3347 negTokenInitSeqSize;
3349 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3351 t = (unsigned char *) malloc(tlen);
3359 /* create the message */
3360 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3364 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3365 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3369 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3370 tlen - (int)(ptr-t))))
3373 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3374 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3375 &ptr, tlen - (int)(ptr-t))))
3378 /* We already encoded the MechSetList */
3379 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3380 spnego_ctx->DER_mechTypes.length);
3382 ptr += spnego_ctx->DER_mechTypes.length;
3385 *ptr++ = CONTEXT | 0x02;
3386 if ((ret = gssint_put_der_length(rspTokenSize,
3387 &ptr, tlen - (int)(ptr - t))))
3390 if ((ret = put_input_token(&ptr, data,
3391 tlen - (int)(ptr - t))))
3395 if (mechListMIC != GSS_C_NO_BUFFER) {
3396 *ptr++ = CONTEXT | 0x03;
3397 if ((ret = gssint_put_der_length(micTokenSize,
3398 &ptr, tlen - (int)(ptr - t))))
3401 if (negHintsCompat) {
3402 ret = put_neg_hints(&ptr, mechListMIC,
3403 tlen - (int)(ptr - t));
3406 } else if ((ret = put_input_token(&ptr, mechListMIC,
3407 tlen - (int)(ptr - t))))
3418 outbuf->length = tlen;
3419 outbuf->value = (void *) t;
3425 * create the server side spnego token passed back to
3426 * gss_accept_sec_context and eventually up to the application program
3427 * and over to the client.
3430 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3431 gss_buffer_t data, gss_buffer_t mechListMIC,
3432 send_token_flag sendtoken,
3433 gss_buffer_t outbuf)
3435 unsigned int tlen = 0;
3436 unsigned int ret = 0;
3437 unsigned int NegTokenTargSize = 0;
3438 unsigned int NegTokenSize = 0;
3439 unsigned int rspTokenSize = 0;
3440 unsigned int micTokenSize = 0;
3441 unsigned int dataLen = 0;
3445 if (outbuf == GSS_C_NO_BUFFER)
3446 return (GSS_S_DEFECTIVE_TOKEN);
3447 if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3448 return (GSS_S_DEFECTIVE_TOKEN);
3451 outbuf->value = NULL;
3454 * ASN.1 encoding of the negResult
3455 * ENUMERATED type is 3 bytes
3456 * ENUMERATED TAG, Length, Value,
3457 * Plus 2 bytes for the CONTEXT id and length.
3462 * calculate data length
3464 * If this is the initial token, include length of
3465 * mech_type and the negotiation result fields.
3467 if (sendtoken == INIT_TOKEN_SEND) {
3468 int mechlistTokenSize;
3470 * 1 byte for the CONTEXT ID(0xa0),
3471 * 1 byte for the OID ID(0x06)
3472 * 1 byte for OID Length field
3473 * Plus the rest... (OID Length, OID value)
3475 mechlistTokenSize = 3 + mech_wanted->length +
3476 gssint_der_length_size(mech_wanted->length);
3478 dataLen += mechlistTokenSize;
3480 if (data != NULL && data->length > 0) {
3481 /* Length of the inner token */
3482 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3485 dataLen += rspTokenSize;
3487 /* Length of the outer token */
3488 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3490 if (mechListMIC != NULL) {
3492 /* Length of the inner token */
3493 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3494 mechListMIC->length;
3496 dataLen += micTokenSize;
3498 /* Length of the outer token */
3499 dataLen += 1 + gssint_der_length_size(micTokenSize);
3502 * Add size of DER encoded:
3503 * NegTokenTarg [ SEQUENCE ] of
3504 * NegResult[0] ENUMERATED {
3505 * accept_completed(0),
3506 * accept_incomplete(1),
3508 * supportedMech [1] MechType OPTIONAL,
3509 * responseToken [2] OCTET STRING OPTIONAL,
3510 * mechListMIC [3] OCTET STRING OPTIONAL
3512 * size = data->length + MechListMic + SupportedMech len +
3513 * Result Length + ASN.1 overhead
3515 NegTokenTargSize = dataLen;
3516 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3519 * NegotiationToken [ CHOICE ]{
3520 * negTokenInit [0] NegTokenInit,
3521 * negTokenTarg [1] NegTokenTarg }
3523 NegTokenSize = dataLen;
3524 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3527 t = (unsigned char *) malloc(tlen);
3530 ret = GSS_S_DEFECTIVE_TOKEN;
3537 * Indicate that we are sending CHOICE 1
3540 *ptr++ = CONTEXT | 0x01;
3541 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3542 ret = GSS_S_DEFECTIVE_TOKEN;
3546 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3547 tlen - (int)(ptr-t)) < 0) {
3548 ret = GSS_S_DEFECTIVE_TOKEN;
3553 * First field of the NegTokenTarg SEQUENCE
3554 * is the ENUMERATED NegResult.
3557 if (gssint_put_der_length(3, &ptr,
3558 tlen - (int)(ptr-t)) < 0) {
3559 ret = GSS_S_DEFECTIVE_TOKEN;
3562 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3563 ret = GSS_S_DEFECTIVE_TOKEN;
3566 if (sendtoken == INIT_TOKEN_SEND) {
3568 * Next, is the Supported MechType
3570 *ptr++ = CONTEXT | 0x01;
3571 if (gssint_put_der_length(mech_wanted->length + 2,
3573 tlen - (int)(ptr - t)) < 0) {
3574 ret = GSS_S_DEFECTIVE_TOKEN;
3577 if (put_mech_oid(&ptr, mech_wanted,
3578 tlen - (int)(ptr - t)) < 0) {
3579 ret = GSS_S_DEFECTIVE_TOKEN;
3583 if (data != NULL && data->length > 0) {
3584 *ptr++ = CONTEXT | 0x02;
3585 if (gssint_put_der_length(rspTokenSize, &ptr,
3586 tlen - (int)(ptr - t)) < 0) {
3587 ret = GSS_S_DEFECTIVE_TOKEN;
3590 if (put_input_token(&ptr, data,
3591 tlen - (int)(ptr - t)) < 0) {
3592 ret = GSS_S_DEFECTIVE_TOKEN;
3596 if (mechListMIC != NULL) {
3597 *ptr++ = CONTEXT | 0x03;
3598 if (gssint_put_der_length(micTokenSize, &ptr,
3599 tlen - (int)(ptr - t)) < 0) {
3600 ret = GSS_S_DEFECTIVE_TOKEN;
3603 if (put_input_token(&ptr, mechListMIC,
3604 tlen - (int)(ptr - t)) < 0) {
3605 ret = GSS_S_DEFECTIVE_TOKEN;
3609 ret = GSS_S_COMPLETE;
3611 if (ret != GSS_S_COMPLETE) {
3615 outbuf->length = ptr - t;
3616 outbuf->value = (void *) t;
3622 /* determine size of token */
3624 g_token_size(gss_OID_const mech, unsigned int body_size)
3629 * Initialize the header size to the
3630 * MECH_OID byte + the bytes needed to indicate the
3631 * length of the OID + the OID itself.
3633 * 0x06 [MECHLENFIELD] MECHDATA
3635 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3638 * Now add the bytes needed for the initial header
3640 * 0x60 + [DER_LEN] + HDRSIZE
3642 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3644 return (hdrsize + body_size);
3648 * generate token header.
3650 * Use DER Definite Length method per RFC2478
3651 * Use of indefinite length encoding will not be compatible
3652 * with Microsoft or others that actually follow the spec.
3655 g_make_token_header(gss_OID_const mech,
3656 unsigned int body_size,
3657 unsigned char **buf,
3658 unsigned int totallen)
3661 unsigned int hdrsize;
3662 unsigned char *p = *buf;
3664 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3666 *(*buf)++ = HEADER_ID;
3667 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3670 *(*buf)++ = MECH_OID;
3671 if ((ret = gssint_put_der_length(mech->length, buf,
3672 totallen - (int)(p - *buf))))
3674 TWRITE_STR(*buf, mech->elements, mech->length);
3679 * NOTE: This checks that the length returned by
3680 * gssint_get_der_length() is not greater than the number of octets
3681 * remaining, even though gssint_get_der_length() already checks, in
3685 g_get_tag_and_length(unsigned char **buf, int tag,
3686 unsigned int buflen, unsigned int *outlen)
3688 unsigned char *ptr = *buf;
3689 int ret = -1; /* pessimists, assume failure ! */
3690 unsigned int encoded_len;
3694 if (buflen > 1 && *ptr == tag) {
3696 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3700 } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
3711 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3713 unsigned char *buf = *buf_in;
3714 unsigned char *endptr = buf + cur_size;
3715 unsigned int seqsize;
3720 * Verify this is a NegotiationToken type token
3721 * - check for a0(context specific identifier)
3722 * - get length and verify that enoughd ata exists
3724 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3725 return (G_BAD_TOK_HEADER);
3727 cur_size = seqsize; /* should indicate bytes remaining */
3730 * Verify the next piece, it should identify this as
3731 * a strucure of type NegTokenInit.
3733 if (*buf++ == SEQUENCE) {
3734 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3735 return (G_BAD_TOK_HEADER);
3737 * Make sure we have the entire buffer as described
3739 if (buf + seqsize > endptr)
3740 return (G_BAD_TOK_HEADER);
3742 return (G_BAD_TOK_HEADER);
3745 cur_size = seqsize; /* should indicate bytes remaining */
3748 * Verify that the first blob is a sequence of mechTypes
3750 if (*buf++ == CONTEXT) {
3751 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3752 return (G_BAD_TOK_HEADER);
3754 * Make sure we have the entire buffer as described
3756 if (buf + bytes > endptr)
3757 return (G_BAD_TOK_HEADER);
3759 return (G_BAD_TOK_HEADER);
3763 * At this point, *buf should be at the beginning of the
3764 * DER encoded list of mech types that are to be negotiated.
3772 /* verify token header. */
3774 g_verify_token_header(gss_OID_const mech,
3775 unsigned int *body_size,
3776 unsigned char **buf_in,
3778 unsigned int toksize)
3780 unsigned char *buf = *buf_in;
3787 return (G_BAD_TOK_HEADER);
3789 if (*buf++ != HEADER_ID)
3790 return (G_BAD_TOK_HEADER);
3792 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3793 return (G_BAD_TOK_HEADER);
3795 if ((seqsize + bytes) != toksize)
3796 return (G_BAD_TOK_HEADER);
3799 return (G_BAD_TOK_HEADER);
3802 if (*buf++ != MECH_OID)
3803 return (G_BAD_TOK_HEADER);
3806 return (G_BAD_TOK_HEADER);
3808 toid.length = *buf++;
3810 if (toksize < toid.length)
3811 return (G_BAD_TOK_HEADER);
3813 toksize -= toid.length;
3815 toid.elements = buf;
3818 if (!g_OID_equal(&toid, mech))
3822 * G_WRONG_MECH is not returned immediately because it's more important
3823 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3826 return (G_BAD_TOK_HEADER);
3832 *body_size = toksize;
3839 * Return non-zero if the oid is one of the kerberos mech oids,
3840 * otherwise return zero.
3842 * N.B. There are 3 oids that represent the kerberos mech:
3843 * RFC-specified GSS_MECH_KRB5_OID,
3844 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3845 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3849 is_kerb_mech(gss_OID oid)
3853 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3855 (void) gss_test_oid_set_member(&minor,
3856 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);