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