xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5unseal.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Copyright 2001 by the Massachusetts Institute of Technology.
6  * Copyright 1993 by OpenVision Technologies, Inc.
7  *
8  * Permission to use, copy, modify, distribute, and sell this software
9  * and its documentation for any purpose is hereby granted without fee,
10  * provided that the above copyright notice appears in all copies and
11  * that both that copyright notice and this permission notice appear in
12  * supporting documentation, and that the name of OpenVision not be used
13  * in advertising or publicity pertaining to distribution of the software
14  * without specific, written prior permission. OpenVision makes no
15  * representations about the suitability of this software for any
16  * purpose.  It is provided "as is" without express or implied warranty.
17  *
18  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
22  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
23  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24  * PERFORMANCE OF THIS SOFTWARE.
25  */
26 
27 /*
28  * Copyright (C) 1998 by the FundsXpress, INC.
29  *
30  * All rights reserved.
31  *
32  * Export of this software from the United States of America may require
33  * a specific license from the United States Government.  It is the
34  * responsibility of any person or organization contemplating export to
35  * obtain such a license before exporting.
36  *
37  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
38  * distribute this software and its documentation for any purpose and
39  * without fee is hereby granted, provided that the above copyright
40  * notice appear in all copies and that both that copyright notice and
41  * this permission notice appear in supporting documentation, and that
42  * the name of FundsXpress. not be used in advertising or publicity pertaining
43  * to distribution of the software without specific, written prior
44  * permission.  FundsXpress makes no representations about the suitability of
45  * this software for any purpose.  It is provided "as is" without express
46  * or implied warranty.
47  *
48  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
49  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
50  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
51  */
52 
53 #include "gssapiP_krb5.h"
54 #include "k5-int.h"
55 
56 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
57    conf_state is only valid if SEAL. */
58 
59 static OM_uint32
60 kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer,
61 	     conf_state, qop_state, toktype)
62     krb5_context context;
63     OM_uint32 *minor_status;
64     krb5_gss_ctx_id_rec *ctx;
65     unsigned char *ptr;
66     int bodysize;
67     gss_buffer_t message_buffer;
68     int *conf_state;
69     int *qop_state;
70     int toktype;
71 {
72     krb5_error_code code;
73     int conflen = 0;
74     int signalg;
75     int sealalg;
76     gss_buffer_desc token;
77     krb5_checksum cksum;
78     krb5_checksum md5cksum;
79     krb5_data plaind;
80     char *data_ptr;
81     krb5_timestamp now;
82     unsigned char *plain;
83     unsigned int cksum_len = 0;
84     size_t plainlen;
85     int direction;
86     krb5_ui_4 seqnum;
87     OM_uint32 retval;
88     size_t sumlen, blocksize;
89     int tmsglen;
90     krb5_keyusage sign_usage = KG_USAGE_SIGN;
91 
92     KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() start\n");
93 
94     /* Solaris Kerberos:  make sure this is initialized */
95     *minor_status = 0;
96 
97     if (toktype == KG_TOK_SEAL_MSG) {
98 	message_buffer->length = 0;
99 	message_buffer->value = NULL;
100     }
101 
102     /* get the sign and seal algorithms */
103 
104     signalg = ptr[0] + (ptr[1]<<8);
105     sealalg = ptr[2] + (ptr[3]<<8);
106 
107     /* Sanity checks */
108 
109     if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) {
110 	*minor_status = 0;
111 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_DEFECTIVE_TOKEN\n");
112 	return GSS_S_DEFECTIVE_TOKEN;
113     }
114 
115     if ((toktype != KG_TOK_SEAL_MSG) &&
116 	(sealalg != 0xffff)) {
117 	*minor_status = 0;
118 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error2 GSS_S_DEFECTIVE_TOKEN\n");
119 	return GSS_S_DEFECTIVE_TOKEN;
120     }
121 
122     /* in the current spec, there is only one valid seal algorithm per
123        key type, so a simple comparison is ok */
124 
125     if ((toktype == KG_TOK_SEAL_MSG) &&
126 	!((sealalg == 0xffff) ||
127 	  (sealalg == ctx->sealalg))) {
128 	*minor_status = 0;
129 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error3 GSS_S_DEFECTIVE_TOKEN\n");
130 	return GSS_S_DEFECTIVE_TOKEN;
131     }
132 
133     /* there are several mappings of seal algorithms to sign algorithms,
134        but few enough that we can try them all. */
135 
136     if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) ||
137 	(ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) ||
138 	(ctx->sealalg == SEAL_ALG_DES3KD &&
139 	 signalg != SGN_ALG_HMAC_SHA1_DES3_KD)||
140 	(ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 &&
141 	signalg != SGN_ALG_HMAC_MD5)) {
142 	*minor_status = 0;
143 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error4 GSS_S_DEFECTIVE_TOKEN\n");
144 	return GSS_S_DEFECTIVE_TOKEN;
145     }
146 
147     KRB5_LOG(KRB5_INFO, "kg_unseal_v1() signalg = %d\n", signalg);
148 
149     switch (signalg) {
150     case SGN_ALG_DES_MAC_MD5:
151     case SGN_ALG_MD2_5:
152     case SGN_ALG_HMAC_MD5:
153 	cksum_len = 8;
154 	if (toktype != KG_TOK_SEAL_MSG)
155 	  sign_usage = 15;
156 	    break;
157     case SGN_ALG_3:
158 	cksum_len = 16;
159 	break;
160     case SGN_ALG_HMAC_SHA1_DES3_KD:
161 	cksum_len = 20;
162 	break;
163     default:
164 	*minor_status = 0;
165 	KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, error signalg=%d\n", signalg);
166 	return GSS_S_DEFECTIVE_TOKEN;
167     }
168 
169 #ifdef _KERNEL
170 	/*
171 	 * Because the ARCFOUR code bypasses the standard
172 	 * crypto interfaces, we must make sure the kernel
173 	 * crypto framework mechanism types are properly
174 	 * initialized here.
175 	 */
176 	context->kef_cipher_mt = get_cipher_mech_type(context,
177 					ctx->seq);
178 	context->kef_hash_mt = get_hash_mech_type(context,
179 					ctx->seq);
180 	if ((code = init_key_kef(context->kef_cipher_mt,
181 				ctx->seq))) {
182 		*minor_status = code;
183 		return (GSS_S_FAILURE);
184 	}
185 	if ((code = init_key_kef(context->kef_cipher_mt,
186 			ctx->enc))) {
187 		*minor_status = code;
188 		return (GSS_S_FAILURE);
189 	}
190 #endif /* _KERNEL */
191 
192     /* get the token parameters */
193 
194     if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction,
195 			       &seqnum))) {
196 	*minor_status = code;
197 	return(GSS_S_BAD_SIG);
198     }
199 
200     /* decode the message, if SEAL */
201 
202     if (toktype == KG_TOK_SEAL_MSG) {
203 	tmsglen = bodysize-(14+cksum_len);
204 	KRB5_LOG1(KRB5_INFO, "kg_unseal_v1() tmsglen = %d cksum_len = %d",
205 		tmsglen, cksum_len);
206 	KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == KG_TOK_SEAL_MSG\n");
207 
208 	if (sealalg != 0xffff) {
209 	    if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) {
210 		*minor_status = ENOMEM;
211 		KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error ENOMEM\n");
212 		return(GSS_S_FAILURE);
213 	    }
214 	    if (ctx->enc->enctype == ENCTYPE_ARCFOUR_HMAC) {
215 	      unsigned char bigend_seqnum[4];
216 	      krb5_keyblock *enc_key;
217 	      int i;
218 	      bigend_seqnum[0] = (seqnum>>24) & 0xff;
219 	      bigend_seqnum[1] = (seqnum>>16) & 0xff;
220 	      bigend_seqnum[2] = (seqnum>>8) & 0xff;
221 	      bigend_seqnum[3] = seqnum & 0xff;
222 	      code = krb5_copy_keyblock (context, ctx->enc, &enc_key);
223 	      if (code)
224 		{
225 		  xfree_wrap(plain, tmsglen);
226 		  *minor_status = code;
227 		  return(GSS_S_FAILURE);
228 		}
229 
230 	      for (i = 0; i <= 15; i++)
231 		((char *) enc_key->contents)[i] ^=0xf0;
232 
233 #ifndef _KERNEL
234 		/*
235 		 * The enc_key contents were modified, delete the
236 		 * key object so it doesn't get used later.
237 		 */
238 		if (enc_key->hKey != CK_INVALID_HANDLE) {
239 			(void)C_DestroyObject(krb_ctx_hSession(context),
240 				enc_key->hKey);
241 			enc_key->hKey = CK_INVALID_HANDLE;
242 		}
243 #endif
244 		KRB5_LOG(KRB5_INFO, "kg_unseal_v1() enc_key->enctype = %d",
245 			enc_key->enctype);
246 
247 		code = kg_arcfour_docrypt (context,
248 				enc_key, 0,
249 				&bigend_seqnum[0], 4,
250 				ptr+14+cksum_len, tmsglen,
251 				plain);
252 		krb5_free_keyblock (context, enc_key);
253             } else {
254 		code = kg_decrypt(context, ctx->enc, KG_USAGE_SEAL, NULL,
255 			ptr+14+cksum_len, plain, tmsglen);
256 	    }
257             if (code) {
258                 xfree_wrap(plain, tmsglen);
259 		*minor_status = code;
260 		return(GSS_S_FAILURE);
261 	    }
262 	} else {
263 	    plain = ptr+14+cksum_len;
264 	}
265 
266 	plainlen = tmsglen;
267 
268 	if ((sealalg == 0xffff) && ctx->big_endian) {
269 	    token.length = tmsglen;
270 	} else {
271 	    conflen = kg_confounder_size(context, ctx->enc);
272 	    /*
273 	     * Solaris Kerberos: we want to perform a sanity check on the
274 	     * pad length, so we know it can not be more than the blocksize.
275 	     */
276 	    code = krb5_c_block_size(context, ctx->enc->enctype, &blocksize);
277 	    if (code != 0) {
278 		if (sealalg != 0xffff)
279 		    xfree_wrap(plain, tmsglen);
280 		*minor_status = code;
281 		return(GSS_S_FAILURE);
282 	    }
283 	    if (plain[tmsglen-1] > blocksize) {
284 		if (sealalg != 0xffff)
285 		    xfree_wrap(plain, tmsglen);
286 		*minor_status = KG_BAD_LENGTH;
287 		return(GSS_S_FAILURE);
288 	    }
289 	    token.length = tmsglen - conflen - plain[tmsglen-1];
290 	}
291 
292 	if (token.length) {
293 	    if ((token.value = (void *) xmalloc(token.length)) == NULL) {
294 		if (sealalg != 0xffff)
295 		    xfree_wrap(plain, tmsglen);
296 		*minor_status = ENOMEM;
297 		KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error2 ENOMEM\n");
298 		return(GSS_S_FAILURE);
299 	    }
300 	    (void) memcpy(token.value, plain+conflen, token.length);
301 	} else {
302 	    token.value = NULL;
303 	}
304     } else if (toktype == KG_TOK_SIGN_MSG) {
305 	KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == KG_TOK_SIGN_MSG\n");
306 	token = *message_buffer;
307 	plain = token.value;
308 	plainlen = token.length;
309     } else {
310 	KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() toktype == NULL\n");
311 	token.length = 0;
312 	token.value = NULL;
313 	plain = token.value;
314 	plainlen = token.length;
315     }
316 
317     /* compute the checksum of the message */
318 
319     /* initialize the the cksum */
320     switch (signalg) {
321     case SGN_ALG_DES_MAC_MD5:
322     case SGN_ALG_MD2_5:
323     case SGN_ALG_DES_MAC:
324     case SGN_ALG_3:
325 	md5cksum.checksum_type = CKSUMTYPE_RSA_MD5;
326 	break;
327     case SGN_ALG_HMAC_MD5:
328       md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
329       break;
330     case SGN_ALG_HMAC_SHA1_DES3_KD:
331 	md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
332 	break;
333     default:
334 	KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, error2 signalg=%d\n", signalg);
335 #ifndef	_KERNEL
336 	abort ();
337 #else
338 	*minor_status = 0;
339 	return(GSS_S_DEFECTIVE_TOKEN);
340 #endif /* _KERNEL */
341     }
342 
343     code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
344     if (code)
345     {
346 	KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_c_checksum_length() error "
347 		"code=%d\n", code);
348 	return(code);
349     }
350     md5cksum.length = (size_t)sumlen;
351 
352     switch (signalg) {
353     case SGN_ALG_DES_MAC_MD5:
354     case SGN_ALG_3:
355 	/* compute the checksum of the message */
356 
357 	/* 8 = bytes of token body to be checksummed according to spec */
358 
359 	if (! (data_ptr = (void *)
360 	       xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) {
361 	    if (sealalg != 0xffff)
362 		xfree_wrap(plain, tmsglen);
363 	    if (toktype == KG_TOK_SEAL_MSG) {
364 		xfree_wrap(token.value, token.length);
365 		/* Solaris Kerberos: just to be safe since token.value is an
366 		 * output parameter.
367 		 */
368 		token.value = NULL;
369 		token.length = 0;
370 	    }
371 	    *minor_status = ENOMEM;
372 	    KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error3 ENOMEM\n");
373 	    return(GSS_S_FAILURE);
374 	}
375 
376 	(void) memcpy(data_ptr, ptr-2, 8);
377 
378 	if (ctx->big_endian)
379 	    (void) memcpy(data_ptr+8, token.value, token.length);
380 	else
381 	    (void) memcpy(data_ptr+8, plain, plainlen);
382 
383 	plaind.length = 8 + (ctx->big_endian ? token.length : plainlen);
384 	plaind.data = data_ptr;
385 	code = krb5_c_make_checksum(context, md5cksum.checksum_type,
386 				    ctx->seq, sign_usage,
387 				    &plaind, &md5cksum);
388 	xfree_wrap(data_ptr, 8 + (ctx->big_endian ? token.length : plainlen));
389 
390 	if (code) {
391 	    if (toktype == KG_TOK_SEAL_MSG) {
392 		xfree_wrap(token.value, token.length);
393 		/* Solaris Kerberos: just to be safe since token.value is an
394 		 * output parameter.
395 		 */
396 		token.value = NULL;
397 		token.length = 0;
398 	    }
399 	    *minor_status = code;
400 	    KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_c_make_checksum() "
401 		    "error code = %d\n", code);
402 	    return(GSS_S_FAILURE);
403 	}
404 
405 	if ((code = kg_encrypt(context, ctx->seq, KG_USAGE_SEAL,
406 			       (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ?
407 				ctx->seq->contents : NULL),
408 			       md5cksum.contents, md5cksum.contents, 16))) {
409 	    xfree_wrap(md5cksum.contents, md5cksum.length);
410 	    if (toktype == KG_TOK_SEAL_MSG) {
411 		xfree_wrap(token.value, token.length);
412 		/* Solaris Kerberos: just to be safe since token.value is an
413 		 * output parameter.
414 		 */
415 		token.value = NULL;
416 		token.length = 0;
417 	    }
418 	    *minor_status = code;
419 	    KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, kg_encrypt() error"
420 		    "code = %d\n", code);
421 	    return GSS_S_FAILURE;
422 	}
423 
424 	if (signalg == 0)
425 	    cksum.length = 8;
426 	else
427 	    cksum.length = 16;
428 	cksum.contents = md5cksum.contents + 16 - cksum.length;
429 
430 	code = memcmp(cksum.contents, ptr+14, cksum.length);
431 	break;
432 
433     case SGN_ALG_MD2_5:
434 	if (!ctx->seed_init &&
435 	    (code = kg_make_seed(context, ctx->subkey, ctx->seed))) {
436 	    xfree_wrap(md5cksum.contents, md5cksum.length);
437 	    if (sealalg != 0xffff)
438 		xfree_wrap(plain, tmsglen);
439 	    if (toktype == KG_TOK_SEAL_MSG) {
440 		xfree_wrap(token.value, token.length);
441 		/* Solaris Kerberos: just to be safe since token.value is an
442 		 * output parameter.
443 		 */
444 		token.value = NULL;
445 		token.length = 0;
446 	    }
447 	    *minor_status = code;
448 	    return GSS_S_FAILURE;
449 	}
450 
451 	if (! (data_ptr = (void *)
452 	       xmalloc(sizeof(ctx->seed) + 8 +
453 		       (ctx->big_endian ? token.length : plainlen)))) {
454 	    xfree_wrap(md5cksum.contents, md5cksum.length);
455 	    if (sealalg == 0)
456 		xfree_wrap(plain, tmsglen);
457 	    if (toktype == KG_TOK_SEAL_MSG) {
458 		xfree_wrap(token.value, token.length);
459 		/* Solaris Kerberos: just to be safe since token.value is an
460 		 * output parameter.
461 		 */
462 		token.value = NULL;
463 		token.length = 0;
464 	    }
465 	    *minor_status = ENOMEM;
466 	    return(GSS_S_FAILURE);
467 	}
468 	(void) memcpy(data_ptr, ptr-2, 8);
469 	(void) memcpy(data_ptr+8, ctx->seed, sizeof(ctx->seed));
470 	if (ctx->big_endian)
471 	    (void) memcpy(data_ptr+8+sizeof(ctx->seed),
472 			  token.value, token.length);
473 	else
474 	    (void) memcpy(data_ptr+8+sizeof(ctx->seed),
475 			  plain, plainlen);
476 	plaind.length = 8 + sizeof(ctx->seed) +
477 	    (ctx->big_endian ? token.length : plainlen);
478 	plaind.data = data_ptr;
479 	xfree_wrap(md5cksum.contents, md5cksum.length);
480 	code = krb5_c_make_checksum(context, md5cksum.checksum_type,
481 				    ctx->seq, KG_USAGE_SIGN,
482 				    &plaind, &md5cksum);
483 	xfree_wrap(data_ptr, 8 + sizeof(ctx->seed) +
484             (ctx->big_endian ? token.length : plainlen));
485 
486 	if (code) {
487 	    if (sealalg == 0)
488 		xfree_wrap(plain, tmsglen);
489 	    if (toktype == KG_TOK_SEAL_MSG) {
490 		xfree_wrap(token.value, token.length);
491 		/* Solaris Kerberos: just to be safe since token.value is an
492 		 * output parameter.
493 		 */
494 		token.value = NULL;
495 		token.length = 0;
496 	    }
497 	    *minor_status = code;
498 	    return(GSS_S_FAILURE);
499 	}
500 
501 	code = memcmp(md5cksum.contents, ptr+14, 8);
502 	/* Falls through to defective-token??  */
503 	/* FALLTHROUGH */
504 
505     default:
506 	*minor_status = 0;
507 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error SGN_ALG_MD2_5 "
508 		"GSS_S_DEFECTIVE_TOKEN\n");
509 	return(GSS_S_DEFECTIVE_TOKEN);
510 
511     case SGN_ALG_HMAC_SHA1_DES3_KD:
512     case SGN_ALG_HMAC_MD5:
513 	/* compute the checksum of the message */
514 
515 	/* 8 = bytes of token body to be checksummed according to spec */
516 
517 	if (! (data_ptr = (void *)
518 	       xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) {
519 	    if (sealalg != 0xffff)
520 		xfree_wrap(plain, tmsglen);
521 	    if (toktype == KG_TOK_SEAL_MSG) {
522 		xfree_wrap(token.value, token.length);
523 		/* Solaris Kerberos: just to be safe since token.value is an
524 		 * output parameter.
525 		 */
526 		token.value = NULL;
527 		token.length = 0;
528 	    }
529 	    *minor_status = ENOMEM;
530 	    return(GSS_S_FAILURE);
531 	}
532 
533 	(void) memcpy(data_ptr, ptr-2, 8);
534 
535 	if (ctx->big_endian) {
536 	    KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 1\n");
537 	    (void) memcpy(data_ptr+8, token.value, token.length);
538 	}
539 	else {
540 	    KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 0\n");
541 	    (void) memcpy(data_ptr+8, plain, plainlen);
542 	}
543 
544 	plaind.length = 8 + (ctx->big_endian ? token.length : plainlen);
545 	plaind.data = data_ptr;
546 
547 	code = krb5_c_make_checksum(context, md5cksum.checksum_type,
548 				    ctx->seq, sign_usage,
549 				    &plaind, &md5cksum);
550 
551 	xfree_wrap(data_ptr, 8 + (ctx->big_endian ? token.length : plainlen));
552 
553 	if (code) {
554 	    if (toktype == KG_TOK_SEAL_MSG) {
555 		xfree_wrap(token.value, token.length);
556 		/* Solaris Kerberos: just to be safe since token.value is an
557 		 * output parameter.
558 		 */
559 		token.value = NULL;
560 		token.length = 0;
561 	    }
562 	    *minor_status = code;
563 	    KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error "
564 		    "SGN_ALG_HMAC_SHA1_DES3_KD GSS_S_FAILURE\n");
565 	    return(GSS_S_FAILURE);
566 	}
567 
568 	/* compare the computed checksum against the transmitted checksum */
569 	code = memcmp(md5cksum.contents, ptr+14, cksum_len);
570 	KRB5_LOG(KRB5_INFO, "kg_unseal_v1() memcmp %d bytes", cksum_len);
571 	break;
572     }
573 
574     xfree_wrap(md5cksum.contents, md5cksum.length);
575     if (sealalg != 0xffff)
576 	xfree_wrap(plain, tmsglen);
577 
578     if (code) {
579 	if (toktype == KG_TOK_SEAL_MSG) {
580 	    xfree_wrap(token.value, token.length);
581 	    /* Solaris Kerberos: just to be safe since token.value is an
582 	     * output parameter.
583 	     */
584 	    token.value = NULL;
585 	    token.length = 0;
586 	}
587 	*minor_status = 0;
588 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG\n");
589 	return(GSS_S_BAD_SIG);
590     }
591 
592     if (conf_state)
593 	*conf_state = (sealalg != 0xffff);
594 
595     if (qop_state)
596 	*qop_state = GSS_C_QOP_DEFAULT;
597 
598     if ((code = krb5_timeofday(context, &now))) {
599 	*minor_status = code;
600 
601 	KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_timeofday()"
602 		"error code = %d\n", code);
603 
604 	return(GSS_S_FAILURE);
605     }
606 
607     if (now > ctx->endtime) {
608 	*minor_status = 0;
609 
610 	KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error "
611 		"now %d > ctx->endtime %d\n", now, ctx->endtime);
612 
613 	return(GSS_S_CONTEXT_EXPIRED);
614     }
615 
616     /* do sequencing checks */
617     if ((ctx->initiate && direction != 0xff) ||
618 	(!ctx->initiate && direction != 0)) {
619 	if (toktype == KG_TOK_SEAL_MSG) {
620 	    xfree_wrap(token.value, token.length);
621 	    /* Solaris Kerberos: just to be safe since token.value is an
622 	     * output parameter.
623 	     */
624 	    token.value = NULL;
625 	    token.length = 0;
626 	}
627 	*minor_status = (OM_uint32) G_BAD_DIRECTION;
628 
629 	KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG "
630 		"G_BAD_DIRECTION ctx->initiate = %d "
631 		"direction = %d\n", ctx->initiate, direction);
632 
633 	return(GSS_S_BAD_SIG);
634     }
635 
636     retval = g_order_check(&(ctx->seqstate), (gssint_uint64)seqnum);
637 
638     /* It got through unscathed, adjust the output message buffer. */
639     if (retval == 0 && toktype == KG_TOK_SEAL_MSG)
640 	*message_buffer = token;
641 
642     *minor_status = 0;
643     KRB5_LOG(KRB5_INFO, "kg_unseal_v1() end, retval = %d\n", retval);
644     return(retval);
645 }
646 
647 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
648    conf_state is only valid if SEAL. */
649 
650 OM_uint32
651 kg_unseal(minor_status, context_handle, input_token_buffer,
652 	  message_buffer, conf_state, qop_state, toktype)
653     OM_uint32 *minor_status;
654     gss_ctx_id_t context_handle;
655     gss_buffer_t input_token_buffer;
656     gss_buffer_t message_buffer;
657     int *conf_state;
658     int *qop_state;
659     int toktype;
660 {
661     krb5_gss_ctx_id_rec *ctx;
662     unsigned char *ptr;
663     int bodysize;
664     int err;
665     int toktype2;
666 
667     KRB5_LOG0(KRB5_INFO, "kg_unseal() start \n");
668 
669     /* validate the context handle */
670     if (! kg_validate_ctx_id(context_handle)) {
671 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
672 
673 	KRB5_LOG0(KRB5_ERR, "kg_unseal() end, kg_validate_ctx_id() error "
674 		"G_VALIDATE_FAILED \n");
675 
676 	return(GSS_S_NO_CONTEXT);
677     }
678 
679     ctx = (krb5_gss_ctx_id_rec *) context_handle;
680 
681     if (! ctx->established) {
682 	*minor_status = KG_CTX_INCOMPLETE;
683 	KRB5_LOG0(KRB5_ERR, "kg_unseal() end, error ! ctx->established \n");
684 	return(GSS_S_NO_CONTEXT);
685     }
686 
687     /* parse the token, leave the data in message_buffer, setting conf_state */
688 
689     /* verify the header */
690     ptr = (unsigned char *) input_token_buffer->value;
691     if (ctx->proto)
692 	switch (toktype) {
693 	case KG_TOK_SIGN_MSG:
694 	    toktype2 = 0x0404;
695 	    break;
696 	case KG_TOK_SEAL_MSG:
697 	    toktype2 = 0x0504;
698 	    break;
699 	case KG_TOK_DEL_CTX:
700 	    toktype2 = 0x0405;
701 	    break;
702 	default:
703 	    toktype2 = toktype;
704 	    break;
705 	}
706     else
707 	toktype2 = toktype;
708     err = g_verify_token_header(ctx->mech_used,
709 				(uint32_t *)&bodysize, &ptr, toktype2,
710 				input_token_buffer->length,
711 				!ctx->proto);
712     if (err) {
713 	*minor_status = err;
714 	return GSS_S_DEFECTIVE_TOKEN;
715     }
716 
717     if (ctx->proto == 0) {
718 	err = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize,
719 			    message_buffer, conf_state, qop_state,
720 			    toktype);
721 
722     } else {
723 	err = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx,
724                                            ptr, bodysize, message_buffer,
725                                            conf_state, qop_state, toktype);
726     }
727 
728     *minor_status = err;
729 
730 #ifndef _KERNEL
731     if (err != 0)
732 	save_error_info (*minor_status, ctx->k5_context);
733 #endif
734 
735     KRB5_LOG(KRB5_INFO, "kg_unseal() end, err = %d", err);
736 
737     return(err);
738 }
739