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