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 2010 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 	ret = GSS_S_DEFECTIVE_TOKEN;
1363 	der_mechTypes.length = 0;
1364 	der_mechTypes.value = NULL;
1365 	*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1366 	supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1367 	*return_token = ERROR_TOKEN_SEND;
1368 	*negState = REJECT;
1369 	*minor_status = 0;
1370 
1371 	ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1372 			       &mechTypes, &req_flags,
1373 			       mechToken, mechListMIC);
1374 	if (ret != GSS_S_COMPLETE) {
1375 		goto cleanup;
1376 	}
1377 	if (cred != GSS_C_NO_CREDENTIAL) {
1378 		ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1379 				       NULL, &supported_mechSet);
1380 		if (ret != GSS_S_COMPLETE) {
1381 			*return_token = NO_TOKEN_SEND;
1382 			goto cleanup;
1383 		}
1384 	} else {
1385 		ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1386 					  GSS_C_ACCEPT, NULL,
1387 					  &supported_mechSet);
1388 		if (ret != GSS_S_COMPLETE) {
1389 			*return_token = NO_TOKEN_SEND;
1390 			goto cleanup;
1391 		}
1392 	}
1393 	/*
1394 	 * Select the best match between the list of mechs
1395 	 * that the initiator requested and the list that
1396 	 * the acceptor will support.
1397 	 */
1398 	mech_wanted = negotiate_mech_type(minor_status,
1399 					  supported_mechSet,
1400 					  mechTypes,
1401 					  negState);
1402 	if (*negState == REJECT) {
1403 		ret = GSS_S_BAD_MECH;
1404 		goto cleanup;
1405 	}
1406 	sc = (spnego_gss_ctx_id_t)*ctx;
1407 	if (sc != NULL) {
1408 		gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1409 		assert(mech_wanted != GSS_C_NO_OID);
1410 	} else
1411 		sc = create_spnego_ctx();
1412 	if (sc == NULL) {
1413 		ret = GSS_S_FAILURE;
1414 		*return_token = NO_TOKEN_SEND;
1415 		generic_gss_release_oid(&tmpmin, &mech_wanted);
1416 		goto cleanup;
1417 	}
1418 	sc->internal_mech = mech_wanted;
1419 	sc->DER_mechTypes = der_mechTypes;
1420 	der_mechTypes.length = 0;
1421 	der_mechTypes.value = NULL;
1422 
1423 	if (*negState == REQUEST_MIC)
1424 		sc->mic_reqd = 1;
1425 
1426 	*return_token = INIT_TOKEN_SEND;
1427 	sc->firstpass = 1;
1428 	*ctx = (gss_ctx_id_t)sc;
1429 	ret = GSS_S_COMPLETE;
1430 cleanup:
1431 	gss_release_oid_set(&tmpmin, &mechTypes);
1432 	gss_release_oid_set(&tmpmin, &supported_mechSet);
1433 	if (der_mechTypes.length != 0)
1434 		gss_release_buffer(&tmpmin, &der_mechTypes);
1435 	return ret;
1436 }
1437 
1438 static OM_uint32
1439 acc_ctx_cont(OM_uint32 *minstat,
1440 	     gss_buffer_t buf,
1441 	     gss_ctx_id_t *ctx,
1442 	     gss_buffer_t *responseToken,
1443 	     gss_buffer_t *mechListMIC,
1444 	     OM_uint32 *negState,
1445 	     send_token_flag *return_token)
1446 {
1447 	OM_uint32 ret, tmpmin;
1448 	gss_OID supportedMech;
1449 	spnego_gss_ctx_id_t sc;
1450 	unsigned int len;
1451 	unsigned char *ptr, *bufstart;
1452 
1453 	sc = (spnego_gss_ctx_id_t)*ctx;
1454 	ret = GSS_S_DEFECTIVE_TOKEN;
1455 	*negState = REJECT;
1456 	*minstat = 0;
1457 	supportedMech = GSS_C_NO_OID;
1458 	*return_token = ERROR_TOKEN_SEND;
1459 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1460 
1461 	ptr = bufstart = buf->value;
1462 #define REMAIN (buf->length - (ptr - bufstart))
1463 	if (REMAIN > INT_MAX)
1464 		return GSS_S_DEFECTIVE_TOKEN;
1465 
1466 	/*
1467 	 * Attempt to work with old Sun SPNEGO.
1468 	 */
1469 	if (*ptr == HEADER_ID) {
1470 		ret = g_verify_token_header(gss_mech_spnego,
1471 					    &len, &ptr, 0, REMAIN);
1472 		if (ret) {
1473 			*minstat = ret;
1474 			return GSS_S_DEFECTIVE_TOKEN;
1475 		}
1476 	}
1477 	if (*ptr != (CONTEXT | 0x01)) {
1478 		return GSS_S_DEFECTIVE_TOKEN;
1479 	}
1480 	ret = get_negTokenResp(minstat, ptr, REMAIN,
1481 			       negState, &supportedMech,
1482 			       responseToken, mechListMIC);
1483 	if (ret != GSS_S_COMPLETE)
1484 		goto cleanup;
1485 
1486 	if (*responseToken == GSS_C_NO_BUFFER &&
1487 	    *mechListMIC == GSS_C_NO_BUFFER) {
1488 
1489 		ret = GSS_S_DEFECTIVE_TOKEN;
1490 		goto cleanup;
1491 	}
1492 	if (supportedMech != GSS_C_NO_OID) {
1493 		ret = GSS_S_DEFECTIVE_TOKEN;
1494 		goto cleanup;
1495 	}
1496 	sc->firstpass = 0;
1497 	*negState = ACCEPT_INCOMPLETE;
1498 	*return_token = CONT_TOKEN_SEND;
1499 cleanup:
1500 	if (supportedMech != GSS_C_NO_OID) {
1501 		generic_gss_release_oid(&tmpmin, &supportedMech);
1502 	}
1503 	return ret;
1504 #undef REMAIN
1505 }
1506 
1507 /*
1508  * Verify that mech OID is either exactly the same as the negotiated
1509  * mech OID, or is a mech OID supported by the negotiated mech.  MS
1510  * implementations can list a most preferred mech using an incorrect
1511  * krb5 OID while emitting a krb5 initiator mech token having the
1512  * correct krb5 mech OID.
1513  */
1514 static OM_uint32
1515 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1516 		spnego_gss_ctx_id_t sc, gss_OID mechoid,
1517 		OM_uint32 *negState, send_token_flag *tokflag)
1518 {
1519 	OM_uint32 ret, tmpmin;
1520 	gss_mechanism mech = NULL;
1521 	gss_OID_set mech_set = GSS_C_NO_OID_SET;
1522 	int present = 0;
1523 
1524 	if (g_OID_equal(sc->internal_mech, mechoid))
1525 		return GSS_S_COMPLETE;
1526 
1527 	/*
1528 	 * SUNW17PACresync
1529 	 * If both mechs are kerb, we are done.
1530 	 */
1531 	if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
1532 		return GSS_S_COMPLETE;
1533 	}
1534 
1535 	mech = gssint_get_mechanism(mechoid);
1536 	if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1537 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1538 		map_errcode(minor_status);
1539 		*negState = REJECT;
1540 		*tokflag = ERROR_TOKEN_SEND;
1541 		return GSS_S_BAD_MECH;
1542 	}
1543 	ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
1544 	if (ret != GSS_S_COMPLETE) {
1545 		*tokflag = NO_TOKEN_SEND;
1546 		map_error(minor_status, mech);
1547 		goto cleanup;
1548 	}
1549 	ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
1550 				      mech_set, &present);
1551 	if (ret != GSS_S_COMPLETE)
1552 		goto cleanup;
1553 	if (!present) {
1554 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1555 		map_errcode(minor_status);
1556 		*negState = REJECT;
1557 		*tokflag = ERROR_TOKEN_SEND;
1558 		ret = GSS_S_BAD_MECH;
1559 	}
1560 cleanup:
1561 	gss_release_oid_set(&tmpmin, &mech_set);
1562 	return ret;
1563 }
1564 #ifndef LEAN_CLIENT
1565 /*
1566  * Wrap call to gss_accept_sec_context() and update state
1567  * accordingly.
1568  */
1569 static OM_uint32
1570 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1571 		 gss_cred_id_t cred, gss_buffer_t mechtok_in,
1572 		 gss_OID *mech_type, gss_buffer_t mechtok_out,
1573 		 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1574 		 gss_cred_id_t *delegated_cred_handle,
1575 		 OM_uint32 *negState, send_token_flag *tokflag)
1576 {
1577 	OM_uint32 ret;
1578 	gss_OID_desc mechoid;
1579 
1580 	if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1581 		/*
1582 		 * mechoid is an alias; don't free it.
1583 		 */
1584 		ret = gssint_get_mech_type(&mechoid, mechtok_in);
1585 		if (ret != GSS_S_COMPLETE) {
1586 			*tokflag = NO_TOKEN_SEND;
1587 			return ret;
1588 		}
1589 		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1590 				      negState, tokflag);
1591 		if (ret != GSS_S_COMPLETE)
1592 			return ret;
1593 	}
1594 
1595 	ret = gss_accept_sec_context(minor_status,
1596 				     &sc->ctx_handle,
1597 				     cred,
1598 				     mechtok_in,
1599 				     GSS_C_NO_CHANNEL_BINDINGS,
1600 				     &sc->internal_name,
1601 				     mech_type,
1602 				     mechtok_out,
1603 				     &sc->ctx_flags,
1604 				     time_rec,
1605 				     delegated_cred_handle);
1606 
1607 	if (ret == GSS_S_COMPLETE) {
1608 #ifdef MS_BUG_TEST
1609 		/*
1610 		 * Force MIC to be not required even if we previously
1611 		 * requested a MIC.
1612 		 */
1613 		char *envstr = getenv("MS_FORCE_NO_MIC");
1614 
1615 		if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1616 		    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1617 		    sc->mic_reqd) {
1618 
1619 			sc->mic_reqd = 0;
1620 		}
1621 #endif
1622 		sc->mech_complete = 1;
1623 		if (ret_flags != NULL)
1624 			*ret_flags = sc->ctx_flags;
1625 
1626 		if (!sc->mic_reqd) {
1627 			*negState = ACCEPT_COMPLETE;
1628 			ret = GSS_S_COMPLETE;
1629 		} else {
1630 			ret = GSS_S_CONTINUE_NEEDED;
1631 		}
1632 	} else if (ret != GSS_S_CONTINUE_NEEDED) {
1633 		*negState = REJECT;
1634 		*tokflag = ERROR_TOKEN_SEND;
1635 	}
1636 	return ret;
1637 }
1638 
1639 /*ARGSUSED*/
1640 OM_uint32
1641 glue_spnego_gss_accept_sec_context(
1642 			    void *context,
1643 			    OM_uint32 *minor_status,
1644 			    gss_ctx_id_t *context_handle,
1645 			    gss_cred_id_t verifier_cred_handle,
1646 			    gss_buffer_t input_token,
1647 			    gss_channel_bindings_t input_chan_bindings,
1648 			    gss_name_t *src_name,
1649 			    gss_OID *mech_type,
1650 			    gss_buffer_t output_token,
1651 			    OM_uint32 *ret_flags,
1652 			    OM_uint32 *time_rec,
1653 			    gss_cred_id_t *delegated_cred_handle)
1654 {
1655 	return(spnego_gss_accept_sec_context(
1656 		    minor_status,
1657 		    context_handle,
1658 		    verifier_cred_handle,
1659 		    input_token,
1660 		    input_chan_bindings,
1661 		    src_name,
1662 		    mech_type,
1663 		    output_token,
1664 		    ret_flags,
1665 		    time_rec,
1666 		    delegated_cred_handle));
1667 }
1668 
1669 /*ARGSUSED*/
1670 OM_uint32
1671 spnego_gss_accept_sec_context(
1672 			    OM_uint32 *minor_status,
1673 			    gss_ctx_id_t *context_handle,
1674 			    gss_cred_id_t verifier_cred_handle,
1675 			    gss_buffer_t input_token,
1676 			    gss_channel_bindings_t input_chan_bindings,
1677 			    gss_name_t *src_name,
1678 			    gss_OID *mech_type,
1679 			    gss_buffer_t output_token,
1680 			    OM_uint32 *ret_flags,
1681 			    OM_uint32 *time_rec,
1682 			    gss_cred_id_t *delegated_cred_handle)
1683 {
1684 	OM_uint32 ret, tmpmin, negState;
1685 	send_token_flag return_token;
1686 	gss_buffer_t mechtok_in, mic_in, mic_out;
1687 	gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1688 	spnego_gss_ctx_id_t sc = NULL;
1689 	OM_uint32 mechstat = GSS_S_FAILURE;
1690 	int sendTokenInit = 0, tmpret;
1691 
1692 	mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1693 
1694 	if (minor_status != NULL)
1695 		*minor_status = 0;
1696 	if (output_token != GSS_C_NO_BUFFER) {
1697 		output_token->length = 0;
1698 		output_token->value = NULL;
1699 	}
1700 
1701 
1702 	if (minor_status == NULL ||
1703 	    output_token == GSS_C_NO_BUFFER ||
1704 	    context_handle == NULL) {
1705 		return GSS_S_CALL_INACCESSIBLE_WRITE;
1706 	}
1707 
1708 	if (input_token == GSS_C_NO_BUFFER) {
1709 		return GSS_S_CALL_INACCESSIBLE_READ;
1710 	}
1711 
1712 	sc = (spnego_gss_ctx_id_t)*context_handle;
1713 	if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1714 		if (src_name != NULL)
1715 			*src_name = GSS_C_NO_NAME;
1716 		if (mech_type != NULL)
1717 			*mech_type = GSS_C_NO_OID;
1718 		if (time_rec != NULL)
1719 			*time_rec = 0;
1720 		if (ret_flags != NULL)
1721 			*ret_flags = 0;
1722 		if (delegated_cred_handle != NULL)
1723 			*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1724 		if (input_token->length == 0) {
1725 			ret = acc_ctx_hints(minor_status,
1726 					    context_handle,
1727 					    verifier_cred_handle,
1728 					    &mic_out,
1729 					    &negState,
1730 					    &return_token);
1731 			if (ret != GSS_S_COMPLETE)
1732 				goto cleanup;
1733 			sendTokenInit = 1;
1734 			ret = GSS_S_CONTINUE_NEEDED;
1735 		} else {
1736 			/* Can set negState to REQUEST_MIC */
1737 			ret = acc_ctx_new(minor_status, input_token,
1738 					  context_handle, verifier_cred_handle,
1739 					  &mechtok_in, &mic_in,
1740 					  &negState, &return_token);
1741 			if (ret != GSS_S_COMPLETE)
1742 				goto cleanup;
1743 			ret = GSS_S_CONTINUE_NEEDED;
1744 		}
1745 	} else {
1746 		/* Can set negState to ACCEPT_INCOMPLETE */
1747 		ret = acc_ctx_cont(minor_status, input_token,
1748 				   context_handle, &mechtok_in,
1749 				   &mic_in, &negState, &return_token);
1750 		if (ret != GSS_S_COMPLETE)
1751 			goto cleanup;
1752 		ret = GSS_S_CONTINUE_NEEDED;
1753 	}
1754 
1755 	sc = (spnego_gss_ctx_id_t)*context_handle;
1756 	/*
1757 	 * Handle mechtok_in and mic_in only if they are
1758 	 * present in input_token.  If neither is present, whether
1759 	 * this is an error depends on whether this is the first
1760 	 * round-trip.  RET is set to a default value according to
1761 	 * whether it is the first round-trip.
1762 	 */
1763 	mechstat = GSS_S_FAILURE;
1764 	if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1765 		ret = acc_ctx_call_acc(minor_status, sc,
1766 				       verifier_cred_handle, mechtok_in,
1767 				       mech_type, &mechtok_out,
1768 				       ret_flags, time_rec,
1769 				       delegated_cred_handle,
1770 				       &negState, &return_token);
1771 	} else if (negState == REQUEST_MIC) {
1772 		mechstat = GSS_S_CONTINUE_NEEDED;
1773 	}
1774 
1775 	if (!HARD_ERROR(ret) && sc->mech_complete &&
1776 	    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1777 
1778 		ret = handle_mic(minor_status, mic_in,
1779 				 (mechtok_out.length != 0),
1780 				 sc, &mic_out,
1781 				 &negState, &return_token);
1782 	}
1783 
1784 cleanup:
1785 	if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1786 		assert(sc != NULL);
1787 		tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1788 						   GSS_C_NO_BUFFER,
1789 						   return_token, output_token);
1790 		if (tmpret < 0)
1791 			ret = GSS_S_FAILURE;
1792 	} else if (return_token != NO_TOKEN_SEND &&
1793 		   return_token != CHECK_MIC) {
1794 		tmpret = make_spnego_tokenTarg_msg(negState,
1795 						   sc ? sc->internal_mech :
1796 						   GSS_C_NO_OID,
1797 						   &mechtok_out, mic_out,
1798 						   return_token,
1799 						   output_token);
1800 		if (tmpret < 0)
1801 			ret = GSS_S_FAILURE;
1802 	}
1803 	if (ret == GSS_S_COMPLETE) {
1804 		*context_handle = (gss_ctx_id_t)sc->ctx_handle;
1805 		if (sc->internal_name != GSS_C_NO_NAME &&
1806 		    src_name != NULL) {
1807 			*src_name = sc->internal_name;
1808 		}
1809 		release_spnego_ctx(&sc);
1810 	}
1811 	gss_release_buffer(&tmpmin, &mechtok_out);
1812 	if (mechtok_in != GSS_C_NO_BUFFER) {
1813 		gss_release_buffer(&tmpmin, mechtok_in);
1814 		free(mechtok_in);
1815 	}
1816 	if (mic_in != GSS_C_NO_BUFFER) {
1817 		gss_release_buffer(&tmpmin, mic_in);
1818 		free(mic_in);
1819 	}
1820 	if (mic_out != GSS_C_NO_BUFFER) {
1821 		gss_release_buffer(&tmpmin, mic_out);
1822 		free(mic_out);
1823 	}
1824 	return ret;
1825 }
1826 #endif /*  LEAN_CLIENT */
1827 
1828 /*ARGSUSED*/
1829 OM_uint32
1830 glue_spnego_gss_display_status(
1831 	void *context,
1832 		OM_uint32 *minor_status,
1833 		OM_uint32 status_value,
1834 		int status_type,
1835 		gss_OID mech_type,
1836 		OM_uint32 *message_context,
1837 		gss_buffer_t status_string)
1838 {
1839 	return (spnego_gss_display_status(minor_status,
1840 					status_value,
1841 					status_type,
1842 					mech_type,
1843 					message_context,
1844 					status_string));
1845 }
1846 
1847 /*ARGSUSED*/
1848 OM_uint32
1849 spnego_gss_display_status(
1850 		OM_uint32 *minor_status,
1851 		OM_uint32 status_value,
1852 		int status_type,
1853 		gss_OID mech_type,
1854 		OM_uint32 *message_context,
1855 		gss_buffer_t status_string)
1856 {
1857 	dsyslog("Entering display_status\n");
1858 
1859 	*message_context = 0;
1860 	switch (status_value) {
1861 	    case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1862 		/* CSTYLED */
1863 		*status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
1864 		break;
1865 	    case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1866 		/* CSTYLED */
1867 		*status_string = make_err_msg("SPNEGO failed to acquire creds");
1868 		break;
1869 	    case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1870 		/* CSTYLED */
1871 		*status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
1872 		break;
1873 	    case ERR_SPNEGO_NEGOTIATION_FAILED:
1874 		/* CSTYLED */
1875 		*status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
1876 		break;
1877 	    case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1878 		/* CSTYLED */
1879 		*status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
1880 		break;
1881 	    default:
1882 		status_string->length = 0;
1883 		status_string->value = "";
1884 		break;
1885 	}
1886 
1887 	dsyslog("Leaving display_status\n");
1888 	return (GSS_S_COMPLETE);
1889 }
1890 
1891 /*ARGSUSED*/
1892 OM_uint32
1893 glue_spnego_gss_import_name(
1894 	void *context,
1895 		    OM_uint32 *minor_status,
1896 		    gss_buffer_t input_name_buffer,
1897 		    gss_OID input_name_type,
1898 		    gss_name_t *output_name)
1899 {
1900 	return(spnego_gss_import_name(minor_status,
1901 				    input_name_buffer,
1902 				    input_name_type,
1903 				    output_name));
1904 }
1905 
1906 /*ARGSUSED*/
1907 OM_uint32
1908 spnego_gss_import_name(
1909 		    OM_uint32 *minor_status,
1910 		    gss_buffer_t input_name_buffer,
1911 		    gss_OID input_name_type,
1912 		    gss_name_t *output_name)
1913 {
1914 	OM_uint32 status;
1915 
1916 	dsyslog("Entering import_name\n");
1917 
1918 	status = gss_import_name(minor_status, input_name_buffer,
1919 			input_name_type, output_name);
1920 
1921 	dsyslog("Leaving import_name\n");
1922 	return (status);
1923 }
1924 
1925 /*ARGSUSED*/
1926 OM_uint32
1927 glue_spnego_gss_release_name(
1928 	void *context,
1929 			OM_uint32 *minor_status,
1930 			gss_name_t *input_name)
1931 {
1932 	return(spnego_gss_release_name(minor_status, input_name));
1933 }
1934 
1935 /*ARGSUSED*/
1936 OM_uint32
1937 spnego_gss_release_name(
1938 			OM_uint32 *minor_status,
1939 			gss_name_t *input_name)
1940 {
1941 	OM_uint32 status;
1942 
1943 	dsyslog("Entering release_name\n");
1944 
1945 	status = gss_release_name(minor_status, input_name);
1946 
1947 	dsyslog("Leaving release_name\n");
1948 	return (status);
1949 }
1950 
1951 /*ARGSUSED*/
1952 OM_uint32
1953 glue_spnego_gss_compare_name(
1954 	void *context,
1955 	OM_uint32 *minor_status,
1956 	const gss_name_t name1,
1957 	const gss_name_t name2,
1958 	int *name_equal)
1959 {
1960 	return(spnego_gss_compare_name(minor_status,
1961 				name1,
1962 				name2,
1963 				name_equal));
1964 }
1965 /*ARGSUSED*/
1966 OM_uint32
1967 spnego_gss_compare_name(
1968 			OM_uint32 *minor_status,
1969 			const gss_name_t name1,
1970 			const gss_name_t name2,
1971 			int *name_equal)
1972 {
1973 	OM_uint32 status = GSS_S_COMPLETE;
1974 	dsyslog("Entering compare_name\n");
1975 
1976 	status = gss_compare_name(minor_status, name1, name2, name_equal);
1977 
1978 	dsyslog("Leaving compare_name\n");
1979 	return (status);
1980 }
1981 
1982 /*ARGSUSED*/
1983 OM_uint32
1984 glue_spnego_gss_display_name(
1985  	void *context,
1986 			OM_uint32 *minor_status,
1987 			gss_name_t input_name,
1988 			gss_buffer_t output_name_buffer,
1989 			gss_OID *output_name_type)
1990 {
1991 	return(spnego_gss_display_name(
1992 		    minor_status,
1993 		    input_name,
1994 		    output_name_buffer,
1995 		    output_name_type));
1996 }
1997 
1998 /*ARGSUSED*/
1999 OM_uint32
2000 spnego_gss_display_name(
2001 			OM_uint32 *minor_status,
2002 			gss_name_t input_name,
2003 			gss_buffer_t output_name_buffer,
2004 			gss_OID *output_name_type)
2005 {
2006 	OM_uint32 status = GSS_S_COMPLETE;
2007 	dsyslog("Entering display_name\n");
2008 
2009 	status = gss_display_name(minor_status, input_name,
2010 			output_name_buffer, output_name_type);
2011 
2012 	dsyslog("Leaving display_name\n");
2013 	return (status);
2014 }
2015 
2016 
2017 /*ARGSUSED*/
2018 OM_uint32
2019 glue_spnego_gss_inquire_names_for_mech(
2020 	void		*context,
2021 	OM_uint32	*minor_status,
2022 	gss_OID		mechanism,
2023 	gss_OID_set	*name_types)
2024 {
2025 	return(spnego_gss_inquire_names_for_mech(minor_status,
2026 						mechanism,
2027 						name_types));
2028 }
2029 /*ARGSUSED*/
2030 OM_uint32
2031 spnego_gss_inquire_names_for_mech(
2032 				OM_uint32	*minor_status,
2033 				gss_OID		mechanism,
2034 				gss_OID_set	*name_types)
2035 {
2036 	OM_uint32   major, minor;
2037 
2038 	dsyslog("Entering inquire_names_for_mech\n");
2039 	/*
2040 	 * We only know how to handle our own mechanism.
2041 	 */
2042 	if ((mechanism != GSS_C_NULL_OID) &&
2043 	    !g_OID_equal(gss_mech_spnego, mechanism)) {
2044 		*minor_status = 0;
2045 		return (GSS_S_FAILURE);
2046 	}
2047 
2048 	major = gss_create_empty_oid_set(minor_status, name_types);
2049 	if (major == GSS_S_COMPLETE) {
2050 		/* Now add our members. */
2051 		if (((major = gss_add_oid_set_member(minor_status,
2052 				(gss_OID) GSS_C_NT_USER_NAME,
2053 				name_types)) == GSS_S_COMPLETE) &&
2054 		    ((major = gss_add_oid_set_member(minor_status,
2055 				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2056 				name_types)) == GSS_S_COMPLETE) &&
2057 		    ((major = gss_add_oid_set_member(minor_status,
2058 				(gss_OID) GSS_C_NT_STRING_UID_NAME,
2059 				name_types)) == GSS_S_COMPLETE)) {
2060 			major = gss_add_oid_set_member(minor_status,
2061 				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2062 				name_types);
2063 		}
2064 
2065 		if (major != GSS_S_COMPLETE)
2066 			(void) gss_release_oid_set(&minor, name_types);
2067 	}
2068 
2069 	dsyslog("Leaving inquire_names_for_mech\n");
2070 	return (major);
2071 }
2072 
2073 OM_uint32
2074 spnego_gss_unwrap(
2075 		OM_uint32 *minor_status,
2076 		gss_ctx_id_t context_handle,
2077 		gss_buffer_t input_message_buffer,
2078 		gss_buffer_t output_message_buffer,
2079 		int *conf_state,
2080 		gss_qop_t *qop_state)
2081 {
2082 	OM_uint32 ret;
2083 	ret = gss_unwrap(minor_status,
2084 			context_handle,
2085 			input_message_buffer,
2086 			output_message_buffer,
2087 			conf_state,
2088 			qop_state);
2089 
2090 	return (ret);
2091 }
2092 
2093 OM_uint32
2094 spnego_gss_wrap(
2095 		OM_uint32 *minor_status,
2096 		gss_ctx_id_t context_handle,
2097 		int conf_req_flag,
2098 		gss_qop_t qop_req,
2099 		gss_buffer_t input_message_buffer,
2100 		int *conf_state,
2101 		gss_buffer_t output_message_buffer)
2102 {
2103 	OM_uint32 ret;
2104 	ret = gss_wrap(minor_status,
2105 		    context_handle,
2106 		    conf_req_flag,
2107 		    qop_req,
2108 		    input_message_buffer,
2109 		    conf_state,
2110 		    output_message_buffer);
2111 
2112 	return (ret);
2113 }
2114 
2115 OM_uint32
2116 spnego_gss_process_context_token(
2117 				OM_uint32	*minor_status,
2118 				const gss_ctx_id_t context_handle,
2119 				const gss_buffer_t token_buffer)
2120 {
2121 	OM_uint32 ret;
2122 	ret = gss_process_context_token(minor_status,
2123 					context_handle,
2124 					token_buffer);
2125 
2126 	return (ret);
2127 }
2128 
2129 OM_uint32
2130 glue_spnego_gss_delete_sec_context(
2131 	void *context,
2132 			    OM_uint32 *minor_status,
2133 			    gss_ctx_id_t *context_handle,
2134 			    gss_buffer_t output_token)
2135 {
2136 	return(spnego_gss_delete_sec_context(minor_status,
2137 					    context_handle, output_token));
2138 }
2139 
2140 OM_uint32
2141 spnego_gss_delete_sec_context(
2142 			    OM_uint32 *minor_status,
2143 			    gss_ctx_id_t *context_handle,
2144 			    gss_buffer_t output_token)
2145 {
2146 	OM_uint32 ret = GSS_S_COMPLETE;
2147 	spnego_gss_ctx_id_t *ctx =
2148 		    (spnego_gss_ctx_id_t *)context_handle;
2149 
2150 	if (context_handle == NULL)
2151 		return (GSS_S_FAILURE);
2152 
2153 	/*
2154 	 * If this is still an SPNEGO mech, release it locally.
2155 	 */
2156 	if (*ctx != NULL &&
2157 	    (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2158 		(void) release_spnego_ctx(ctx);
2159                 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2160 		if (output_token) {
2161 			output_token->length = 0;
2162 			output_token->value = NULL;
2163 		}
2164 	} else {
2165 		ret = gss_delete_sec_context(minor_status,
2166 				    context_handle,
2167 				    output_token);
2168 	}
2169 
2170 	return (ret);
2171 }
2172 
2173 OM_uint32
2174 glue_spnego_gss_context_time(
2175 	void *context,
2176 	OM_uint32	*minor_status,
2177 	const gss_ctx_id_t context_handle,
2178 	OM_uint32	*time_rec)
2179 {
2180 	return(spnego_gss_context_time(minor_status,
2181 				    context_handle,
2182 				    time_rec));
2183 }
2184 
2185 OM_uint32
2186 spnego_gss_context_time(
2187 			OM_uint32	*minor_status,
2188 			const gss_ctx_id_t context_handle,
2189 			OM_uint32	*time_rec)
2190 {
2191 	OM_uint32 ret;
2192 	ret = gss_context_time(minor_status,
2193 			    context_handle,
2194 			    time_rec);
2195 	return (ret);
2196 }
2197 
2198 #ifndef LEAN_CLIENT
2199 OM_uint32
2200 glue_spnego_gss_export_sec_context(
2201 	void *context,
2202 	OM_uint32	  *minor_status,
2203 	gss_ctx_id_t *context_handle,
2204 	gss_buffer_t interprocess_token)
2205 {
2206 	return(spnego_gss_export_sec_context(minor_status,
2207 				    context_handle,
2208 				    interprocess_token));
2209 }
2210 OM_uint32
2211 spnego_gss_export_sec_context(
2212 			    OM_uint32	  *minor_status,
2213 			    gss_ctx_id_t *context_handle,
2214 			    gss_buffer_t interprocess_token)
2215 {
2216 	OM_uint32 ret;
2217 	ret = gss_export_sec_context(minor_status,
2218 				    context_handle,
2219 				    interprocess_token);
2220 	return (ret);
2221 }
2222 
2223 OM_uint32
2224 glue_spnego_gss_import_sec_context(
2225 	void *context,
2226 	OM_uint32		*minor_status,
2227 	const gss_buffer_t	interprocess_token,
2228 	gss_ctx_id_t		*context_handle)
2229 {
2230 	return(spnego_gss_import_sec_context(minor_status,
2231 				    interprocess_token,
2232 				    context_handle));
2233 }
2234 OM_uint32
2235 spnego_gss_import_sec_context(
2236 	OM_uint32		*minor_status,
2237 	const gss_buffer_t	interprocess_token,
2238 	gss_ctx_id_t		*context_handle)
2239 {
2240 	OM_uint32 ret;
2241 	ret = gss_import_sec_context(minor_status,
2242 				    interprocess_token,
2243 				    context_handle);
2244 	return (ret);
2245 }
2246 #endif /* LEAN_CLIENT */
2247 
2248 OM_uint32
2249 glue_spnego_gss_inquire_context(
2250 	void *context,
2251 			OM_uint32	*minor_status,
2252 			const gss_ctx_id_t context_handle,
2253 			gss_name_t	*src_name,
2254 			gss_name_t	*targ_name,
2255 			OM_uint32	*lifetime_rec,
2256 			gss_OID		*mech_type,
2257 			OM_uint32	*ctx_flags,
2258 			int		*locally_initiated,
2259 			int		*opened)
2260 {
2261 	return(spnego_gss_inquire_context(
2262 		    minor_status,
2263 		    context_handle,
2264 		    src_name,
2265 		    targ_name,
2266 		    lifetime_rec,
2267 		    mech_type,
2268 		    ctx_flags,
2269 		    locally_initiated,
2270 		    opened));
2271 }
2272 
2273 OM_uint32
2274 spnego_gss_inquire_context(
2275 			OM_uint32	*minor_status,
2276 			const gss_ctx_id_t context_handle,
2277 			gss_name_t	*src_name,
2278 			gss_name_t	*targ_name,
2279 			OM_uint32	*lifetime_rec,
2280 			gss_OID		*mech_type,
2281 			OM_uint32	*ctx_flags,
2282 			int		*locally_initiated,
2283 			int		*opened)
2284 {
2285 	OM_uint32 ret = GSS_S_COMPLETE;
2286 
2287 	ret = gss_inquire_context(minor_status,
2288 				context_handle,
2289 				src_name,
2290 				targ_name,
2291 				lifetime_rec,
2292 				mech_type,
2293 				ctx_flags,
2294 				locally_initiated,
2295 				opened);
2296 
2297 	return (ret);
2298 }
2299 
2300 OM_uint32
2301 glue_spnego_gss_wrap_size_limit(
2302 	void *context,
2303 	OM_uint32	*minor_status,
2304 	const gss_ctx_id_t context_handle,
2305 	int		conf_req_flag,
2306 	gss_qop_t	qop_req,
2307 	OM_uint32	req_output_size,
2308 	OM_uint32	*max_input_size)
2309 {
2310 	return(spnego_gss_wrap_size_limit(minor_status,
2311 				context_handle,
2312 				conf_req_flag,
2313 				qop_req,
2314 				req_output_size,
2315 				max_input_size));
2316 }
2317 
2318 OM_uint32
2319 spnego_gss_wrap_size_limit(
2320 	OM_uint32	*minor_status,
2321 	const gss_ctx_id_t context_handle,
2322 	int		conf_req_flag,
2323 	gss_qop_t	qop_req,
2324 	OM_uint32	req_output_size,
2325 	OM_uint32	*max_input_size)
2326 {
2327 	OM_uint32 ret;
2328 	ret = gss_wrap_size_limit(minor_status,
2329 				context_handle,
2330 				conf_req_flag,
2331 				qop_req,
2332 				req_output_size,
2333 				max_input_size);
2334 	return (ret);
2335 }
2336 
2337 #if 0 /* SUNW17PACresync */
2338 OM_uint32
2339 spnego_gss_get_mic(
2340 		OM_uint32 *minor_status,
2341 		const gss_ctx_id_t context_handle,
2342 		gss_qop_t  qop_req,
2343 		const gss_buffer_t message_buffer,
2344 		gss_buffer_t message_token)
2345 {
2346 	OM_uint32 ret;
2347 	ret = gss_get_mic(minor_status,
2348 		    context_handle,
2349 		    qop_req,
2350 		    message_buffer,
2351 		    message_token);
2352 	return (ret);
2353 }
2354 #endif
2355 
2356 OM_uint32
2357 spnego_gss_verify_mic(
2358 		OM_uint32 *minor_status,
2359 		const gss_ctx_id_t context_handle,
2360 		const gss_buffer_t msg_buffer,
2361 		const gss_buffer_t token_buffer,
2362 		gss_qop_t *qop_state)
2363 {
2364 	OM_uint32 ret;
2365 	ret = gss_verify_mic(minor_status,
2366 			    context_handle,
2367 			    msg_buffer,
2368 			    token_buffer,
2369 			    qop_state);
2370 	return (ret);
2371 }
2372 
2373 OM_uint32
2374 spnego_gss_inquire_sec_context_by_oid(
2375 		OM_uint32 *minor_status,
2376 		const gss_ctx_id_t context_handle,
2377 		const gss_OID desired_object,
2378 		gss_buffer_set_t *data_set)
2379 {
2380 	OM_uint32 ret;
2381 	ret = gss_inquire_sec_context_by_oid(minor_status,
2382 			    context_handle,
2383 			    desired_object,
2384 			    data_set);
2385 	return (ret);
2386 }
2387 
2388 /*
2389  * SUNW17PACresync
2390  * These GSS funcs not needed yet, so disable them.
2391  * Revisit for full 1.7 resync.
2392  */
2393 #if 0
2394 OM_uint32
2395 spnego_gss_set_sec_context_option(
2396 		OM_uint32 *minor_status,
2397 		gss_ctx_id_t *context_handle,
2398 		const gss_OID desired_object,
2399 		const gss_buffer_t value)
2400 {
2401 	OM_uint32 ret;
2402 	ret = gss_set_sec_context_option(minor_status,
2403 			    context_handle,
2404 			    desired_object,
2405 			    value);
2406 	return (ret);
2407 }
2408 
2409 OM_uint32
2410 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2411 		     gss_ctx_id_t context_handle,
2412 		     int conf_req_flag,
2413 		     gss_qop_t qop_req,
2414 		     gss_buffer_t input_assoc_buffer,
2415 		     gss_buffer_t input_payload_buffer,
2416 		     int *conf_state,
2417 		     gss_buffer_t output_message_buffer)
2418 {
2419 	OM_uint32 ret;
2420 	ret = gss_wrap_aead(minor_status,
2421 			    context_handle,
2422 			    conf_req_flag,
2423 			    qop_req,
2424 			    input_assoc_buffer,
2425 			    input_payload_buffer,
2426 			    conf_state,
2427 			    output_message_buffer);
2428 
2429 	return (ret);
2430 }
2431 
2432 OM_uint32
2433 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2434 		       gss_ctx_id_t context_handle,
2435 		       gss_buffer_t input_message_buffer,
2436 		       gss_buffer_t input_assoc_buffer,
2437 		       gss_buffer_t output_payload_buffer,
2438 		       int *conf_state,
2439 		       gss_qop_t *qop_state)
2440 {
2441 	OM_uint32 ret;
2442 	ret = gss_unwrap_aead(minor_status,
2443 			      context_handle,
2444 			      input_message_buffer,
2445 			      input_assoc_buffer,
2446 			      output_payload_buffer,
2447 			      conf_state,
2448 			      qop_state);
2449 	return (ret);
2450 }
2451 
2452 OM_uint32
2453 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2454 		    gss_ctx_id_t context_handle,
2455 		    int conf_req_flag,
2456 		    gss_qop_t qop_req,
2457 		    int *conf_state,
2458 		    gss_iov_buffer_desc *iov,
2459 		    int iov_count)
2460 {
2461 	OM_uint32 ret;
2462 	ret = gss_wrap_iov(minor_status,
2463 			   context_handle,
2464 			   conf_req_flag,
2465 			   qop_req,
2466 			   conf_state,
2467 			   iov,
2468 			   iov_count);
2469 	return (ret);
2470 }
2471 
2472 OM_uint32
2473 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2474 		      gss_ctx_id_t context_handle,
2475 		      int *conf_state,
2476 		      gss_qop_t *qop_state,
2477 		      gss_iov_buffer_desc *iov,
2478 		      int iov_count)
2479 {
2480 	OM_uint32 ret;
2481 	ret = gss_unwrap_iov(minor_status,
2482 			     context_handle,
2483 			     conf_state,
2484 			     qop_state,
2485 			     iov,
2486 			     iov_count);
2487 	return (ret);
2488 }
2489 
2490 OM_uint32
2491 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2492 			   gss_ctx_id_t context_handle,
2493 			   int conf_req_flag,
2494 			   gss_qop_t qop_req,
2495 			   int *conf_state,
2496 			   gss_iov_buffer_desc *iov,
2497 			   int iov_count)
2498 {
2499 	OM_uint32 ret;
2500 	ret = gss_wrap_iov_length(minor_status,
2501 				  context_handle,
2502 				  conf_req_flag,
2503 				  qop_req,
2504 				  conf_state,
2505 				  iov,
2506 				  iov_count);
2507 	return (ret);
2508 }
2509 
2510 
2511 OM_uint32
2512 spnego_gss_complete_auth_token(
2513 		OM_uint32 *minor_status,
2514 		const gss_ctx_id_t context_handle,
2515 		gss_buffer_t input_message_buffer)
2516 {
2517 	OM_uint32 ret;
2518 	ret = gss_complete_auth_token(minor_status,
2519 				      context_handle,
2520 				      input_message_buffer);
2521 	return (ret);
2522 }
2523 #endif /* 0 */
2524 
2525 /*
2526  * We will release everything but the ctx_handle so that it
2527  * can be passed back to init/accept context. This routine should
2528  * not be called until after the ctx_handle memory is assigned to
2529  * the supplied context handle from init/accept context.
2530  */
2531 static void
2532 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2533 {
2534 	spnego_gss_ctx_id_t context;
2535 	OM_uint32 minor_stat;
2536 	context = *ctx;
2537 
2538 	if (context != NULL) {
2539 		(void) gss_release_buffer(&minor_stat,
2540 					&context->DER_mechTypes);
2541 
2542 		(void) generic_gss_release_oid(&minor_stat,
2543 				&context->internal_mech);
2544 
2545 		if (context->optionStr != NULL) {
2546 			free(context->optionStr);
2547 			context->optionStr = NULL;
2548 		}
2549 		free(context);
2550 		*ctx = NULL;
2551 	}
2552 }
2553 
2554 /*
2555  * Can't use gss_indicate_mechs by itself to get available mechs for
2556  * SPNEGO because it will also return the SPNEGO mech and we do not
2557  * want to consider SPNEGO as an available security mech for
2558  * negotiation. For this reason, get_available_mechs will return
2559  * all available mechs except SPNEGO.
2560  *
2561  * If a ptr to a creds list is given, this function will attempt
2562  * to acquire creds for the creds given and trim the list of
2563  * returned mechanisms to only those for which creds are valid.
2564  *
2565  */
2566 static OM_uint32
2567 get_available_mechs(OM_uint32 *minor_status,
2568 	gss_name_t name, gss_cred_usage_t usage,
2569 	gss_cred_id_t *creds, gss_OID_set *rmechs)
2570 {
2571 	unsigned int	i;
2572 	int		found = 0;
2573 	OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2574 	gss_OID_set mechs, goodmechs;
2575 
2576 	major_status = gss_indicate_mechs(minor_status, &mechs);
2577 
2578 	if (major_status != GSS_S_COMPLETE) {
2579 		return (major_status);
2580 	}
2581 
2582 	major_status = gss_create_empty_oid_set(minor_status, rmechs);
2583 
2584 	if (major_status != GSS_S_COMPLETE) {
2585 		(void) gss_release_oid_set(minor_status, &mechs);
2586 		return (major_status);
2587 	}
2588 
2589 	for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2590 		if ((mechs->elements[i].length
2591 		    != spnego_mechanism.mech_type.length) ||
2592 		    memcmp(mechs->elements[i].elements,
2593 			spnego_mechanism.mech_type.elements,
2594 			spnego_mechanism.mech_type.length)) {
2595 			/*
2596 			 * Solaris Kerberos: gss_indicate_mechs is stupid as
2597 			 * it never inferences any of the related OIDs of the
2598 			 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2599 			 * We add KRB5_WRONG here so that old MS clients can
2600 			 * negotiate this mechanism, which allows extensions
2601 			 * in Kerberos (clock skew adjustment, refresh ccache).
2602 			 */
2603 			if (is_kerb_mech(&mechs->elements[i])) {
2604 			    extern gss_OID_desc * const gss_mech_krb5_wrong;
2605 
2606 				major_status =
2607 				  gss_add_oid_set_member(minor_status,
2608 				  gss_mech_krb5_wrong, rmechs);
2609 			}
2610 
2611 			major_status = gss_add_oid_set_member(minor_status,
2612 							      &mechs->elements[i],
2613 							      rmechs);
2614 			if (major_status == GSS_S_COMPLETE)
2615 				found++;
2616 		}
2617 	}
2618 
2619 	/*
2620 	 * If the caller wanted a list of creds returned,
2621 	 * trim the list of mechanisms down to only those
2622 	 * for which the creds are valid.
2623 	 */
2624 	if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2625 		major_status = gss_acquire_cred(minor_status,
2626 						name, GSS_C_INDEFINITE,
2627 						*rmechs, usage, creds,
2628 						&goodmechs, NULL);
2629 
2630 		/*
2631 		 * Drop the old list in favor of the new
2632 		 * "trimmed" list.
2633 		 */
2634 		(void) gss_release_oid_set(&tmpmin, rmechs);
2635 		if (major_status == GSS_S_COMPLETE) {
2636 			(void) gssint_copy_oid_set(&tmpmin,
2637 					goodmechs, rmechs);
2638 			(void) gss_release_oid_set(&tmpmin, &goodmechs);
2639 		}
2640 	}
2641 
2642 	(void) gss_release_oid_set(&tmpmin, &mechs);
2643 	if (found == 0 || major_status != GSS_S_COMPLETE) {
2644 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2645 		map_errcode(minor_status);
2646 		if (major_status == GSS_S_COMPLETE)
2647 			major_status = GSS_S_FAILURE;
2648 	}
2649 
2650 	return (major_status);
2651 }
2652 
2653 /* following are token creation and reading routines */
2654 
2655 /*
2656  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2657  * advance the buffer, otherwise, decode the mech_oid from the buffer and
2658  * place in gss_OID.
2659  */
2660 static gss_OID
2661 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2662 {
2663 	OM_uint32	status;
2664 	gss_OID_desc 	toid;
2665 	gss_OID		mech_out = NULL;
2666 	unsigned char		*start, *end;
2667 
2668 	if (length < 1 || **buff_in != MECH_OID)
2669 		return (NULL);
2670 
2671 	start = *buff_in;
2672 	end = start + length;
2673 
2674 	(*buff_in)++;
2675 	toid.length = *(*buff_in)++;
2676 
2677 	if ((*buff_in + toid.length) > end)
2678 		return (NULL);
2679 
2680 	toid.elements = *buff_in;
2681 	*buff_in += toid.length;
2682 
2683 	status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2684 
2685 	if (status != GSS_S_COMPLETE) {
2686 		map_errcode(minor_status);
2687 		mech_out = NULL;
2688 	}
2689 
2690 	return (mech_out);
2691 }
2692 
2693 /*
2694  * der encode the given mechanism oid into buf_out, advancing the
2695  * buffer pointer.
2696  */
2697 
2698 static int
2699 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2700 {
2701 	if (buflen < mech->length + 2)
2702 		return (-1);
2703 	*(*buf_out)++ = MECH_OID;
2704 	*(*buf_out)++ = (unsigned char) mech->length;
2705 	memcpy((void *)(*buf_out), mech->elements, mech->length);
2706 	*buf_out += mech->length;
2707 	return (0);
2708 }
2709 
2710 /*
2711  * verify that buff_in points to an octet string, if it does not,
2712  * return NULL and don't advance the pointer. If it is an octet string
2713  * decode buff_in into a gss_buffer_t and return it, advancing the
2714  * buffer pointer.
2715  */
2716 static gss_buffer_t
2717 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2718 {
2719 	gss_buffer_t input_token;
2720 	unsigned int bytes;
2721 
2722 	if (**buff_in != OCTET_STRING)
2723 		return (NULL);
2724 
2725 	(*buff_in)++;
2726 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2727 
2728 	if (input_token == NULL)
2729 		return (NULL);
2730 
2731 	input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2732 	if ((int)input_token->length == -1) {
2733 		free(input_token);
2734 		return (NULL);
2735 	}
2736 	input_token->value = malloc(input_token->length);
2737 
2738 	if (input_token->value == NULL) {
2739 		free(input_token);
2740 		return (NULL);
2741 	}
2742 
2743 	(void) memcpy(input_token->value, *buff_in, input_token->length);
2744 	*buff_in += input_token->length;
2745 	return (input_token);
2746 }
2747 
2748 /*
2749  * verify that the input token length is not 0. If it is, just return.
2750  * If the token length is greater than 0, der encode as an octet string
2751  * and place in buf_out, advancing buf_out.
2752  */
2753 
2754 static int
2755 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2756 		unsigned int buflen)
2757 {
2758 	int ret;
2759 
2760 	/* if token length is 0, we do not want to send */
2761 	if (input_token->length == 0)
2762 		return (0);
2763 
2764 	if (input_token->length > buflen)
2765 		return (-1);
2766 
2767 	*(*buf_out)++ = OCTET_STRING;
2768 	if ((ret = gssint_put_der_length(input_token->length, buf_out,
2769 			    input_token->length)))
2770 		return (ret);
2771 	TWRITE_STR(*buf_out, input_token->value, input_token->length);
2772 	return (0);
2773 }
2774 
2775 /*
2776  * verify that buff_in points to a sequence of der encoding. The mech
2777  * set is the only sequence of encoded object in the token, so if it is
2778  * a sequence of encoding, decode the mechset into a gss_OID_set and
2779  * return it, advancing the buffer pointer.
2780  */
2781 static gss_OID_set
2782 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2783 	     unsigned int buff_length)
2784 {
2785 	gss_OID_set returned_mechSet;
2786 	OM_uint32 major_status;
2787 	int length; /* SUNW17PACresync */
2788 	OM_uint32 bytes;
2789 	OM_uint32 set_length;
2790 	unsigned char		*start;
2791 	int i;
2792 
2793 	if (**buff_in != SEQUENCE_OF)
2794 		return (NULL);
2795 
2796 	start = *buff_in;
2797 	(*buff_in)++;
2798 
2799 	length = gssint_get_der_length(buff_in, buff_length, &bytes);
2800 	if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
2801 		return (NULL);
2802 
2803 	major_status = gss_create_empty_oid_set(minor_status,
2804 						&returned_mechSet);
2805 	if (major_status != GSS_S_COMPLETE)
2806 		return (NULL);
2807 
2808 	for (set_length = 0, i = 0; set_length < length; i++) {
2809 		gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2810 			buff_length - (*buff_in - start));
2811 		if (temp != NULL) {
2812 		    major_status = gss_add_oid_set_member(minor_status,
2813 					temp, &returned_mechSet);
2814 		    if (major_status == GSS_S_COMPLETE) {
2815 			set_length += returned_mechSet->elements[i].length +2;
2816 			if (generic_gss_release_oid(minor_status, &temp))
2817 			    map_errcode(minor_status);
2818 		    }
2819 		}
2820 	}
2821 
2822 	return (returned_mechSet);
2823 }
2824 
2825 /*
2826  * Encode mechSet into buf.
2827  */
2828 static int
2829 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
2830 {
2831 	unsigned char *ptr;
2832 	unsigned int i;
2833 	unsigned int tlen, ilen;
2834 
2835 	tlen = ilen = 0;
2836 	for (i = 0; i < mechSet->count; i++) {
2837 		/*
2838 		 * 0x06 [DER LEN] [OID]
2839 		 */
2840 		ilen += 1 +
2841 			gssint_der_length_size(mechSet->elements[i].length) +
2842 			mechSet->elements[i].length;
2843 	}
2844 	/*
2845 	 * 0x30 [DER LEN]
2846 	 */
2847 	tlen = 1 + gssint_der_length_size(ilen) + ilen;
2848 	ptr = malloc(tlen);
2849 	if (ptr == NULL)
2850 		return -1;
2851 
2852 	buf->value = ptr;
2853 	buf->length = tlen;
2854 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
2855 
2856 	*ptr++ = SEQUENCE_OF;
2857 	if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
2858 		return -1;
2859 	for (i = 0; i < mechSet->count; i++) {
2860 		if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
2861 			return -1;
2862 		}
2863 	}
2864 	return 0;
2865 #undef REMAIN
2866 }
2867 
2868 /*
2869  * Verify that buff_in is pointing to a BIT_STRING with the correct
2870  * length and padding for the req_flags. If it is, decode req_flags
2871  * and return them, otherwise, return NULL.
2872  */
2873 static OM_uint32
2874 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
2875 	      OM_uint32 *req_flags)
2876 {
2877 	unsigned int len;
2878 
2879 	if (**buff_in != (CONTEXT | 0x01))
2880 		return (0);
2881 
2882 	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
2883 				bodysize, &len) < 0)
2884 		return GSS_S_DEFECTIVE_TOKEN;
2885 
2886 	if (*(*buff_in)++ != BIT_STRING)
2887 		return GSS_S_DEFECTIVE_TOKEN;
2888 
2889 	if (*(*buff_in)++ != BIT_STRING_LENGTH)
2890 		return GSS_S_DEFECTIVE_TOKEN;
2891 
2892 	if (*(*buff_in)++ != BIT_STRING_PADDING)
2893 		return GSS_S_DEFECTIVE_TOKEN;
2894 
2895 	*req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
2896 	return (0);
2897 }
2898 
2899 static OM_uint32
2900 get_negTokenInit(OM_uint32 *minor_status,
2901 		 gss_buffer_t buf,
2902 		 gss_buffer_t der_mechSet,
2903 		 gss_OID_set *mechSet,
2904 		 OM_uint32 *req_flags,
2905 		 gss_buffer_t *mechtok,
2906 		 gss_buffer_t *mechListMIC)
2907 {
2908 	OM_uint32 err;
2909 	unsigned char *ptr, *bufstart;
2910 	unsigned int len;
2911 	gss_buffer_desc tmpbuf;
2912 
2913 	*minor_status = 0;
2914 	der_mechSet->length = 0;
2915 	der_mechSet->value = NULL;
2916 	*mechSet = GSS_C_NO_OID_SET;
2917 	*req_flags = 0;
2918 	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
2919 
2920 	ptr = bufstart = buf->value;
2921 	if ((buf->length - (ptr - bufstart)) > INT_MAX)
2922 		return GSS_S_FAILURE;
2923 #define REMAIN (buf->length - (ptr - bufstart))
2924 
2925 	err = g_verify_token_header(gss_mech_spnego,
2926 				    &len, &ptr, 0, REMAIN);
2927 	if (err) {
2928 		*minor_status = err;
2929 		map_errcode(minor_status);
2930 		return GSS_S_FAILURE;
2931 	}
2932 	*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
2933 	if (*minor_status) {
2934 		map_errcode(minor_status);
2935 		return GSS_S_FAILURE;
2936 	}
2937 
2938 	/* alias into input_token */
2939 	tmpbuf.value = ptr;
2940 	tmpbuf.length = REMAIN;
2941 	*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
2942 	if (*mechSet == NULL)
2943 		return GSS_S_FAILURE;
2944 
2945 	tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
2946 	der_mechSet->value = malloc(tmpbuf.length);
2947 	if (der_mechSet->value == NULL)
2948 		return GSS_S_FAILURE;
2949 	memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
2950 	der_mechSet->length = tmpbuf.length;
2951 
2952 	err = get_req_flags(&ptr, REMAIN, req_flags);
2953 	if (err != GSS_S_COMPLETE) {
2954 		return err;
2955 	}
2956 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
2957 				 REMAIN, &len) >= 0) {
2958 		*mechtok = get_input_token(&ptr, len);
2959 		if (*mechtok == GSS_C_NO_BUFFER) {
2960 			return GSS_S_FAILURE;
2961 		}
2962 	}
2963 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
2964 				 REMAIN, &len) >= 0) {
2965 		*mechListMIC = get_input_token(&ptr, len);
2966 		if (*mechListMIC == GSS_C_NO_BUFFER) {
2967 			return GSS_S_FAILURE;
2968 		}
2969 	}
2970 	return GSS_S_COMPLETE;
2971 #undef REMAIN
2972 }
2973 
2974 static OM_uint32
2975 get_negTokenResp(OM_uint32 *minor_status,
2976 		 unsigned char *buf, unsigned int buflen,
2977 		 OM_uint32 *negState,
2978 		 gss_OID *supportedMech,
2979 		 gss_buffer_t *responseToken,
2980 		 gss_buffer_t *mechListMIC)
2981 {
2982 	unsigned char *ptr, *bufstart;
2983 	unsigned int len;
2984 	int tmplen;
2985 	unsigned int tag, bytes;
2986 
2987 	*negState = ACCEPT_DEFECTIVE_TOKEN;
2988 	*supportedMech = GSS_C_NO_OID;
2989 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
2990 	ptr = bufstart = buf;
2991 #define REMAIN (buflen - (ptr - bufstart))
2992 
2993 	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
2994 		return GSS_S_DEFECTIVE_TOKEN;
2995 	if (*ptr++ == SEQUENCE) {
2996 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
2997 		if (tmplen < 0)
2998 			return GSS_S_DEFECTIVE_TOKEN;
2999 	}
3000 	if (REMAIN < 1)
3001 		tag = 0;
3002 	else
3003 		tag = *ptr++;
3004 
3005 	if (tag == CONTEXT) {
3006 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3007 		if (tmplen < 0)
3008 			return GSS_S_DEFECTIVE_TOKEN;
3009 
3010 		if (g_get_tag_and_length(&ptr, ENUMERATED,
3011 					 REMAIN, &len) < 0)
3012 			return GSS_S_DEFECTIVE_TOKEN;
3013 
3014 		if (len != ENUMERATION_LENGTH)
3015 			return GSS_S_DEFECTIVE_TOKEN;
3016 
3017 		if (REMAIN < 1)
3018 			return GSS_S_DEFECTIVE_TOKEN;
3019 		*negState = *ptr++;
3020 
3021 		if (REMAIN < 1)
3022 			tag = 0;
3023 		else
3024 			tag = *ptr++;
3025 	}
3026 	if (tag == (CONTEXT | 0x01)) {
3027 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3028 		if (tmplen < 0)
3029 			return GSS_S_DEFECTIVE_TOKEN;
3030 
3031 		*supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3032 		if (*supportedMech == GSS_C_NO_OID)
3033 			return GSS_S_DEFECTIVE_TOKEN;
3034 
3035 		if (REMAIN < 1)
3036 			tag = 0;
3037 		else
3038 			tag = *ptr++;
3039 	}
3040 	if (tag == (CONTEXT | 0x02)) {
3041 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3042 		if (tmplen < 0)
3043 			return GSS_S_DEFECTIVE_TOKEN;
3044 
3045 		*responseToken = get_input_token(&ptr, REMAIN);
3046 		if (*responseToken == GSS_C_NO_BUFFER)
3047 			return GSS_S_DEFECTIVE_TOKEN;
3048 
3049 		if (REMAIN < 1)
3050 			tag = 0;
3051 		else
3052 			tag = *ptr++;
3053 	}
3054 	if (tag == (CONTEXT | 0x03)) {
3055 		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3056 		if (tmplen < 0)
3057 			return GSS_S_DEFECTIVE_TOKEN;
3058 
3059 		*mechListMIC = get_input_token(&ptr, REMAIN);
3060 		if (*mechListMIC == GSS_C_NO_BUFFER)
3061 			return GSS_S_DEFECTIVE_TOKEN;
3062 	}
3063 	return GSS_S_COMPLETE;
3064 #undef REMAIN
3065 }
3066 
3067 /*
3068  * der encode the passed negResults as an ENUMERATED type and
3069  * place it in buf_out, advancing the buffer.
3070  */
3071 
3072 static int
3073 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3074 	      unsigned int buflen)
3075 {
3076 	if (buflen < 3)
3077 		return (-1);
3078 	*(*buf_out)++ = ENUMERATED;
3079 	*(*buf_out)++ = ENUMERATION_LENGTH;
3080 	*(*buf_out)++ = (unsigned char) negResult;
3081 	return (0);
3082 }
3083 
3084 /*
3085  * This routine compares the recieved mechset to the mechset that
3086  * this server can support. It looks sequentially through the mechset
3087  * and the first one that matches what the server can support is
3088  * chosen as the negotiated mechanism. If one is found, negResult
3089  * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3090  * it's not the first mech, otherwise we return NULL and negResult
3091  * is set to REJECT.
3092  *
3093  * NOTE: There is currently no way to specify a preference order of
3094  * mechanisms supported by the acceptor.
3095  */
3096 static gss_OID
3097 negotiate_mech_type(OM_uint32 *minor_status,
3098 		    gss_OID_set supported_mechSet,
3099 		    gss_OID_set mechset,
3100 		    OM_uint32 *negResult)
3101 {
3102 	gss_OID returned_mech;
3103 	OM_uint32 status;
3104 	int present;
3105 	unsigned int i;
3106 
3107 	for (i = 0; i < mechset->count; i++) {
3108 		gss_OID mech_oid = &mechset->elements[i];
3109 
3110 		/*
3111 		 * Solaris Kerberos: MIT compares against MS' wrong OID, but
3112 		 * we actually want to select it if the client supports, as this
3113 		 * will enable features on MS clients that allow credential
3114 		 * refresh on rekeying and caching system times from servers.
3115 		 */
3116 #if 0
3117 		/* Accept wrong mechanism OID from MS clients */
3118 		if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3119 		    memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3120 			mech_oid = (gss_OID)&gss_mech_krb5_oid;
3121 #endif
3122 
3123 		gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3124 		if (!present)
3125 			continue;
3126 
3127 		if (i == 0)
3128 			*negResult = ACCEPT_INCOMPLETE;
3129 		else
3130 			*negResult = REQUEST_MIC;
3131 
3132 		status = generic_gss_copy_oid(minor_status,
3133 					      &mechset->elements[i],
3134 					      &returned_mech);
3135 		if (status != GSS_S_COMPLETE) {
3136 			*negResult = REJECT;
3137 			map_errcode(minor_status);
3138 			return (NULL);
3139 		}
3140 		return (returned_mech);
3141 	}
3142 	*negResult = REJECT;
3143 	return (NULL);
3144 }
3145 
3146 /*
3147  * the next two routines make a token buffer suitable for
3148  * spnego_gss_display_status. These currently take the string
3149  * in name and place it in the token. Eventually, if
3150  * spnego_gss_display_status returns valid error messages,
3151  * these routines will be changes to return the error string.
3152  */
3153 static spnego_token_t
3154 make_spnego_token(char *name)
3155 {
3156 	return (spnego_token_t)strdup(name);
3157 }
3158 
3159 static gss_buffer_desc
3160 make_err_msg(char *name)
3161 {
3162 	gss_buffer_desc buffer;
3163 
3164 	if (name == NULL) {
3165 		buffer.length = 0;
3166 		buffer.value = NULL;
3167 	} else {
3168 		buffer.length = strlen(name)+1;
3169 		buffer.value = make_spnego_token(name);
3170 	}
3171 
3172 	return (buffer);
3173 }
3174 
3175 /*
3176  * Create the client side spnego token passed back to gss_init_sec_context
3177  * and eventually up to the application program and over to the server.
3178  *
3179  * Use DER rules, definite length method per RFC 2478
3180  */
3181 static int
3182 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3183 			  int negHintsCompat,
3184 			  gss_buffer_t mechListMIC, OM_uint32 req_flags,
3185 			  gss_buffer_t data, send_token_flag sendtoken,
3186 			  gss_buffer_t outbuf)
3187 {
3188 	int ret = 0;
3189 	unsigned int tlen, dataLen = 0;
3190 	unsigned int negTokenInitSize = 0;
3191 	unsigned int negTokenInitSeqSize = 0;
3192 	unsigned int negTokenInitContSize = 0;
3193 	unsigned int rspTokenSize = 0;
3194 	unsigned int mechListTokenSize = 0;
3195 	unsigned int micTokenSize = 0;
3196 	unsigned char *t;
3197 	unsigned char *ptr;
3198 
3199 	if (outbuf == GSS_C_NO_BUFFER)
3200 		return (-1);
3201 
3202 	outbuf->length = 0;
3203 	outbuf->value = NULL;
3204 
3205 	/* calculate the data length */
3206 
3207 	/*
3208 	 * 0xa0 [DER LEN] [mechTypes]
3209 	 */
3210 	mechListTokenSize = 1 +
3211 		gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3212 		spnego_ctx->DER_mechTypes.length;
3213 	dataLen += mechListTokenSize;
3214 
3215 	/*
3216 	 * If a token from gss_init_sec_context exists,
3217 	 * add the length of the token + the ASN.1 overhead
3218 	 */
3219 	if (data != NULL) {
3220 		/*
3221 		 * Encoded in final output as:
3222 		 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3223 		 * -----s--------|--------s2----------
3224 		 */
3225 		rspTokenSize = 1 +
3226 			gssint_der_length_size(data->length) +
3227 			data->length;
3228 		dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3229 			rspTokenSize;
3230 	}
3231 
3232 	if (mechListMIC) {
3233 		/*
3234 		 * Encoded in final output as:
3235 		 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3236 		 *	--s--     -----tlen------------
3237 		 */
3238 		micTokenSize = 1 +
3239 			gssint_der_length_size(mechListMIC->length) +
3240 			mechListMIC->length;
3241 		dataLen += 1 +
3242 			gssint_der_length_size(micTokenSize) +
3243 			micTokenSize;
3244 	}
3245 
3246 	/*
3247 	 * Add size of DER encoding
3248 	 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3249 	 *   0x30 [DER_LEN] [data]
3250 	 *
3251 	 */
3252 	negTokenInitContSize = dataLen;
3253 	negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3254 	dataLen = negTokenInitSeqSize;
3255 
3256 	/*
3257 	 * negTokenInitSize indicates the bytes needed to
3258 	 * hold the ASN.1 encoding of the entire NegTokenInit
3259 	 * SEQUENCE.
3260 	 * 0xa0 [DER_LEN] + data
3261 	 *
3262 	 */
3263 	negTokenInitSize = 1 +
3264 		gssint_der_length_size(negTokenInitSeqSize) +
3265 		negTokenInitSeqSize;
3266 
3267 	tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3268 
3269 	t = (unsigned char *) malloc(tlen);
3270 
3271 	if (t == NULL) {
3272 		return (-1);
3273 	}
3274 
3275 	ptr = t;
3276 
3277 	/* create the message */
3278 	if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3279 			    &ptr, tlen)))
3280 		goto errout;
3281 
3282 	*ptr++ = CONTEXT; /* NegotiationToken identifier */
3283 	if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3284 		goto errout;
3285 
3286 	*ptr++ = SEQUENCE;
3287 	if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3288 					 tlen - (int)(ptr-t))))
3289 		goto errout;
3290 
3291 	*ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3292 	if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3293 					 &ptr, tlen - (int)(ptr-t))))
3294 		goto errout;
3295 
3296 	/* We already encoded the MechSetList */
3297 	(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3298 		      spnego_ctx->DER_mechTypes.length);
3299 
3300 	ptr += spnego_ctx->DER_mechTypes.length;
3301 
3302 	if (data != NULL) {
3303 		*ptr++ = CONTEXT | 0x02;
3304 		if ((ret = gssint_put_der_length(rspTokenSize,
3305 				&ptr, tlen - (int)(ptr - t))))
3306 			goto errout;
3307 
3308 		if ((ret = put_input_token(&ptr, data,
3309 			tlen - (int)(ptr - t))))
3310 			goto errout;
3311 	}
3312 
3313 	if (mechListMIC != GSS_C_NO_BUFFER) {
3314 		*ptr++ = CONTEXT | 0x03;
3315 		if ((ret = gssint_put_der_length(micTokenSize,
3316 				&ptr, tlen - (int)(ptr - t))))
3317 			goto errout;
3318 
3319 		if (negHintsCompat) {
3320 			ret = put_neg_hints(&ptr, mechListMIC,
3321 					    tlen - (int)(ptr - t));
3322 			if (ret)
3323 				goto errout;
3324 		} else if ((ret = put_input_token(&ptr, mechListMIC,
3325 				tlen - (int)(ptr - t))))
3326 			goto errout;
3327 	}
3328 
3329 errout:
3330 	if (ret != 0) {
3331 		if (t)
3332 			free(t);
3333 		t = NULL;
3334 		tlen = 0;
3335 	}
3336 	outbuf->length = tlen;
3337 	outbuf->value = (void *) t;
3338 
3339 	return (ret);
3340 }
3341 
3342 /*
3343  * create the server side spnego token passed back to
3344  * gss_accept_sec_context and eventually up to the application program
3345  * and over to the client.
3346  */
3347 static int
3348 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3349 			  gss_buffer_t data, gss_buffer_t mechListMIC,
3350 			  send_token_flag sendtoken,
3351 			  gss_buffer_t outbuf)
3352 {
3353 	unsigned int tlen = 0;
3354 	unsigned int ret = 0;
3355 	unsigned int NegTokenTargSize = 0;
3356 	unsigned int NegTokenSize = 0;
3357 	unsigned int rspTokenSize = 0;
3358 	unsigned int micTokenSize = 0;
3359 	unsigned int dataLen = 0;
3360 	unsigned char *t;
3361 	unsigned char *ptr;
3362 
3363 	if (outbuf == GSS_C_NO_BUFFER)
3364 		return (GSS_S_DEFECTIVE_TOKEN);
3365 
3366 	outbuf->length = 0;
3367 	outbuf->value = NULL;
3368 
3369 	/*
3370 	 * ASN.1 encoding of the negResult
3371 	 * ENUMERATED type is 3 bytes
3372 	 *  ENUMERATED TAG, Length, Value,
3373 	 * Plus 2 bytes for the CONTEXT id and length.
3374 	 */
3375 	dataLen = 5;
3376 
3377 	/*
3378 	 * calculate data length
3379 	 *
3380 	 * If this is the initial token, include length of
3381 	 * mech_type and the negotiation result fields.
3382 	 */
3383 	if (sendtoken == INIT_TOKEN_SEND) {
3384 		int mechlistTokenSize;
3385 		/*
3386 		 * 1 byte for the CONTEXT ID(0xa0),
3387 		 * 1 byte for the OID ID(0x06)
3388 		 * 1 byte for OID Length field
3389 		 * Plus the rest... (OID Length, OID value)
3390 		 */
3391 		mechlistTokenSize = 3 + mech_wanted->length +
3392 			gssint_der_length_size(mech_wanted->length);
3393 
3394 		dataLen += mechlistTokenSize;
3395 	}
3396 	if (data != NULL && data->length > 0) {
3397 		/* Length of the inner token */
3398 		rspTokenSize = 1 + gssint_der_length_size(data->length) +
3399 			data->length;
3400 
3401 		dataLen += rspTokenSize;
3402 
3403 		/* Length of the outer token */
3404 		dataLen += 1 + gssint_der_length_size(rspTokenSize);
3405 	}
3406 	if (mechListMIC != NULL) {
3407 
3408 		/* Length of the inner token */
3409 		micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3410 			mechListMIC->length;
3411 
3412 		dataLen += micTokenSize;
3413 
3414 		/* Length of the outer token */
3415 		dataLen += 1 + gssint_der_length_size(micTokenSize);
3416 	}
3417 	/*
3418 	 * Add size of DER encoded:
3419 	 * NegTokenTarg [ SEQUENCE ] of
3420 	 *    NegResult[0] ENUMERATED {
3421 	 *	accept_completed(0),
3422 	 *	accept_incomplete(1),
3423 	 *	reject(2) }
3424 	 *    supportedMech [1] MechType OPTIONAL,
3425 	 *    responseToken [2] OCTET STRING OPTIONAL,
3426 	 *    mechListMIC   [3] OCTET STRING OPTIONAL
3427 	 *
3428 	 * size = data->length + MechListMic + SupportedMech len +
3429 	 *	Result Length + ASN.1 overhead
3430 	 */
3431 	NegTokenTargSize = dataLen;
3432 	dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3433 
3434 	/*
3435 	 * NegotiationToken [ CHOICE ]{
3436 	 *    negTokenInit  [0]	 NegTokenInit,
3437 	 *    negTokenTarg  [1]	 NegTokenTarg }
3438 	 */
3439 	NegTokenSize = dataLen;
3440 	dataLen += 1 + gssint_der_length_size(NegTokenSize);
3441 
3442 	tlen = dataLen;
3443 	t = (unsigned char *) malloc(tlen);
3444 
3445 	if (t == NULL) {
3446 		ret = GSS_S_DEFECTIVE_TOKEN;
3447 		goto errout;
3448 	}
3449 
3450 	ptr = t;
3451 
3452 	/*
3453 	 * Indicate that we are sending CHOICE 1
3454 	 * (NegTokenTarg)
3455 	 */
3456 	*ptr++ = CONTEXT | 0x01;
3457 	if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3458 		ret = GSS_S_DEFECTIVE_TOKEN;
3459 		goto errout;
3460 	}
3461 	*ptr++ = SEQUENCE;
3462 	if (gssint_put_der_length(NegTokenTargSize, &ptr,
3463 				  tlen - (int)(ptr-t)) < 0) {
3464 		ret = GSS_S_DEFECTIVE_TOKEN;
3465 		goto errout;
3466 	}
3467 
3468 	/*
3469 	 * First field of the NegTokenTarg SEQUENCE
3470 	 * is the ENUMERATED NegResult.
3471 	 */
3472 	*ptr++ = CONTEXT;
3473 	if (gssint_put_der_length(3, &ptr,
3474 				  tlen - (int)(ptr-t)) < 0) {
3475 		ret = GSS_S_DEFECTIVE_TOKEN;
3476 		goto errout;
3477 	}
3478 	if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3479 		ret = GSS_S_DEFECTIVE_TOKEN;
3480 		goto errout;
3481 	}
3482 	if (sendtoken == INIT_TOKEN_SEND) {
3483 		/*
3484 		 * Next, is the Supported MechType
3485 		 */
3486 		*ptr++ = CONTEXT | 0x01;
3487 		if (gssint_put_der_length(mech_wanted->length + 2,
3488 					  &ptr,
3489 					  tlen - (int)(ptr - t)) < 0) {
3490 			ret = GSS_S_DEFECTIVE_TOKEN;
3491 			goto errout;
3492 		}
3493 		if (put_mech_oid(&ptr, mech_wanted,
3494 				 tlen - (int)(ptr - t)) < 0) {
3495 			ret = GSS_S_DEFECTIVE_TOKEN;
3496 			goto errout;
3497 		}
3498 	}
3499 	if (data != NULL && data->length > 0) {
3500 		*ptr++ = CONTEXT | 0x02;
3501 		if (gssint_put_der_length(rspTokenSize, &ptr,
3502 					  tlen - (int)(ptr - t)) < 0) {
3503 			ret = GSS_S_DEFECTIVE_TOKEN;
3504 			goto errout;
3505 		}
3506 		if (put_input_token(&ptr, data,
3507 				    tlen - (int)(ptr - t)) < 0) {
3508 			ret = GSS_S_DEFECTIVE_TOKEN;
3509 			goto errout;
3510 		}
3511 	}
3512 	if (mechListMIC != NULL) {
3513 		*ptr++ = CONTEXT | 0x03;
3514 		if (gssint_put_der_length(micTokenSize, &ptr,
3515 					  tlen - (int)(ptr - t)) < 0) {
3516 			ret = GSS_S_DEFECTIVE_TOKEN;
3517 			goto errout;
3518 		}
3519 		if (put_input_token(&ptr, mechListMIC,
3520 				    tlen - (int)(ptr - t)) < 0) {
3521 			ret = GSS_S_DEFECTIVE_TOKEN;
3522 			goto errout;
3523 		}
3524 	}
3525 	ret = GSS_S_COMPLETE;
3526 errout:
3527 	if (ret != GSS_S_COMPLETE) {
3528 		if (t)
3529 			free(t);
3530 	} else {
3531 		outbuf->length = ptr - t;
3532 		outbuf->value = (void *) t;
3533 	}
3534 
3535 	return (ret);
3536 }
3537 
3538 /* determine size of token */
3539 static int
3540 g_token_size(gss_OID_const mech, unsigned int body_size)
3541 {
3542 	int hdrsize;
3543 
3544 	/*
3545 	 * Initialize the header size to the
3546 	 * MECH_OID byte + the bytes needed to indicate the
3547 	 * length of the OID + the OID itself.
3548 	 *
3549 	 * 0x06 [MECHLENFIELD] MECHDATA
3550 	 */
3551 	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3552 
3553 	/*
3554 	 * Now add the bytes needed for the initial header
3555 	 * token bytes:
3556 	 * 0x60 + [DER_LEN] + HDRSIZE
3557 	 */
3558 	hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3559 
3560 	return (hdrsize + body_size);
3561 }
3562 
3563 /*
3564  * generate token header.
3565  *
3566  * Use DER Definite Length method per RFC2478
3567  * Use of indefinite length encoding will not be compatible
3568  * with Microsoft or others that actually follow the spec.
3569  */
3570 static int
3571 g_make_token_header(gss_OID_const mech,
3572 		    unsigned int body_size,
3573 		    unsigned char **buf,
3574 		    unsigned int totallen)
3575 {
3576 	int ret = 0;
3577 	unsigned int hdrsize;
3578 	unsigned char *p = *buf;
3579 
3580 	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3581 
3582 	*(*buf)++ = HEADER_ID;
3583 	if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3584 		return (ret);
3585 
3586 	*(*buf)++ = MECH_OID;
3587 	if ((ret = gssint_put_der_length(mech->length, buf,
3588 			    totallen - (int)(p - *buf))))
3589 		return (ret);
3590 	TWRITE_STR(*buf, mech->elements, mech->length);
3591 	return (0);
3592 }
3593 
3594 /*
3595  * NOTE: This checks that the length returned by
3596  * gssint_get_der_length() is not greater than the number of octets
3597  * remaining, even though gssint_get_der_length() already checks, in
3598  * theory.
3599  */
3600 static int
3601 g_get_tag_and_length(unsigned char **buf, int tag,
3602 		     unsigned int buflen, unsigned int *outlen)
3603 {
3604 	unsigned char *ptr = *buf;
3605 	int ret = -1; /* pessimists, assume failure ! */
3606 	unsigned int encoded_len;
3607 	unsigned int tmplen = 0;
3608 
3609 	*outlen = 0;
3610 	if (buflen > 1 && *ptr == tag) {
3611 		ptr++;
3612 		tmplen = gssint_get_der_length(&ptr, buflen - 1,
3613 						&encoded_len);
3614 		if (tmplen < 0) {
3615 			ret = -1;
3616 		} else if (tmplen > buflen - (ptr - *buf)) {
3617 			ret = -1;
3618 		} else
3619 			ret = 0;
3620 	}
3621 	*outlen = tmplen;
3622 	*buf = ptr;
3623 	return (ret);
3624 }
3625 
3626 static int
3627 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3628 {
3629 	unsigned char *buf = *buf_in;
3630 	unsigned char *endptr = buf + cur_size;
3631 	unsigned int seqsize;
3632 	int ret = 0;
3633 	unsigned int bytes;
3634 
3635 	/*
3636 	 * Verify this is a NegotiationToken type token
3637 	 * - check for a0(context specific identifier)
3638 	 * - get length and verify that enoughd ata exists
3639 	 */
3640 	if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3641 		return (G_BAD_TOK_HEADER);
3642 
3643 	cur_size = seqsize; /* should indicate bytes remaining */
3644 
3645 	/*
3646 	 * Verify the next piece, it should identify this as
3647 	 * a strucure of type NegTokenInit.
3648 	 */
3649 	if (*buf++ == SEQUENCE) {
3650 		if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3651 			return (G_BAD_TOK_HEADER);
3652 		/*
3653 		 * Make sure we have the entire buffer as described
3654 		 */
3655 		if (buf + seqsize > endptr)
3656 			return (G_BAD_TOK_HEADER);
3657 	} else {
3658 		return (G_BAD_TOK_HEADER);
3659 	}
3660 
3661 	cur_size = seqsize; /* should indicate bytes remaining */
3662 
3663 	/*
3664 	 * Verify that the first blob is a sequence of mechTypes
3665 	 */
3666 	if (*buf++ == CONTEXT) {
3667 		if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3668 			return (G_BAD_TOK_HEADER);
3669 		/*
3670 		 * Make sure we have the entire buffer as described
3671 		 */
3672 		if (buf + bytes > endptr)
3673 			return (G_BAD_TOK_HEADER);
3674 	} else {
3675 		return (G_BAD_TOK_HEADER);
3676 	}
3677 
3678 	/*
3679 	 * At this point, *buf should be at the beginning of the
3680 	 * DER encoded list of mech types that are to be negotiated.
3681 	 */
3682 	*buf_in = buf;
3683 
3684 	return (ret);
3685 
3686 }
3687 
3688 /* verify token header. */
3689 static int
3690 g_verify_token_header(gss_OID_const mech,
3691 		    unsigned int *body_size,
3692 		    unsigned char **buf_in,
3693 		    int tok_type,
3694 		    unsigned int toksize)
3695 {
3696 	unsigned char *buf = *buf_in;
3697 	int seqsize;
3698 	gss_OID_desc toid;
3699 	int ret = 0;
3700 	unsigned int bytes;
3701 
3702 	if (toksize-- < 1)
3703 		return (G_BAD_TOK_HEADER);
3704 
3705 	if (*buf++ != HEADER_ID)
3706 		return (G_BAD_TOK_HEADER);
3707 
3708 	if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3709 		return (G_BAD_TOK_HEADER);
3710 
3711 	if ((seqsize + bytes) != toksize)
3712 		return (G_BAD_TOK_HEADER);
3713 
3714 	if (toksize-- < 1)
3715 		return (G_BAD_TOK_HEADER);
3716 
3717 
3718 	if (*buf++ != MECH_OID)
3719 		return (G_BAD_TOK_HEADER);
3720 
3721 	if (toksize-- < 1)
3722 		return (G_BAD_TOK_HEADER);
3723 
3724 	toid.length = *buf++;
3725 
3726 	if (toksize < toid.length)
3727 		return (G_BAD_TOK_HEADER);
3728 	else
3729 		toksize -= toid.length;
3730 
3731 	toid.elements = buf;
3732 	buf += toid.length;
3733 
3734 	if (!g_OID_equal(&toid, mech))
3735 		ret = G_WRONG_MECH;
3736 
3737 	/*
3738 	 * G_WRONG_MECH is not returned immediately because it's more important
3739 	 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3740 	 */
3741 	if (toksize < 2)
3742 		return (G_BAD_TOK_HEADER);
3743 	else
3744 		toksize -= 2;
3745 
3746 	if (!ret) {
3747 		*buf_in = buf;
3748 		*body_size = toksize;
3749 	}
3750 
3751 	return (ret);
3752 }
3753 
3754 /*
3755  * Return non-zero if the oid is one of the kerberos mech oids,
3756  * otherwise return zero.
3757  *
3758  * N.B. There are 3 oids that represent the kerberos mech:
3759  * RFC-specified GSS_MECH_KRB5_OID,
3760  * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
3761  * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
3762  */
3763 
3764 static int
3765 is_kerb_mech(gss_OID oid)
3766 {
3767 	int answer = 0;
3768 	OM_uint32 minor;
3769 	extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3770 
3771 	(void) gss_test_oid_set_member(&minor,
3772 		oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3773 
3774 	return (answer);
3775 }
3776