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