Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
      3  * All rights reserved.
      4  *
      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.
      9  *
     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.
     23  *
     24  */
     25 
     26 /*
     27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     28  * Use is subject to license terms.
     29  *
     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.
     33  *
     34  */
     35 /*
     36  * Copyright (c) 2006-2008, Novell, Inc.
     37  * All rights reserved.
     38  *
     39  * Redistribution and use in source and binary forms, with or without
     40  * modification, are permitted provided that the following conditions are met:
     41  *
     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.
     49  *
     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.
     61  */
     62 /* #pragma ident	"@(#)spnego_mech.c	1.7	04/09/28 SMI" */
     63 
     64 #include	<sys/param.h>
     65 #include	<unistd.h>
     66 #include	<assert.h>
     67 #include	<stdio.h>
     68 #include	<stdlib.h>
     69 #include	<string.h>
     70 #include	<k5-int.h>
     71 #include	<krb5.h>
     72 #include	<mglueP.h>
     73 #include	"gssapiP_spnego.h"
     74 #include	<gssapi_err_generic.h>
     75 
     76 
     77 /*
     78  * SUNW17PACresync
     79  * MIT has diff names for these GSS utilities.  Solaris needs to change
     80  * them globally to get in sync w/MIT.
     81  * Revisit for full 1.7 resync.
     82  */
     83 #define gssint_get_modOptions __gss_get_modOptions
     84 #define gssint_der_length_size der_length_size
     85 #define gssint_get_der_length get_der_length
     86 #define gssint_put_der_length put_der_length
     87 #define gssint_get_mechanism __gss_get_mechanism
     88 #define gssint_copy_oid_set gss_copy_oid_set
     89 #define gssint_get_mech_type __gss_get_mech_type
     90 
     91 
     92 #undef g_token_size
     93 #undef g_verify_token_header
     94 #undef g_make_token_header
     95 
     96 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
     97 typedef const gss_OID_desc *gss_OID_const;
     98 
     99 /* der routines defined in libgss */
    100 extern unsigned int gssint_der_length_size(OM_uint32);
    101 extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
    102 extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
    103 
    104 
    105 /* private routines for spnego_mechanism */
    106 static spnego_token_t make_spnego_token(char *);
    107 static gss_buffer_desc make_err_msg(char *);
    108 static int g_token_size(gss_OID_const, unsigned int);
    109 static int g_make_token_header(gss_OID_const, unsigned int,
    110 			       unsigned char **, unsigned int);
    111 static int g_verify_token_header(gss_OID_const, unsigned int *,
    112 				 unsigned char **,
    113 				 int, unsigned int);
    114 static int g_verify_neg_token_init(unsigned char **, unsigned int);
    115 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
    116 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
    117 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
    118 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
    119 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
    120 	gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
    121 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
    122 static void check_spnego_options(spnego_gss_ctx_id_t);
    123 static spnego_gss_ctx_id_t create_spnego_ctx(void);
    124 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
    125 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
    126 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
    127 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
    128 
    129 static OM_uint32
    130 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
    131 	    gss_buffer_t *, OM_uint32 *, send_token_flag *);
    132 static OM_uint32
    133 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
    134 	   gss_buffer_t *, OM_uint32 *, send_token_flag *);
    135 
    136 static OM_uint32
    137 init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
    138 	     gss_OID_set *, send_token_flag *);
    139 static OM_uint32
    140 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
    141 	      gss_buffer_t *, gss_buffer_t *,
    142 	      OM_uint32 *, send_token_flag *);
    143 static OM_uint32
    144 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
    145 	      gss_buffer_t *, gss_buffer_t *,
    146 	      OM_uint32 *, send_token_flag *);
    147 static OM_uint32
    148 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
    149 		  gss_OID, gss_buffer_t *, gss_buffer_t *,
    150 		  OM_uint32 *, send_token_flag *);
    151 static OM_uint32
    152 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
    153 		   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
    154 		   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
    155 		   OM_uint32 *, send_token_flag *);
    156 
    157 static OM_uint32
    158 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
    159 	    gss_cred_id_t, gss_buffer_t *,
    160 	    gss_buffer_t *, OM_uint32 *, send_token_flag *);
    161 static OM_uint32
    162 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
    163 	     gss_buffer_t *, gss_buffer_t *,
    164 	     OM_uint32 *, send_token_flag *);
    165 static OM_uint32
    166 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
    167 		OM_uint32 *, send_token_flag *);
    168 static OM_uint32
    169 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
    170 		 gss_buffer_t, gss_OID *, gss_buffer_t,
    171 		 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
    172 		 OM_uint32 *, send_token_flag *);
    173 
    174 static gss_OID
    175 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
    176 		OM_uint32 *);
    177 static int
    178 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
    179 
    180 static int
    181 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
    182 			int,
    183 			gss_buffer_t,
    184 			OM_uint32, gss_buffer_t, send_token_flag,
    185 			gss_buffer_t);
    186 static int
    187 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
    188 			gss_buffer_t, send_token_flag,
    189 			gss_buffer_t);
    190 
    191 static OM_uint32
    192 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
    193 		 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
    194 		 gss_buffer_t *);
    195 static OM_uint32
    196 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
    197 		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
    198 
    199 static int
    200 is_kerb_mech(gss_OID oid);
    201 
    202 /* SPNEGO oid structure */
    203 static const gss_OID_desc spnego_oids[] = {
    204 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
    205 };
    206 
    207 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
    208 static const gss_OID_set_desc spnego_oidsets[] = {
    209 	{1, (gss_OID) spnego_oids+0},
    210 };
    211 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
    212 
    213 static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
    214 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
    215 static OM_uint32
    216 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
    217 	      gss_buffer_t *, OM_uint32 *, send_token_flag *);
    218 
    219 
    220 /*
    221  * The Mech OID for SPNEGO:
    222  * { iso(1) org(3) dod(6) internet(1) security(5)
    223  *  mechanism(5) spnego(2) }
    224  */
    225 static struct gss_config spnego_mechanism =
    226 {
    227 	{SPNEGO_OID_LENGTH, SPNEGO_OID},
    228 	NULL,
    229 	glue_spnego_gss_acquire_cred,
    230 	glue_spnego_gss_release_cred,
    231 	glue_spnego_gss_init_sec_context,
    232 #ifndef LEAN_CLIENT
    233 	glue_spnego_gss_accept_sec_context,
    234 #else
    235 	NULL,
    236 #endif  /* LEAN_CLIENT */
    237 /* EXPORT DELETE START */ /* CRYPT DELETE START */
    238 	NULL,  /* unseal */
    239 /* EXPORT DELETE END */ /* CRYPT DELETE END */
    240 	NULL,				/* gss_process_context_token */
    241 	glue_spnego_gss_delete_sec_context,	/* gss_delete_sec_context */
    242 	glue_spnego_gss_context_time,
    243 	glue_spnego_gss_display_status,
    244 	NULL,				/* gss_indicate_mechs */
    245 	glue_spnego_gss_compare_name,
    246 	glue_spnego_gss_display_name,
    247 	glue_spnego_gss_import_name, /* glue */
    248 	glue_spnego_gss_release_name,
    249 	NULL,				/* gss_inquire_cred */
    250 	NULL,				/* gss_add_cred */
    251 /* EXPORT DELETE START */ /* CRYPT DELETE START */
    252 	NULL, /* seal */
    253 /* EXPORT DELETE END */ /* CRYPT DELETE END */
    254 #ifndef LEAN_CLIENT
    255 	glue_spnego_gss_export_sec_context,	/* gss_export_sec_context */
    256 	glue_spnego_gss_import_sec_context,	/* gss_import_sec_context */
    257 #else
    258 	NULL,				/* gss_export_sec_context */
    259 	NULL,				/* gss_import_sec_context */
    260 #endif /* LEAN_CLIENT */
    261 	NULL, 				/* gss_inquire_cred_by_mech */
    262 	glue_spnego_gss_inquire_names_for_mech,
    263 	glue_spnego_gss_inquire_context,
    264 	NULL,				/* gss_internal_release_oid */
    265 	glue_spnego_gss_wrap_size_limit,
    266 	NULL, /* pname */
    267 	NULL, /* userok */
    268 	NULL, /* gss_export_name */
    269 /* EXPORT DELETE START */
    270 /* CRYPT DELETE START */
    271 #if 0
    272 /* CRYPT DELETE END */
    273 	NULL, /* seal */
    274 	NULL, /* unseal */
    275 /* CRYPT DELETE START */
    276 #endif
    277 /* CRYPT DELETE END */
    278 /* EXPORT DELETE END */
    279 	NULL, /* sign */
    280 	NULL, /* verify */
    281 	NULL, /* gss_store_cred */
    282         spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
    283 };
    284 
    285 #ifdef _GSS_STATIC_LINK
    286 #include "mglueP.h"
    287 
    288 static int gss_spnegomechglue_init(void)
    289 {
    290 	struct gss_mech_config mech_spnego;
    291 
    292 	memset(&mech_spnego, 0, sizeof(mech_spnego));
    293 	mech_spnego.mech = &spnego_mechanism;
    294 	mech_spnego.mechNameStr = "spnego";
    295 	mech_spnego.mech_type = GSS_C_NO_OID;
    296 
    297 	return gssint_register_mechinfo(&mech_spnego);
    298 }
    299 #else
    300 gss_mechanism KRB5_CALLCONV
    301 gss_mech_initialize(void)
    302 {
    303 	return (&spnego_mechanism);
    304 }
    305 
    306 #if 0 /* SUNW17PACresync */
    307 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
    308 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
    309  int gss_krb5int_lib_init(void)
    310 #endif
    311 
    312 #endif /* _GSS_STATIC_LINK */
    313 
    314 static int gss_spnegoint_lib_init(void)
    315 {
    316 #ifdef _GSS_STATIC_LINK
    317 	return gss_spnegomechglue_init();
    318 #else
    319 	return 0;
    320 #endif
    321 }
    322 
    323 static void gss_spnegoint_lib_fini(void)
    324 {
    325 }
    326 
    327 /*ARGSUSED*/
    328 OM_uint32
    329 glue_spnego_gss_acquire_cred(
    330 	void *context,
    331 	OM_uint32 *minor_status,
    332 	gss_name_t desired_name,
    333 	OM_uint32 time_req,
    334 	gss_OID_set desired_mechs,
    335 	gss_cred_usage_t cred_usage,
    336 	gss_cred_id_t *output_cred_handle,
    337 	gss_OID_set *actual_mechs,
    338 	OM_uint32 *time_rec)
    339 {
    340 	return(spnego_gss_acquire_cred(minor_status,
    341 					desired_name,
    342 					time_req,
    343 					desired_mechs,
    344 					cred_usage,
    345 					output_cred_handle,
    346 					actual_mechs,
    347 					time_rec));
    348 }
    349 
    350 /*ARGSUSED*/
    351 OM_uint32
    352 spnego_gss_acquire_cred(OM_uint32 *minor_status,
    353 			gss_name_t desired_name,
    354 			OM_uint32 time_req,
    355 			gss_OID_set desired_mechs,
    356 			gss_cred_usage_t cred_usage,
    357 			gss_cred_id_t *output_cred_handle,
    358 			gss_OID_set *actual_mechs,
    359 			OM_uint32 *time_rec)
    360 {
    361 	OM_uint32 status;
    362 	gss_OID_set amechs;
    363 	dsyslog("Entering spnego_gss_acquire_cred\n");
    364 
    365 	if (actual_mechs)
    366 		*actual_mechs = NULL;
    367 
    368 	if (time_rec)
    369 		*time_rec = 0;
    370 
    371 	/*
    372 	 * If the user did not specify a list of mechs,
    373 	 * use get_available_mechs to collect a list of
    374 	 * mechs for which creds are available.
    375 	 */
    376 	if (desired_mechs == GSS_C_NULL_OID_SET) {
    377 		status = get_available_mechs(minor_status,
    378 				desired_name, cred_usage,
    379 				output_cred_handle, &amechs);
    380 	} else {
    381 		/*
    382 		 * The caller gave a specific list of mechanisms,
    383 		 * so just get whatever creds are available.
    384 		 * gss_acquire_creds will return the subset of mechs for
    385 		 * which the given 'output_cred_handle' is valid.
    386 		 */
    387 		status = gss_acquire_cred(minor_status,
    388 				desired_name, time_req,
    389 				desired_mechs, cred_usage,
    390 				output_cred_handle, &amechs,
    391 				time_rec);
    392 	}
    393 
    394 	if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
    395 		(void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
    396 	}
    397 	(void) gss_release_oid_set(minor_status, &amechs);
    398 
    399 	dsyslog("Leaving spnego_gss_acquire_cred\n");
    400 	return (status);
    401 }
    402 
    403 /*ARGSUSED*/
    404 OM_uint32
    405 glue_spnego_gss_release_cred(void *context,
    406 			    OM_uint32 *minor_status,
    407 			    gss_cred_id_t *cred_handle)
    408 {
    409 	return( spnego_gss_release_cred(minor_status, cred_handle));
    410 }
    411 
    412 /*ARGSUSED*/
    413 OM_uint32
    414 spnego_gss_release_cred(OM_uint32 *minor_status,
    415 			gss_cred_id_t *cred_handle)
    416 {
    417 	OM_uint32 status;
    418 
    419 	dsyslog("Entering spnego_gss_release_cred\n");
    420 
    421 	if (minor_status == NULL || cred_handle == NULL)
    422 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    423 
    424 	*minor_status = 0;
    425 
    426 	if (*cred_handle == GSS_C_NO_CREDENTIAL)
    427 		return (GSS_S_COMPLETE);
    428 
    429 	status = gss_release_cred(minor_status, cred_handle);
    430 
    431 	dsyslog("Leaving spnego_gss_release_cred\n");
    432 	return (status);
    433 }
    434 
    435 static void
    436 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
    437 {
    438 	spnego_ctx->optionStr = gssint_get_modOptions(
    439 		(const gss_OID)&spnego_oids[0]);
    440 }
    441 
    442 static spnego_gss_ctx_id_t
    443 create_spnego_ctx(void)
    444 {
    445 	spnego_gss_ctx_id_t spnego_ctx = NULL;
    446 	spnego_ctx = (spnego_gss_ctx_id_t)
    447 		malloc(sizeof (spnego_gss_ctx_id_rec));
    448 
    449 	if (spnego_ctx == NULL) {
    450 		return (NULL);
    451 	}
    452 
    453 	spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
    454 	spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
    455 	spnego_ctx->internal_mech = NULL;
    456 	spnego_ctx->optionStr = NULL;
    457 	spnego_ctx->DER_mechTypes.length = 0;
    458 	spnego_ctx->DER_mechTypes.value = NULL;
    459 	spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
    460 	spnego_ctx->mic_reqd = 0;
    461 	spnego_ctx->mic_sent = 0;
    462 	spnego_ctx->mic_rcvd = 0;
    463 	spnego_ctx->mech_complete = 0;
    464 	spnego_ctx->nego_done = 0;
    465 	spnego_ctx->internal_name = GSS_C_NO_NAME;
    466 	spnego_ctx->actual_mech = GSS_C_NO_OID;
    467 
    468 	check_spnego_options(spnego_ctx);
    469 
    470 	return (spnego_ctx);
    471 }
    472 
    473 /*
    474  * Both initiator and acceptor call here to verify and/or create
    475  * mechListMIC, and to consistency-check the MIC state.
    476  */
    477 static OM_uint32
    478 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
    479 	   int send_mechtok, spnego_gss_ctx_id_t sc,
    480 	   gss_buffer_t *mic_out,
    481 	   OM_uint32 *negState, send_token_flag *tokflag)
    482 {
    483 	OM_uint32 ret;
    484 
    485 	ret = GSS_S_FAILURE;
    486 	*mic_out = GSS_C_NO_BUFFER;
    487 	if (mic_in != GSS_C_NO_BUFFER) {
    488 		if (sc->mic_rcvd) {
    489 			/* Reject MIC if we've already received a MIC. */
    490 			*negState = REJECT;
    491 			*tokflag = ERROR_TOKEN_SEND;
    492 			return GSS_S_DEFECTIVE_TOKEN;
    493 		}
    494 	} else if (sc->mic_reqd && !send_mechtok) {
    495 		/*
    496 		 * If the peer sends the final mechanism token, it
    497 		 * must send the MIC with that token if the
    498 		 * negotiation requires MICs.
    499 		 */
    500 		*negState = REJECT;
    501 		*tokflag = ERROR_TOKEN_SEND;
    502 		return GSS_S_DEFECTIVE_TOKEN;
    503 	}
    504 	ret = process_mic(minor_status, mic_in, sc, mic_out,
    505 			  negState, tokflag);
    506 	if (ret != GSS_S_COMPLETE) {
    507 		return ret;
    508 	}
    509 	if (sc->mic_reqd) {
    510 		assert(sc->mic_sent || sc->mic_rcvd);
    511 	}
    512 	if (sc->mic_sent && sc->mic_rcvd) {
    513 		ret = GSS_S_COMPLETE;
    514 		*negState = ACCEPT_COMPLETE;
    515 		if (*mic_out == GSS_C_NO_BUFFER) {
    516 			/*
    517 			 * We sent a MIC on the previous pass; we
    518 			 * shouldn't be sending a mechanism token.
    519 			 */
    520 			assert(!send_mechtok);
    521 			*tokflag = NO_TOKEN_SEND;
    522 		} else {
    523 			*tokflag = CONT_TOKEN_SEND;
    524 		}
    525 	} else if (sc->mic_reqd) {
    526 		*negState = ACCEPT_INCOMPLETE;
    527 		ret = GSS_S_CONTINUE_NEEDED;
    528 	} else if (*negState == ACCEPT_COMPLETE) {
    529 		ret = GSS_S_COMPLETE;
    530 	} else {
    531 		ret = GSS_S_CONTINUE_NEEDED;
    532 	}
    533 	return ret;
    534 }
    535 
    536 /*
    537  * Perform the actual verification and/or generation of mechListMIC.
    538  */
    539 static OM_uint32
    540 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
    541 	    spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
    542 	    OM_uint32 *negState, send_token_flag *tokflag)
    543 {
    544 	OM_uint32 ret, tmpmin;
    545 	gss_qop_t qop_state;
    546 	gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
    547 
    548 	ret = GSS_S_FAILURE;
    549 	if (mic_in != GSS_C_NO_BUFFER) {
    550 		ret = gss_verify_mic(minor_status, sc->ctx_handle,
    551 				     &sc->DER_mechTypes,
    552 				     mic_in, &qop_state);
    553 		if (ret != GSS_S_COMPLETE) {
    554 			*negState = REJECT;
    555 			*tokflag = ERROR_TOKEN_SEND;
    556 			return ret;
    557 		}
    558 		/* If we got a MIC, we must send a MIC. */
    559 		sc->mic_reqd = 1;
    560 		sc->mic_rcvd = 1;
    561 	}
    562 	if (sc->mic_reqd && !sc->mic_sent) {
    563 		ret = gss_get_mic(minor_status, sc->ctx_handle,
    564 				  GSS_C_QOP_DEFAULT,
    565 				  &sc->DER_mechTypes,
    566 				  &tmpmic);
    567 		if (ret != GSS_S_COMPLETE) {
    568 			gss_release_buffer(&tmpmin, &tmpmic);
    569 			*tokflag = NO_TOKEN_SEND;
    570 			return ret;
    571 		}
    572 		*mic_out = malloc(sizeof(gss_buffer_desc));
    573 		if (*mic_out == GSS_C_NO_BUFFER) {
    574 			gss_release_buffer(&tmpmin, &tmpmic);
    575 			*tokflag = NO_TOKEN_SEND;
    576 			return GSS_S_FAILURE;
    577 		}
    578 		**mic_out = tmpmic;
    579 		sc->mic_sent = 1;
    580 	}
    581 	return GSS_S_COMPLETE;
    582 }
    583 
    584 /*
    585  * Initial call to spnego_gss_init_sec_context().
    586  */
    587 static OM_uint32
    588 init_ctx_new(OM_uint32 *minor_status,
    589 	     gss_cred_id_t cred,
    590 	     gss_ctx_id_t *ctx,
    591 	     gss_OID_set *mechSet,
    592 	     send_token_flag *tokflag)
    593 {
    594 	OM_uint32 ret, tmpmin;
    595 	gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
    596 	spnego_gss_ctx_id_t sc = NULL;
    597 
    598 	/* determine negotiation mech set */
    599 	if (cred == GSS_C_NO_CREDENTIAL) {
    600 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
    601 					  GSS_C_INITIATE, &creds, mechSet);
    602 		gss_release_cred(&tmpmin, &creds);
    603 	} else {
    604 		/*
    605 		 * Use the list of mechs included in the cred that we
    606 		 * were given.
    607 		 */
    608 		ret = gss_inquire_cred(minor_status, cred,
    609 				       NULL, NULL, NULL, mechSet);
    610 	}
    611 	if (ret != GSS_S_COMPLETE)
    612 		return ret;
    613 
    614 	sc = create_spnego_ctx();
    615 	if (sc == NULL)
    616 		return GSS_S_FAILURE;
    617 
    618 	/*
    619 	 * need to pull the first mech from mechSet to do first
    620 	 * gss_init_sec_context()
    621 	 */
    622 	ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
    623 				   &sc->internal_mech);
    624 	if (ret != GSS_S_COMPLETE) {
    625 	    map_errcode(minor_status);
    626 	    goto cleanup;
    627 	}
    628 
    629 	if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
    630 		generic_gss_release_oid(&tmpmin, &sc->internal_mech);
    631 		ret = GSS_S_FAILURE;
    632 		goto cleanup;
    633 	}
    634 	/*
    635 	 * The actual context is not yet determined, set the output
    636 	 * context handle to refer to the spnego context itself.
    637 	 */
    638 	sc->ctx_handle = GSS_C_NO_CONTEXT;
    639 	*ctx = (gss_ctx_id_t)sc;
    640 	*tokflag = INIT_TOKEN_SEND;
    641 	ret = GSS_S_CONTINUE_NEEDED;
    642 
    643 cleanup:
    644 	gss_release_oid_set(&tmpmin, mechSet);
    645 	return ret;
    646 }
    647 
    648 /*
    649  * Called by second and later calls to spnego_gss_init_sec_context()
    650  * to decode reply and update state.
    651  */
    652 static OM_uint32
    653 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
    654 	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
    655 	      OM_uint32 *negState, send_token_flag *tokflag)
    656 {
    657 	OM_uint32 ret, tmpmin, acc_negState;
    658 	unsigned char *ptr;
    659 	spnego_gss_ctx_id_t sc;
    660 	gss_OID supportedMech = GSS_C_NO_OID;
    661 
    662 	sc = (spnego_gss_ctx_id_t)*ctx;
    663 	*negState = REJECT;
    664 	*tokflag = ERROR_TOKEN_SEND;
    665 
    666 	ptr = buf->value;
    667 	ret = get_negTokenResp(minor_status, ptr, buf->length,
    668 			       &acc_negState, &supportedMech,
    669 			       responseToken, mechListMIC);
    670 	if (ret != GSS_S_COMPLETE)
    671 		goto cleanup;
    672 	if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
    673 	    supportedMech == GSS_C_NO_OID &&
    674 	    *responseToken == GSS_C_NO_BUFFER &&
    675 	    *mechListMIC == GSS_C_NO_BUFFER) {
    676 		/* Reject "empty" token. */
    677 		ret = GSS_S_DEFECTIVE_TOKEN;
    678 	}
    679 	if (acc_negState == REJECT) {
    680 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
    681 		map_errcode(minor_status);
    682 		*tokflag = NO_TOKEN_SEND;
    683 		ret = GSS_S_FAILURE;
    684 		goto cleanup;
    685 	}
    686 	/*
    687 	 * nego_done is false for the first call to init_ctx_cont()
    688 	 */
    689 	if (!sc->nego_done) {
    690 		ret = init_ctx_nego(minor_status, sc,
    691 				    acc_negState,
    692 				    supportedMech, responseToken,
    693 				    mechListMIC,
    694 				    negState, tokflag);
    695 	} else if (!sc->mech_complete &&
    696 		   *responseToken == GSS_C_NO_BUFFER) {
    697 		/*
    698 		 * mech not finished and mech token missing
    699 		 */
    700 		ret = GSS_S_DEFECTIVE_TOKEN;
    701 	} else if (sc->mic_reqd &&
    702 		   (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
    703 		*negState = ACCEPT_INCOMPLETE;
    704 		*tokflag = CONT_TOKEN_SEND;
    705 		ret = GSS_S_CONTINUE_NEEDED;
    706 	} else {
    707 		*negState = ACCEPT_COMPLETE;
    708 		*tokflag = NO_TOKEN_SEND;
    709 		ret = GSS_S_COMPLETE;
    710 	}
    711 cleanup:
    712 	if (supportedMech != GSS_C_NO_OID)
    713 		generic_gss_release_oid(&tmpmin, &supportedMech);
    714 	return ret;
    715 }
    716 
    717 /*
    718  * Consistency checking and mechanism negotiation handling for second
    719  * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
    720  * update internal state if acceptor has counter-proposed.
    721  */
    722 static OM_uint32
    723 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
    724 	      OM_uint32 acc_negState, gss_OID supportedMech,
    725 	      gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
    726 	      OM_uint32 *negState, send_token_flag *tokflag)
    727 {
    728 	OM_uint32 ret;
    729 
    730 	*negState = REJECT;
    731 	*tokflag = ERROR_TOKEN_SEND;
    732 	ret = GSS_S_DEFECTIVE_TOKEN;
    733 	/*
    734 	 * Both supportedMech and negState must be present in first
    735 	 * acceptor token.
    736 	 */
    737 	if (supportedMech == GSS_C_NO_OID) {
    738 		*minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
    739 		map_errcode(minor_status);
    740 		return GSS_S_DEFECTIVE_TOKEN;
    741 	}
    742 	if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
    743 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
    744 		map_errcode(minor_status);
    745 		return GSS_S_DEFECTIVE_TOKEN;
    746 	}
    747 
    748 	/*
    749 	 * If the mechanism we sent is not the mechanism returned from
    750 	 * the server, we need to handle the server's counter
    751 	 * proposal.  There is a bug in SAMBA servers that always send
    752 	 * the old Kerberos mech OID, even though we sent the new one.
    753 	 * So we will treat all the Kerberos mech OIDS as the same.
    754          */
    755 	if (!(is_kerb_mech(supportedMech) &&
    756 	      is_kerb_mech(sc->internal_mech)) &&
    757 	    !g_OID_equal(supportedMech, sc->internal_mech)) {
    758 		ret = init_ctx_reselect(minor_status, sc,
    759 					acc_negState, supportedMech,
    760 					responseToken, mechListMIC,
    761 					negState, tokflag);
    762 
    763 	} else if (*responseToken == GSS_C_NO_BUFFER) {
    764 		if (sc->mech_complete) {
    765 			/*
    766 			 * Mech completed on first call to its
    767 			 * init_sec_context().  Acceptor sends no mech
    768 			 * token.
    769 			 */
    770 			*negState = ACCEPT_COMPLETE;
    771 			*tokflag = NO_TOKEN_SEND;
    772 			ret = GSS_S_COMPLETE;
    773 		} else {
    774 			/*
    775 			 * Reject missing mech token when optimistic
    776 			 * mech selected.
    777 			 */
    778 			*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
    779 			map_errcode(minor_status);
    780 			ret = GSS_S_DEFECTIVE_TOKEN;
    781 		}
    782 	} else if (sc->mech_complete) {
    783 		/* Reject spurious mech token. */
    784 		ret = GSS_S_DEFECTIVE_TOKEN;
    785 	} else {
    786 		*negState = ACCEPT_INCOMPLETE;
    787 		*tokflag = CONT_TOKEN_SEND;
    788 		ret = GSS_S_CONTINUE_NEEDED;
    789 	}
    790 	sc->nego_done = 1;
    791 	return ret;
    792 }
    793 
    794 /*
    795  * Handle acceptor's counter-proposal of an alternative mechanism.
    796  */
    797 static OM_uint32
    798 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
    799 		  OM_uint32 acc_negState, gss_OID supportedMech,
    800 		  gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
    801 		  OM_uint32 *negState, send_token_flag *tokflag)
    802 {
    803 	OM_uint32 ret, tmpmin;
    804 
    805 	generic_gss_release_oid(&tmpmin, &sc->internal_mech);
    806 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
    807 			       GSS_C_NO_BUFFER);
    808 
    809 	ret = generic_gss_copy_oid(minor_status, supportedMech,
    810 				   &sc->internal_mech);
    811 	if (ret != GSS_S_COMPLETE) {
    812 		map_errcode(minor_status);
    813 		sc->internal_mech = GSS_C_NO_OID;
    814 		*tokflag = NO_TOKEN_SEND;
    815 		return ret;
    816 	}
    817 	if (*responseToken != GSS_C_NO_BUFFER) {
    818 		/* Reject spurious mech token. */
    819 		return GSS_S_DEFECTIVE_TOKEN;
    820 	}
    821 	/*
    822 	 * Windows 2003 and earlier don't correctly send a
    823 	 * negState of request-mic when counter-proposing a
    824 	 * mechanism.  They probably don't handle mechListMICs
    825 	 * properly either.
    826 	 */
    827 	if (acc_negState != REQUEST_MIC)
    828 		return GSS_S_DEFECTIVE_TOKEN;
    829 
    830 	sc->mech_complete = 0;
    831 	sc->mic_reqd = 1;
    832 	*negState = REQUEST_MIC;
    833 	*tokflag = CONT_TOKEN_SEND;
    834 	return GSS_S_CONTINUE_NEEDED;
    835 }
    836 
    837 /*
    838  * Wrap call to mechanism gss_init_sec_context() and update state
    839  * accordingly.
    840  */
    841 static OM_uint32
    842 init_ctx_call_init(OM_uint32 *minor_status,
    843 		   spnego_gss_ctx_id_t sc,
    844 		   gss_cred_id_t claimant_cred_handle,
    845 		   gss_name_t target_name,
    846 		   OM_uint32 req_flags,
    847 		   OM_uint32 time_req,
    848 		   gss_buffer_t mechtok_in,
    849 		   gss_OID *actual_mech,
    850 		   gss_buffer_t mechtok_out,
    851 		   OM_uint32 *ret_flags,
    852 		   OM_uint32 *time_rec,
    853 		   OM_uint32 *negState,
    854 		   send_token_flag *send_token)
    855 {
    856 	OM_uint32 ret;
    857 
    858 	ret = gss_init_sec_context(minor_status,
    859 				   claimant_cred_handle,
    860 				   &sc->ctx_handle,
    861 				   target_name,
    862 				   sc->internal_mech,
    863 				   (req_flags | GSS_C_INTEG_FLAG),
    864 				   time_req,
    865 				   GSS_C_NO_CHANNEL_BINDINGS,
    866 				   mechtok_in,
    867 				   &sc->actual_mech,
    868 				   mechtok_out,
    869 				   &sc->ctx_flags,
    870 				   time_rec);
    871 	if (ret == GSS_S_COMPLETE) {
    872 		sc->mech_complete = 1;
    873 		if (ret_flags != NULL)
    874 			*ret_flags = sc->ctx_flags;
    875 		/*
    876 		 * If this isn't the first time we've been called,
    877 		 * we're done unless a MIC needs to be
    878 		 * generated/handled.
    879 		 */
    880 		if (*send_token == CONT_TOKEN_SEND &&
    881 		    mechtok_out->length == 0 &&
    882 		    (!sc->mic_reqd ||
    883 		     !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
    884 
    885 			*negState = ACCEPT_COMPLETE;
    886 			ret = GSS_S_COMPLETE;
    887 			if (mechtok_out->length == 0) {
    888 				*send_token = NO_TOKEN_SEND;
    889 			}
    890 		} else {
    891 			*negState = ACCEPT_INCOMPLETE;
    892 			ret = GSS_S_CONTINUE_NEEDED;
    893 		}
    894 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
    895 		if (*send_token == INIT_TOKEN_SEND) {
    896 			/* Don't output token on error if first call. */
    897 			*send_token = NO_TOKEN_SEND;
    898 		} else {
    899 			*send_token = ERROR_TOKEN_SEND;
    900 		}
    901 		*negState = REJECT;
    902 	}
    903 	return ret;
    904 }
    905 
    906 /*ARGSUSED*/
    907 OM_uint32
    908 glue_spnego_gss_init_sec_context(
    909 	void *context,
    910 	OM_uint32 *minor_status,
    911 	gss_cred_id_t claimant_cred_handle,
    912 	gss_ctx_id_t *context_handle,
    913 	gss_name_t target_name,
    914 	gss_OID mech_type,
    915 	OM_uint32 req_flags,
    916 	OM_uint32 time_req,
    917 	gss_channel_bindings_t input_chan_bindings,
    918 	gss_buffer_t input_token,
    919 	gss_OID *actual_mech,
    920 	gss_buffer_t output_token,
    921 	OM_uint32 *ret_flags,
    922 	OM_uint32 *time_rec)
    923 {
    924 	return(spnego_gss_init_sec_context(
    925 		    minor_status,
    926 		    claimant_cred_handle,
    927 		    context_handle,
    928 		    target_name,
    929 		    mech_type,
    930 		    req_flags,
    931 		    time_req,
    932 		    input_chan_bindings,
    933 		    input_token,
    934 		    actual_mech,
    935 		    output_token,
    936 		    ret_flags,
    937 		    time_rec));
    938 }
    939 
    940 /*ARGSUSED*/
    941 OM_uint32
    942 spnego_gss_init_sec_context(
    943 			OM_uint32 *minor_status,
    944 			gss_cred_id_t claimant_cred_handle,
    945 			gss_ctx_id_t *context_handle,
    946 			gss_name_t target_name,
    947 			gss_OID mech_type,
    948 			OM_uint32 req_flags,
    949 			OM_uint32 time_req,
    950 			gss_channel_bindings_t input_chan_bindings,
    951 			gss_buffer_t input_token,
    952 			gss_OID *actual_mech,
    953 			gss_buffer_t output_token,
    954 			OM_uint32 *ret_flags,
    955 			OM_uint32 *time_rec)
    956 {
    957 	/*
    958 	 * send_token is used to indicate in later steps
    959 	 * what type of token, if any should be sent or processed.
    960 	 * NO_TOKEN_SEND = no token should be sent
    961 	 * INIT_TOKEN_SEND = initial token will be sent
    962 	 * CONT_TOKEN_SEND = continuing tokens to be sent
    963 	 * CHECK_MIC = no token to be sent, but have a MIC to check.
    964 	 */
    965 	send_token_flag send_token = NO_TOKEN_SEND;
    966 	OM_uint32 tmpmin, ret, negState;
    967 	gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
    968 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
    969 	gss_OID_set mechSet = GSS_C_NO_OID_SET;
    970 	spnego_gss_ctx_id_t spnego_ctx = NULL;
    971 
    972 	dsyslog("Entering init_sec_context\n");
    973 
    974 	mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
    975 	negState = REJECT;
    976 
    977 	if (minor_status != NULL)
    978 		*minor_status = 0;
    979 	if (output_token != GSS_C_NO_BUFFER) {
    980 		output_token->length = 0;
    981 		output_token->value = NULL;
    982 	}
    983 	if (minor_status == NULL ||
    984 	    output_token == GSS_C_NO_BUFFER ||
    985 	    context_handle == NULL)
    986 		return GSS_S_CALL_INACCESSIBLE_WRITE;
    987 
    988 	if (actual_mech != NULL)
    989 		*actual_mech = GSS_C_NO_OID;
    990 
    991 	if (*context_handle == GSS_C_NO_CONTEXT) {
    992 		ret = init_ctx_new(minor_status, claimant_cred_handle,
    993 				   context_handle, &mechSet, &send_token);
    994 		if (ret != GSS_S_CONTINUE_NEEDED) {
    995 			goto cleanup;
    996 		}
    997 	} else {
    998 		ret = init_ctx_cont(minor_status, context_handle,
    999 				    input_token, &mechtok_in,
   1000 				    &mechListMIC_in, &negState, &send_token);
   1001 		if (HARD_ERROR(ret)) {
   1002 			goto cleanup;
   1003 		}
   1004 	}
   1005 	spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
   1006 	if (!spnego_ctx->mech_complete) {
   1007 		ret = init_ctx_call_init(
   1008 			minor_status, spnego_ctx,
   1009 			claimant_cred_handle,
   1010 			target_name, req_flags,
   1011 			time_req, mechtok_in,
   1012 			actual_mech, &mechtok_out,
   1013 			ret_flags, time_rec,
   1014 			&negState, &send_token);
   1015 	}
   1016 	/* create mic/check mic */
   1017 	if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
   1018 	    (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
   1019 
   1020 		ret = handle_mic(minor_status,
   1021 				 mechListMIC_in,
   1022 				 (mechtok_out.length != 0),
   1023 				 spnego_ctx, &mechListMIC_out,
   1024 				 &negState, &send_token);
   1025 	}
   1026 cleanup:
   1027 	if (send_token == INIT_TOKEN_SEND) {
   1028 		if (make_spnego_tokenInit_msg(spnego_ctx,
   1029 					      0,
   1030 					      mechListMIC_out,
   1031 					      req_flags,
   1032 					      &mechtok_out, send_token,
   1033 					      output_token) < 0) {
   1034 			ret = GSS_S_FAILURE;
   1035 		}
   1036 	} else if (send_token != NO_TOKEN_SEND) {
   1037 		if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
   1038 					      &mechtok_out, mechListMIC_out,
   1039 					      send_token,
   1040 					      output_token) < 0) {
   1041 			ret = GSS_S_FAILURE;
   1042 		}
   1043 	}
   1044 	gss_release_buffer(&tmpmin, &mechtok_out);
   1045 	if (ret == GSS_S_COMPLETE) {
   1046 		/*
   1047 		 * Now, switch the output context to refer to the
   1048 		 * negotiated mechanism's context.
   1049 		 */
   1050 		*context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
   1051 		if (actual_mech != NULL)
   1052 			*actual_mech = spnego_ctx->actual_mech;
   1053 		if (ret_flags != NULL)
   1054 			*ret_flags = spnego_ctx->ctx_flags;
   1055 		release_spnego_ctx(&spnego_ctx);
   1056 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
   1057 		if (spnego_ctx != NULL) {
   1058 			gss_delete_sec_context(&tmpmin,
   1059 					       &spnego_ctx->ctx_handle,
   1060 					       GSS_C_NO_BUFFER);
   1061 			release_spnego_ctx(&spnego_ctx);
   1062 		}
   1063 		*context_handle = GSS_C_NO_CONTEXT;
   1064 	}
   1065 	if (mechtok_in != GSS_C_NO_BUFFER) {
   1066 		gss_release_buffer(&tmpmin, mechtok_in);
   1067 		free(mechtok_in);
   1068 	}
   1069 	if (mechListMIC_in != GSS_C_NO_BUFFER) {
   1070 		gss_release_buffer(&tmpmin, mechListMIC_in);
   1071 		free(mechListMIC_in);
   1072 	}
   1073 	if (mechListMIC_out != GSS_C_NO_BUFFER) {
   1074 		gss_release_buffer(&tmpmin, mechListMIC_out);
   1075 		free(mechListMIC_out);
   1076 	}
   1077 	if (mechSet != GSS_C_NO_OID_SET) {
   1078 		gss_release_oid_set(&tmpmin, &mechSet);
   1079 	}
   1080 	return ret;
   1081 } /* init_sec_context */
   1082 
   1083 /* We don't want to import KRB5 headers here */
   1084 static const gss_OID_desc gss_mech_krb5_oid =
   1085 	{ 9, "\052\206\110\206\367\022\001\002\002" };
   1086 static const gss_OID_desc gss_mech_krb5_wrong_oid =
   1087 	{ 9, "\052\206\110\202\367\022\001\002\002" };
   1088 
   1089 /*
   1090  * verify that the input token length is not 0. If it is, just return.
   1091  * If the token length is greater than 0, der encode as a sequence
   1092  * and place in buf_out, advancing buf_out.
   1093  */
   1094 
   1095 static int
   1096 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
   1097 	      unsigned int buflen)
   1098 {
   1099 	int ret;
   1100 
   1101 	/* if token length is 0, we do not want to send */
   1102 	if (input_token->length == 0)
   1103 		return (0);
   1104 
   1105 	if (input_token->length > buflen)
   1106 		return (-1);
   1107 
   1108 	*(*buf_out)++ = SEQUENCE;
   1109 	if ((ret = gssint_put_der_length(input_token->length, buf_out,
   1110 			    input_token->length)))
   1111 		return (ret);
   1112 	TWRITE_STR(*buf_out, input_token->value, input_token->length);
   1113 	return (0);
   1114 }
   1115 
   1116 /*
   1117  * NegHints ::= SEQUENCE {
   1118  *    hintName       [0]  GeneralString      OPTIONAL,
   1119  *    hintAddress    [1]  OCTET STRING       OPTIONAL
   1120  * }
   1121  */
   1122 
   1123 #define HOST_PREFIX	"host@"
   1124 #define HOST_PREFIX_LEN	(sizeof(HOST_PREFIX) - 1)
   1125 
   1126 static int
   1127 make_NegHints(OM_uint32 *minor_status,
   1128 	      gss_cred_id_t cred, gss_buffer_t *outbuf)
   1129 {
   1130 	gss_buffer_desc hintNameBuf;
   1131 	gss_name_t hintName = GSS_C_NO_NAME;
   1132 	gss_name_t hintKerberosName;
   1133 	gss_OID hintNameType;
   1134 	OM_uint32 major_status;
   1135 	OM_uint32 minor;
   1136 	unsigned int tlen = 0;
   1137 	unsigned int hintNameSize = 0;
   1138 	unsigned int negHintsSize = 0;
   1139 	unsigned char *ptr;
   1140 	unsigned char *t;
   1141 
   1142 	*outbuf = GSS_C_NO_BUFFER;
   1143 
   1144 	if (cred != GSS_C_NO_CREDENTIAL) {
   1145 		major_status = gss_inquire_cred(minor_status,
   1146 						cred,
   1147 						&hintName,
   1148 						NULL,
   1149 						NULL,
   1150 						NULL);
   1151 		if (major_status != GSS_S_COMPLETE)
   1152 			return (major_status);
   1153 	}
   1154 
   1155 	if (hintName == GSS_C_NO_NAME) {
   1156 		krb5_error_code code;
   1157 		krb5int_access kaccess;
   1158 		char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
   1159 
   1160 		code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
   1161 		if (code != 0) {
   1162 			*minor_status = code;
   1163 			return (GSS_S_FAILURE);
   1164 		}
   1165 
   1166 		/* this breaks mutual authentication but Samba relies on it */
   1167 		code = (*kaccess.clean_hostname)(NULL, NULL,
   1168 						 &hostname[HOST_PREFIX_LEN],
   1169 						 MAXHOSTNAMELEN);
   1170 		if (code != 0) {
   1171 			*minor_status = code;
   1172 			return (GSS_S_FAILURE);
   1173 		}
   1174 
   1175 		hintNameBuf.value = hostname;
   1176 		hintNameBuf.length = strlen(hostname);
   1177 
   1178 		major_status = gss_import_name(minor_status,
   1179 					       &hintNameBuf,
   1180 					       GSS_C_NT_HOSTBASED_SERVICE,
   1181 					       &hintName);
   1182 		if (major_status != GSS_S_COMPLETE) {
   1183 			return (major_status);
   1184 		}
   1185 	}
   1186 
   1187 	hintNameBuf.value = NULL;
   1188 	hintNameBuf.length = 0;
   1189 
   1190 	major_status = gss_canonicalize_name(minor_status,
   1191 					     hintName,
   1192 					     (gss_OID)&gss_mech_krb5_oid,
   1193 					     &hintKerberosName);
   1194 	if (major_status != GSS_S_COMPLETE) {
   1195 		gss_release_name(&minor, &hintName);
   1196 		return (major_status);
   1197 	}
   1198 	gss_release_name(&minor, &hintName);
   1199 
   1200 	major_status = gss_display_name(minor_status,
   1201 					hintKerberosName,
   1202 					&hintNameBuf,
   1203 					&hintNameType);
   1204 	if (major_status != GSS_S_COMPLETE) {
   1205 		gss_release_name(&minor, &hintName);
   1206 		return (major_status);
   1207 	}
   1208 	gss_release_name(&minor, &hintKerberosName);
   1209 
   1210 	/*
   1211 	 * Now encode the name hint into a NegHints ASN.1 type
   1212 	 */
   1213 	major_status = GSS_S_FAILURE;
   1214 
   1215 	/* Length of DER encoded GeneralString */
   1216 	tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
   1217 		hintNameBuf.length;
   1218 	hintNameSize = tlen;
   1219 
   1220 	/* Length of DER encoded hintName */
   1221 	tlen += 1 + gssint_der_length_size(hintNameSize);
   1222 	negHintsSize = tlen;
   1223 
   1224 	t = (unsigned char *)malloc(tlen);
   1225 	if (t == NULL) {
   1226 		*minor_status = ENOMEM;
   1227 		goto errout;
   1228 	}
   1229 
   1230 	ptr = t;
   1231 
   1232 	*ptr++ = CONTEXT | 0x00; /* hintName identifier */
   1233 	if (gssint_put_der_length(hintNameSize,
   1234 				  &ptr, tlen - (int)(ptr-t)))
   1235 		goto errout;
   1236 
   1237 	*ptr++ = GENERAL_STRING;
   1238 	if (gssint_put_der_length(hintNameBuf.length,
   1239 				  &ptr, tlen - (int)(ptr-t)))
   1240 		goto errout;
   1241 
   1242 	memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
   1243 	ptr += hintNameBuf.length;
   1244 
   1245 	*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
   1246 	if (*outbuf == NULL) {
   1247 		*minor_status = ENOMEM;
   1248 		goto errout;
   1249 	}
   1250 	(*outbuf)->value = (void *)t;
   1251 	(*outbuf)->length = ptr - t;
   1252 
   1253 	t = NULL; /* don't free */
   1254 
   1255 	*minor_status = 0;
   1256 	major_status = GSS_S_COMPLETE;
   1257 
   1258 errout:
   1259 	if (t != NULL) {
   1260 		free(t);
   1261 	}
   1262 
   1263 	gss_release_buffer(&minor, &hintNameBuf);
   1264 	return (major_status);
   1265 }
   1266 
   1267 static OM_uint32
   1268 acc_ctx_hints(OM_uint32 *minor_status,
   1269 	      gss_ctx_id_t *ctx,
   1270 	      gss_cred_id_t cred,
   1271 	      gss_buffer_t *mechListMIC,
   1272 	      OM_uint32 *negState,
   1273 	      send_token_flag *return_token)
   1274 {
   1275 	OM_uint32 tmpmin, ret;
   1276 	gss_OID_set supported_mechSet;
   1277 	spnego_gss_ctx_id_t sc = NULL;
   1278 
   1279 	*mechListMIC = GSS_C_NO_BUFFER;
   1280 	supported_mechSet = GSS_C_NO_OID_SET;
   1281 	*return_token = ERROR_TOKEN_SEND;
   1282 	*negState = REJECT;
   1283 	*minor_status = 0;
   1284 
   1285 	*ctx = GSS_C_NO_CONTEXT;
   1286 	ret = GSS_S_DEFECTIVE_TOKEN;
   1287 
   1288 	if (cred != GSS_C_NO_CREDENTIAL) {
   1289 		ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
   1290 				       NULL, &supported_mechSet);
   1291 		if (ret != GSS_S_COMPLETE) {
   1292 			*return_token = NO_TOKEN_SEND;
   1293 			goto cleanup;
   1294 		}
   1295 	} else {
   1296 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
   1297 					  GSS_C_ACCEPT, NULL,
   1298 					  &supported_mechSet);
   1299 		if (ret != GSS_S_COMPLETE) {
   1300 			*return_token = NO_TOKEN_SEND;
   1301 			goto cleanup;
   1302 		}
   1303 	}
   1304 
   1305 	ret = make_NegHints(minor_status, cred, mechListMIC);
   1306 	if (ret != GSS_S_COMPLETE) {
   1307 		*return_token = NO_TOKEN_SEND;
   1308 		goto cleanup;
   1309 	}
   1310 
   1311 	/*
   1312 	 * Select the best match between the list of mechs
   1313 	 * that the initiator requested and the list that
   1314 	 * the acceptor will support.
   1315 	 */
   1316 	sc = create_spnego_ctx();
   1317 	if (sc == NULL) {
   1318 		ret = GSS_S_FAILURE;
   1319 		*return_token = NO_TOKEN_SEND;
   1320 		goto cleanup;
   1321 	}
   1322 	if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
   1323 		ret = GSS_S_FAILURE;
   1324 		*return_token = NO_TOKEN_SEND;
   1325 		goto cleanup;
   1326 	}
   1327 	sc->internal_mech = GSS_C_NO_OID;
   1328 
   1329 	*negState = ACCEPT_INCOMPLETE;
   1330 	*return_token = INIT_TOKEN_SEND;
   1331 	sc->firstpass = 1;
   1332 	*ctx = (gss_ctx_id_t)sc;
   1333 	ret = GSS_S_COMPLETE;
   1334 
   1335 cleanup:
   1336 	gss_release_oid_set(&tmpmin, &supported_mechSet);
   1337 	return ret;
   1338 }
   1339 
   1340 /*
   1341  * Set negState to REJECT if the token is defective, else
   1342  * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
   1343  * preferred mechanism is supported.
   1344  */
   1345 static OM_uint32
   1346 acc_ctx_new(OM_uint32 *minor_status,
   1347 	    gss_buffer_t buf,
   1348 	    gss_ctx_id_t *ctx,
   1349 	    gss_cred_id_t cred,
   1350 	    gss_buffer_t *mechToken,
   1351 	    gss_buffer_t *mechListMIC,
   1352 	    OM_uint32 *negState,
   1353 	    send_token_flag *return_token)
   1354 {
   1355 	OM_uint32 tmpmin, ret, req_flags;
   1356 	gss_OID_set supported_mechSet, mechTypes;
   1357 	gss_buffer_desc der_mechTypes;
   1358 	gss_OID mech_wanted;
   1359 	spnego_gss_ctx_id_t sc = NULL;
   1360 
   1361 	*ctx = GSS_C_NO_CONTEXT;
   1362 
   1363 	ret = GSS_S_DEFECTIVE_TOKEN;
   1364 	der_mechTypes.length = 0;
   1365 	der_mechTypes.value = NULL;
   1366 	*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
   1367 	supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
   1368 	*return_token = ERROR_TOKEN_SEND;
   1369 	*negState = REJECT;
   1370 	*minor_status = 0;
   1371 
   1372 	ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
   1373 			       &mechTypes, &req_flags,
   1374 			       mechToken, mechListMIC);
   1375 	if (ret != GSS_S_COMPLETE) {
   1376 		goto cleanup;
   1377 	}
   1378 	if (cred != GSS_C_NO_CREDENTIAL) {
   1379 		ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
   1380 				       NULL, &supported_mechSet);
   1381 		if (ret != GSS_S_COMPLETE) {
   1382 			*return_token = NO_TOKEN_SEND;
   1383 			goto cleanup;
   1384 		}
   1385 	} else {
   1386 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
   1387 					  GSS_C_ACCEPT, NULL,
   1388 					  &supported_mechSet);
   1389 		if (ret != GSS_S_COMPLETE) {
   1390 			*return_token = NO_TOKEN_SEND;
   1391 			goto cleanup;
   1392 		}
   1393 	}
   1394 	/*
   1395 	 * Select the best match between the list of mechs
   1396 	 * that the initiator requested and the list that
   1397 	 * the acceptor will support.
   1398 	 */
   1399 	mech_wanted = negotiate_mech_type(minor_status,
   1400 					  supported_mechSet,
   1401 					  mechTypes,
   1402 					  negState);
   1403 	if (*negState == REJECT) {
   1404 		ret = GSS_S_BAD_MECH;
   1405 		goto cleanup;
   1406 	}
   1407 	sc = (spnego_gss_ctx_id_t)*ctx;
   1408 	if (sc != NULL) {
   1409 		gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
   1410 		assert(mech_wanted != GSS_C_NO_OID);
   1411 	} else
   1412 		sc = create_spnego_ctx();
   1413 	if (sc == NULL) {
   1414 		ret = GSS_S_FAILURE;
   1415 		*return_token = NO_TOKEN_SEND;
   1416 		generic_gss_release_oid(&tmpmin, &mech_wanted);
   1417 		goto cleanup;
   1418 	}
   1419 	sc->internal_mech = mech_wanted;
   1420 	sc->DER_mechTypes = der_mechTypes;
   1421 	der_mechTypes.length = 0;
   1422 	der_mechTypes.value = NULL;
   1423 
   1424 	if (*negState == REQUEST_MIC)
   1425 		sc->mic_reqd = 1;
   1426 
   1427 	*return_token = INIT_TOKEN_SEND;
   1428 	sc->firstpass = 1;
   1429 	*ctx = (gss_ctx_id_t)sc;
   1430 	ret = GSS_S_COMPLETE;
   1431 cleanup:
   1432 	gss_release_oid_set(&tmpmin, &mechTypes);
   1433 	gss_release_oid_set(&tmpmin, &supported_mechSet);
   1434 	if (der_mechTypes.length != 0)
   1435 		gss_release_buffer(&tmpmin, &der_mechTypes);
   1436 	return ret;
   1437 }
   1438 
   1439 static OM_uint32
   1440 acc_ctx_cont(OM_uint32 *minstat,
   1441 	     gss_buffer_t buf,
   1442 	     gss_ctx_id_t *ctx,
   1443 	     gss_buffer_t *responseToken,
   1444 	     gss_buffer_t *mechListMIC,
   1445 	     OM_uint32 *negState,
   1446 	     send_token_flag *return_token)
   1447 {
   1448 	OM_uint32 ret, tmpmin;
   1449 	gss_OID supportedMech;
   1450 	spnego_gss_ctx_id_t sc;
   1451 	unsigned int len;
   1452 	unsigned char *ptr, *bufstart;
   1453 
   1454 	sc = (spnego_gss_ctx_id_t)*ctx;
   1455 	ret = GSS_S_DEFECTIVE_TOKEN;
   1456 	*negState = REJECT;
   1457 	*minstat = 0;
   1458 	supportedMech = GSS_C_NO_OID;
   1459 	*return_token = ERROR_TOKEN_SEND;
   1460 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
   1461 
   1462 	ptr = bufstart = buf->value;
   1463 #define REMAIN (buf->length - (ptr - bufstart))
   1464 	if (REMAIN > INT_MAX)
   1465 		return GSS_S_DEFECTIVE_TOKEN;
   1466 
   1467 	/*
   1468 	 * Attempt to work with old Sun SPNEGO.
   1469 	 */
   1470 	if (*ptr == HEADER_ID) {
   1471 		ret = g_verify_token_header(gss_mech_spnego,
   1472 					    &len, &ptr, 0, REMAIN);
   1473 		if (ret) {
   1474 			*minstat = ret;
   1475 			return GSS_S_DEFECTIVE_TOKEN;
   1476 		}
   1477 	}
   1478 	if (*ptr != (CONTEXT | 0x01)) {
   1479 		return GSS_S_DEFECTIVE_TOKEN;
   1480 	}
   1481 	ret = get_negTokenResp(minstat, ptr, REMAIN,
   1482 			       negState, &supportedMech,
   1483 			       responseToken, mechListMIC);
   1484 	if (ret != GSS_S_COMPLETE)
   1485 		goto cleanup;
   1486 
   1487 	if (*responseToken == GSS_C_NO_BUFFER &&
   1488 	    *mechListMIC == GSS_C_NO_BUFFER) {
   1489 
   1490 		ret = GSS_S_DEFECTIVE_TOKEN;
   1491 		goto cleanup;
   1492 	}
   1493 	if (supportedMech != GSS_C_NO_OID) {
   1494 		ret = GSS_S_DEFECTIVE_TOKEN;
   1495 		goto cleanup;
   1496 	}
   1497 	sc->firstpass = 0;
   1498 	*negState = ACCEPT_INCOMPLETE;
   1499 	*return_token = CONT_TOKEN_SEND;
   1500 cleanup:
   1501 	if (supportedMech != GSS_C_NO_OID) {
   1502 		generic_gss_release_oid(&tmpmin, &supportedMech);
   1503 	}
   1504 	return ret;
   1505 #undef REMAIN
   1506 }
   1507 
   1508 /*
   1509  * Verify that mech OID is either exactly the same as the negotiated
   1510  * mech OID, or is a mech OID supported by the negotiated mech.  MS
   1511  * implementations can list a most preferred mech using an incorrect
   1512  * krb5 OID while emitting a krb5 initiator mech token having the
   1513  * correct krb5 mech OID.
   1514  */
   1515 static OM_uint32
   1516 acc_ctx_vfy_oid(OM_uint32 *minor_status,
   1517 		spnego_gss_ctx_id_t sc, gss_OID mechoid,
   1518 		OM_uint32 *negState, send_token_flag *tokflag)
   1519 {
   1520 	OM_uint32 ret, tmpmin;
   1521 	gss_mechanism mech = NULL;
   1522 	gss_OID_set mech_set = GSS_C_NO_OID_SET;
   1523 	int present = 0;
   1524 
   1525 	if (g_OID_equal(sc->internal_mech, mechoid))
   1526 		return GSS_S_COMPLETE;
   1527 
   1528 	/*
   1529 	 * SUNW17PACresync
   1530 	 * If both mechs are kerb, we are done.
   1531 	 */
   1532 	if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
   1533 		return GSS_S_COMPLETE;
   1534 	}
   1535 
   1536 	mech = gssint_get_mechanism(mechoid);
   1537 	if (mech == NULL || mech->gss_indicate_mechs == NULL) {
   1538 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
   1539 		map_errcode(minor_status);
   1540 		*negState = REJECT;
   1541 		*tokflag = ERROR_TOKEN_SEND;
   1542 		return GSS_S_BAD_MECH;
   1543 	}
   1544 	ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
   1545 	if (ret != GSS_S_COMPLETE) {
   1546 		*tokflag = NO_TOKEN_SEND;
   1547 		map_error(minor_status, mech);
   1548 		goto cleanup;
   1549 	}
   1550 	ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
   1551 				      mech_set, &present);
   1552 	if (ret != GSS_S_COMPLETE)
   1553 		goto cleanup;
   1554 	if (!present) {
   1555 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
   1556 		map_errcode(minor_status);
   1557 		*negState = REJECT;
   1558 		*tokflag = ERROR_TOKEN_SEND;
   1559 		ret = GSS_S_BAD_MECH;
   1560 	}
   1561 cleanup:
   1562 	gss_release_oid_set(&tmpmin, &mech_set);
   1563 	return ret;
   1564 }
   1565 #ifndef LEAN_CLIENT
   1566 /*
   1567  * Wrap call to gss_accept_sec_context() and update state
   1568  * accordingly.
   1569  */
   1570 static OM_uint32
   1571 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
   1572 		 gss_cred_id_t cred, gss_buffer_t mechtok_in,
   1573 		 gss_OID *mech_type, gss_buffer_t mechtok_out,
   1574 		 OM_uint32 *ret_flags, OM_uint32 *time_rec,
   1575 		 gss_cred_id_t *delegated_cred_handle,
   1576 		 OM_uint32 *negState, send_token_flag *tokflag)
   1577 {
   1578 	OM_uint32 ret;
   1579 	gss_OID_desc mechoid;
   1580 
   1581 	if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
   1582 		/*
   1583 		 * mechoid is an alias; don't free it.
   1584 		 */
   1585 		ret = gssint_get_mech_type(&mechoid, mechtok_in);
   1586 		if (ret != GSS_S_COMPLETE) {
   1587 			*tokflag = NO_TOKEN_SEND;
   1588 			return ret;
   1589 		}
   1590 		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
   1591 				      negState, tokflag);
   1592 		if (ret != GSS_S_COMPLETE)
   1593 			return ret;
   1594 	}
   1595 
   1596 	ret = gss_accept_sec_context(minor_status,
   1597 				     &sc->ctx_handle,
   1598 				     cred,
   1599 				     mechtok_in,
   1600 				     GSS_C_NO_CHANNEL_BINDINGS,
   1601 				     &sc->internal_name,
   1602 				     mech_type,
   1603 				     mechtok_out,
   1604 				     &sc->ctx_flags,
   1605 				     time_rec,
   1606 				     delegated_cred_handle);
   1607 
   1608 	if (ret == GSS_S_COMPLETE) {
   1609 #ifdef MS_BUG_TEST
   1610 		/*
   1611 		 * Force MIC to be not required even if we previously
   1612 		 * requested a MIC.
   1613 		 */
   1614 		char *envstr = getenv("MS_FORCE_NO_MIC");
   1615 
   1616 		if (envstr != NULL && strcmp(envstr, "1") == 0 &&
   1617 		    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
   1618 		    sc->mic_reqd) {
   1619 
   1620 			sc->mic_reqd = 0;
   1621 		}
   1622 #endif
   1623 		sc->mech_complete = 1;
   1624 		if (ret_flags != NULL)
   1625 			*ret_flags = sc->ctx_flags;
   1626 
   1627 		if (!sc->mic_reqd) {
   1628 			*negState = ACCEPT_COMPLETE;
   1629 			ret = GSS_S_COMPLETE;
   1630 		} else {
   1631 			ret = GSS_S_CONTINUE_NEEDED;
   1632 		}
   1633 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
   1634 		*negState = REJECT;
   1635 		*tokflag = ERROR_TOKEN_SEND;
   1636 	}
   1637 	return ret;
   1638 }
   1639 
   1640 /*ARGSUSED*/
   1641 OM_uint32
   1642 glue_spnego_gss_accept_sec_context(
   1643 			    void *context,
   1644 			    OM_uint32 *minor_status,
   1645 			    gss_ctx_id_t *context_handle,
   1646 			    gss_cred_id_t verifier_cred_handle,
   1647 			    gss_buffer_t input_token,
   1648 			    gss_channel_bindings_t input_chan_bindings,
   1649 			    gss_name_t *src_name,
   1650 			    gss_OID *mech_type,
   1651 			    gss_buffer_t output_token,
   1652 			    OM_uint32 *ret_flags,
   1653 			    OM_uint32 *time_rec,
   1654 			    gss_cred_id_t *delegated_cred_handle)
   1655 {
   1656 	return(spnego_gss_accept_sec_context(
   1657 		    minor_status,
   1658 		    context_handle,
   1659 		    verifier_cred_handle,
   1660 		    input_token,
   1661 		    input_chan_bindings,
   1662 		    src_name,
   1663 		    mech_type,
   1664 		    output_token,
   1665 		    ret_flags,
   1666 		    time_rec,
   1667 		    delegated_cred_handle));
   1668 }
   1669 
   1670 /*ARGSUSED*/
   1671 OM_uint32
   1672 spnego_gss_accept_sec_context(
   1673 			    OM_uint32 *minor_status,
   1674 			    gss_ctx_id_t *context_handle,
   1675 			    gss_cred_id_t verifier_cred_handle,
   1676 			    gss_buffer_t input_token,
   1677 			    gss_channel_bindings_t input_chan_bindings,
   1678 			    gss_name_t *src_name,
   1679 			    gss_OID *mech_type,
   1680 			    gss_buffer_t output_token,
   1681 			    OM_uint32 *ret_flags,
   1682 			    OM_uint32 *time_rec,
   1683 			    gss_cred_id_t *delegated_cred_handle)
   1684 {
   1685 	OM_uint32 ret, tmpmin, negState;
   1686 	send_token_flag return_token;
   1687 	gss_buffer_t mechtok_in, mic_in, mic_out;
   1688 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
   1689 	spnego_gss_ctx_id_t sc = NULL;
   1690 	OM_uint32 mechstat = GSS_S_FAILURE;
   1691 	int sendTokenInit = 0;
   1692 
   1693 	mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
   1694 
   1695 	if (minor_status != NULL)
   1696 		*minor_status = 0;
   1697 	if (output_token != GSS_C_NO_BUFFER) {
   1698 		output_token->length = 0;
   1699 		output_token->value = NULL;
   1700 	}
   1701 
   1702 
   1703 	if (minor_status == NULL ||
   1704 	    output_token == GSS_C_NO_BUFFER ||
   1705 	    context_handle == NULL) {
   1706 		return GSS_S_CALL_INACCESSIBLE_WRITE;
   1707 	}
   1708 
   1709 	if (input_token == GSS_C_NO_BUFFER) {
   1710 		return GSS_S_CALL_INACCESSIBLE_READ;
   1711 	}
   1712 
   1713 	sc = (spnego_gss_ctx_id_t)*context_handle;
   1714 	if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
   1715 		if (src_name != NULL)
   1716 			*src_name = GSS_C_NO_NAME;
   1717 		if (mech_type != NULL)
   1718 			*mech_type = GSS_C_NO_OID;
   1719 		if (time_rec != NULL)
   1720 			*time_rec = 0;
   1721 		if (ret_flags != NULL)
   1722 			*ret_flags = 0;
   1723 		if (delegated_cred_handle != NULL)
   1724 			*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
   1725 		if (input_token->length == 0) {
   1726 			sendTokenInit = 1;
   1727 			ret = acc_ctx_hints(minor_status,
   1728 					    context_handle,
   1729 					    verifier_cred_handle,
   1730 					    &mic_out,
   1731 					    &negState,
   1732 					    &return_token);
   1733 			if (ret != GSS_S_COMPLETE)
   1734 				goto cleanup;
   1735 			ret = GSS_S_CONTINUE_NEEDED;
   1736 		} else {
   1737 			/* Can set negState to REQUEST_MIC */
   1738 			ret = acc_ctx_new(minor_status, input_token,
   1739 					  context_handle, verifier_cred_handle,
   1740 					  &mechtok_in, &mic_in,
   1741 					  &negState, &return_token);
   1742 			if (ret != GSS_S_COMPLETE)
   1743 				goto cleanup;
   1744 			ret = GSS_S_CONTINUE_NEEDED;
   1745 		}
   1746 	} else {
   1747 		/* Can set negState to ACCEPT_INCOMPLETE */
   1748 		ret = acc_ctx_cont(minor_status, input_token,
   1749 				   context_handle, &mechtok_in,
   1750 				   &mic_in, &negState, &return_token);
   1751 		if (ret != GSS_S_COMPLETE)
   1752 			goto cleanup;
   1753 		ret = GSS_S_CONTINUE_NEEDED;
   1754 	}
   1755 
   1756 	sc = (spnego_gss_ctx_id_t)*context_handle;
   1757 	/*
   1758 	 * Handle mechtok_in and mic_in only if they are
   1759 	 * present in input_token.  If neither is present, whether
   1760 	 * this is an error depends on whether this is the first
   1761 	 * round-trip.  RET is set to a default value according to
   1762 	 * whether it is the first round-trip.
   1763 	 */
   1764 	mechstat = GSS_S_FAILURE;
   1765 	if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
   1766 		ret = acc_ctx_call_acc(minor_status, sc,
   1767 				       verifier_cred_handle, mechtok_in,
   1768 				       mech_type, &mechtok_out,
   1769 				       ret_flags, time_rec,
   1770 				       delegated_cred_handle,
   1771 				       &negState, &return_token);
   1772 	} else if (negState == REQUEST_MIC) {
   1773 		mechstat = GSS_S_CONTINUE_NEEDED;
   1774 	}
   1775 
   1776 	if (!HARD_ERROR(ret) && sc->mech_complete &&
   1777 	    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
   1778 
   1779 		ret = handle_mic(minor_status, mic_in,
   1780 				 (mechtok_out.length != 0),
   1781 				 sc, &mic_out,
   1782 				 &negState, &return_token);
   1783 	}
   1784 
   1785 cleanup:
   1786 	if (return_token != NO_TOKEN_SEND && return_token != CHECK_MIC) {
   1787 		/* For acceptor-sends-first send a tokenInit */
   1788 		int tmpret;
   1789 
   1790 		assert(sc != NULL);
   1791 
   1792 		if (sendTokenInit) {
   1793 			tmpret = make_spnego_tokenInit_msg(sc,
   1794 							   1,
   1795 							   mic_out,
   1796 							   0,
   1797 							   GSS_C_NO_BUFFER,
   1798 							   return_token,
   1799 							   output_token);
   1800 		} else {
   1801 			tmpret = make_spnego_tokenTarg_msg(negState, sc->internal_mech,
   1802 							   &mechtok_out, mic_out,
   1803 							   return_token,
   1804 							   output_token);
   1805 		}
   1806 		if (tmpret < 0)
   1807 			ret = GSS_S_FAILURE;
   1808 	}
   1809 	if (ret == GSS_S_COMPLETE) {
   1810 		*context_handle = (gss_ctx_id_t)sc->ctx_handle;
   1811 		if (sc->internal_name != GSS_C_NO_NAME &&
   1812 		    src_name != NULL) {
   1813 			*src_name = sc->internal_name;
   1814 		}
   1815 		release_spnego_ctx(&sc);
   1816 	}
   1817 	gss_release_buffer(&tmpmin, &mechtok_out);
   1818 	if (mechtok_in != GSS_C_NO_BUFFER) {
   1819 		gss_release_buffer(&tmpmin, mechtok_in);
   1820 		free(mechtok_in);
   1821 	}
   1822 	if (mic_in != GSS_C_NO_BUFFER) {
   1823 		gss_release_buffer(&tmpmin, mic_in);
   1824 		free(mic_in);
   1825 	}
   1826 	if (mic_out != GSS_C_NO_BUFFER) {
   1827 		gss_release_buffer(&tmpmin, mic_out);
   1828 		free(mic_out);
   1829 	}
   1830 	return ret;
   1831 }
   1832 #endif /*  LEAN_CLIENT */
   1833 
   1834 /*ARGSUSED*/
   1835 OM_uint32
   1836 glue_spnego_gss_display_status(
   1837 	void *context,
   1838 		OM_uint32 *minor_status,
   1839 		OM_uint32 status_value,
   1840 		int status_type,
   1841 		gss_OID mech_type,
   1842 		OM_uint32 *message_context,
   1843 		gss_buffer_t status_string)
   1844 {
   1845 	return (spnego_gss_display_status(minor_status,
   1846 					status_value,
   1847 					status_type,
   1848 					mech_type,
   1849 					message_context,
   1850 					status_string));
   1851 }
   1852 
   1853 /*ARGSUSED*/
   1854 OM_uint32
   1855 spnego_gss_display_status(
   1856 		OM_uint32 *minor_status,
   1857 		OM_uint32 status_value,
   1858 		int status_type,
   1859 		gss_OID mech_type,
   1860 		OM_uint32 *message_context,
   1861 		gss_buffer_t status_string)
   1862 {
   1863 	dsyslog("Entering display_status\n");
   1864 
   1865 	*message_context = 0;
   1866 	switch (status_value) {
   1867 	    case ERR_SPNEGO_NO_MECHS_AVAILABLE:
   1868 		/* CSTYLED */
   1869 		*status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
   1870 		break;
   1871 	    case ERR_SPNEGO_NO_CREDS_ACQUIRED:
   1872 		/* CSTYLED */
   1873 		*status_string = make_err_msg("SPNEGO failed to acquire creds");
   1874 		break;
   1875 	    case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
   1876 		/* CSTYLED */
   1877 		*status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
   1878 		break;
   1879 	    case ERR_SPNEGO_NEGOTIATION_FAILED:
   1880 		/* CSTYLED */
   1881 		*status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
   1882 		break;
   1883 	    case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
   1884 		/* CSTYLED */
   1885 		*status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
   1886 		break;
   1887 	    default:
   1888 		status_string->length = 0;
   1889 		status_string->value = "";
   1890 		break;
   1891 	}
   1892 
   1893 	dsyslog("Leaving display_status\n");
   1894 	return (GSS_S_COMPLETE);
   1895 }
   1896 
   1897 /*ARGSUSED*/
   1898 OM_uint32
   1899 glue_spnego_gss_import_name(
   1900 	void *context,
   1901 		    OM_uint32 *minor_status,
   1902 		    gss_buffer_t input_name_buffer,
   1903 		    gss_OID input_name_type,
   1904 		    gss_name_t *output_name)
   1905 {
   1906 	return(spnego_gss_import_name(minor_status,
   1907 				    input_name_buffer,
   1908 				    input_name_type,
   1909 				    output_name));
   1910 }
   1911 
   1912 /*ARGSUSED*/
   1913 OM_uint32
   1914 spnego_gss_import_name(
   1915 		    OM_uint32 *minor_status,
   1916 		    gss_buffer_t input_name_buffer,
   1917 		    gss_OID input_name_type,
   1918 		    gss_name_t *output_name)
   1919 {
   1920 	OM_uint32 status;
   1921 
   1922 	dsyslog("Entering import_name\n");
   1923 
   1924 	status = gss_import_name(minor_status, input_name_buffer,
   1925 			input_name_type, output_name);
   1926 
   1927 	dsyslog("Leaving import_name\n");
   1928 	return (status);
   1929 }
   1930 
   1931 /*ARGSUSED*/
   1932 OM_uint32
   1933 glue_spnego_gss_release_name(
   1934 	void *context,
   1935 			OM_uint32 *minor_status,
   1936 			gss_name_t *input_name)
   1937 {
   1938 	return(spnego_gss_release_name(minor_status, input_name));
   1939 }
   1940 
   1941 /*ARGSUSED*/
   1942 OM_uint32
   1943 spnego_gss_release_name(
   1944 			OM_uint32 *minor_status,
   1945 			gss_name_t *input_name)
   1946 {
   1947 	OM_uint32 status;
   1948 
   1949 	dsyslog("Entering release_name\n");
   1950 
   1951 	status = gss_release_name(minor_status, input_name);
   1952 
   1953 	dsyslog("Leaving release_name\n");
   1954 	return (status);
   1955 }
   1956 
   1957 /*ARGSUSED*/
   1958 OM_uint32
   1959 glue_spnego_gss_compare_name(
   1960 	void *context,
   1961 	OM_uint32 *minor_status,
   1962 	const gss_name_t name1,
   1963 	const gss_name_t name2,
   1964 	int *name_equal)
   1965 {
   1966 	return(spnego_gss_compare_name(minor_status,
   1967 				name1,
   1968 				name2,
   1969 				name_equal));
   1970 }
   1971 /*ARGSUSED*/
   1972 OM_uint32
   1973 spnego_gss_compare_name(
   1974 			OM_uint32 *minor_status,
   1975 			const gss_name_t name1,
   1976 			const gss_name_t name2,
   1977 			int *name_equal)
   1978 {
   1979 	OM_uint32 status = GSS_S_COMPLETE;
   1980 	dsyslog("Entering compare_name\n");
   1981 
   1982 	status = gss_compare_name(minor_status, name1, name2, name_equal);
   1983 
   1984 	dsyslog("Leaving compare_name\n");
   1985 	return (status);
   1986 }
   1987 
   1988 /*ARGSUSED*/
   1989 OM_uint32
   1990 glue_spnego_gss_display_name(
   1991  	void *context,
   1992 			OM_uint32 *minor_status,
   1993 			gss_name_t input_name,
   1994 			gss_buffer_t output_name_buffer,
   1995 			gss_OID *output_name_type)
   1996 {
   1997 	return(spnego_gss_display_name(
   1998 		    minor_status,
   1999 		    input_name,
   2000 		    output_name_buffer,
   2001 		    output_name_type));
   2002 }
   2003 
   2004 /*ARGSUSED*/
   2005 OM_uint32
   2006 spnego_gss_display_name(
   2007 			OM_uint32 *minor_status,
   2008 			gss_name_t input_name,
   2009 			gss_buffer_t output_name_buffer,
   2010 			gss_OID *output_name_type)
   2011 {
   2012 	OM_uint32 status = GSS_S_COMPLETE;
   2013 	dsyslog("Entering display_name\n");
   2014 
   2015 	status = gss_display_name(minor_status, input_name,
   2016 			output_name_buffer, output_name_type);
   2017 
   2018 	dsyslog("Leaving display_name\n");
   2019 	return (status);
   2020 }
   2021 
   2022 
   2023 /*ARGSUSED*/
   2024 OM_uint32
   2025 glue_spnego_gss_inquire_names_for_mech(
   2026 	void		*context,
   2027 	OM_uint32	*minor_status,
   2028 	gss_OID		mechanism,
   2029 	gss_OID_set	*name_types)
   2030 {
   2031 	return(spnego_gss_inquire_names_for_mech(minor_status,
   2032 						mechanism,
   2033 						name_types));
   2034 }
   2035 /*ARGSUSED*/
   2036 OM_uint32
   2037 spnego_gss_inquire_names_for_mech(
   2038 				OM_uint32	*minor_status,
   2039 				gss_OID		mechanism,
   2040 				gss_OID_set	*name_types)
   2041 {
   2042 	OM_uint32   major, minor;
   2043 
   2044 	dsyslog("Entering inquire_names_for_mech\n");
   2045 	/*
   2046 	 * We only know how to handle our own mechanism.
   2047 	 */
   2048 	if ((mechanism != GSS_C_NULL_OID) &&
   2049 	    !g_OID_equal(gss_mech_spnego, mechanism)) {
   2050 		*minor_status = 0;
   2051 		return (GSS_S_FAILURE);
   2052 	}
   2053 
   2054 	major = gss_create_empty_oid_set(minor_status, name_types);
   2055 	if (major == GSS_S_COMPLETE) {
   2056 		/* Now add our members. */
   2057 		if (((major = gss_add_oid_set_member(minor_status,
   2058 				(gss_OID) GSS_C_NT_USER_NAME,
   2059 				name_types)) == GSS_S_COMPLETE) &&
   2060 		    ((major = gss_add_oid_set_member(minor_status,
   2061 				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
   2062 				name_types)) == GSS_S_COMPLETE) &&
   2063 		    ((major = gss_add_oid_set_member(minor_status,
   2064 				(gss_OID) GSS_C_NT_STRING_UID_NAME,
   2065 				name_types)) == GSS_S_COMPLETE)) {
   2066 			major = gss_add_oid_set_member(minor_status,
   2067 				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
   2068 				name_types);
   2069 		}
   2070 
   2071 		if (major != GSS_S_COMPLETE)
   2072 			(void) gss_release_oid_set(&minor, name_types);
   2073 	}
   2074 
   2075 	dsyslog("Leaving inquire_names_for_mech\n");
   2076 	return (major);
   2077 }
   2078 
   2079 OM_uint32
   2080 spnego_gss_unwrap(
   2081 		OM_uint32 *minor_status,
   2082 		gss_ctx_id_t context_handle,
   2083 		gss_buffer_t input_message_buffer,
   2084 		gss_buffer_t output_message_buffer,
   2085 		int *conf_state,
   2086 		gss_qop_t *qop_state)
   2087 {
   2088 	OM_uint32 ret;
   2089 	ret = gss_unwrap(minor_status,
   2090 			context_handle,
   2091 			input_message_buffer,
   2092 			output_message_buffer,
   2093 			conf_state,
   2094 			qop_state);
   2095 
   2096 	return (ret);
   2097 }
   2098 
   2099 OM_uint32
   2100 spnego_gss_wrap(
   2101 		OM_uint32 *minor_status,
   2102 		gss_ctx_id_t context_handle,
   2103 		int conf_req_flag,
   2104 		gss_qop_t qop_req,
   2105 		gss_buffer_t input_message_buffer,
   2106 		int *conf_state,
   2107 		gss_buffer_t output_message_buffer)
   2108 {
   2109 	OM_uint32 ret;
   2110 	ret = gss_wrap(minor_status,
   2111 		    context_handle,
   2112 		    conf_req_flag,
   2113 		    qop_req,
   2114 		    input_message_buffer,
   2115 		    conf_state,
   2116 		    output_message_buffer);
   2117 
   2118 	return (ret);
   2119 }
   2120 
   2121 OM_uint32
   2122 spnego_gss_process_context_token(
   2123 				OM_uint32	*minor_status,
   2124 				const gss_ctx_id_t context_handle,
   2125 				const gss_buffer_t token_buffer)
   2126 {
   2127 	OM_uint32 ret;
   2128 	ret = gss_process_context_token(minor_status,
   2129 					context_handle,
   2130 					token_buffer);
   2131 
   2132 	return (ret);
   2133 }
   2134 
   2135 OM_uint32
   2136 glue_spnego_gss_delete_sec_context(
   2137 	void *context,
   2138 			    OM_uint32 *minor_status,
   2139 			    gss_ctx_id_t *context_handle,
   2140 			    gss_buffer_t output_token)
   2141 {
   2142 	return(spnego_gss_delete_sec_context(minor_status,
   2143 					    context_handle, output_token));
   2144 }
   2145 
   2146 OM_uint32
   2147 spnego_gss_delete_sec_context(
   2148 			    OM_uint32 *minor_status,
   2149 			    gss_ctx_id_t *context_handle,
   2150 			    gss_buffer_t output_token)
   2151 {
   2152 	OM_uint32 ret = GSS_S_COMPLETE;
   2153 	spnego_gss_ctx_id_t *ctx =
   2154 		    (spnego_gss_ctx_id_t *)context_handle;
   2155 
   2156 	if (context_handle == NULL)
   2157 		return (GSS_S_FAILURE);
   2158 
   2159 	/*
   2160 	 * If this is still an SPNEGO mech, release it locally.
   2161 	 */
   2162 	if (*ctx != NULL &&
   2163 	    (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
   2164 		(void) release_spnego_ctx(ctx);
   2165                 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
   2166 		if (output_token) {
   2167 			output_token->length = 0;
   2168 			output_token->value = NULL;
   2169 		}
   2170 	} else {
   2171 		ret = gss_delete_sec_context(minor_status,
   2172 				    context_handle,
   2173 				    output_token);
   2174 	}
   2175 
   2176 	return (ret);
   2177 }
   2178 
   2179 OM_uint32
   2180 glue_spnego_gss_context_time(
   2181 	void *context,
   2182 	OM_uint32	*minor_status,
   2183 	const gss_ctx_id_t context_handle,
   2184 	OM_uint32	*time_rec)
   2185 {
   2186 	return(spnego_gss_context_time(minor_status,
   2187 				    context_handle,
   2188 				    time_rec));
   2189 }
   2190 
   2191 OM_uint32
   2192 spnego_gss_context_time(
   2193 			OM_uint32	*minor_status,
   2194 			const gss_ctx_id_t context_handle,
   2195 			OM_uint32	*time_rec)
   2196 {
   2197 	OM_uint32 ret;
   2198 	ret = gss_context_time(minor_status,
   2199 			    context_handle,
   2200 			    time_rec);
   2201 	return (ret);
   2202 }
   2203 
   2204 #ifndef LEAN_CLIENT
   2205 OM_uint32
   2206 glue_spnego_gss_export_sec_context(
   2207 	void *context,
   2208 	OM_uint32	  *minor_status,
   2209 	gss_ctx_id_t *context_handle,
   2210 	gss_buffer_t interprocess_token)
   2211 {
   2212 	return(spnego_gss_export_sec_context(minor_status,
   2213 				    context_handle,
   2214 				    interprocess_token));
   2215 }
   2216 OM_uint32
   2217 spnego_gss_export_sec_context(
   2218 			    OM_uint32	  *minor_status,
   2219 			    gss_ctx_id_t *context_handle,
   2220 			    gss_buffer_t interprocess_token)
   2221 {
   2222 	OM_uint32 ret;
   2223 	ret = gss_export_sec_context(minor_status,
   2224 				    context_handle,
   2225 				    interprocess_token);
   2226 	return (ret);
   2227 }
   2228 
   2229 OM_uint32
   2230 glue_spnego_gss_import_sec_context(
   2231 	void *context,
   2232 	OM_uint32		*minor_status,
   2233 	const gss_buffer_t	interprocess_token,
   2234 	gss_ctx_id_t		*context_handle)
   2235 {
   2236 	return(spnego_gss_import_sec_context(minor_status,
   2237 				    interprocess_token,
   2238 				    context_handle));
   2239 }
   2240 OM_uint32
   2241 spnego_gss_import_sec_context(
   2242 	OM_uint32		*minor_status,
   2243 	const gss_buffer_t	interprocess_token,
   2244 	gss_ctx_id_t		*context_handle)
   2245 {
   2246 	OM_uint32 ret;
   2247 	ret = gss_import_sec_context(minor_status,
   2248 				    interprocess_token,
   2249 				    context_handle);
   2250 	return (ret);
   2251 }
   2252 #endif /* LEAN_CLIENT */
   2253 
   2254 OM_uint32
   2255 glue_spnego_gss_inquire_context(
   2256 	void *context,
   2257 			OM_uint32	*minor_status,
   2258 			const gss_ctx_id_t context_handle,
   2259 			gss_name_t	*src_name,
   2260 			gss_name_t	*targ_name,
   2261 			OM_uint32	*lifetime_rec,
   2262 			gss_OID		*mech_type,
   2263 			OM_uint32	*ctx_flags,
   2264 			int		*locally_initiated,
   2265 			int		*opened)
   2266 {
   2267 	return(spnego_gss_inquire_context(
   2268 		    minor_status,
   2269 		    context_handle,
   2270 		    src_name,
   2271 		    targ_name,
   2272 		    lifetime_rec,
   2273 		    mech_type,
   2274 		    ctx_flags,
   2275 		    locally_initiated,
   2276 		    opened));
   2277 }
   2278 
   2279 OM_uint32
   2280 spnego_gss_inquire_context(
   2281 			OM_uint32	*minor_status,
   2282 			const gss_ctx_id_t context_handle,
   2283 			gss_name_t	*src_name,
   2284 			gss_name_t	*targ_name,
   2285 			OM_uint32	*lifetime_rec,
   2286 			gss_OID		*mech_type,
   2287 			OM_uint32	*ctx_flags,
   2288 			int		*locally_initiated,
   2289 			int		*opened)
   2290 {
   2291 	OM_uint32 ret = GSS_S_COMPLETE;
   2292 
   2293 	ret = gss_inquire_context(minor_status,
   2294 				context_handle,
   2295 				src_name,
   2296 				targ_name,
   2297 				lifetime_rec,
   2298 				mech_type,
   2299 				ctx_flags,
   2300 				locally_initiated,
   2301 				opened);
   2302 
   2303 	return (ret);
   2304 }
   2305 
   2306 OM_uint32
   2307 glue_spnego_gss_wrap_size_limit(
   2308 	void *context,
   2309 	OM_uint32	*minor_status,
   2310 	const gss_ctx_id_t context_handle,
   2311 	int		conf_req_flag,
   2312 	gss_qop_t	qop_req,
   2313 	OM_uint32	req_output_size,
   2314 	OM_uint32	*max_input_size)
   2315 {
   2316 	return(spnego_gss_wrap_size_limit(minor_status,
   2317 				context_handle,
   2318 				conf_req_flag,
   2319 				qop_req,
   2320 				req_output_size,
   2321 				max_input_size));
   2322 }
   2323 
   2324 OM_uint32
   2325 spnego_gss_wrap_size_limit(
   2326 	OM_uint32	*minor_status,
   2327 	const gss_ctx_id_t context_handle,
   2328 	int		conf_req_flag,
   2329 	gss_qop_t	qop_req,
   2330 	OM_uint32	req_output_size,
   2331 	OM_uint32	*max_input_size)
   2332 {
   2333 	OM_uint32 ret;
   2334 	ret = gss_wrap_size_limit(minor_status,
   2335 				context_handle,
   2336 				conf_req_flag,
   2337 				qop_req,
   2338 				req_output_size,
   2339 				max_input_size);
   2340 	return (ret);
   2341 }
   2342 
   2343 #if 0 /* SUNW17PACresync */
   2344 OM_uint32
   2345 spnego_gss_get_mic(
   2346 		OM_uint32 *minor_status,
   2347 		const gss_ctx_id_t context_handle,
   2348 		gss_qop_t  qop_req,
   2349 		const gss_buffer_t message_buffer,
   2350 		gss_buffer_t message_token)
   2351 {
   2352 	OM_uint32 ret;
   2353 	ret = gss_get_mic(minor_status,
   2354 		    context_handle,
   2355 		    qop_req,
   2356 		    message_buffer,
   2357 		    message_token);
   2358 	return (ret);
   2359 }
   2360 #endif
   2361 
   2362 OM_uint32
   2363 spnego_gss_verify_mic(
   2364 		OM_uint32 *minor_status,
   2365 		const gss_ctx_id_t context_handle,
   2366 		const gss_buffer_t msg_buffer,
   2367 		const gss_buffer_t token_buffer,
   2368 		gss_qop_t *qop_state)
   2369 {
   2370 	OM_uint32 ret;
   2371 	ret = gss_verify_mic(minor_status,
   2372 			    context_handle,
   2373 			    msg_buffer,
   2374 			    token_buffer,
   2375 			    qop_state);
   2376 	return (ret);
   2377 }
   2378 
   2379 OM_uint32
   2380 spnego_gss_inquire_sec_context_by_oid(
   2381 		OM_uint32 *minor_status,
   2382 		const gss_ctx_id_t context_handle,
   2383 		const gss_OID desired_object,
   2384 		gss_buffer_set_t *data_set)
   2385 {
   2386 	OM_uint32 ret;
   2387 	ret = gss_inquire_sec_context_by_oid(minor_status,
   2388 			    context_handle,
   2389 			    desired_object,
   2390 			    data_set);
   2391 	return (ret);
   2392 }
   2393 
   2394 /*
   2395  * SUNW17PACresync
   2396  * These GSS funcs not needed yet, so disable them.
   2397  * Revisit for full 1.7 resync.
   2398  */
   2399 #if 0
   2400 OM_uint32
   2401 spnego_gss_set_sec_context_option(
   2402 		OM_uint32 *minor_status,
   2403 		gss_ctx_id_t *context_handle,
   2404 		const gss_OID desired_object,
   2405 		const gss_buffer_t value)
   2406 {
   2407 	OM_uint32 ret;
   2408 	ret = gss_set_sec_context_option(minor_status,
   2409 			    context_handle,
   2410 			    desired_object,
   2411 			    value);
   2412 	return (ret);
   2413 }
   2414 
   2415 OM_uint32
   2416 spnego_gss_wrap_aead(OM_uint32 *minor_status,
   2417 		     gss_ctx_id_t context_handle,
   2418 		     int conf_req_flag,
   2419 		     gss_qop_t qop_req,
   2420 		     gss_buffer_t input_assoc_buffer,
   2421 		     gss_buffer_t input_payload_buffer,
   2422 		     int *conf_state,
   2423 		     gss_buffer_t output_message_buffer)
   2424 {
   2425 	OM_uint32 ret;
   2426 	ret = gss_wrap_aead(minor_status,
   2427 			    context_handle,
   2428 			    conf_req_flag,
   2429 			    qop_req,
   2430 			    input_assoc_buffer,
   2431 			    input_payload_buffer,
   2432 			    conf_state,
   2433 			    output_message_buffer);
   2434 
   2435 	return (ret);
   2436 }
   2437 
   2438 OM_uint32
   2439 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
   2440 		       gss_ctx_id_t context_handle,
   2441 		       gss_buffer_t input_message_buffer,
   2442 		       gss_buffer_t input_assoc_buffer,
   2443 		       gss_buffer_t output_payload_buffer,
   2444 		       int *conf_state,
   2445 		       gss_qop_t *qop_state)
   2446 {
   2447 	OM_uint32 ret;
   2448 	ret = gss_unwrap_aead(minor_status,
   2449 			      context_handle,
   2450 			      input_message_buffer,
   2451 			      input_assoc_buffer,
   2452 			      output_payload_buffer,
   2453 			      conf_state,
   2454 			      qop_state);
   2455 	return (ret);
   2456 }
   2457 
   2458 OM_uint32
   2459 spnego_gss_wrap_iov(OM_uint32 *minor_status,
   2460 		    gss_ctx_id_t context_handle,
   2461 		    int conf_req_flag,
   2462 		    gss_qop_t qop_req,
   2463 		    int *conf_state,
   2464 		    gss_iov_buffer_desc *iov,
   2465 		    int iov_count)
   2466 {
   2467 	OM_uint32 ret;
   2468 	ret = gss_wrap_iov(minor_status,
   2469 			   context_handle,
   2470 			   conf_req_flag,
   2471 			   qop_req,
   2472 			   conf_state,
   2473 			   iov,
   2474 			   iov_count);
   2475 	return (ret);
   2476 }
   2477 
   2478 OM_uint32
   2479 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
   2480 		      gss_ctx_id_t context_handle,
   2481 		      int *conf_state,
   2482 		      gss_qop_t *qop_state,
   2483 		      gss_iov_buffer_desc *iov,
   2484 		      int iov_count)
   2485 {
   2486 	OM_uint32 ret;
   2487 	ret = gss_unwrap_iov(minor_status,
   2488 			     context_handle,
   2489 			     conf_state,
   2490 			     qop_state,
   2491 			     iov,
   2492 			     iov_count);
   2493 	return (ret);
   2494 }
   2495 
   2496 OM_uint32
   2497 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
   2498 			   gss_ctx_id_t context_handle,
   2499 			   int conf_req_flag,
   2500 			   gss_qop_t qop_req,
   2501 			   int *conf_state,
   2502 			   gss_iov_buffer_desc *iov,
   2503 			   int iov_count)
   2504 {
   2505 	OM_uint32 ret;
   2506 	ret = gss_wrap_iov_length(minor_status,
   2507 				  context_handle,
   2508 				  conf_req_flag,
   2509 				  qop_req,
   2510 				  conf_state,
   2511 				  iov,
   2512 				  iov_count);
   2513 	return (ret);
   2514 }
   2515 
   2516 
   2517 OM_uint32
   2518 spnego_gss_complete_auth_token(
   2519 		OM_uint32 *minor_status,
   2520 		const gss_ctx_id_t context_handle,
   2521 		gss_buffer_t input_message_buffer)
   2522 {
   2523 	OM_uint32 ret;
   2524 	ret = gss_complete_auth_token(minor_status,
   2525 				      context_handle,
   2526 				      input_message_buffer);
   2527 	return (ret);
   2528 }
   2529 #endif /* 0 */
   2530 
   2531 /*
   2532  * We will release everything but the ctx_handle so that it
   2533  * can be passed back to init/accept context. This routine should
   2534  * not be called until after the ctx_handle memory is assigned to
   2535  * the supplied context handle from init/accept context.
   2536  */
   2537 static void
   2538 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
   2539 {
   2540 	spnego_gss_ctx_id_t context;
   2541 	OM_uint32 minor_stat;
   2542 	context = *ctx;
   2543 
   2544 	if (context != NULL) {
   2545 		(void) gss_release_buffer(&minor_stat,
   2546 					&context->DER_mechTypes);
   2547 
   2548 		(void) generic_gss_release_oid(&minor_stat,
   2549 				&context->internal_mech);
   2550 
   2551 		if (context->optionStr != NULL) {
   2552 			free(context->optionStr);
   2553 			context->optionStr = NULL;
   2554 		}
   2555 		free(context);
   2556 		*ctx = NULL;
   2557 	}
   2558 }
   2559 
   2560 /*
   2561  * Can't use gss_indicate_mechs by itself to get available mechs for
   2562  * SPNEGO because it will also return the SPNEGO mech and we do not
   2563  * want to consider SPNEGO as an available security mech for
   2564  * negotiation. For this reason, get_available_mechs will return
   2565  * all available mechs except SPNEGO.
   2566  *
   2567  * If a ptr to a creds list is given, this function will attempt
   2568  * to acquire creds for the creds given and trim the list of
   2569  * returned mechanisms to only those for which creds are valid.
   2570  *
   2571  */
   2572 static OM_uint32
   2573 get_available_mechs(OM_uint32 *minor_status,
   2574 	gss_name_t name, gss_cred_usage_t usage,
   2575 	gss_cred_id_t *creds, gss_OID_set *rmechs)
   2576 {
   2577 	unsigned int	i;
   2578 	int		found = 0;
   2579 	OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
   2580 	gss_OID_set mechs, goodmechs;
   2581 
   2582 	major_status = gss_indicate_mechs(minor_status, &mechs);
   2583 
   2584 	if (major_status != GSS_S_COMPLETE) {
   2585 		return (major_status);
   2586 	}
   2587 
   2588 	major_status = gss_create_empty_oid_set(minor_status, rmechs);
   2589 
   2590 	if (major_status != GSS_S_COMPLETE) {
   2591 		(void) gss_release_oid_set(minor_status, &mechs);
   2592 		return (major_status);
   2593 	}
   2594 
   2595 	for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
   2596 		if ((mechs->elements[i].length
   2597 		    != spnego_mechanism.mech_type.length) ||
   2598 		    memcmp(mechs->elements[i].elements,
   2599 			spnego_mechanism.mech_type.elements,
   2600 			spnego_mechanism.mech_type.length)) {
   2601 			/*
   2602 			 * Solaris Kerberos: gss_indicate_mechs is stupid as
   2603 			 * it never inferences any of the related OIDs of the
   2604 			 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
   2605 			 * We add KRB5_WRONG here so that old MS clients can
   2606 			 * negotiate this mechanism, which allows extensions
   2607 			 * in Kerberos (clock skew adjustment, refresh ccache).
   2608 			 */
   2609 			if (is_kerb_mech(&mechs->elements[i])) {
   2610 			    extern gss_OID_desc * const gss_mech_krb5_wrong;
   2611 
   2612 				major_status =
   2613 				  gss_add_oid_set_member(minor_status,
   2614 				  gss_mech_krb5_wrong, rmechs);
   2615 			}
   2616 
   2617 			major_status = gss_add_oid_set_member(minor_status,
   2618 							      &mechs->elements[i],
   2619 							      rmechs);
   2620 			if (major_status == GSS_S_COMPLETE)
   2621 				found++;
   2622 		}
   2623 	}
   2624 
   2625 	/*
   2626 	 * If the caller wanted a list of creds returned,
   2627 	 * trim the list of mechanisms down to only those
   2628 	 * for which the creds are valid.
   2629 	 */
   2630 	if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
   2631 		major_status = gss_acquire_cred(minor_status,
   2632 						name, GSS_C_INDEFINITE,
   2633 						*rmechs, usage, creds,
   2634 						&goodmechs, NULL);
   2635 
   2636 		/*
   2637 		 * Drop the old list in favor of the new
   2638 		 * "trimmed" list.
   2639 		 */
   2640 		(void) gss_release_oid_set(&tmpmin, rmechs);
   2641 		if (major_status == GSS_S_COMPLETE) {
   2642 			(void) gssint_copy_oid_set(&tmpmin,
   2643 					goodmechs, rmechs);
   2644 			(void) gss_release_oid_set(&tmpmin, &goodmechs);
   2645 		}
   2646 	}
   2647 
   2648 	(void) gss_release_oid_set(&tmpmin, &mechs);
   2649 	if (found == 0 || major_status != GSS_S_COMPLETE) {
   2650 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
   2651 		map_errcode(minor_status);
   2652 		if (major_status == GSS_S_COMPLETE)
   2653 			major_status = GSS_S_FAILURE;
   2654 	}
   2655 
   2656 	return (major_status);
   2657 }
   2658 
   2659 /* following are token creation and reading routines */
   2660 
   2661 /*
   2662  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
   2663  * advance the buffer, otherwise, decode the mech_oid from the buffer and
   2664  * place in gss_OID.
   2665  */
   2666 static gss_OID
   2667 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
   2668 {
   2669 	OM_uint32	status;
   2670 	gss_OID_desc 	toid;
   2671 	gss_OID		mech_out = NULL;
   2672 	unsigned char		*start, *end;
   2673 
   2674 	if (length < 1 || **buff_in != MECH_OID)
   2675 		return (NULL);
   2676 
   2677 	start = *buff_in;
   2678 	end = start + length;
   2679 
   2680 	(*buff_in)++;
   2681 	toid.length = *(*buff_in)++;
   2682 
   2683 	if ((*buff_in + toid.length) > end)
   2684 		return (NULL);
   2685 
   2686 	toid.elements = *buff_in;
   2687 	*buff_in += toid.length;
   2688 
   2689 	status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
   2690 
   2691 	if (status != GSS_S_COMPLETE) {
   2692 		map_errcode(minor_status);
   2693 		mech_out = NULL;
   2694 	}
   2695 
   2696 	return (mech_out);
   2697 }
   2698 
   2699 /*
   2700  * der encode the given mechanism oid into buf_out, advancing the
   2701  * buffer pointer.
   2702  */
   2703 
   2704 static int
   2705 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
   2706 {
   2707 	if (buflen < mech->length + 2)
   2708 		return (-1);
   2709 	*(*buf_out)++ = MECH_OID;
   2710 	*(*buf_out)++ = (unsigned char) mech->length;
   2711 	memcpy((void *)(*buf_out), mech->elements, mech->length);
   2712 	*buf_out += mech->length;
   2713 	return (0);
   2714 }
   2715 
   2716 /*
   2717  * verify that buff_in points to an octet string, if it does not,
   2718  * return NULL and don't advance the pointer. If it is an octet string
   2719  * decode buff_in into a gss_buffer_t and return it, advancing the
   2720  * buffer pointer.
   2721  */
   2722 static gss_buffer_t
   2723 get_input_token(unsigned char **buff_in, unsigned int buff_length)
   2724 {
   2725 	gss_buffer_t input_token;
   2726 	unsigned int bytes;
   2727 
   2728 	if (**buff_in != OCTET_STRING)
   2729 		return (NULL);
   2730 
   2731 	(*buff_in)++;
   2732 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
   2733 
   2734 	if (input_token == NULL)
   2735 		return (NULL);
   2736 
   2737 	input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
   2738 	if ((int)input_token->length == -1) {
   2739 		free(input_token);
   2740 		return (NULL);
   2741 	}
   2742 	input_token->value = malloc(input_token->length);
   2743 
   2744 	if (input_token->value == NULL) {
   2745 		free(input_token);
   2746 		return (NULL);
   2747 	}
   2748 
   2749 	(void) memcpy(input_token->value, *buff_in, input_token->length);
   2750 	*buff_in += input_token->length;
   2751 	return (input_token);
   2752 }
   2753 
   2754 /*
   2755  * verify that the input token length is not 0. If it is, just return.
   2756  * If the token length is greater than 0, der encode as an octet string
   2757  * and place in buf_out, advancing buf_out.
   2758  */
   2759 
   2760 static int
   2761 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
   2762 		unsigned int buflen)
   2763 {
   2764 	int ret;
   2765 
   2766 	/* if token length is 0, we do not want to send */
   2767 	if (input_token->length == 0)
   2768 		return (0);
   2769 
   2770 	if (input_token->length > buflen)
   2771 		return (-1);
   2772 
   2773 	*(*buf_out)++ = OCTET_STRING;
   2774 	if ((ret = gssint_put_der_length(input_token->length, buf_out,
   2775 			    input_token->length)))
   2776 		return (ret);
   2777 	TWRITE_STR(*buf_out, input_token->value, input_token->length);
   2778 	return (0);
   2779 }
   2780 
   2781 /*
   2782  * verify that buff_in points to a sequence of der encoding. The mech
   2783  * set is the only sequence of encoded object in the token, so if it is
   2784  * a sequence of encoding, decode the mechset into a gss_OID_set and
   2785  * return it, advancing the buffer pointer.
   2786  */
   2787 static gss_OID_set
   2788 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
   2789 	     unsigned int buff_length)
   2790 {
   2791 	gss_OID_set returned_mechSet;
   2792 	OM_uint32 major_status;
   2793 	int length; /* SUNW17PACresync */
   2794 	OM_uint32 bytes;
   2795 	OM_uint32 set_length;
   2796 	unsigned char		*start;
   2797 	int i;
   2798 
   2799 	if (**buff_in != SEQUENCE_OF)
   2800 		return (NULL);
   2801 
   2802 	start = *buff_in;
   2803 	(*buff_in)++;
   2804 
   2805 	length = gssint_get_der_length(buff_in, buff_length, &bytes);
   2806 	if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
   2807 		return (NULL);
   2808 
   2809 	major_status = gss_create_empty_oid_set(minor_status,
   2810 						&returned_mechSet);
   2811 	if (major_status != GSS_S_COMPLETE)
   2812 		return (NULL);
   2813 
   2814 	for (set_length = 0, i = 0; set_length < length; i++) {
   2815 		gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
   2816 			buff_length - (*buff_in - start));
   2817 		if (temp != NULL) {
   2818 		    major_status = gss_add_oid_set_member(minor_status,
   2819 					temp, &returned_mechSet);
   2820 		    if (major_status == GSS_S_COMPLETE) {
   2821 			set_length += returned_mechSet->elements[i].length +2;
   2822 			if (generic_gss_release_oid(minor_status, &temp))
   2823 			    map_errcode(minor_status);
   2824 		    }
   2825 		}
   2826 	}
   2827 
   2828 	return (returned_mechSet);
   2829 }
   2830 
   2831 /*
   2832  * Encode mechSet into buf.
   2833  */
   2834 static int
   2835 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
   2836 {
   2837 	unsigned char *ptr;
   2838 	unsigned int i;
   2839 	unsigned int tlen, ilen;
   2840 
   2841 	tlen = ilen = 0;
   2842 	for (i = 0; i < mechSet->count; i++) {
   2843 		/*
   2844 		 * 0x06 [DER LEN] [OID]
   2845 		 */
   2846 		ilen += 1 +
   2847 			gssint_der_length_size(mechSet->elements[i].length) +
   2848 			mechSet->elements[i].length;
   2849 	}
   2850 	/*
   2851 	 * 0x30 [DER LEN]
   2852 	 */
   2853 	tlen = 1 + gssint_der_length_size(ilen) + ilen;
   2854 	ptr = malloc(tlen);
   2855 	if (ptr == NULL)
   2856 		return -1;
   2857 
   2858 	buf->value = ptr;
   2859 	buf->length = tlen;
   2860 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
   2861 
   2862 	*ptr++ = SEQUENCE_OF;
   2863 	if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
   2864 		return -1;
   2865 	for (i = 0; i < mechSet->count; i++) {
   2866 		if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
   2867 			return -1;
   2868 		}
   2869 	}
   2870 	return 0;
   2871 #undef REMAIN
   2872 }
   2873 
   2874 /*
   2875  * Verify that buff_in is pointing to a BIT_STRING with the correct
   2876  * length and padding for the req_flags. If it is, decode req_flags
   2877  * and return them, otherwise, return NULL.
   2878  */
   2879 static OM_uint32
   2880 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
   2881 	      OM_uint32 *req_flags)
   2882 {
   2883 	unsigned int len;
   2884 
   2885 	if (**buff_in != (CONTEXT | 0x01))
   2886 		return (0);
   2887 
   2888 	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
   2889 				bodysize, &len) < 0)
   2890 		return GSS_S_DEFECTIVE_TOKEN;
   2891 
   2892 	if (*(*buff_in)++ != BIT_STRING)
   2893 		return GSS_S_DEFECTIVE_TOKEN;
   2894 
   2895 	if (*(*buff_in)++ != BIT_STRING_LENGTH)
   2896 		return GSS_S_DEFECTIVE_TOKEN;
   2897 
   2898 	if (*(*buff_in)++ != BIT_STRING_PADDING)
   2899 		return GSS_S_DEFECTIVE_TOKEN;
   2900 
   2901 	*req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
   2902 	return (0);
   2903 }
   2904 
   2905 static OM_uint32
   2906 get_negTokenInit(OM_uint32 *minor_status,
   2907 		 gss_buffer_t buf,
   2908 		 gss_buffer_t der_mechSet,
   2909 		 gss_OID_set *mechSet,
   2910 		 OM_uint32 *req_flags,
   2911 		 gss_buffer_t *mechtok,
   2912 		 gss_buffer_t *mechListMIC)
   2913 {
   2914 	OM_uint32 err;
   2915 	unsigned char *ptr, *bufstart;
   2916 	unsigned int len;
   2917 	gss_buffer_desc tmpbuf;
   2918 
   2919 	*minor_status = 0;
   2920 	der_mechSet->length = 0;
   2921 	der_mechSet->value = NULL;
   2922 	*mechSet = GSS_C_NO_OID_SET;
   2923 	*req_flags = 0;
   2924 	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
   2925 
   2926 	ptr = bufstart = buf->value;
   2927 	if ((buf->length - (ptr - bufstart)) > INT_MAX)
   2928 		return GSS_S_FAILURE;
   2929 #define REMAIN (buf->length - (ptr - bufstart))
   2930 
   2931 	err = g_verify_token_header(gss_mech_spnego,
   2932 				    &len, &ptr, 0, REMAIN);
   2933 	if (err) {
   2934 		*minor_status = err;
   2935 		map_errcode(minor_status);
   2936 		return GSS_S_FAILURE;
   2937 	}
   2938 	*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
   2939 	if (*minor_status) {
   2940 		map_errcode(minor_status);
   2941 		return GSS_S_FAILURE;
   2942 	}
   2943 
   2944 	/* alias into input_token */
   2945 	tmpbuf.value = ptr;
   2946 	tmpbuf.length = REMAIN;
   2947 	*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
   2948 	if (*mechSet == NULL)
   2949 		return GSS_S_FAILURE;
   2950 
   2951 	tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
   2952 	der_mechSet->value = malloc(tmpbuf.length);
   2953 	if (der_mechSet->value == NULL)
   2954 		return GSS_S_FAILURE;
   2955 	memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
   2956 	der_mechSet->length = tmpbuf.length;
   2957 
   2958 	err = get_req_flags(&ptr, REMAIN, req_flags);
   2959 	if (err != GSS_S_COMPLETE) {
   2960 		return err;
   2961 	}
   2962 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
   2963 				 REMAIN, &len) >= 0) {
   2964 		*mechtok = get_input_token(&ptr, len);
   2965 		if (*mechtok == GSS_C_NO_BUFFER) {
   2966 			return GSS_S_FAILURE;
   2967 		}
   2968 	}
   2969 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
   2970 				 REMAIN, &len) >= 0) {
   2971 		*mechListMIC = get_input_token(&ptr, len);
   2972 		if (*mechListMIC == GSS_C_NO_BUFFER) {
   2973 			return GSS_S_FAILURE;
   2974 		}
   2975 	}
   2976 	return GSS_S_COMPLETE;
   2977 #undef REMAIN
   2978 }
   2979 
   2980 static OM_uint32
   2981 get_negTokenResp(OM_uint32 *minor_status,
   2982 		 unsigned char *buf, unsigned int buflen,
   2983 		 OM_uint32 *negState,
   2984 		 gss_OID *supportedMech,
   2985 		 gss_buffer_t *responseToken,
   2986 		 gss_buffer_t *mechListMIC)
   2987 {
   2988 	unsigned char *ptr, *bufstart;
   2989 	unsigned int len;
   2990 	int tmplen;
   2991 	unsigned int tag, bytes;
   2992 
   2993 	*negState = ACCEPT_DEFECTIVE_TOKEN;
   2994 	*supportedMech = GSS_C_NO_OID;
   2995 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
   2996 	ptr = bufstart = buf;
   2997 #define REMAIN (buflen - (ptr - bufstart))
   2998 
   2999 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
   3000 		return GSS_S_DEFECTIVE_TOKEN;
   3001 	if (*ptr++ == SEQUENCE) {
   3002 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
   3003 		if (tmplen < 0)
   3004 			return GSS_S_DEFECTIVE_TOKEN;
   3005 	}
   3006 	if (REMAIN < 1)
   3007 		tag = 0;
   3008 	else
   3009 		tag = *ptr++;
   3010 
   3011 	if (tag == CONTEXT) {
   3012 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
   3013 		if (tmplen < 0)
   3014 			return GSS_S_DEFECTIVE_TOKEN;
   3015 
   3016 		if (g_get_tag_and_length(&ptr, ENUMERATED,
   3017 					 REMAIN, &len) < 0)
   3018 			return GSS_S_DEFECTIVE_TOKEN;
   3019 
   3020 		if (len != ENUMERATION_LENGTH)
   3021 			return GSS_S_DEFECTIVE_TOKEN;
   3022 
   3023 		if (REMAIN < 1)
   3024 			return GSS_S_DEFECTIVE_TOKEN;
   3025 		*negState = *ptr++;
   3026 
   3027 		if (REMAIN < 1)
   3028 			tag = 0;
   3029 		else
   3030 			tag = *ptr++;
   3031 	}
   3032 	if (tag == (CONTEXT | 0x01)) {
   3033 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
   3034 		if (tmplen < 0)
   3035 			return GSS_S_DEFECTIVE_TOKEN;
   3036 
   3037 		*supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
   3038 		if (*supportedMech == GSS_C_NO_OID)
   3039 			return GSS_S_DEFECTIVE_TOKEN;
   3040 
   3041 		if (REMAIN < 1)
   3042 			tag = 0;
   3043 		else
   3044 			tag = *ptr++;
   3045 	}
   3046 	if (tag == (CONTEXT | 0x02)) {
   3047 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
   3048 		if (tmplen < 0)
   3049 			return GSS_S_DEFECTIVE_TOKEN;
   3050 
   3051 		*responseToken = get_input_token(&ptr, REMAIN);
   3052 		if (*responseToken == GSS_C_NO_BUFFER)
   3053 			return GSS_S_DEFECTIVE_TOKEN;
   3054 
   3055 		if (REMAIN < 1)
   3056 			tag = 0;
   3057 		else
   3058 			tag = *ptr++;
   3059 	}
   3060 	if (tag == (CONTEXT | 0x03)) {
   3061 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
   3062 		if (tmplen < 0)
   3063 			return GSS_S_DEFECTIVE_TOKEN;
   3064 
   3065 		*mechListMIC = get_input_token(&ptr, REMAIN);
   3066 		if (*mechListMIC == GSS_C_NO_BUFFER)
   3067 			return GSS_S_DEFECTIVE_TOKEN;
   3068 	}
   3069 	return GSS_S_COMPLETE;
   3070 #undef REMAIN
   3071 }
   3072 
   3073 /*
   3074  * der encode the passed negResults as an ENUMERATED type and
   3075  * place it in buf_out, advancing the buffer.
   3076  */
   3077 
   3078 static int
   3079 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
   3080 	      unsigned int buflen)
   3081 {
   3082 	if (buflen < 3)
   3083 		return (-1);
   3084 	*(*buf_out)++ = ENUMERATED;
   3085 	*(*buf_out)++ = ENUMERATION_LENGTH;
   3086 	*(*buf_out)++ = (unsigned char) negResult;
   3087 	return (0);
   3088 }
   3089 
   3090 /*
   3091  * This routine compares the recieved mechset to the mechset that
   3092  * this server can support. It looks sequentially through the mechset
   3093  * and the first one that matches what the server can support is
   3094  * chosen as the negotiated mechanism. If one is found, negResult
   3095  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
   3096  * it's not the first mech, otherwise we return NULL and negResult
   3097  * is set to REJECT.
   3098  *
   3099  * NOTE: There is currently no way to specify a preference order of
   3100  * mechanisms supported by the acceptor.
   3101  */
   3102 static gss_OID
   3103 negotiate_mech_type(OM_uint32 *minor_status,
   3104 		    gss_OID_set supported_mechSet,
   3105 		    gss_OID_set mechset,
   3106 		    OM_uint32 *negResult)
   3107 {
   3108 	gss_OID returned_mech;
   3109 	OM_uint32 status;
   3110 	int present;
   3111 	unsigned int i;
   3112 
   3113 	for (i = 0; i < mechset->count; i++) {
   3114 		gss_OID mech_oid = &mechset->elements[i];
   3115 
   3116 		/*
   3117 		 * Solaris Kerberos: MIT compares against MS' wrong OID, but
   3118 		 * we actually want to select it if the client supports, as this
   3119 		 * will enable features on MS clients that allow credential
   3120 		 * refresh on rekeying and caching system times from servers.
   3121 		 */
   3122 #if 0
   3123 		/* Accept wrong mechanism OID from MS clients */
   3124 		if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
   3125 		    memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
   3126 			mech_oid = (gss_OID)&gss_mech_krb5_oid;
   3127 #endif
   3128 
   3129 		gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
   3130 		if (!present)
   3131 			continue;
   3132 
   3133 		if (i == 0)
   3134 			*negResult = ACCEPT_INCOMPLETE;
   3135 		else
   3136 			*negResult = REQUEST_MIC;
   3137 
   3138 		status = generic_gss_copy_oid(minor_status,
   3139 					      &mechset->elements[i],
   3140 					      &returned_mech);
   3141 		if (status != GSS_S_COMPLETE) {
   3142 			*negResult = REJECT;
   3143 			map_errcode(minor_status);
   3144 			return (NULL);
   3145 		}
   3146 		return (returned_mech);
   3147 	}
   3148 	*negResult = REJECT;
   3149 	return (NULL);
   3150 }
   3151 
   3152 /*
   3153  * the next two routines make a token buffer suitable for
   3154  * spnego_gss_display_status. These currently take the string
   3155  * in name and place it in the token. Eventually, if
   3156  * spnego_gss_display_status returns valid error messages,
   3157  * these routines will be changes to return the error string.
   3158  */
   3159 static spnego_token_t
   3160 make_spnego_token(char *name)
   3161 {
   3162 	return (spnego_token_t)strdup(name);
   3163 }
   3164 
   3165 static gss_buffer_desc
   3166 make_err_msg(char *name)
   3167 {
   3168 	gss_buffer_desc buffer;
   3169 
   3170 	if (name == NULL) {
   3171 		buffer.length = 0;
   3172 		buffer.value = NULL;
   3173 	} else {
   3174 		buffer.length = strlen(name)+1;
   3175 		buffer.value = make_spnego_token(name);
   3176 	}
   3177 
   3178 	return (buffer);
   3179 }
   3180 
   3181 /*
   3182  * Create the client side spnego token passed back to gss_init_sec_context
   3183  * and eventually up to the application program and over to the server.
   3184  *
   3185  * Use DER rules, definite length method per RFC 2478
   3186  */
   3187 static int
   3188 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
   3189 			  int negHintsCompat,
   3190 			  gss_buffer_t mechListMIC, OM_uint32 req_flags,
   3191 			  gss_buffer_t data, send_token_flag sendtoken,
   3192 			  gss_buffer_t outbuf)
   3193 {
   3194 	int ret = 0;
   3195 	unsigned int tlen, dataLen = 0;
   3196 	unsigned int negTokenInitSize = 0;
   3197 	unsigned int negTokenInitSeqSize = 0;
   3198 	unsigned int negTokenInitContSize = 0;
   3199 	unsigned int rspTokenSize = 0;
   3200 	unsigned int mechListTokenSize = 0;
   3201 	unsigned int micTokenSize = 0;
   3202 	unsigned char *t;
   3203 	unsigned char *ptr;
   3204 
   3205 	if (outbuf == GSS_C_NO_BUFFER)
   3206 		return (-1);
   3207 
   3208 	outbuf->length = 0;
   3209 	outbuf->value = NULL;
   3210 
   3211 	/* calculate the data length */
   3212 
   3213 	/*
   3214 	 * 0xa0 [DER LEN] [mechTypes]
   3215 	 */
   3216 	mechListTokenSize = 1 +
   3217 		gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
   3218 		spnego_ctx->DER_mechTypes.length;
   3219 	dataLen += mechListTokenSize;
   3220 
   3221 	/*
   3222 	 * If a token from gss_init_sec_context exists,
   3223 	 * add the length of the token + the ASN.1 overhead
   3224 	 */
   3225 	if (data != NULL) {
   3226 		/*
   3227 		 * Encoded in final output as:
   3228 		 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
   3229 		 * -----s--------|--------s2----------
   3230 		 */
   3231 		rspTokenSize = 1 +
   3232 			gssint_der_length_size(data->length) +
   3233 			data->length;
   3234 		dataLen += 1 + gssint_der_length_size(rspTokenSize) +
   3235 			rspTokenSize;
   3236 	}
   3237 
   3238 	if (mechListMIC) {
   3239 		/*
   3240 		 * Encoded in final output as:
   3241 		 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
   3242 		 *	--s--     -----tlen------------
   3243 		 */
   3244 		micTokenSize = 1 +
   3245 			gssint_der_length_size(mechListMIC->length) +
   3246 			mechListMIC->length;
   3247 		dataLen += 1 +
   3248 			gssint_der_length_size(micTokenSize) +
   3249 			micTokenSize;
   3250 	}
   3251 
   3252 	/*
   3253 	 * Add size of DER encoding
   3254 	 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
   3255 	 *   0x30 [DER_LEN] [data]
   3256 	 *
   3257 	 */
   3258 	negTokenInitContSize = dataLen;
   3259 	negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
   3260 	dataLen = negTokenInitSeqSize;
   3261 
   3262 	/*
   3263 	 * negTokenInitSize indicates the bytes needed to
   3264 	 * hold the ASN.1 encoding of the entire NegTokenInit
   3265 	 * SEQUENCE.
   3266 	 * 0xa0 [DER_LEN] + data
   3267 	 *
   3268 	 */
   3269 	negTokenInitSize = 1 +
   3270 		gssint_der_length_size(negTokenInitSeqSize) +
   3271 		negTokenInitSeqSize;
   3272 
   3273 	tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
   3274 
   3275 	t = (unsigned char *) malloc(tlen);
   3276 
   3277 	if (t == NULL) {
   3278 		return (-1);
   3279 	}
   3280 
   3281 	ptr = t;
   3282 
   3283 	/* create the message */
   3284 	if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
   3285 			    &ptr, tlen)))
   3286 		goto errout;
   3287 
   3288 	*ptr++ = CONTEXT; /* NegotiationToken identifier */
   3289 	if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
   3290 		goto errout;
   3291 
   3292 	*ptr++ = SEQUENCE;
   3293 	if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
   3294 					 tlen - (int)(ptr-t))))
   3295 		goto errout;
   3296 
   3297 	*ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
   3298 	if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
   3299 					 &ptr, tlen - (int)(ptr-t))))
   3300 		goto errout;
   3301 
   3302 	/* We already encoded the MechSetList */
   3303 	(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
   3304 		      spnego_ctx->DER_mechTypes.length);
   3305 
   3306 	ptr += spnego_ctx->DER_mechTypes.length;
   3307 
   3308 	if (data != NULL) {
   3309 		*ptr++ = CONTEXT | 0x02;
   3310 		if ((ret = gssint_put_der_length(rspTokenSize,
   3311 				&ptr, tlen - (int)(ptr - t))))
   3312 			goto errout;
   3313 
   3314 		if ((ret = put_input_token(&ptr, data,
   3315 			tlen - (int)(ptr - t))))
   3316 			goto errout;
   3317 	}
   3318 
   3319 	if (mechListMIC != GSS_C_NO_BUFFER) {
   3320 		*ptr++ = CONTEXT | 0x03;
   3321 		if ((ret = gssint_put_der_length(micTokenSize,
   3322 				&ptr, tlen - (int)(ptr - t))))
   3323 			goto errout;
   3324 
   3325 		if (negHintsCompat) {
   3326 			ret = put_neg_hints(&ptr, mechListMIC,
   3327 					    tlen - (int)(ptr - t));
   3328 			if (ret)
   3329 				goto errout;
   3330 		} else if ((ret = put_input_token(&ptr, mechListMIC,
   3331 				tlen - (int)(ptr - t))))
   3332 			goto errout;
   3333 	}
   3334 
   3335 errout:
   3336 	if (ret != 0) {
   3337 		if (t)
   3338 			free(t);
   3339 		t = NULL;
   3340 		tlen = 0;
   3341 	}
   3342 	outbuf->length = tlen;
   3343 	outbuf->value = (void *) t;
   3344 
   3345 	return (ret);
   3346 }
   3347 
   3348 /*
   3349  * create the server side spnego token passed back to
   3350  * gss_accept_sec_context and eventually up to the application program
   3351  * and over to the client.
   3352  */
   3353 static int
   3354 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
   3355 			  gss_buffer_t data, gss_buffer_t mechListMIC,
   3356 			  send_token_flag sendtoken,
   3357 			  gss_buffer_t outbuf)
   3358 {
   3359 	unsigned int tlen = 0;
   3360 	unsigned int ret = 0;
   3361 	unsigned int NegTokenTargSize = 0;
   3362 	unsigned int NegTokenSize = 0;
   3363 	unsigned int rspTokenSize = 0;
   3364 	unsigned int micTokenSize = 0;
   3365 	unsigned int dataLen = 0;
   3366 	unsigned char *t;
   3367 	unsigned char *ptr;
   3368 
   3369 	if (outbuf == GSS_C_NO_BUFFER)
   3370 		return (GSS_S_DEFECTIVE_TOKEN);
   3371 
   3372 	outbuf->length = 0;
   3373 	outbuf->value = NULL;
   3374 
   3375 	/*
   3376 	 * ASN.1 encoding of the negResult
   3377 	 * ENUMERATED type is 3 bytes
   3378 	 *  ENUMERATED TAG, Length, Value,
   3379 	 * Plus 2 bytes for the CONTEXT id and length.
   3380 	 */
   3381 	dataLen = 5;
   3382 
   3383 	/*
   3384 	 * calculate data length
   3385 	 *
   3386 	 * If this is the initial token, include length of
   3387 	 * mech_type and the negotiation result fields.
   3388 	 */
   3389 	if (sendtoken == INIT_TOKEN_SEND) {
   3390 		int mechlistTokenSize;
   3391 		/*
   3392 		 * 1 byte for the CONTEXT ID(0xa0),
   3393 		 * 1 byte for the OID ID(0x06)
   3394 		 * 1 byte for OID Length field
   3395 		 * Plus the rest... (OID Length, OID value)
   3396 		 */
   3397 		mechlistTokenSize = 3 + mech_wanted->length +
   3398 			gssint_der_length_size(mech_wanted->length);
   3399 
   3400 		dataLen += mechlistTokenSize;
   3401 	}
   3402 	if (data != NULL && data->length > 0) {
   3403 		/* Length of the inner token */
   3404 		rspTokenSize = 1 + gssint_der_length_size(data->length) +
   3405 			data->length;
   3406 
   3407 		dataLen += rspTokenSize;
   3408 
   3409 		/* Length of the outer token */
   3410 		dataLen += 1 + gssint_der_length_size(rspTokenSize);
   3411 	}
   3412 	if (mechListMIC != NULL) {
   3413 
   3414 		/* Length of the inner token */
   3415 		micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
   3416 			mechListMIC->length;
   3417 
   3418 		dataLen += micTokenSize;
   3419 
   3420 		/* Length of the outer token */
   3421 		dataLen += 1 + gssint_der_length_size(micTokenSize);
   3422 	}
   3423 	/*
   3424 	 * Add size of DER encoded:
   3425 	 * NegTokenTarg [ SEQUENCE ] of
   3426 	 *    NegResult[0] ENUMERATED {
   3427 	 *	accept_completed(0),
   3428 	 *	accept_incomplete(1),
   3429 	 *	reject(2) }
   3430 	 *    supportedMech [1] MechType OPTIONAL,
   3431 	 *    responseToken [2] OCTET STRING OPTIONAL,
   3432 	 *    mechListMIC   [3] OCTET STRING OPTIONAL
   3433 	 *
   3434 	 * size = data->length + MechListMic + SupportedMech len +
   3435 	 *	Result Length + ASN.1 overhead
   3436 	 */
   3437 	NegTokenTargSize = dataLen;
   3438 	dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
   3439 
   3440 	/*
   3441 	 * NegotiationToken [ CHOICE ]{
   3442 	 *    negTokenInit  [0]	 NegTokenInit,
   3443 	 *    negTokenTarg  [1]	 NegTokenTarg }
   3444 	 */
   3445 	NegTokenSize = dataLen;
   3446 	dataLen += 1 + gssint_der_length_size(NegTokenSize);
   3447 
   3448 	tlen = dataLen;
   3449 	t = (unsigned char *) malloc(tlen);
   3450 
   3451 	if (t == NULL) {
   3452 		ret = GSS_S_DEFECTIVE_TOKEN;
   3453 		goto errout;
   3454 	}
   3455 
   3456 	ptr = t;
   3457 
   3458 	/*
   3459 	 * Indicate that we are sending CHOICE 1
   3460 	 * (NegTokenTarg)
   3461 	 */
   3462 	*ptr++ = CONTEXT | 0x01;
   3463 	if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
   3464 		ret = GSS_S_DEFECTIVE_TOKEN;
   3465 		goto errout;
   3466 	}
   3467 	*ptr++ = SEQUENCE;
   3468 	if (gssint_put_der_length(NegTokenTargSize, &ptr,
   3469 				  tlen - (int)(ptr-t)) < 0) {
   3470 		ret = GSS_S_DEFECTIVE_TOKEN;
   3471 		goto errout;
   3472 	}
   3473 
   3474 	/*
   3475 	 * First field of the NegTokenTarg SEQUENCE
   3476 	 * is the ENUMERATED NegResult.
   3477 	 */
   3478 	*ptr++ = CONTEXT;
   3479 	if (gssint_put_der_length(3, &ptr,
   3480 				  tlen - (int)(ptr-t)) < 0) {
   3481 		ret = GSS_S_DEFECTIVE_TOKEN;
   3482 		goto errout;
   3483 	}
   3484 	if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
   3485 		ret = GSS_S_DEFECTIVE_TOKEN;
   3486 		goto errout;
   3487 	}
   3488 	if (sendtoken == INIT_TOKEN_SEND) {
   3489 		/*
   3490 		 * Next, is the Supported MechType
   3491 		 */
   3492 		*ptr++ = CONTEXT | 0x01;
   3493 		if (gssint_put_der_length(mech_wanted->length + 2,
   3494 					  &ptr,
   3495 					  tlen - (int)(ptr - t)) < 0) {
   3496 			ret = GSS_S_DEFECTIVE_TOKEN;
   3497 			goto errout;
   3498 		}
   3499 		if (put_mech_oid(&ptr, mech_wanted,
   3500 				 tlen - (int)(ptr - t)) < 0) {
   3501 			ret = GSS_S_DEFECTIVE_TOKEN;
   3502 			goto errout;
   3503 		}
   3504 	}
   3505 	if (data != NULL && data->length > 0) {
   3506 		*ptr++ = CONTEXT | 0x02;
   3507 		if (gssint_put_der_length(rspTokenSize, &ptr,
   3508 					  tlen - (int)(ptr - t)) < 0) {
   3509 			ret = GSS_S_DEFECTIVE_TOKEN;
   3510 			goto errout;
   3511 		}
   3512 		if (put_input_token(&ptr, data,
   3513 				    tlen - (int)(ptr - t)) < 0) {
   3514 			ret = GSS_S_DEFECTIVE_TOKEN;
   3515 			goto errout;
   3516 		}
   3517 	}
   3518 	if (mechListMIC != NULL) {
   3519 		*ptr++ = CONTEXT | 0x03;
   3520 		if (gssint_put_der_length(micTokenSize, &ptr,
   3521 					  tlen - (int)(ptr - t)) < 0) {
   3522 			ret = GSS_S_DEFECTIVE_TOKEN;
   3523 			goto errout;
   3524 		}
   3525 		if (put_input_token(&ptr, mechListMIC,
   3526 				    tlen - (int)(ptr - t)) < 0) {
   3527 			ret = GSS_S_DEFECTIVE_TOKEN;
   3528 			goto errout;
   3529 		}
   3530 	}
   3531 	ret = GSS_S_COMPLETE;
   3532 errout:
   3533 	if (ret != GSS_S_COMPLETE) {
   3534 		if (t)
   3535 			free(t);
   3536 	} else {
   3537 		outbuf->length = ptr - t;
   3538 		outbuf->value = (void *) t;
   3539 	}
   3540 
   3541 	return (ret);
   3542 }
   3543 
   3544 /* determine size of token */
   3545 static int
   3546 g_token_size(gss_OID_const mech, unsigned int body_size)
   3547 {
   3548 	int hdrsize;
   3549 
   3550 	/*
   3551 	 * Initialize the header size to the
   3552 	 * MECH_OID byte + the bytes needed to indicate the
   3553 	 * length of the OID + the OID itself.
   3554 	 *
   3555 	 * 0x06 [MECHLENFIELD] MECHDATA
   3556 	 */
   3557 	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
   3558 
   3559 	/*
   3560 	 * Now add the bytes needed for the initial header
   3561 	 * token bytes:
   3562 	 * 0x60 + [DER_LEN] + HDRSIZE
   3563 	 */
   3564 	hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
   3565 
   3566 	return (hdrsize + body_size);
   3567 }
   3568 
   3569 /*
   3570  * generate token header.
   3571  *
   3572  * Use DER Definite Length method per RFC2478
   3573  * Use of indefinite length encoding will not be compatible
   3574  * with Microsoft or others that actually follow the spec.
   3575  */
   3576 static int
   3577 g_make_token_header(gss_OID_const mech,
   3578 		    unsigned int body_size,
   3579 		    unsigned char **buf,
   3580 		    unsigned int totallen)
   3581 {
   3582 	int ret = 0;
   3583 	unsigned int hdrsize;
   3584 	unsigned char *p = *buf;
   3585 
   3586 	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
   3587 
   3588 	*(*buf)++ = HEADER_ID;
   3589 	if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
   3590 		return (ret);
   3591 
   3592 	*(*buf)++ = MECH_OID;
   3593 	if ((ret = gssint_put_der_length(mech->length, buf,
   3594 			    totallen - (int)(p - *buf))))
   3595 		return (ret);
   3596 	TWRITE_STR(*buf, mech->elements, mech->length);
   3597 	return (0);
   3598 }
   3599 
   3600 /*
   3601  * NOTE: This checks that the length returned by
   3602  * gssint_get_der_length() is not greater than the number of octets
   3603  * remaining, even though gssint_get_der_length() already checks, in
   3604  * theory.
   3605  */
   3606 static int
   3607 g_get_tag_and_length(unsigned char **buf, int tag,
   3608 		     unsigned int buflen, unsigned int *outlen)
   3609 {
   3610 	unsigned char *ptr = *buf;
   3611 	int ret = -1; /* pessimists, assume failure ! */
   3612 	unsigned int encoded_len;
   3613 	unsigned int tmplen = 0;
   3614 
   3615 	*outlen = 0;
   3616 	if (buflen > 1 && *ptr == tag) {
   3617 		ptr++;
   3618 		tmplen = gssint_get_der_length(&ptr, buflen - 1,
   3619 						&encoded_len);
   3620 		if (tmplen < 0) {
   3621 			ret = -1;
   3622 		} else if (tmplen > buflen - (ptr - *buf)) {
   3623 			ret = -1;
   3624 		} else
   3625 			ret = 0;
   3626 	}
   3627 	*outlen = tmplen;
   3628 	*buf = ptr;
   3629 	return (ret);
   3630 }
   3631 
   3632 static int
   3633 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
   3634 {
   3635 	unsigned char *buf = *buf_in;
   3636 	unsigned char *endptr = buf + cur_size;
   3637 	unsigned int seqsize;
   3638 	int ret = 0;
   3639 	unsigned int bytes;
   3640 
   3641 	/*
   3642 	 * Verify this is a NegotiationToken type token
   3643 	 * - check for a0(context specific identifier)
   3644 	 * - get length and verify that enoughd ata exists
   3645 	 */
   3646 	if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
   3647 		return (G_BAD_TOK_HEADER);
   3648 
   3649 	cur_size = seqsize; /* should indicate bytes remaining */
   3650 
   3651 	/*
   3652 	 * Verify the next piece, it should identify this as
   3653 	 * a strucure of type NegTokenInit.
   3654 	 */
   3655 	if (*buf++ == SEQUENCE) {
   3656 		if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
   3657 			return (G_BAD_TOK_HEADER);
   3658 		/*
   3659 		 * Make sure we have the entire buffer as described
   3660 		 */
   3661 		if (buf + seqsize > endptr)
   3662 			return (G_BAD_TOK_HEADER);
   3663 	} else {
   3664 		return (G_BAD_TOK_HEADER);
   3665 	}
   3666 
   3667 	cur_size = seqsize; /* should indicate bytes remaining */
   3668 
   3669 	/*
   3670 	 * Verify that the first blob is a sequence of mechTypes
   3671 	 */
   3672 	if (*buf++ == CONTEXT) {
   3673 		if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
   3674 			return (G_BAD_TOK_HEADER);
   3675 		/*
   3676 		 * Make sure we have the entire buffer as described
   3677 		 */
   3678 		if (buf + bytes > endptr)
   3679 			return (G_BAD_TOK_HEADER);
   3680 	} else {
   3681 		return (G_BAD_TOK_HEADER);
   3682 	}
   3683 
   3684 	/*
   3685 	 * At this point, *buf should be at the beginning of the
   3686 	 * DER encoded list of mech types that are to be negotiated.
   3687 	 */
   3688 	*buf_in = buf;
   3689 
   3690 	return (ret);
   3691 
   3692 }
   3693 
   3694 /* verify token header. */
   3695 static int
   3696 g_verify_token_header(gss_OID_const mech,
   3697 		    unsigned int *body_size,
   3698 		    unsigned char **buf_in,
   3699 		    int tok_type,
   3700 		    unsigned int toksize)
   3701 {
   3702 	unsigned char *buf = *buf_in;
   3703 	int seqsize;
   3704 	gss_OID_desc toid;
   3705 	int ret = 0;
   3706 	unsigned int bytes;
   3707 
   3708 	if (toksize-- < 1)
   3709 		return (G_BAD_TOK_HEADER);
   3710 
   3711 	if (*buf++ != HEADER_ID)
   3712 		return (G_BAD_TOK_HEADER);
   3713 
   3714 	if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
   3715 		return (G_BAD_TOK_HEADER);
   3716 
   3717 	if ((seqsize + bytes) != toksize)
   3718 		return (G_BAD_TOK_HEADER);
   3719 
   3720 	if (toksize-- < 1)
   3721 		return (G_BAD_TOK_HEADER);
   3722 
   3723 
   3724 	if (*buf++ != MECH_OID)
   3725 		return (G_BAD_TOK_HEADER);
   3726 
   3727 	if (toksize-- < 1)
   3728 		return (G_BAD_TOK_HEADER);
   3729 
   3730 	toid.length = *buf++;
   3731 
   3732 	if (toksize < toid.length)
   3733 		return (G_BAD_TOK_HEADER);
   3734 	else
   3735 		toksize -= toid.length;
   3736 
   3737 	toid.elements = buf;
   3738 	buf += toid.length;
   3739 
   3740 	if (!g_OID_equal(&toid, mech))
   3741 		ret = G_WRONG_MECH;
   3742 
   3743 	/*
   3744 	 * G_WRONG_MECH is not returned immediately because it's more important
   3745 	 * to return G_BAD_TOK_HEADER if the token header is in fact bad
   3746 	 */
   3747 	if (toksize < 2)
   3748 		return (G_BAD_TOK_HEADER);
   3749 	else
   3750 		toksize -= 2;
   3751 
   3752 	if (!ret) {
   3753 		*buf_in = buf;
   3754 		*body_size = toksize;
   3755 	}
   3756 
   3757 	return (ret);
   3758 }
   3759 
   3760 /*
   3761  * Return non-zero if the oid is one of the kerberos mech oids,
   3762  * otherwise return zero.
   3763  *
   3764  * N.B. There are 3 oids that represent the kerberos mech:
   3765  * RFC-specified GSS_MECH_KRB5_OID,
   3766  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
   3767  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
   3768  */
   3769 
   3770 static int
   3771 is_kerb_mech(gss_OID oid)
   3772 {
   3773 	int answer = 0;
   3774 	OM_uint32 minor;
   3775 	extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
   3776 
   3777 	(void) gss_test_oid_set_member(&minor,
   3778 		oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
   3779 
   3780 	return (answer);
   3781 }
   3782