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