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