1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * A module that implements the spnego security mechanism.
27  * It is used to negotiate the security mechanism between
28  * peers using the GSS-API.
29  *
30  */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 #include	<stdio.h>
35 #include	<stdlib.h>
36 #include	<errno.h>
37 #include	"gssapiP_spnego.h"
38 #include	<mechglueP.h>
39 #include	<gssapi_err_generic.h>
40 #include	<rpc/types.h>
41 #include	<libintl.h>
42 
43 /* der routines defined in libgss */
44 extern unsigned int der_length_size(OM_uint32);
45 extern int get_der_length(uchar_t **, OM_uint32, OM_uint32*);
46 extern int put_der_length(OM_uint32, uchar_t **, OM_uint32);
47 
48 
49 /* private routines for spnego_mechanism */
50 static spnego_token_t make_spnego_token(char *);
51 static gss_buffer_desc make_err_msg(char *);
52 static int g_token_size(gss_OID, OM_uint32);
53 static int g_make_token_header(gss_OID, int, uchar_t **, int);
54 static int g_verify_token_header(gss_OID, int *, uchar_t **, int, int);
55 static int g_verify_neg_token_init(uchar_t **, int);
56 static OM_uint32 get_negResult(unsigned char **, int);
57 static gss_OID get_mech_oid(OM_uint32 *, uchar_t **, size_t);
58 static gss_buffer_t get_input_token(unsigned char **, int);
59 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
60 static OM_uint32 get_req_flags(uchar_t **, int *, OM_uint32 *);
61 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
62 	gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
63 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
64 static void check_spnego_options(spnego_gss_ctx_id_t);
65 static spnego_gss_ctx_id_t create_spnego_ctx(void);
66 static int put_mech_set(uchar_t **, gss_OID_set, int);
67 static int put_input_token(uchar_t **, gss_buffer_t, int);
68 static int put_mech_oid(uchar_t **, gss_OID_desc *, int);
69 static int put_negResult(uchar_t **, OM_uint32, int);
70 
71 static gss_OID
72 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
73 		OM_uint32 *, bool_t *);
74 static int
75 g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
76 
77 static int
78 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_OID_set,
79 			gss_buffer_t, send_token_flag,
80 			gss_buffer_t);
81 static int
82 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
83 			gss_buffer_t, send_token_flag, int,
84 			gss_buffer_t);
85 
86 /*
87  * The Mech OID for SPNEGO:
88  * { iso(1) org(3) dod(6) internet(1) security(5)
89  *  mechanism(5) spnego(2) }
90  */
91 static struct gss_config spnego_mechanism =
92 {{SPNEGO_OID_LENGTH, SPNEGO_OID},
93 	NULL,
94 	spnego_gss_acquire_cred,
95 	spnego_gss_release_cred,
96 	spnego_gss_init_sec_context,
97 	spnego_gss_accept_sec_context,
98 /* EXPORT DELETE START */ /* CRYPT DELETE START */
99 	spnego_gss_unseal,		/* gss_unseal */
100 /* EXPORT DELETE END */ /* CRYPT DELETE END */
101 	NULL,				/* gss_process_context_token */
102 	spnego_gss_delete_sec_context,	/* gss_delete_sec_context */
103 	spnego_gss_context_time,	/* gss_context_time */
104 	spnego_gss_display_status,
105 	NULL,				/* gss_indicate_mechs */
106 	NULL,				/* gss_compare_name */
107 	spnego_gss_display_name,
108 	spnego_gss_import_name,
109 	spnego_gss_release_name,
110 	spnego_gss_inquire_cred,	/* gss_inquire_cred */
111 	NULL,				/* gss_add_cred */
112 /* EXPORT DELETE START */ /* CRYPT DELETE START */
113 	spnego_gss_seal,		/* gss_seal */
114 /* EXPORT DELETE END */ /* CRYPT DELETE END */
115 	spnego_gss_export_sec_context,	/* gss_export_sec_context */
116 	spnego_gss_import_sec_context,	/* gss_import_sec_context */
117 	NULL, 				/* gss_inquire_cred_by_mech */
118 	spnego_gss_inquire_names_for_mech,
119 	spnego_gss_inquire_context,	/* gss_inquire_context */
120 	NULL,				/* gss_internal_release_oid */
121 	spnego_gss_wrap_size_limit,	/* gss_wrap_size_limit */
122 	NULL,				/* gss_pname_to_uid */
123 	NULL,				/* __gss_userok */
124 	NULL,				/* gss_export_name */
125 /* EXPORT DELETE START */
126 /* CRYPT DELETE START */
127 #if 0
128 /* CRYPT DELETE END */
129 	spnego_gss_seal,
130 	spnego_gss_unseal,
131 /* CRYPT DELETE START */
132 #endif
133 /* CRYPT DELETE END */
134 /* EXPORT DELETE END */
135 	spnego_gss_sign,		/* gss_sign */
136 	spnego_gss_verify,		/* gss_verify */
137 	NULL,				/* gss_store_cred */
138 };
139 
140 gss_mechanism
141 gss_mech_initialize(const gss_OID oid)
142 {
143 	dsyslog("Entering gss_mech_initialize\n");
144 
145 	if (oid == NULL ||
146 		!g_OID_equal(oid, &spnego_mechanism.mech_type)) {
147 		dsyslog("invalid spnego mechanism oid.\n");
148 		return (NULL);
149 	}
150 
151 	dsyslog("Leaving gss_mech_initialize\n");
152 	return (&spnego_mechanism);
153 }
154 
155 /*ARGSUSED*/
156 OM_uint32
157 spnego_gss_acquire_cred(void *ctx,
158 			OM_uint32 *minor_status,
159 			gss_name_t desired_name,
160 			OM_uint32 time_req,
161 			gss_OID_set desired_mechs,
162 			gss_cred_usage_t cred_usage,
163 			gss_cred_id_t *output_cred_handle,
164 			gss_OID_set *actual_mechs,
165 			OM_uint32 *time_rec)
166 {
167 	OM_uint32 status;
168 	gss_OID_set amechs;
169 	dsyslog("Entering spnego_gss_acquire_cred\n");
170 
171 	if (actual_mechs)
172 		*actual_mechs = NULL;
173 
174 	if (time_rec)
175 		*time_rec = 0;
176 
177 	/*
178 	 * If the user did not specify a list of mechs,
179 	 * use get_available_mechs to collect a list of
180 	 * mechs for which creds are available.
181 	 */
182 	if (desired_mechs == GSS_C_NULL_OID_SET) {
183 		status = get_available_mechs(minor_status,
184 				desired_name, cred_usage,
185 				output_cred_handle, &amechs);
186 	} else {
187 		/*
188 		 * The caller gave a specific list of mechanisms,
189 		 * so just get whatever creds are available.
190 		 * gss_acquire_creds will return the subset of mechs for
191 		 * which the given 'output_cred_handle' is valid.
192 		 */
193 		status = gss_acquire_cred(minor_status,
194 				desired_name, time_req,
195 				desired_mechs, cred_usage,
196 				output_cred_handle, &amechs,
197 				time_rec);
198 	}
199 
200 	if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
201 		(void) gss_copy_oid_set(minor_status, amechs, actual_mechs);
202 	}
203 	(void) gss_release_oid_set(minor_status, &amechs);
204 
205 	dsyslog("Leaving spnego_gss_acquire_cred\n");
206 	return (status);
207 }
208 
209 /*ARGSUSED*/
210 OM_uint32
211 spnego_gss_release_cred(void *ctx,
212 			OM_uint32 *minor_status,
213 			gss_cred_id_t *cred_handle)
214 {
215 	OM_uint32 status;
216 
217 	dsyslog("Entering spnego_gss_release_cred\n");
218 
219 	if (minor_status == NULL || cred_handle == NULL)
220 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
221 
222 	*minor_status = 0;
223 
224 	if (*cred_handle == GSS_C_NO_CREDENTIAL)
225 		return (GSS_S_COMPLETE);
226 
227 	status = gss_release_cred(minor_status, cred_handle);
228 
229 	dsyslog("Leaving spnego_gss_release_cred\n");
230 	return (status);
231 }
232 
233 static void
234 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
235 {
236 	spnego_ctx->optionStr = __gss_get_modOptions(
237 		(const gss_OID)&spnego_oids[0]);
238 	if (spnego_ctx->optionStr != NULL &&
239 		strstr(spnego_ctx->optionStr, "msinterop")) {
240 			spnego_ctx->MS_Interop = 1;
241 	} else {
242 		spnego_ctx->MS_Interop = 0;
243 	}
244 }
245 
246 static spnego_gss_ctx_id_t
247 create_spnego_ctx(void)
248 {
249 	spnego_gss_ctx_id_t spnego_ctx = NULL;
250 	spnego_ctx = (spnego_gss_ctx_id_t)
251 		malloc(sizeof (spnego_gss_ctx_id_rec));
252 
253 	if (spnego_ctx == NULL) {
254 		return (NULL);
255 	}
256 
257 	spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
258 	spnego_ctx->internal_mech = NULL;
259 	spnego_ctx->optionStr = NULL;
260 	spnego_ctx->optimistic = 0;
261 	spnego_ctx->MS_Interop = 0;
262 	spnego_ctx->DER_mechTypes.length = NULL;
263 	spnego_ctx->DER_mechTypes.value = GSS_C_NO_BUFFER;
264 
265 	check_spnego_options(spnego_ctx);
266 
267 	return (spnego_ctx);
268 }
269 
270 /*ARGSUSED*/
271 OM_uint32
272 spnego_gss_init_sec_context(void *ct,
273 			OM_uint32 *minor_status,
274 			gss_cred_id_t claimant_cred_handle,
275 			gss_ctx_id_t *context_handle,
276 			gss_name_t target_name,
277 			gss_OID mech_type,
278 			OM_uint32 req_flags,
279 			OM_uint32 time_req,
280 			gss_channel_bindings_t input_chan_bindings,
281 			gss_buffer_t input_token,
282 			gss_OID *actual_mech,
283 			gss_buffer_t output_token,
284 			OM_uint32 *ret_flags,
285 			OM_uint32 *time_rec)
286 {
287 	OM_uint32 ret = 0;
288 	OM_uint32 status = 0;
289 	OM_uint32 mstat;
290 	OM_uint32 local_ret_flags = 0;
291 
292 	/*
293 	 * send_token is used to indicate in later steps
294 	 * what type of token, if any should be sent or processed.
295 	 * NO_TOKEN_SEND = no token should be sent
296 	 * INIT_TOKEN_SEND = initial token will be sent
297 	 * CONT_TOKEN_SEND = continuing tokens to be sent
298 	 * CHECK_MIC = no token to be sent, but have a MIC to check.
299 	 */
300 	send_token_flag send_token = NO_TOKEN_SEND;
301 
302 	gss_OID_set mechSet;
303 	spnego_gss_ctx_id_t spnego_ctx = NULL;
304 	gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
305 	gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
306 	gss_buffer_t mechListMIC = NULL;
307 	gss_cred_id_t *credlistptr = NULL, credlist;
308 	gss_qop_t *qop_state = NULL;
309 	unsigned char *ptr;
310 	int len;
311 
312 	dsyslog("Entering init_sec_context\n");
313 
314 	if (context_handle == NULL)
315 		return (GSS_S_NO_CONTEXT);
316 
317 	*minor_status = 0;
318 	output_token->length = 0;
319 	output_token->value = NULL;
320 
321 	if (actual_mech)
322 		*actual_mech = NULL;
323 
324 	if (*context_handle == GSS_C_NO_CONTEXT) {
325 
326 		/* determine negotiation mech set */
327 		if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
328 			credlistptr = &credlist;
329 
330 			mstat = get_available_mechs(minor_status,
331 				GSS_C_NO_NAME, GSS_C_INITIATE,
332 				credlistptr, &mechSet);
333 		} else {
334 			/*
335 			 * Use the list of mechs included in the
336 			 * cred that we were given.
337 			 */
338 			mstat = gss_inquire_cred(minor_status,
339 					claimant_cred_handle,
340 					NULL, NULL, NULL,
341 					&mechSet);
342 		}
343 		if (mstat != GSS_S_COMPLETE)
344 			return (mstat);
345 
346 		if ((spnego_ctx = create_spnego_ctx()) == NULL) {
347 			ret = GSS_S_FAILURE;
348 			goto cleanup;
349 		}
350 
351 		/*
352 		 * need to pull the first mech from mechSet to do first
353 		 * init ctx
354 		 */
355 		status = generic_gss_copy_oid(minor_status,
356 					mechSet->elements,
357 					&spnego_ctx->internal_mech);
358 
359 		if (status != GSS_S_COMPLETE) {
360 			ret = GSS_S_FAILURE;
361 			goto cleanup;
362 		}
363 
364 		if (input_token != NULL && input_token->value != NULL) {
365 			ret = GSS_S_DEFECTIVE_TOKEN;
366 			goto cleanup;
367 		}
368 
369 		/*
370 		 * The actual context is not yet determined,
371 		 * set the output context_handle to refer to
372 		 * the spnego context itself.
373 		 */
374 		spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
375 		*context_handle = (gss_ctx_id_t)spnego_ctx;
376 		send_token = INIT_TOKEN_SEND;
377 		ret = GSS_S_CONTINUE_NEEDED;
378 	} else {
379 		mechSet = NULL;
380 		spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
381 
382 		if (input_token == NULL || input_token->value == NULL) {
383 			ret = GSS_S_DEFECTIVE_TOKEN;
384 			goto cleanup;
385 		}
386 		ptr = (unsigned char *) input_token->value;
387 
388 		switch (get_negResult(&ptr, input_token->length)) {
389 		case ACCEPT_DEFECTIVE_TOKEN:
390 			*minor_status = 1;
391 			ret = GSS_S_DEFECTIVE_TOKEN;
392 			break;
393 		case ACCEPT_INCOMPLETE: {
394 			/* pull out mech from token */
395 			gss_OID internal_mech =
396 				get_mech_oid(minor_status, &ptr,
397 				input_token->length -
398 				(ptr - (uchar_t *)input_token->value));
399 
400 			/*
401 			 * check if first mech in neg set, if it isn't,
402 			 * release and copy chosen mech to context,
403 			 * delete internal context from prior mech
404 			 */
405 			if (internal_mech != NULL &&
406 			    ((internal_mech->length !=
407 			    spnego_ctx->internal_mech->length) ||
408 			    /* CSTYLED */
409 			    memcmp(spnego_ctx->internal_mech->elements,
410 				    internal_mech->elements,
411 				    spnego_ctx->internal_mech->length))) {
412 
413 			    (void) gss_delete_sec_context(&mstat,
414 					&spnego_ctx->ctx_handle, NULL);
415 
416 			    spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
417 			    (void) generic_gss_release_oid(
418 				&mstat, &spnego_ctx->internal_mech);
419 
420 			    status = generic_gss_copy_oid(
421 					minor_status, internal_mech,
422 					&spnego_ctx->internal_mech);
423 
424 			    if (status != GSS_S_COMPLETE)
425 				ret = GSS_S_DEFECTIVE_TOKEN;
426 			    else
427 				ret = GSS_S_COMPLETE;
428 
429 			    (void) generic_gss_release_oid(&mstat,
430 				&internal_mech);
431 			} else if (internal_mech == NULL) {
432 				ret = GSS_S_DEFECTIVE_TOKEN;
433 				send_token = NO_TOKEN_SEND;
434 			} else {
435 				ret = GSS_S_COMPLETE;
436 			}
437 			if (ret == GSS_S_COMPLETE) {
438 			/*
439 			 * Check for a token, it may contain
440 			 * an error message.
441 			 */
442 			    if (*ptr ==  (CONTEXT | 0x02)) {
443 				    if (g_get_tag_and_length(&ptr,
444 					(CONTEXT | 0x02),
445 					input_token->length -
446 					(ptr - (uchar_t *)input_token->value),
447 					&len) < 0) {
448 					ret = GSS_S_DEFECTIVE_TOKEN;
449 				    } else {
450 					i_input_token = get_input_token(&ptr,
451 						len);
452 					if (i_input_token  != NULL) {
453 						ret = GSS_S_CONTINUE_NEEDED;
454 						send_token = CONT_TOKEN_SEND;
455 					} else {
456 						ret = GSS_S_DEFECTIVE_TOKEN;
457 						send_token = NO_TOKEN_SEND;
458 					}
459 				    }
460 			    }
461 			}
462 			break;
463 		}
464 		case ACCEPT_COMPLETE:
465 			/* pull out mech from token */
466 			if (spnego_ctx->internal_mech != NULL)
467 				(void) generic_gss_release_oid(&mstat,
468 				    &spnego_ctx->internal_mech);
469 
470 			spnego_ctx->internal_mech =
471 				get_mech_oid(minor_status, &ptr,
472 				input_token->length -
473 				(ptr - (uchar_t *)input_token->value));
474 
475 			if (spnego_ctx->internal_mech == NULL) {
476 				/* CSTYLED */
477 				*minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
478 				ret = GSS_S_FAILURE;
479 			}
480 
481 			if (ret != GSS_S_FAILURE && *ptr == (CONTEXT | 0x02)) {
482 			    if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
483 				input_token->length -
484 				(ptr - (uchar_t *)input_token->value),
485 				&len) < 0) {
486 				ret = GSS_S_DEFECTIVE_TOKEN;
487 			    } else {
488 				i_input_token = get_input_token(&ptr, len);
489 				if (i_input_token  != NULL) {
490 					ret = GSS_S_COMPLETE;
491 					send_token = CHECK_MIC;
492 				} else {
493 					ret = GSS_S_DEFECTIVE_TOKEN;
494 					send_token = NO_TOKEN_SEND;
495 				}
496 			    }
497 			} else if (ret == GSS_S_CONTINUE_NEEDED ||
498 				ret == GSS_S_COMPLETE) {
499 				send_token = CHECK_MIC;
500 			}
501 
502 			/*
503 			 * If we sent "optimistic" initial token,
504 			 * but the acceptor did not send a response token,
505 			 * this is an error.
506 			 */
507 			if (ret == GSS_S_COMPLETE &&
508 			    i_input_token == GSS_C_NO_BUFFER &&
509 			    spnego_ctx->last_status == GSS_S_CONTINUE_NEEDED &&
510 			    spnego_ctx->optimistic) {
511 				/* CSTYLED */
512 				*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
513 				ret = GSS_S_DEFECTIVE_TOKEN;
514 				send_token = NO_TOKEN_SEND;
515 			}
516 
517 			if (send_token != NO_TOKEN_SEND) {
518 				if (i_input_token == NULL)
519 					ret = GSS_S_COMPLETE;
520 				else
521 					ret = GSS_S_CONTINUE_NEEDED;
522 				send_token = CHECK_MIC;
523 			}
524 			break;
525 
526 		case REJECT:
527 			ret = GSS_S_BAD_MECH;
528 			*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
529 			send_token = NO_TOKEN_SEND;
530 			break;
531 
532 		default:
533 			ret = GSS_S_FAILURE;
534 			send_token = NO_TOKEN_SEND;
535 			break;
536 		}
537 	}
538 
539 
540 	if (send_token == NO_TOKEN_SEND) {
541 		output_token->length = 0;
542 		output_token->value = NULL;
543 		goto cleanup;
544 	}
545 
546 	i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
547 
548 	if (i_output_token == NULL) {
549 		ret = status = GSS_S_FAILURE;
550 		goto cleanup;
551 	}
552 
553 	i_output_token->length = 0;
554 	i_output_token->value = NULL;
555 
556 	if (ret == GSS_S_CONTINUE_NEEDED) {
557 		gss_OID inner_mech_type = GSS_C_NO_OID;
558 
559 		status = gss_init_sec_context(minor_status,
560 				claimant_cred_handle,
561 				&spnego_ctx->ctx_handle,
562 				target_name,
563 				spnego_ctx->internal_mech,
564 				req_flags,
565 				time_req,
566 				NULL,
567 				i_input_token,
568 				&inner_mech_type,
569 				i_output_token,
570 				&local_ret_flags,
571 				time_rec);
572 
573 		if (ret_flags)
574 			*ret_flags = local_ret_flags;
575 
576 		spnego_ctx->last_status = status;
577 
578 		if (i_input_token != GSS_C_NO_BUFFER) {
579 		    (void) gss_release_buffer(&mstat, i_input_token);
580 		    free(i_input_token);
581 		}
582 
583 		if ((status != GSS_S_COMPLETE) &&
584 		    (status != GSS_S_CONTINUE_NEEDED)) {
585 			ret = status;
586 		}
587 
588 		/* create mic/check mic */
589 		if ((i_output_token->length == 0) &&
590 		    (status == GSS_S_COMPLETE) &&
591 		    (local_ret_flags & GSS_C_INTEG_FLAG)) {
592 			if (*ptr == (CONTEXT | 0x03)) {
593 			    if (g_get_tag_and_length(&ptr,
594 					(CONTEXT | 0x03),
595 					input_token->length -
596 					(ptr - (uchar_t *)input_token->value),
597 					&len) < 0) {
598 				ret = GSS_S_DEFECTIVE_TOKEN;
599 			    } else {
600 				ret = GSS_S_COMPLETE;
601 				mechListMIC = get_input_token(&ptr, len);
602 				if (mechListMIC == NULL)
603 					ret = GSS_S_DEFECTIVE_TOKEN;
604 				else if (!spnego_ctx->MS_Interop &&
605 					spnego_ctx->DER_mechTypes.length > 0) {
606 					status = gss_verify_mic(minor_status,
607 					    spnego_ctx->ctx_handle,
608 					    &spnego_ctx->DER_mechTypes,
609 					    mechListMIC,
610 					    qop_state);
611 				}
612 			    }
613 			} else if (!spnego_ctx->MS_Interop) {
614 			/*
615 			 * If no MIC was sent and we are in
616 			 * "standard" mode (i.e. NOT MS_Interop),
617 			 * the MIC must be present.
618 			 */
619 			    ret = GSS_S_DEFECTIVE_TOKEN;
620 			} else {
621 			    /* In "MS_Interop" mode, MIC is ignored. */
622 			    ret = GSS_S_COMPLETE;
623 			}
624 		}
625 	}
626 
627 	if ((status == GSS_S_COMPLETE) &&
628 	    (ret == GSS_S_COMPLETE)) {
629 		if (actual_mech) {
630 			(void) generic_gss_release_oid(&mstat, actual_mech);
631 			ret = generic_gss_copy_oid(&mstat,
632 				spnego_ctx->internal_mech, actual_mech);
633 			if (ret != GSS_S_COMPLETE)
634 				goto cleanup;
635 		}
636 
637 	} else if (ret == GSS_S_CONTINUE_NEEDED) {
638 		if (make_spnego_tokenInit_msg(spnego_ctx,
639 				mechSet,
640 				i_output_token, send_token,
641 				output_token) < 0) {
642 			ret = GSS_S_DEFECTIVE_TOKEN;
643 		}
644 	}
645 
646 cleanup:
647 	if (status != GSS_S_COMPLETE)
648 		ret = status;
649 	if (ret != GSS_S_COMPLETE &&
650 	    ret != GSS_S_CONTINUE_NEEDED) {
651 		if (spnego_ctx != NULL &&
652 		    spnego_ctx->ctx_handle != NULL)
653 			gss_delete_sec_context(&mstat, &spnego_ctx->ctx_handle,
654 				GSS_C_NO_BUFFER);
655 
656 		if (spnego_ctx != NULL)
657 			release_spnego_ctx(&spnego_ctx);
658 
659 		*context_handle = GSS_C_NO_CONTEXT;
660 
661 		if (output_token)
662 			(void) gss_release_buffer(&mstat, output_token);
663 	}
664 
665 	if (i_output_token != GSS_C_NO_BUFFER) {
666 		(void) gss_release_buffer(&mstat, i_output_token);
667 		free(i_output_token);
668 	}
669 
670 	if (mechListMIC != GSS_C_NO_BUFFER) {
671 		(void) gss_release_buffer(&mstat, mechListMIC);
672 		free(mechListMIC);
673 	}
674 
675 	if (mechSet != NULL)
676 		(void) gss_release_oid_set(&mstat, &mechSet);
677 
678 	if (credlistptr != NULL)
679 		(void) gss_release_cred(&mstat, credlistptr);
680 
681 	return (ret);
682 } /* init_sec_context */
683 
684 /*ARGSUSED*/
685 OM_uint32
686 spnego_gss_accept_sec_context(void *ct,
687 			    OM_uint32 *minor_status,
688 			    gss_ctx_id_t *context_handle,
689 			    gss_cred_id_t verifier_cred_handle,
690 			    gss_buffer_t input_token,
691 			    gss_channel_bindings_t input_chan_bindings,
692 			    gss_name_t *src_name,
693 			    gss_OID *mech_type,
694 			    gss_buffer_t output_token,
695 			    OM_uint32 *ret_flags,
696 			    OM_uint32 *time_rec,
697 			    gss_cred_id_t *delegated_cred_handle)
698 {
699 	spnego_gss_ctx_id_t spnego_ctx = NULL;
700 	gss_OID mech_wanted = NULL;
701 	gss_OID_set mechSet = GSS_C_NO_OID_SET;
702 	gss_OID_set supported_mechSet = GSS_C_NO_OID_SET;
703 	gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
704 	gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
705 	gss_buffer_t mechListMIC = GSS_C_NO_BUFFER;
706 	gss_cred_id_t acquired_cred = NULL;
707 	gss_name_t internal_name = GSS_C_NO_NAME;
708 	OM_uint32 status = GSS_S_COMPLETE;
709 	OM_uint32 ret = GSS_S_COMPLETE;
710 	unsigned char *ptr;
711 	unsigned char *bufstart;
712 	int bodysize;
713 	int err, len;
714 	OM_uint32 negResult;
715 	OM_uint32 minor_stat;
716 	OM_uint32 mstat;
717 	OM_uint32 req_flags;
718 	OM_uint32 mechsetlen;
719 	gss_qop_t qop_state;
720 	send_token_flag return_token =  NO_TOKEN_SEND;
721 	bool_t firstMech;
722 	bool_t Need_Cred = FALSE;
723 	OM_uint32 local_ret_flags = 0;
724 	uchar_t *buf, *tmp;
725 
726 	dsyslog("Entering accept_sec_context\n");
727 
728 	if (context_handle == NULL)
729 		return (GSS_S_NO_CONTEXT);
730 
731 	if (src_name)
732 		*src_name = (gss_name_t)NULL;
733 
734 	output_token->length = 0;
735 	output_token->value = NULL;
736 	*minor_status = 0;
737 
738 	if (mech_type)
739 		*mech_type = GSS_C_NULL_OID;
740 
741 	/* return a bogus cred handle */
742 	if (delegated_cred_handle)
743 		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
744 
745 	if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
746 		Need_Cred = TRUE;
747 	}
748 
749 	/* Check for defective input token. */
750 	ptr = bufstart = (unsigned char *) input_token->value;
751 	if (err = g_verify_token_header((gss_OID)gss_mech_spnego, &bodysize,
752 					&ptr, 0,
753 					input_token->length)) {
754 		*minor_status = err;
755 		ret = GSS_S_DEFECTIVE_TOKEN;
756 		negResult = REJECT;
757 		return_token = ERROR_TOKEN_SEND;
758 		goto senderror;
759 	}
760 
761 	/*
762 	 * set up of context, determine mech to be used, save mechset
763 	 * for use later in integrety check.
764 	 */
765 	if (*context_handle == GSS_C_NO_CONTEXT) {
766 		if ((spnego_ctx = create_spnego_ctx()) == NULL)
767 			return (GSS_S_FAILURE);
768 
769 		/*
770 		 * Until the accept operation is complete, the
771 		 * context_handle returned should refer to
772 		 * the spnego context.
773 		 */
774 		*context_handle = (gss_ctx_id_t)spnego_ctx;
775 		minor_stat = get_available_mechs(minor_status,
776 				GSS_C_NO_NAME, GSS_C_ACCEPT,
777 				NULL, &supported_mechSet);
778 
779 		if (minor_stat != GSS_S_COMPLETE) {
780 			release_spnego_ctx(&spnego_ctx);
781 			*context_handle = GSS_C_NO_CONTEXT;
782 			return (minor_stat);
783 		}
784 
785 		if (Need_Cred) {
786 			minor_stat = gss_acquire_cred(minor_status,
787 					GSS_C_NO_NAME, NULL,
788 					supported_mechSet,
789 					GSS_C_ACCEPT,
790 					&acquired_cred, NULL,
791 					NULL);
792 
793 			if (minor_stat != GSS_S_COMPLETE) {
794 				(void) gss_release_oid_set(minor_status,
795 						&supported_mechSet);
796 				release_spnego_ctx(&spnego_ctx);
797 				*context_handle = GSS_C_NO_CONTEXT;
798 				return (minor_stat);
799 			} else {
800 				verifier_cred_handle = acquired_cred;
801 			}
802 		}
803 
804 		if (err = g_verify_neg_token_init(&ptr, input_token->length)) {
805 			*minor_status = err;
806 			ret = GSS_S_DEFECTIVE_TOKEN;
807 			negResult = REJECT;
808 			return_token = ERROR_TOKEN_SEND;
809 			goto senderror;
810 		}
811 
812 		/*
813 		 * Allocate space to hold the mechTypes
814 		 * because we need it later.
815 		 */
816 		mechsetlen = input_token->length - (ptr - bufstart);
817 		buf = (uchar_t *)malloc(mechsetlen);
818 		if (buf == NULL) {
819 			ret = GSS_S_FAILURE;
820 			goto cleanup;
821 		}
822 		(void) memcpy(buf, ptr, mechsetlen);
823 		ptr = bufstart = buf;
824 
825 		/*
826 		 * Get pointers to the DER encoded MechSet so we
827 		 * can properly check and calculate a MIC later.
828 		 */
829 		spnego_ctx->DER_mechTypes.value = ptr;
830 		mechSet = get_mech_set(minor_status, &ptr, mechsetlen);
831 		if (mechSet == NULL) {
832 			ret = GSS_S_DEFECTIVE_TOKEN;
833 			negResult = REJECT;
834 			return_token = ERROR_TOKEN_SEND;
835 			goto senderror;
836 		}
837 		spnego_ctx->DER_mechTypes.length = ptr - bufstart;
838 		mechsetlen -= (ptr - bufstart);
839 
840 		/*
841 		 * Select the best match between the list of mechs
842 		 * that the initiator requested and the list that
843 		 * the acceptor will support.
844 		 */
845 		mech_wanted = negotiate_mech_type(minor_status,
846 						supported_mechSet,
847 						mechSet,
848 						&negResult,
849 						&firstMech);
850 
851 		(void) gss_release_oid_set(&minor_stat, &supported_mechSet);
852 		(void) gss_release_oid_set(&minor_stat, &mechSet);
853 		supported_mechSet = NULL;
854 		mechSet = NULL;
855 
856 		if (get_req_flags(&ptr, (int *)&mechsetlen, &req_flags) ==
857 			ACCEPT_DEFECTIVE_TOKEN) {
858 			negResult = REJECT;
859 		}
860 
861 		tmp = ptr;
862 		if (negResult == ACCEPT_COMPLETE) {
863 			if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
864 						mechsetlen,
865 						&len) < 0) {
866 				negResult = REJECT;
867 			} else {
868 				i_input_token = get_input_token(&ptr, len);
869 				if (i_input_token == NULL) {
870 					negResult = REJECT;
871 				}
872 			}
873 			return_token = INIT_TOKEN_SEND;
874 		}
875 		if (negResult == REJECT) {
876 			ret = GSS_S_DEFECTIVE_TOKEN;
877 			return_token = ERROR_TOKEN_SEND;
878 		} else {
879 			ret = GSS_S_CONTINUE_NEEDED;
880 			return_token = INIT_TOKEN_SEND;
881 		}
882 
883 		mechsetlen -= ptr - tmp;
884 		/*
885 		 * Check to see if there is a MechListMIC field
886 		 */
887 		if (negResult == ACCEPT_COMPLETE && mechsetlen > 0) {
888 			tmp = ptr;
889 			if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
890 						mechsetlen, &len) >= 0) {
891 				mechListMIC = get_input_token(&ptr, len);
892 				if (mechListMIC == GSS_C_NO_BUFFER) {
893 					negResult = REJECT;
894 					return_token = ERROR_TOKEN_SEND;
895 					ret = GSS_S_DEFECTIVE_TOKEN;
896 				}
897 				mechsetlen -= (ptr - tmp);
898 			}
899 		}
900 	} else {
901 		/*
902 		 * get internal input token and context for continued
903 		 * calls of spnego_gss_init_sec_context.
904 		 */
905 		i_input_token = get_input_token(&ptr,
906 					input_token->length -
907 					(ptr - (uchar_t *)input_token->value));
908 		if (i_input_token == NULL) {
909 			negResult = REJECT;
910 			return_token = ERROR_TOKEN_SEND;
911 			ret = GSS_S_DEFECTIVE_TOKEN;
912 		} else {
913 			spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
914 			return_token = CONT_TOKEN_SEND;
915 		}
916 	}
917 
918 	/*
919 	 * If we still don't have a cred, we have an error.
920 	 */
921 	if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
922 		ret = GSS_S_FAILURE;
923 		goto cleanup;
924 	}
925 
926 	/* If we have an error already, bail out */
927 	if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED)
928 		goto senderror;
929 
930 	if (i_input_token != GSS_C_NO_BUFFER) {
931 		i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
932 
933 		if (i_output_token == NULL) {
934 			ret = GSS_S_FAILURE;
935 			goto cleanup;
936 		}
937 
938 		i_output_token->length = 0;
939 		i_output_token->value = NULL;
940 
941 		status = gss_accept_sec_context(&minor_stat,
942 						&spnego_ctx->ctx_handle,
943 						verifier_cred_handle,
944 						i_input_token,
945 						GSS_C_NO_CHANNEL_BINDINGS,
946 						&internal_name,
947 						mech_type,
948 						i_output_token,
949 						&local_ret_flags,
950 						time_rec,
951 						delegated_cred_handle);
952 
953 		if ((status != GSS_S_COMPLETE) &&
954 		    (status != GSS_S_CONTINUE_NEEDED)) {
955 			*minor_status = minor_stat;
956 			(void) gss_release_buffer(&mstat, i_input_token);
957 
958 			if (i_input_token != GSS_C_NO_BUFFER) {
959 				free(i_input_token);
960 				i_input_token = GSS_C_NO_BUFFER;
961 			}
962 
963 			ret = status;
964 
965 			/*
966 			 * Reject the request with an error token.
967 			 */
968 			negResult = REJECT;
969 			return_token = ERROR_TOKEN_SEND;
970 
971 			goto senderror;
972 		}
973 
974 		if (ret_flags)
975 			*ret_flags = local_ret_flags;
976 
977 		if (i_input_token != GSS_C_NO_BUFFER) {
978 			(void) gss_release_buffer(&mstat, i_input_token);
979 			free(i_input_token);
980 			i_input_token = GSS_C_NO_BUFFER;
981 		}
982 
983 		/* If we got a MIC, verify it if possible */
984 		if ((status == GSS_S_COMPLETE) &&
985 		    (local_ret_flags & GSS_C_INTEG_FLAG) &&
986 		    mechListMIC != GSS_C_NO_BUFFER &&
987 		    !spnego_ctx->MS_Interop) {
988 
989 			ret = gss_verify_mic(minor_status,
990 				    spnego_ctx->ctx_handle,
991 				    &spnego_ctx->DER_mechTypes,
992 				    mechListMIC,
993 				    &qop_state);
994 
995 			(void) gss_release_buffer(&mstat, mechListMIC);
996 			free(mechListMIC);
997 			mechListMIC = GSS_C_NO_BUFFER;
998 
999 			if (ret != GSS_S_COMPLETE) {
1000 				negResult = REJECT;
1001 				return_token = ERROR_TOKEN_SEND;
1002 				goto senderror;
1003 			}
1004 		}
1005 
1006 		/*
1007 		 * If the MIC was verified OK, create a new MIC
1008 		 * for the response message.
1009 		 */
1010 		if (status == GSS_S_COMPLETE &&
1011 		    (local_ret_flags & GSS_C_INTEG_FLAG) &&
1012 		    !spnego_ctx->MS_Interop) {
1013 
1014 			mechListMIC = (gss_buffer_t)
1015 				malloc(sizeof (gss_buffer_desc));
1016 
1017 			if (mechListMIC == NULL ||
1018 			    spnego_ctx->DER_mechTypes.length == 0) {
1019 				ret = GSS_S_FAILURE;
1020 				goto cleanup;
1021 			}
1022 
1023 			ret = gss_get_mic(minor_status,
1024 					spnego_ctx->ctx_handle,
1025 					GSS_C_QOP_DEFAULT,
1026 					&spnego_ctx->DER_mechTypes,
1027 					mechListMIC);
1028 
1029 			if (ret != GSS_S_COMPLETE) {
1030 				negResult = REJECT;
1031 				return_token = ERROR_TOKEN_SEND;
1032 				goto senderror;
1033 			}
1034 		}
1035 		ret = status;
1036 
1037 		if (status == GSS_S_COMPLETE) {
1038 			if (internal_name != NULL && src_name != NULL)
1039 				*src_name = internal_name;
1040 		}
1041 
1042 
1043 		if (status == GSS_S_CONTINUE_NEEDED) {
1044 			if (return_token == INIT_TOKEN_SEND)
1045 				negResult = ACCEPT_INCOMPLETE;
1046 		}
1047 	}
1048 
1049 senderror:
1050 	if ((return_token == INIT_TOKEN_SEND) ||
1051 	    (return_token == CONT_TOKEN_SEND) ||
1052 	    (return_token == ERROR_TOKEN_SEND)) {
1053 		int MS_Interop = 0;
1054 
1055 		if (spnego_ctx)
1056 			MS_Interop = spnego_ctx->MS_Interop;
1057 
1058 		/*
1059 		 * create response for the initiator.
1060 		 */
1061 		err = make_spnego_tokenTarg_msg(negResult,
1062 					mech_wanted,
1063 					i_output_token,
1064 					mechListMIC,
1065 					return_token,
1066 					MS_Interop,
1067 					output_token);
1068 
1069 		(void) gss_release_buffer(&mstat, mechListMIC);
1070 		free(mechListMIC);
1071 
1072 		/*
1073 		 * If we could not make the response token,
1074 		 * we will have to fail without sending a response.
1075 		 */
1076 		if (err) {
1077 			(void) gss_release_buffer(&mstat, output_token);
1078 		}
1079 	} else {
1080 		(void) gss_release_buffer(&mstat, output_token);
1081 	}
1082 
1083 cleanup:
1084 	if (ret != GSS_S_COMPLETE &&
1085 	    ret != GSS_S_CONTINUE_NEEDED) {
1086 		if (spnego_ctx != NULL) {
1087 			(void) gss_delete_sec_context(&mstat,
1088 				&spnego_ctx->ctx_handle, NULL);
1089 
1090 			spnego_ctx->ctx_handle = NULL;
1091 
1092 			release_spnego_ctx(&spnego_ctx);
1093 		}
1094 		*context_handle = GSS_C_NO_CONTEXT;
1095 	}
1096 	if (mech_wanted != NULL) {
1097 		generic_gss_release_oid(&mstat, &mech_wanted);
1098 	}
1099 
1100 	(void) gss_release_cred(minor_status, &acquired_cred);
1101 	(void) gss_release_oid_set(minor_status, &supported_mechSet);
1102 
1103 	(void) gss_release_buffer(&mstat, i_output_token);
1104 	free(i_output_token);
1105 
1106 	return (ret);
1107 }
1108 
1109 /*ARGSUSED*/
1110 OM_uint32
1111 spnego_gss_display_status(void *ctx,
1112 		OM_uint32 *minor_status,
1113 		OM_uint32 status_value,
1114 		int status_type,
1115 		gss_OID mech_type,
1116 		OM_uint32 *message_context,
1117 		gss_buffer_t status_string)
1118 {
1119 	dsyslog("Entering display_status\n");
1120 
1121 	*message_context = 0;
1122 	switch (status_value) {
1123 	    case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1124 		/* CSTYLED */
1125 		*status_string = make_err_msg(gettext("SPNEGO cannot find mechanisms to negotiate"));
1126 		break;
1127 	    case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1128 		/* CSTYLED */
1129 		*status_string = make_err_msg(gettext("SPNEGO failed to acquire creds"));
1130 		break;
1131 	    case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1132 		/* CSTYLED */
1133 		*status_string = make_err_msg(gettext("SPNEGO acceptor did not select a mechanism"));
1134 		break;
1135 	    case ERR_SPNEGO_NEGOTIATION_FAILED:
1136 		/* CSTYLED */
1137 		*status_string = make_err_msg(gettext("SPNEGO failed to negotiate a mechanism"));
1138 		break;
1139 	    case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1140 		/* CSTYLED */
1141 		*status_string = make_err_msg(gettext("SPNEGO acceptor did not return a valid token"));
1142 		break;
1143 	    default:
1144 		status_string->length = 0;
1145 		status_string->value = "";
1146 		break;
1147 	}
1148 
1149 	dsyslog("Leaving display_status\n");
1150 	return (GSS_S_COMPLETE);
1151 }
1152 
1153 /*ARGSUSED*/
1154 OM_uint32
1155 spnego_gss_import_name(void *ctx,
1156 		    OM_uint32 *minor_status,
1157 		    gss_buffer_t input_name_buffer,
1158 		    gss_OID input_name_type,
1159 		    gss_name_t *output_name)
1160 {
1161 	OM_uint32 status;
1162 
1163 	dsyslog("Entering import_name\n");
1164 
1165 	status = gss_import_name(minor_status, input_name_buffer,
1166 			input_name_type, output_name);
1167 
1168 	dsyslog("Leaving import_name\n");
1169 	return (status);
1170 }
1171 
1172 /*ARGSUSED*/
1173 OM_uint32
1174 spnego_gss_release_name(void *ctx,
1175 			OM_uint32 *minor_status,
1176 			gss_name_t *input_name)
1177 {
1178 	OM_uint32 status;
1179 
1180 	dsyslog("Entering release_name\n");
1181 
1182 	status = gss_release_name(minor_status, input_name);
1183 
1184 	dsyslog("Leaving release_name\n");
1185 	return (status);
1186 }
1187 
1188 /*ARGSUSED*/
1189 OM_uint32
1190 spnego_gss_display_name(void *ctx,
1191 			OM_uint32 *minor_status,
1192 			gss_name_t input_name,
1193 			gss_buffer_t output_name_buffer,
1194 			gss_OID *output_name_type)
1195 {
1196 	OM_uint32 status = GSS_S_COMPLETE;
1197 	dsyslog("Entering display_name\n");
1198 
1199 	status = gss_display_name(minor_status, input_name,
1200 			output_name_buffer, output_name_type);
1201 
1202 	dsyslog("Leaving display_name\n");
1203 	return (status);
1204 }
1205 
1206 /*ARGSUSED*/
1207 OM_uint32
1208 spnego_gss_inquire_cred(void *ctx,
1209 	OM_uint32 *minor_status,
1210 	const gss_cred_id_t cred_handle,
1211 	gss_name_t *name,
1212 	OM_uint32 *lifetime,
1213 	gss_cred_usage_t *cred_usage,
1214 	gss_OID_set *mechanisms)
1215 {
1216 	/* A SPNEGO cred is just a standard libgss cred record */
1217 	return (gss_inquire_cred(minor_status,
1218 			cred_handle, name,
1219 			lifetime, cred_usage,
1220 			mechanisms));
1221 }
1222 
1223 /*ARGSUSED*/
1224 OM_uint32
1225 spnego_gss_inquire_names_for_mech(void *ctx,
1226 				OM_uint32	*minor_status,
1227 				gss_OID		mechanism,
1228 				gss_OID_set	*name_types)
1229 {
1230 	OM_uint32   major, minor;
1231 
1232 	dsyslog("Entering inquire_names_for_mech\n");
1233 	/*
1234 	 * We only know how to handle our own mechanism.
1235 	 */
1236 	if ((mechanism != GSS_C_NULL_OID) &&
1237 	    !g_OID_equal(gss_mech_spnego, mechanism)) {
1238 		*minor_status = 0;
1239 		return (GSS_S_FAILURE);
1240 	}
1241 
1242 	major = gss_create_empty_oid_set(minor_status, name_types);
1243 	if (major == GSS_S_COMPLETE) {
1244 		/* Now add our members. */
1245 		if (((major = gss_add_oid_set_member(minor_status,
1246 				(gss_OID) GSS_C_NT_USER_NAME,
1247 				name_types)) == GSS_S_COMPLETE) &&
1248 		    ((major = gss_add_oid_set_member(minor_status,
1249 				(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1250 				name_types)) == GSS_S_COMPLETE) &&
1251 		    ((major = gss_add_oid_set_member(minor_status,
1252 				(gss_OID) GSS_C_NT_STRING_UID_NAME,
1253 				name_types)) == GSS_S_COMPLETE)) {
1254 			major = gss_add_oid_set_member(minor_status,
1255 				(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1256 				name_types);
1257 		}
1258 
1259 		if (major != GSS_S_COMPLETE)
1260 			(void) gss_release_oid_set(&minor, name_types);
1261 	}
1262 
1263 	dsyslog("Leaving inquire_names_for_mech\n");
1264 	return (major);
1265 }
1266 
1267 OM_uint32
1268 spnego_gss_unseal(void *context,
1269 		OM_uint32 *minor_status,
1270 		gss_ctx_id_t context_handle,
1271 		gss_buffer_t input_message_buffer,
1272 		gss_buffer_t output_message_buffer,
1273 		int *conf_state,
1274 		int *qop_state)
1275 {
1276 	OM_uint32 ret;
1277 	spnego_gss_ctx_id_t ctx =
1278 		    (spnego_gss_ctx_id_t)context_handle;
1279 
1280 	if (context_handle == NULL)
1281 		return (GSS_S_NO_CONTEXT);
1282 
1283 	ret = gss_unseal(minor_status,
1284 			ctx->ctx_handle,
1285 			input_message_buffer,
1286 			output_message_buffer,
1287 			conf_state,
1288 			qop_state);
1289 
1290 	return (ret);
1291 }
1292 
1293 OM_uint32
1294 spnego_gss_seal(void *context,
1295 		OM_uint32 *minor_status,
1296 		gss_ctx_id_t context_handle,
1297 		int conf_req_flag,
1298 		int qop_req,
1299 		gss_buffer_t input_message_buffer,
1300 		int *conf_state,
1301 		gss_buffer_t output_message_buffer)
1302 {
1303 	OM_uint32 ret;
1304 	spnego_gss_ctx_id_t ctx =
1305 		    (spnego_gss_ctx_id_t)context_handle;
1306 
1307 	if (context_handle == NULL)
1308 		return (GSS_S_NO_CONTEXT);
1309 
1310 	ret = gss_seal(minor_status,
1311 		    ctx->ctx_handle,
1312 		    conf_req_flag,
1313 		    qop_req,
1314 		    input_message_buffer,
1315 		    conf_state,
1316 		    output_message_buffer);
1317 
1318 	return (ret);
1319 }
1320 
1321 OM_uint32
1322 spnego_gss_process_context_token(void *context,
1323 				OM_uint32	*minor_status,
1324 				const gss_ctx_id_t context_handle,
1325 				const gss_buffer_t token_buffer)
1326 {
1327 	OM_uint32 ret;
1328 	spnego_gss_ctx_id_t ctx =
1329 		    (spnego_gss_ctx_id_t)context_handle;
1330 
1331 	if (context_handle == NULL)
1332 		return (GSS_S_NO_CONTEXT);
1333 
1334 	ret = gss_process_context_token(minor_status,
1335 					ctx->ctx_handle,
1336 					token_buffer);
1337 
1338 	return (ret);
1339 }
1340 
1341 OM_uint32
1342 spnego_gss_delete_sec_context(void *context,
1343 			    OM_uint32 *minor_status,
1344 			    gss_ctx_id_t *context_handle,
1345 			    gss_buffer_t output_token)
1346 {
1347 	OM_uint32 ret = GSS_S_COMPLETE;
1348 	spnego_gss_ctx_id_t *ctx =
1349 		    (spnego_gss_ctx_id_t *)context_handle;
1350 
1351 	if (context_handle == NULL || *ctx == NULL)
1352 		return (GSS_S_NO_CONTEXT);
1353 
1354 	/*
1355 	 * If this is still a SPNEGO mech, release it locally.
1356 	 */
1357 	if ((*ctx)->ctx_handle == GSS_C_NO_CONTEXT) {
1358 		(void) release_spnego_ctx(ctx);
1359 	} else {
1360 		ret = gss_delete_sec_context(minor_status,
1361 				    &(*ctx)->ctx_handle,
1362 				    output_token);
1363 	}
1364 
1365 	return (ret);
1366 }
1367 
1368 OM_uint32
1369 spnego_gss_context_time(void *context,
1370 			OM_uint32	*minor_status,
1371 			const gss_ctx_id_t context_handle,
1372 			OM_uint32	*time_rec)
1373 {
1374 	OM_uint32 ret;
1375 	spnego_gss_ctx_id_t ctx =
1376 		    (spnego_gss_ctx_id_t)context_handle;
1377 
1378 	if (context_handle == NULL)
1379 		return (GSS_S_NO_CONTEXT);
1380 
1381 	ret = gss_context_time(minor_status,
1382 			    ctx->ctx_handle,
1383 			    time_rec);
1384 
1385 	return (ret);
1386 }
1387 
1388 OM_uint32
1389 spnego_gss_export_sec_context(void *context,
1390 			    OM_uint32	  *minor_status,
1391 			    gss_ctx_id_t *context_handle,
1392 			    gss_buffer_t interprocess_token)
1393 {
1394 	OM_uint32 ret;
1395 	spnego_gss_ctx_id_t *ctx =
1396 		    (spnego_gss_ctx_id_t *)context_handle;
1397 
1398 	if (context_handle == NULL || *ctx == NULL)
1399 		return (GSS_S_NO_CONTEXT);
1400 
1401 	ret = gss_export_sec_context(minor_status,
1402 				    &(*ctx)->ctx_handle,
1403 				    interprocess_token);
1404 	return (ret);
1405 }
1406 
1407 OM_uint32
1408 spnego_gss_import_sec_context(void *context,
1409 	OM_uint32		*minor_status,
1410 	const gss_buffer_t	interprocess_token,
1411 	gss_ctx_id_t		*context_handle)
1412 {
1413 	OM_uint32 ret;
1414 	spnego_gss_ctx_id_t ctx;
1415 
1416 	if (context_handle == NULL)
1417 		return (GSS_S_NO_CONTEXT);
1418 
1419 	if ((ctx = create_spnego_ctx()) == NULL) {
1420 		*minor_status = ENOMEM;
1421 		return (GSS_S_FAILURE);
1422 	}
1423 
1424 	ret = gss_import_sec_context(minor_status,
1425 				    interprocess_token,
1426 				    &(ctx->ctx_handle));
1427 	if (GSS_ERROR(ret)) {
1428 		(void) release_spnego_ctx(&ctx);
1429 		return (ret);
1430 	}
1431 
1432 	*context_handle = (gss_ctx_id_t)ctx;
1433 
1434 	return (ret);
1435 }
1436 
1437 OM_uint32
1438 spnego_gss_inquire_context(void *context,
1439 			OM_uint32	*minor_status,
1440 			const gss_ctx_id_t context_handle,
1441 			gss_name_t	*src_name,
1442 			gss_name_t	*targ_name,
1443 			OM_uint32	*lifetime_rec,
1444 			gss_OID		*mech_type,
1445 			OM_uint32	*ctx_flags,
1446 			int		*locally_initiated,
1447 			int		*open)
1448 {
1449 	OM_uint32 ret = GSS_S_COMPLETE;
1450 	spnego_gss_ctx_id_t ctx =
1451 		    (spnego_gss_ctx_id_t)context_handle;
1452 
1453 	if (context_handle == NULL)
1454 		return (GSS_S_NO_CONTEXT);
1455 
1456 	ret = gss_inquire_context(minor_status,
1457 				ctx->ctx_handle,
1458 				src_name,
1459 				targ_name,
1460 				lifetime_rec,
1461 				mech_type,
1462 				ctx_flags,
1463 				locally_initiated,
1464 				open);
1465 
1466 	return (ret);
1467 }
1468 
1469 OM_uint32
1470 spnego_gss_wrap_size_limit(void *context,
1471 	OM_uint32	*minor_status,
1472 	const gss_ctx_id_t context_handle,
1473 	int		conf_req_flag,
1474 	gss_qop_t	qop_req,
1475 	OM_uint32	req_output_size,
1476 	OM_uint32	*max_input_size)
1477 {
1478 	OM_uint32 ret;
1479 	spnego_gss_ctx_id_t ctx =
1480 		    (spnego_gss_ctx_id_t)context_handle;
1481 
1482 	if (context_handle == NULL)
1483 		return (GSS_S_NO_CONTEXT);
1484 
1485 	ret = gss_wrap_size_limit(minor_status,
1486 				ctx->ctx_handle,
1487 				conf_req_flag,
1488 				qop_req,
1489 				req_output_size,
1490 				max_input_size);
1491 	return (ret);
1492 }
1493 
1494 OM_uint32
1495 spnego_gss_sign(void *context,
1496 		OM_uint32 *minor_status,
1497 		const gss_ctx_id_t context_handle,
1498 		int  qop_req,
1499 		const gss_buffer_t message_buffer,
1500 		gss_buffer_t message_token)
1501 {
1502 	OM_uint32 ret;
1503 	spnego_gss_ctx_id_t ctx =
1504 		    (spnego_gss_ctx_id_t)context_handle;
1505 
1506 	if (context_handle == NULL)
1507 		return (GSS_S_NO_CONTEXT);
1508 
1509 	ret = gss_sign(minor_status,
1510 		    ctx->ctx_handle,
1511 		    qop_req,
1512 		    message_buffer,
1513 		    message_token);
1514 
1515 	return (ret);
1516 }
1517 
1518 OM_uint32
1519 spnego_gss_verify(void *context,
1520 		OM_uint32 *minor_status,
1521 		const gss_ctx_id_t context_handle,
1522 		const gss_buffer_t msg_buffer,
1523 		const gss_buffer_t token_buffer,
1524 		int *qop_state)
1525 {
1526 	OM_uint32 ret;
1527 	spnego_gss_ctx_id_t ctx =
1528 		    (spnego_gss_ctx_id_t)context_handle;
1529 
1530 	if (context_handle == NULL)
1531 		return (GSS_S_NO_CONTEXT);
1532 
1533 	ret = gss_verify_mic(minor_status,
1534 			    ctx->ctx_handle,
1535 			    msg_buffer,
1536 			    token_buffer,
1537 			    (uint32_t *)qop_state);
1538 	return (ret);
1539 }
1540 
1541 /*
1542  * We will release everything but the ctx_handle so that it
1543  * can be passed back to init/accept context. This routine should
1544  * not be called until after the ctx_handle memory is assigned to
1545  * the supplied context handle from init/accept context.
1546  */
1547 static void
1548 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
1549 {
1550 	spnego_gss_ctx_id_t context;
1551 	OM_uint32 minor_stat;
1552 
1553 	if (ctx != NULL)
1554 		context = *ctx;
1555 	else
1556 		return;
1557 
1558 	if (context != NULL) {
1559 		(void) gss_release_buffer(&minor_stat,
1560 					&context->DER_mechTypes);
1561 
1562 		(void) generic_gss_release_oid(&minor_stat,
1563 				&context->internal_mech);
1564 
1565 		if (context->optionStr != NULL) {
1566 			free(context->optionStr);
1567 			context->optionStr = NULL;
1568 		}
1569 		if (context->ctx_handle != GSS_C_NO_CONTEXT)
1570 			gss_delete_sec_context(&minor_stat,
1571 				&context->ctx_handle,
1572 				GSS_C_NO_BUFFER);
1573 
1574 		free(context);
1575 		*ctx = NULL;
1576 	}
1577 }
1578 
1579 /*
1580  * Can't use gss_indicate_mechs by itself to get available mechs for
1581  * SPNEGO because it will also return the SPNEGO mech and we do not
1582  * want to consider SPNEGO as an available security mech for
1583  * negotiation. For this reason, get_available_mechs will return
1584  * all available mechs except SPNEGO.
1585  *
1586  * If a ptr to a creds list is given, this function will attempt
1587  * to acquire creds for the creds given and trim the list of
1588  * returned mechanisms to only those for which creds are valid.
1589  *
1590  */
1591 static OM_uint32
1592 get_available_mechs(OM_uint32 *minor_status,
1593 	gss_name_t name, gss_cred_usage_t usage,
1594 	gss_cred_id_t *creds, gss_OID_set *rmechs)
1595 {
1596 	int		i;
1597 	int		found = 0;
1598 	OM_uint32 stat = GSS_S_COMPLETE;
1599 	gss_OID_set mechs, goodmechs;
1600 
1601 	stat = gss_indicate_mechs(minor_status, &mechs);
1602 
1603 	if (stat != GSS_S_COMPLETE) {
1604 		return (stat);
1605 	}
1606 
1607 	stat = gss_create_empty_oid_set(minor_status, rmechs);
1608 
1609 	if (stat != GSS_S_COMPLETE) {
1610 		(void) gss_release_oid_set(minor_status, &mechs);
1611 		return (stat);
1612 	}
1613 
1614 	for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
1615 		if ((mechs->elements[i].length
1616 		    != spnego_mechanism.mech_type.length) ||
1617 		    memcmp(mechs->elements[i].elements,
1618 			spnego_mechanism.mech_type.elements,
1619 			spnego_mechanism.mech_type.length)) {
1620 
1621 			stat = gss_add_oid_set_member(minor_status,
1622 					    &mechs->elements[i],
1623 					    rmechs);
1624 			if (stat == GSS_S_COMPLETE)
1625 				found++;
1626 		}
1627 	}
1628 
1629 	/*
1630 	 * If the caller wanted a list of creds returned,
1631 	 * trim the list of mechanisms down to only those
1632 	 * for which the creds are valid.
1633 	 */
1634 	if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
1635 		stat = gss_acquire_cred(minor_status,
1636 			name, NULL, *rmechs, usage, creds,
1637 			&goodmechs, NULL);
1638 
1639 		/*
1640 		 * Drop the old list in favor of the new
1641 		 * "trimmed" list.
1642 		 */
1643 		(void) gss_release_oid_set(minor_status, rmechs);
1644 		if (stat == GSS_S_COMPLETE) {
1645 			(void) gss_copy_oid_set(minor_status,
1646 					goodmechs, rmechs);
1647 			(void) gss_release_oid_set(minor_status, &goodmechs);
1648 		}
1649 	}
1650 
1651 	(void) gss_release_oid_set(minor_status, &mechs);
1652 	if (found == 0 || stat != GSS_S_COMPLETE) {
1653 		*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
1654 		if (stat == GSS_S_COMPLETE)
1655 			stat = GSS_S_FAILURE;
1656 	}
1657 
1658 	return (stat);
1659 }
1660 
1661 /* following are token creation and reading routines */
1662 
1663 /*
1664  * If buff_in is not pointing to a MECH_OID, then return NULL and do not
1665  * advance the buffer, otherwise, decode the mech_oid from the buffer and
1666  * place in gss_OID.
1667  */
1668 static gss_OID
1669 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
1670 {
1671 	OM_uint32	status;
1672 	gss_OID_desc 	toid;
1673 	gss_OID		mech_out = NULL;
1674 	uchar_t		*start, *end;
1675 
1676 	if (length < 1 || **buff_in != MECH_OID)
1677 		return (NULL);
1678 
1679 	start = *buff_in;
1680 	end = start + length;
1681 
1682 	(*buff_in)++;
1683 	toid.length = *(*buff_in)++;
1684 
1685 	if ((*buff_in + toid.length) > end)
1686 		return (NULL);
1687 
1688 	toid.elements = *buff_in;
1689 	*buff_in += toid.length;
1690 
1691 	status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
1692 
1693 	if (status != GSS_S_COMPLETE)
1694 		mech_out = NULL;
1695 
1696 	return (mech_out);
1697 }
1698 
1699 /*
1700  * der encode the given mechanism oid into buf_out, advancing the
1701  * buffer pointer.
1702  */
1703 
1704 static int
1705 put_mech_oid(unsigned char **buf_out, gss_OID_desc *mech, int buflen)
1706 {
1707 	if (buflen < mech->length + 2)
1708 		return (-1);
1709 	*(*buf_out)++ = MECH_OID;
1710 	*(*buf_out)++ = (unsigned char) mech->length;
1711 	memcpy((void *)(*buf_out), mech->elements, mech->length);
1712 	*buf_out += mech->length;
1713 	return (0);
1714 }
1715 
1716 /*
1717  * verify that buff_in points to an octet string, if it does not,
1718  * return NULL and don't advance the pointer. If it is an octet string
1719  * decode buff_in into a gss_buffer_t and return it, advancing the
1720  * buffer pointer.
1721  */
1722 static gss_buffer_t
1723 get_input_token(unsigned char **buff_in, int buff_length)
1724 {
1725 	gss_buffer_t input_token;
1726 	unsigned int bytes;
1727 
1728 	if (**buff_in != OCTET_STRING)
1729 		return (NULL);
1730 
1731 	(*buff_in)++;
1732 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
1733 
1734 	if (input_token == NULL)
1735 		return (NULL);
1736 
1737 	input_token->length = get_der_length(buff_in, buff_length, &bytes);
1738 	if ((int)input_token->length == -1) {
1739 		free(input_token);
1740 		return (NULL);
1741 	}
1742 	input_token->value = malloc(input_token->length);
1743 
1744 	if (input_token->value == NULL) {
1745 		free(input_token);
1746 		return (NULL);
1747 	}
1748 
1749 	(void) memcpy(input_token->value, *buff_in, input_token->length);
1750 	*buff_in += input_token->length;
1751 	return (input_token);
1752 }
1753 
1754 /*
1755  * verify that the input token length is not 0. If it is, just return.
1756  * If the token length is greater than 0, der encode as an octet string
1757  * and place in buf_out, advancing buf_out.
1758  */
1759 
1760 static int
1761 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
1762 		int buflen)
1763 {
1764 	int ret;
1765 
1766 	/* if token length is 0, we do not want to send */
1767 	if (input_token->length == 0)
1768 		return (0);
1769 
1770 	if (input_token->length > buflen)
1771 		return (-1);
1772 
1773 	*(*buf_out)++ = OCTET_STRING;
1774 	if ((ret = put_der_length(input_token->length, buf_out,
1775 			    input_token->length)))
1776 		return (ret);
1777 	TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
1778 	return (0);
1779 }
1780 
1781 /*
1782  * verify that buff_in points to a sequence of der encoding. The mech
1783  * set is the only sequence of encoded object in the token, so if it is
1784  * a sequence of encoding, decode the mechset into a gss_OID_set and
1785  * return it, advancing the buffer pointer.
1786  */
1787 static gss_OID_set
1788 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
1789 {
1790 	gss_OID_set returned_mechSet;
1791 	OM_uint32 major_status;
1792 	OM_uint32 length;
1793 	OM_uint32 bytes;
1794 	OM_uint32 set_length;
1795 	uchar_t		*start;
1796 	int i;
1797 
1798 	if (**buff_in != SEQUENCE_OF)
1799 		return (NULL);
1800 
1801 	start = *buff_in;
1802 	(*buff_in)++;
1803 
1804 	length = get_der_length(buff_in, buff_length, &bytes);
1805 
1806 	major_status = gss_create_empty_oid_set(minor_status,
1807 						&returned_mechSet);
1808 	if (major_status != GSS_S_COMPLETE)
1809 		return (NULL);
1810 
1811 	for (set_length = 0, i = 0; set_length < length; i++) {
1812 		gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
1813 			buff_length - (*buff_in - start));
1814 		if (temp != NULL) {
1815 		    major_status = gss_add_oid_set_member(minor_status,
1816 					temp, &returned_mechSet);
1817 		    if (major_status == GSS_S_COMPLETE) {
1818 			set_length += returned_mechSet->elements[i].length +2;
1819 			generic_gss_release_oid(minor_status, &temp);
1820 		    }
1821 		}
1822 	}
1823 
1824 	return (returned_mechSet);
1825 }
1826 
1827 /*
1828  * der encode the passed mechSet and place it into buf_out,
1829  * advancing the buffer pointer.
1830  */
1831 static int
1832 put_mech_set(uchar_t **buf_out, gss_OID_set mechSet, int buflen)
1833 {
1834 	int i, ret;
1835 	OM_uint32 length = 0;
1836 	uchar_t *start;
1837 
1838 	if (buf_out == NULL || *buf_out == NULL)
1839 		return (-1);
1840 
1841 	start = *buf_out;
1842 
1843 	*(*buf_out)++ = SEQUENCE_OF;
1844 
1845 	for (i = 0; i < mechSet->count; i++) {
1846 		/*
1847 		 * Mech OID ASN.1 size = 2 + length.
1848 		 * 1 = 0x06, 1 for length of OID
1849 		 * typically, less than 128, so only 1 byte needed.
1850 		 */
1851 		length += 1 + der_length_size(mechSet->elements[i].length) +
1852 			mechSet->elements[i].length;
1853 	}
1854 	if (length > (buflen-1))
1855 		return (-1);
1856 
1857 	if (put_der_length(length, buf_out, buflen-1) < 0)
1858 		return (-1);
1859 
1860 	for (i = 0; i < mechSet->count; i++) {
1861 		if ((ret = put_mech_oid(buf_out, &mechSet->elements[i],
1862 			buflen - (int)(*buf_out	 - start))))
1863 			return (ret);
1864 	}
1865 	return (0);
1866 }
1867 
1868 /*
1869  * Verify that buff_in is pointing to a BIT_STRING with the correct
1870  * length and padding for the req_flags. If it is, decode req_flags
1871  * and return them, otherwise, return NULL.
1872  */
1873 static OM_uint32
1874 get_req_flags(unsigned char **buff_in, int *bodysize, OM_uint32 *req_flags)
1875 {
1876 	int len;
1877 	uchar_t *start = *buff_in;
1878 
1879 	/* It is OK if no ReqFlags data is sent. */
1880 	if (**buff_in != (CONTEXT | 0x01))
1881 		return (0);
1882 
1883 	/* If they are sent, make sure the fields are correct. */
1884 	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1885 				*bodysize, &len) < 0)
1886 		return (ACCEPT_DEFECTIVE_TOKEN);
1887 
1888 	/* We don't care what the flags are. */
1889 	(*buff_in) += len;
1890 
1891 	/* Don't return any flags, this field is useless */
1892 	*req_flags = 0;
1893 
1894 	*bodysize -= *buff_in - start;
1895 	return (0);
1896 }
1897 
1898 /*
1899  * get the negotiation results, decoding the ENUMERATED type result
1900  * from the buffer, advancing the buffer pointer.
1901  */
1902 static OM_uint32
1903 get_negResult(unsigned char **buff_in, int bodysize)
1904 {
1905 	unsigned char *iptr = *buff_in;
1906 	int len;
1907 	unsigned int bytes;
1908 	OM_uint32 result;
1909 	/*
1910 	 * Verify that the data is ASN.1 encoded correctly
1911 	 */
1912 	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1913 				bodysize, &len) < 0)
1914 		return (ACCEPT_DEFECTIVE_TOKEN);
1915 
1916 	if (*(*buff_in)++ == SEQUENCE) {
1917 		if ((len = get_der_length(buff_in,
1918 				bodysize - (*buff_in - iptr),
1919 				&bytes)) < 0)
1920 			return (ACCEPT_DEFECTIVE_TOKEN);
1921 	} else {
1922 		return (ACCEPT_INCOMPLETE);
1923 	}
1924 
1925 	/*
1926 	 * if we find an octet string, we need to return
1927 	 * incomplete so that we process the token correctly.
1928 	 * Anything else unexpected, we reject.
1929 	 */
1930 	if (*(*buff_in)++ == CONTEXT) {
1931 		if ((len = get_der_length(buff_in, bodysize -
1932 				(*buff_in - iptr), &bytes)) < 0)
1933 			return (ACCEPT_DEFECTIVE_TOKEN);
1934 	} else {
1935 		return (ACCEPT_INCOMPLETE);
1936 	}
1937 
1938 	if (*(*buff_in) == OCTET_STRING)
1939 		return (ACCEPT_INCOMPLETE);
1940 
1941 	if (*(*buff_in)++ != ENUMERATED)
1942 		return (ACCEPT_DEFECTIVE_TOKEN);
1943 
1944 	if (*(*buff_in)++ != ENUMERATION_LENGTH)
1945 		return (ACCEPT_DEFECTIVE_TOKEN);
1946 
1947 	/*
1948 	 * Save the result byte to return later.
1949 	 * This is the result
1950 	 */
1951 	result = (OM_uint32)*(*buff_in)++;
1952 
1953 	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1954 				bodysize - (*buff_in - iptr),
1955 				&len) < 0)
1956 		result = ACCEPT_DEFECTIVE_TOKEN;
1957 
1958 	return (result);
1959 }
1960 
1961 /*
1962  * der encode the passed negResults as an ENUMERATED type and
1963  * place it in buf_out, advancing the buffer.
1964  */
1965 
1966 static int
1967 put_negResult(uchar_t **buf_out, OM_uint32 negResult, int buflen)
1968 {
1969 	if (buflen < 3)
1970 		return (-1);
1971 	*(*buf_out)++ = ENUMERATED;
1972 	*(*buf_out)++ = ENUMERATION_LENGTH;
1973 	*(*buf_out)++ = (unsigned char) negResult;
1974 	return (0);
1975 }
1976 
1977 /*
1978  * This routine compares the recieved mechset to the mechset that
1979  * this server can support. It looks sequentially through the mechset
1980  * and the first one that matches what the server can support is
1981  * chosen as the negotiated mechanism. If one is found, negResult
1982  * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
1983  * is set to REJECT. Also, for purposes of determining latter behavior,
1984  * the flag, firstMech is used to indicate if the chosen mechanism is the
1985  * first of the mechset or not.
1986  */
1987 static gss_OID
1988 negotiate_mech_type(OM_uint32 *minor_status,
1989 		gss_OID_set supported_mechSet,
1990 		gss_OID_set mechset,
1991 		OM_uint32 *negResult,
1992 		bool_t *firstMech)
1993 {
1994 	gss_OID returned_mech;
1995 	OM_uint32 status;
1996 	int present;
1997 	int i;
1998 
1999 	for (i = 0; i < mechset->count; i++) {
2000 		gss_test_oid_set_member(minor_status, &mechset->elements[i],
2001 					supported_mechSet, &present);
2002 		if (present == TRUE) {
2003 			*negResult = ACCEPT_COMPLETE;
2004 
2005 			if (i == 0)
2006 				*firstMech = TRUE;
2007 			else
2008 				*firstMech = FALSE;
2009 
2010 			status = generic_gss_copy_oid(minor_status,
2011 						    &mechset->elements[i],
2012 						    &returned_mech);
2013 
2014 			if (status != GSS_S_COMPLETE) {
2015 				*negResult = REJECT;
2016 				return (NULL);
2017 			}
2018 
2019 			return (returned_mech);
2020 		}
2021 	}
2022 
2023 	*negResult = REJECT;
2024 	return (NULL);
2025 }
2026 
2027 /*
2028  * the next two routines make a token buffer suitable for
2029  * spnego_gss_display_status. These currently take the string
2030  * in name and place it in the token. Eventually, if
2031  * spnego_gss_display_status returns valid error messages,
2032  * these routines will be changes to return the error string.
2033  */
2034 static spnego_token_t
2035 make_spnego_token(char *name)
2036 {
2037 	spnego_token_t token;
2038 
2039 	token = (spnego_token_t)malloc(strlen(name)+1);
2040 
2041 	if (token == NULL)
2042 		return (NULL);
2043 	strcpy(token, name);
2044 	return (token);
2045 }
2046 
2047 static gss_buffer_desc
2048 make_err_msg(char *name)
2049 {
2050 	gss_buffer_desc buffer;
2051 
2052 	if (name == NULL) {
2053 		buffer.length = 0;
2054 		buffer.value = NULL;
2055 	} else {
2056 		buffer.length = strlen(name)+1;
2057 		buffer.value = make_spnego_token(name);
2058 	}
2059 
2060 	return (buffer);
2061 }
2062 
2063 /*
2064  * Create the client side spnego token passed back to gss_init_sec_context
2065  * and eventually up to the application program and over to the server.
2066  *
2067  * Use DER rules, definite length method per RFC 2478
2068  */
2069 static int
2070 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
2071 		gss_OID_set mechSet,
2072 		gss_buffer_t data, send_token_flag sendtoken,
2073 		gss_buffer_t outbuf)
2074 {
2075 	OM_uint32 status, minor_stat;
2076 	int tlen, dataLen = 0, ret = 0;
2077 	int MechSetLen = 0;
2078 	int negTokenInitSize = 0;
2079 	int i;
2080 	unsigned char *t;
2081 	unsigned char *ptr;
2082 	unsigned char *MechListPtr = NULL;
2083 	gss_buffer_desc MICbuff;
2084 
2085 	if (outbuf == GSS_C_NO_BUFFER)
2086 		return (-1);
2087 
2088 	outbuf->length = 0;
2089 	outbuf->value = NULL;
2090 
2091 	/* calculate the data length */
2092 
2093 	/* no token generated if sendtoken is not init or cont */
2094 	if ((sendtoken < INIT_TOKEN_SEND) ||
2095 	    (sendtoken > CONT_TOKEN_SEND)) {
2096 		return (-1);
2097 	}
2098 
2099 	/*
2100 	 * if this is the init token, we will send the mechset
2101 	 * so include it's length.
2102 	 */
2103 	if (sendtoken == INIT_TOKEN_SEND) {
2104 		/*
2105 		 * Count bytes for the mechSet data
2106 		 * Encoded in final output as:
2107 		 * 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
2108 		 */
2109 		for (i = 0; i < mechSet->count; i++)
2110 			MechSetLen += 1 +
2111 				der_length_size(mechSet->elements[i].length) +
2112 				mechSet->elements[i].length;
2113 
2114 		MechSetLen += 1 + der_length_size(MechSetLen);
2115 		dataLen += 1 + der_length_size(MechSetLen) + MechSetLen;
2116 
2117 		MechListPtr = (uchar_t *)malloc(dataLen);
2118 		ptr = (uchar_t *)MechListPtr;
2119 
2120 		if (MechListPtr != NULL) {
2121 			if ((ret = put_mech_set(&ptr, mechSet, dataLen))) {
2122 				free(MechListPtr);
2123 				goto errout;
2124 			}
2125 		} else {
2126 			ret = -1;
2127 			goto errout;
2128 		}
2129 
2130 		/*
2131 		 * The MIC is done over the DER encoded mechSet.
2132 		 */
2133 		spnego_ctx->DER_mechTypes.value = MechListPtr;
2134 		spnego_ctx->DER_mechTypes.length = ptr - MechListPtr;
2135 
2136 		/*
2137 		 * Only send the MIC if we are *NOT* interoperating
2138 		 * with Microsoft.
2139 		 */
2140 		if (!spnego_ctx->MS_Interop) {
2141 			/*
2142 			 * MechListMIC = DER(MIC(DER(MechSet)))
2143 			 * Calculate it here, stick it in the buffer later.
2144 			 */
2145 			MICbuff.length = 0;
2146 			MICbuff.value = NULL;
2147 			status = gss_get_mic(&minor_stat,
2148 				spnego_ctx->ctx_handle,
2149 				GSS_C_QOP_DEFAULT,
2150 				&spnego_ctx->DER_mechTypes,
2151 				&MICbuff);
2152 			/*
2153 			 * If the MIC operation succeeded, use it,
2154 			 * but don't fail if it did not succeed.
2155 			 * MIC is optional and is not supported by all
2156 			 * mechanisms all the time.
2157 			 */
2158 			if (status  == GSS_S_COMPLETE) {
2159 				/*
2160 				 * Encoded in final output as:
2161 				 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
2162 				 *	--s--   -------tlen------------
2163 				 */
2164 				tlen = 1 + der_length_size(MICbuff.length) +
2165 					MICbuff.length;
2166 
2167 				dataLen += 1 + der_length_size(tlen) + tlen;
2168 			}
2169 		}
2170 	}
2171 
2172 	/*
2173 	 * If a token from gss_init_sec_context exists,
2174 	 * add the length of the token + the ASN.1 overhead
2175 	 */
2176 	if (data != NULL) {
2177 		/*
2178 		 * Encoded in final output as:
2179 		 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
2180 		 * -----s--------|--------s2----------
2181 		 */
2182 		tlen = 1 + der_length_size(data->length) + data->length;
2183 
2184 		dataLen += 1 + der_length_size(tlen) + tlen;
2185 	}
2186 
2187 	/*
2188 	 * Add size of DER encoding
2189 	 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
2190 	 *   0x30 [DER_LEN] [data]
2191 	 *
2192 	 */
2193 	dataLen += 1 + der_length_size(dataLen);
2194 
2195 	/*
2196 	 * negTokenInitSize indicates the bytes needed to
2197 	 * hold the ASN.1 encoding of the entire NegTokenInit
2198 	 * SEQUENCE.
2199 	 * 0xa0 [DER_LEN] + data
2200 	 *
2201 	 */
2202 	negTokenInitSize = dataLen;
2203 
2204 	tlen = g_token_size((gss_OID)gss_mech_spnego,
2205 			    negTokenInitSize + 1 +
2206 			    der_length_size(negTokenInitSize));
2207 
2208 	t = (unsigned char *) malloc(tlen);
2209 
2210 	if (t == NULL) {
2211 		return (-1);
2212 	}
2213 
2214 	ptr = t;
2215 
2216 	/* create the message */
2217 	if ((ret = g_make_token_header((gss_OID)gss_mech_spnego,
2218 			    1 + negTokenInitSize +
2219 			    der_length_size(negTokenInitSize),
2220 			    &ptr, tlen)))
2221 		goto errout;
2222 
2223 	if (sendtoken == INIT_TOKEN_SEND) {
2224 		*ptr++ = CONTEXT; /* NegotiationToken identifier */
2225 		if ((ret = put_der_length(negTokenInitSize, &ptr, tlen)))
2226 			goto errout;
2227 
2228 		*ptr++ = SEQUENCE;
2229 		if ((ret = put_der_length(negTokenInitSize - 4, &ptr,
2230 			    tlen - (int)(ptr-t))))
2231 			goto errout;
2232 
2233 		*ptr++ = CONTEXT; /* MechTypeList identifier */
2234 		if ((ret = put_der_length(spnego_ctx->DER_mechTypes.length,
2235 			&ptr, tlen - (int)(ptr-t))))
2236 			goto errout;
2237 
2238 		/* We already encoded the MechSetList */
2239 		(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
2240 			spnego_ctx->DER_mechTypes.length);
2241 
2242 		ptr += spnego_ctx->DER_mechTypes.length;
2243 
2244 	}
2245 
2246 	if (data != NULL) {
2247 		*ptr++ = CONTEXT | 0x02;
2248 		if ((ret = put_der_length(data->length + 4,
2249 				&ptr, tlen - (int)(ptr - t))))
2250 			goto errout;
2251 
2252 		if ((ret = put_input_token(&ptr, data,
2253 			tlen - (int)(ptr - t))))
2254 			goto errout;
2255 
2256 		/*
2257 		 * We are in "optimistic" mode if we send a token
2258 		 * with out initial message.
2259 		 */
2260 		spnego_ctx->optimistic = (sendtoken == INIT_TOKEN_SEND);
2261 	}
2262 
2263 	if (!spnego_ctx->MS_Interop && MICbuff.length > 0) {
2264 		/* We already calculated the MechListMIC above */
2265 		int len = 1 +  der_length_size(MICbuff.length) + MICbuff.length;
2266 		*ptr++ = CONTEXT | 0x03;
2267 		if ((ret = put_der_length(len, &ptr, tlen - (int)(ptr - t))))
2268 			goto errout;
2269 
2270 		if ((ret = put_input_token(&ptr, &MICbuff,
2271 				tlen - (int)(ptr - t))))
2272 			goto errout;
2273 
2274 		(void) gss_release_buffer(&minor_stat, &MICbuff);
2275 	}
2276 
2277 errout:
2278 	if (ret != 0) {
2279 		if (t)
2280 			free(t);
2281 		t = NULL;
2282 		tlen = 0;
2283 	}
2284 	outbuf->length = tlen;
2285 	outbuf->value = (void *) t;
2286 
2287 	return (ret);
2288 }
2289 
2290 /*
2291  * create the server side spnego token passed back to
2292  * gss_accept_sec_context and eventually up to the application program
2293  * and over to the client.
2294  */
2295 static int
2296 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
2297 			gss_buffer_t data, gss_buffer_t mechListMIC,
2298 			send_token_flag sendtoken, int MS_Flag,
2299 			gss_buffer_t outbuf)
2300 {
2301 	int tlen;
2302 	int ret;
2303 	int NegTokenTargSize;
2304 	int negresultTokenSize;
2305 	int NegTokenSize;
2306 	int rspTokenSize;
2307 	int micTokenSize;
2308 	int dataLen = 0;
2309 	unsigned char *t;
2310 	unsigned char *ptr;
2311 
2312 	if (outbuf == GSS_C_NO_BUFFER)
2313 		return (GSS_S_DEFECTIVE_TOKEN);
2314 
2315 	outbuf->length = 0;
2316 	outbuf->value = NULL;
2317 
2318 	/*
2319 	 * ASN.1 encoding of the negResult
2320 	 * ENUMERATED type is 3 bytes
2321 	 *  ENUMERATED TAG, Length, Value,
2322 	 * Plus 2 bytes for the CONTEXT id and length.
2323 	 */
2324 	negresultTokenSize = 5;
2325 
2326 	/*
2327 	 * calculate data length
2328 	 *
2329 	 * If this is the initial token, include length of
2330 	 * mech_type and the negotiation result fields.
2331 	 */
2332 	if (sendtoken == INIT_TOKEN_SEND) {
2333 
2334 		if (mech_wanted != NULL) {
2335 			int mechlistTokenSize;
2336 			/*
2337 			 * 1 byte for the CONTEXT ID(0xa0),
2338 			 * 1 byte for the OID ID(0x06)
2339 			 * 1 byte for OID Length field
2340 			 * Plus the rest... (OID Length, OID value)
2341 			 */
2342 			mechlistTokenSize = 3 + mech_wanted->length +
2343 				der_length_size(mech_wanted->length);
2344 
2345 			dataLen = negresultTokenSize + mechlistTokenSize;
2346 		}
2347 	} else {
2348 		/*
2349 		 * If this is a response from a server, count
2350 		 * the space needed for the negResult field.
2351 		 * LENGTH(2) + ENUM(2) + result
2352 		 */
2353 		dataLen = negresultTokenSize;
2354 	}
2355 	if (data != NULL && data->length > 0) {
2356 		/* Length of the inner token */
2357 		rspTokenSize = 1 + der_length_size(data->length) +
2358 			data->length;
2359 
2360 		dataLen += rspTokenSize;
2361 
2362 		/* Length of the outer token */
2363 		dataLen += 1 + der_length_size(rspTokenSize);
2364 	}
2365 	if (mechListMIC != NULL) {
2366 
2367 		/* Length of the inner token */
2368 		micTokenSize = 1 + der_length_size(mechListMIC->length) +
2369 			mechListMIC->length;
2370 
2371 		dataLen += micTokenSize;
2372 
2373 		/* Length of the outer token */
2374 		dataLen += 1 + der_length_size(micTokenSize);
2375 	} else if (data != NULL && data->length > 0 && MS_Flag) {
2376 		dataLen += rspTokenSize;
2377 		dataLen += 1 + der_length_size(rspTokenSize);
2378 	}
2379 
2380 	/*
2381 	 * Add size of DER encoded:
2382 	 * NegTokenTarg [ SEQUENCE ] of
2383 	 *    NegResult[0] ENUMERATED {
2384 	 *	accept_completed(0),
2385 	 *	accept_incomplete(1),
2386 	 *	reject(2) }
2387 	 *    supportedMech [1] MechType OPTIONAL,
2388 	 *    responseToken [2] OCTET STRING OPTIONAL,
2389 	 *    mechListMIC   [3] OCTET STRING OPTIONAL
2390 	 *
2391 	 * size = data->length + MechListMic + SupportedMech len +
2392 	 *	Result Length + ASN.1 overhead
2393 	 */
2394 	NegTokenTargSize = dataLen;
2395 	dataLen += 1 + der_length_size(NegTokenTargSize);
2396 
2397 	/*
2398 	 * NegotiationToken [ CHOICE ]{
2399 	 *    negTokenInit  [0]	 NegTokenInit,
2400 	 *    negTokenTarg  [1]	 NegTokenTarg }
2401 	 */
2402 	NegTokenSize = dataLen;
2403 	dataLen += 1 + der_length_size(NegTokenSize);
2404 
2405 	tlen = dataLen;
2406 	t = (unsigned char *) malloc(tlen);
2407 
2408 	if (t == NULL) {
2409 		ret = GSS_S_DEFECTIVE_TOKEN;
2410 		goto errout;
2411 	}
2412 
2413 	ptr = t;
2414 
2415 	if (sendtoken == INIT_TOKEN_SEND ||
2416 	    sendtoken == ERROR_TOKEN_SEND) {
2417 		/*
2418 		 * Indicate that we are sending CHOICE 1
2419 		 * (NegTokenTarg)
2420 		 */
2421 		*ptr++ = CONTEXT | 0x01;
2422 		if ((ret = put_der_length(NegTokenSize, &ptr, dataLen))) {
2423 			ret = GSS_S_DEFECTIVE_TOKEN;
2424 			goto errout;
2425 		}
2426 
2427 		*ptr++ = SEQUENCE;
2428 		if ((ret = put_der_length(NegTokenTargSize, &ptr,
2429 			    tlen - (int)(ptr-t)))) {
2430 			ret = GSS_S_DEFECTIVE_TOKEN;
2431 			goto errout;
2432 		}
2433 
2434 		/*
2435 		 * First field of the NegTokenTarg SEQUENCE
2436 		 * is the ENUMERATED NegResult.
2437 		 */
2438 		*ptr++ = CONTEXT;
2439 		if ((ret = put_der_length(3, &ptr,
2440 			tlen - (int)(ptr-t)))) {
2441 			ret = GSS_S_DEFECTIVE_TOKEN;
2442 			goto errout;
2443 		}
2444 		if ((ret = put_negResult(&ptr, status,
2445 				tlen - (int)(ptr - t)))) {
2446 			ret = GSS_S_DEFECTIVE_TOKEN;
2447 			goto errout;
2448 		}
2449 
2450 		if (sendtoken != ERROR_TOKEN_SEND && mech_wanted != NULL) {
2451 			/*
2452 			 * Next, is the Supported MechType
2453 			 */
2454 			*ptr++ = CONTEXT | 0x01;
2455 			if ((ret = put_der_length(mech_wanted->length + 2,
2456 				&ptr, tlen - (int)(ptr - t)))) {
2457 				ret = GSS_S_DEFECTIVE_TOKEN;
2458 				goto errout;
2459 			}
2460 			if ((ret = put_mech_oid(&ptr, mech_wanted,
2461 				tlen - (int)(ptr - t)))) {
2462 				ret = GSS_S_DEFECTIVE_TOKEN;
2463 				goto errout;
2464 			}
2465 		}
2466 	}
2467 
2468 	if (data != NULL && data->length > 0) {
2469 		*ptr++ = CONTEXT | 0x02;
2470 		if ((ret = put_der_length(rspTokenSize, &ptr,
2471 			    tlen - (int)(ptr - t)))) {
2472 			ret = GSS_S_DEFECTIVE_TOKEN;
2473 			goto errout;
2474 		}
2475 		if ((ret = put_input_token(&ptr, data,
2476 				tlen - (int)(ptr - t)))) {
2477 			ret = GSS_S_DEFECTIVE_TOKEN;
2478 			goto errout;
2479 		}
2480 	}
2481 	if (mechListMIC != NULL) {
2482 		*ptr++ = CONTEXT | 0x03;
2483 		if ((ret = put_der_length(micTokenSize, &ptr,
2484 			    tlen - (int)(ptr - t)))) {
2485 			ret = GSS_S_DEFECTIVE_TOKEN;
2486 			goto errout;
2487 		}
2488 		if ((ret = put_input_token(&ptr, mechListMIC,
2489 				tlen - (int)(ptr - t)))) {
2490 			ret = GSS_S_DEFECTIVE_TOKEN;
2491 			goto errout;
2492 		}
2493 	} else if (data != NULL && data->length > 0 && MS_Flag) {
2494 		*ptr++ = CONTEXT | 0x03;
2495 		if ((ret = put_der_length(rspTokenSize, &ptr,
2496 			    tlen - (int)(ptr - t)))) {
2497 			ret = GSS_S_DEFECTIVE_TOKEN;
2498 			goto errout;
2499 		}
2500 		if ((ret = put_input_token(&ptr, data,
2501 				tlen - (int)(ptr - t)))) {
2502 			ret = GSS_S_DEFECTIVE_TOKEN;
2503 		}
2504 	}
2505 errout:
2506 	if (ret != 0) {
2507 		if (t)
2508 			free(t);
2509 	} else {
2510 		outbuf->length = ptr - t;
2511 		outbuf->value = (void *) t;
2512 	}
2513 
2514 	return (ret);
2515 }
2516 
2517 /* determine size of token */
2518 static int
2519 g_token_size(gss_OID mech, unsigned int body_size)
2520 {
2521 	int hdrsize;
2522 
2523 	/*
2524 	 * Initialize the header size to the
2525 	 * MECH_OID byte + the bytes needed to indicate the
2526 	 * length of the OID + the OID itself.
2527 	 *
2528 	 * 0x06 [MECHLENFIELD] MECHDATA
2529 	 */
2530 	hdrsize = 1 + der_length_size(mech->length) + mech->length;
2531 
2532 	/*
2533 	 * Now add the bytes needed for the initial header
2534 	 * token bytes:
2535 	 * 0x60 + [DER_LEN] + HDRSIZE
2536 	 */
2537 	hdrsize += 1 + der_length_size(body_size + hdrsize);
2538 
2539 	return (hdrsize + body_size);
2540 }
2541 
2542 /*
2543  * generate token header.
2544  *
2545  * Use DER Definite Length method per RFC2478
2546  * Use of indefinite length encoding will not be compatible
2547  * with Microsoft or others that actually follow the spec.
2548  */
2549 static int
2550 g_make_token_header(gss_OID mech,
2551 		    int body_size,
2552 		    unsigned char **buf,
2553 		    int totallen)
2554 {
2555 	int hdrsize, ret = 0;
2556 	unsigned char *p = *buf;
2557 
2558 	hdrsize = 1 + der_length_size(mech->length) + mech->length;
2559 
2560 	*(*buf)++ = HEADER_ID;
2561 	if ((ret = put_der_length(hdrsize + body_size, buf, totallen)))
2562 		return (ret);
2563 
2564 	*(*buf)++ = MECH_OID;
2565 	if ((ret = put_der_length(mech->length, buf,
2566 			    totallen - (int)(p - *buf))))
2567 		return (ret);
2568 	TWRITE_STR(*buf, mech->elements, ((int)mech->length));
2569 	return (0);
2570 }
2571 
2572 static int
2573 g_get_tag_and_length(unsigned char **buf, uchar_t tag, int buflen, int *outlen)
2574 {
2575 	unsigned char *ptr = *buf;
2576 	int ret = -1; /* pessimists, assume failure ! */
2577 	OM_uint32 encoded_len;
2578 
2579 	if (buflen > 0 && *ptr == tag) {
2580 		ptr++;
2581 		*outlen = get_der_length(&ptr, buflen, &encoded_len);
2582 		if (*outlen < 0)
2583 			ret = *outlen;
2584 		if ((ptr + *outlen) > (*buf + buflen))
2585 			ret = -1;
2586 		else
2587 			ret = 0;
2588 	}
2589 
2590 	*buf = ptr;
2591 	return (ret);
2592 }
2593 
2594 static int
2595 g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
2596 {
2597 	unsigned char *buf = *buf_in;
2598 	unsigned char *endptr = buf + cur_size;
2599 	int seqsize;
2600 	int ret = 0;
2601 	unsigned int bytes;
2602 
2603 	/*
2604 	 * Verify this is a NegotiationToken type token
2605 	 * - check for a0(context specific identifier)
2606 	 * - get length and verify that enoughd ata exists
2607 	 */
2608 	if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
2609 		return (G_BAD_TOK_HEADER);
2610 
2611 	cur_size = seqsize; /* should indicate bytes remaining */
2612 
2613 	/*
2614 	 * Verify the next piece, it should identify this as
2615 	 * a strucure of type NegTokenInit.
2616 	 */
2617 	if (*buf++ == SEQUENCE) {
2618 		if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2619 			return (G_BAD_TOK_HEADER);
2620 		/*
2621 		 * Make sure we have the entire buffer as described
2622 		 */
2623 		if (buf + seqsize > endptr)
2624 			return (G_BAD_TOK_HEADER);
2625 	} else {
2626 		return (G_BAD_TOK_HEADER);
2627 	}
2628 
2629 	cur_size = seqsize; /* should indicate bytes remaining */
2630 
2631 	/*
2632 	 * Verify that the first blob is a sequence of mechTypes
2633 	 */
2634 	if (*buf++ == CONTEXT) {
2635 		if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2636 			return (G_BAD_TOK_HEADER);
2637 		/*
2638 		 * Make sure we have the entire buffer as described
2639 		 */
2640 		if (buf + bytes > endptr)
2641 			return (G_BAD_TOK_HEADER);
2642 	} else {
2643 		return (G_BAD_TOK_HEADER);
2644 	}
2645 
2646 	/*
2647 	 * At this point, *buf should be at the beginning of the
2648 	 * DER encoded list of mech types that are to be negotiated.
2649 	 */
2650 	*buf_in = buf;
2651 
2652 	return (ret);
2653 
2654 }
2655 
2656 /* verify token header. */
2657 static int
2658 g_verify_token_header(gss_OID mech,
2659 		    int *body_size,
2660 		    unsigned char **buf_in,
2661 		    int tok_type,
2662 		    int toksize)
2663 {
2664 	unsigned char *buf = *buf_in;
2665 	int seqsize;
2666 	gss_OID_desc toid;
2667 	int ret = 0;
2668 	unsigned int bytes;
2669 
2670 	if ((toksize -= 1) < 0)
2671 		return (G_BAD_TOK_HEADER);
2672 
2673 	if (*buf++ != HEADER_ID)
2674 		return (G_BAD_TOK_HEADER);
2675 
2676 	if ((seqsize = get_der_length(&buf, toksize, &bytes)) < 0)
2677 		return (G_BAD_TOK_HEADER);
2678 
2679 	if ((seqsize + bytes) != toksize)
2680 		return (G_BAD_TOK_HEADER);
2681 
2682 	if ((toksize -= 1) < 0)
2683 		return (G_BAD_TOK_HEADER);
2684 
2685 
2686 	if (*buf++ != MECH_OID)
2687 		return (G_BAD_TOK_HEADER);
2688 
2689 	if ((toksize -= 1) < 0)
2690 		return (G_BAD_TOK_HEADER);
2691 
2692 	toid.length = *buf++;
2693 
2694 	if ((toksize -= toid.length) < 0)
2695 		return (G_BAD_TOK_HEADER);
2696 
2697 	toid.elements = buf;
2698 	buf += toid.length;
2699 
2700 	if (!g_OID_equal(&toid, mech))
2701 		ret = G_WRONG_MECH;
2702 
2703 	/*
2704 	 * G_WRONG_MECH is not returned immediately because it's more important
2705 	 * to return G_BAD_TOK_HEADER if the token header is in fact bad
2706 	 */
2707 	if ((toksize -= 2) < 0)
2708 		return (G_BAD_TOK_HEADER);
2709 
2710 	if (!ret) {
2711 		*buf_in = buf;
2712 		*body_size = toksize;
2713 	}
2714 
2715 	return (ret);
2716 }
2717