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