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