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