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