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