xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5unseal.c (revision e5803b76927480e8f9b67b22201c484ccf4c2bcf)
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 
504     default:
505 	*minor_status = 0;
506 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error SGN_ALG_MD2_5 "
507 		"GSS_S_DEFECTIVE_TOKEN\n");
508 	return(GSS_S_DEFECTIVE_TOKEN);
509 
510     case SGN_ALG_HMAC_SHA1_DES3_KD:
511     case SGN_ALG_HMAC_MD5:
512 	/* compute the checksum of the message */
513 
514 	/* 8 = bytes of token body to be checksummed according to spec */
515 
516 	if (! (data_ptr = (void *)
517 	       xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) {
518 	    if (sealalg != 0xffff)
519 		xfree_wrap(plain, tmsglen);
520 	    if (toktype == KG_TOK_SEAL_MSG) {
521 		xfree_wrap(token.value, token.length);
522 		/* Solaris Kerberos: just to be safe since token.value is an
523 		 * output parameter.
524 		 */
525 		token.value = NULL;
526 		token.length = 0;
527 	    }
528 	    *minor_status = ENOMEM;
529 	    return(GSS_S_FAILURE);
530 	}
531 
532 	(void) memcpy(data_ptr, ptr-2, 8);
533 
534 	if (ctx->big_endian) {
535 	    KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 1\n");
536 	    (void) memcpy(data_ptr+8, token.value, token.length);
537 	}
538 	else {
539 	    KRB5_LOG0(KRB5_INFO, "kg_unseal_v1() ctx->big_endian = 0\n");
540 	    (void) memcpy(data_ptr+8, plain, plainlen);
541 	}
542 
543 	plaind.length = 8 + (ctx->big_endian ? token.length : plainlen);
544 	plaind.data = data_ptr;
545 
546 	code = krb5_c_make_checksum(context, md5cksum.checksum_type,
547 				    ctx->seq, sign_usage,
548 				    &plaind, &md5cksum);
549 
550 	xfree_wrap(data_ptr, 8 + (ctx->big_endian ? token.length : plainlen));
551 
552 	if (code) {
553 	    if (toktype == KG_TOK_SEAL_MSG) {
554 		xfree_wrap(token.value, token.length);
555 		/* Solaris Kerberos: just to be safe since token.value is an
556 		 * output parameter.
557 		 */
558 		token.value = NULL;
559 		token.length = 0;
560 	    }
561 	    *minor_status = code;
562 	    KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error "
563 		    "SGN_ALG_HMAC_SHA1_DES3_KD GSS_S_FAILURE\n");
564 	    return(GSS_S_FAILURE);
565 	}
566 
567 	/* compare the computed checksum against the transmitted checksum */
568 	code = memcmp(md5cksum.contents, ptr+14, cksum_len);
569 	KRB5_LOG(KRB5_INFO, "kg_unseal_v1() memcmp %d bytes", cksum_len);
570 	break;
571     }
572 
573     xfree_wrap(md5cksum.contents, md5cksum.length);
574     if (sealalg != 0xffff)
575 	xfree_wrap(plain, tmsglen);
576 
577     if (code) {
578 	if (toktype == KG_TOK_SEAL_MSG) {
579 	    xfree_wrap(token.value, token.length);
580 	    /* Solaris Kerberos: just to be safe since token.value is an
581 	     * output parameter.
582 	     */
583 	    token.value = NULL;
584 	    token.length = 0;
585 	}
586 	*minor_status = 0;
587 	KRB5_LOG0(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG\n");
588 	return(GSS_S_BAD_SIG);
589     }
590 
591     if (conf_state)
592 	*conf_state = (sealalg != 0xffff);
593 
594     if (qop_state)
595 	*qop_state = GSS_C_QOP_DEFAULT;
596 
597     if ((code = krb5_timeofday(context, &now))) {
598 	*minor_status = code;
599 
600 	KRB5_LOG(KRB5_ERR, "kg_unseal_v1() end, krb5_timeofday()"
601 		"error code = %d\n", code);
602 
603 	return(GSS_S_FAILURE);
604     }
605 
606     if (now > ctx->endtime) {
607 	*minor_status = 0;
608 
609 	KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error "
610 		"now %d > ctx->endtime %d\n", now, ctx->endtime);
611 
612 	return(GSS_S_CONTEXT_EXPIRED);
613     }
614 
615     /* do sequencing checks */
616     if ((ctx->initiate && direction != 0xff) ||
617 	(!ctx->initiate && direction != 0)) {
618 	if (toktype == KG_TOK_SEAL_MSG) {
619 	    xfree_wrap(token.value, token.length);
620 	    /* Solaris Kerberos: just to be safe since token.value is an
621 	     * output parameter.
622 	     */
623 	    token.value = NULL;
624 	    token.length = 0;
625 	}
626 	*minor_status = (OM_uint32) G_BAD_DIRECTION;
627 
628 	KRB5_LOG1(KRB5_ERR, "kg_unseal_v1() end, error GSS_S_BAD_SIG "
629 		"G_BAD_DIRECTION ctx->initiate = %d "
630 		"direction = %d\n", ctx->initiate, direction);
631 
632 	return(GSS_S_BAD_SIG);
633     }
634 
635     retval = g_order_check(&(ctx->seqstate), (gssint_uint64)seqnum);
636 
637     /* It got through unscathed, adjust the output message buffer. */
638     if (retval == 0 && toktype == KG_TOK_SEAL_MSG)
639 	*message_buffer = token;
640 
641     *minor_status = 0;
642     KRB5_LOG(KRB5_INFO, "kg_unseal_v1() end, retval = %d\n", retval);
643     return(retval);
644 }
645 
646 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
647    conf_state is only valid if SEAL. */
648 
649 OM_uint32
650 kg_unseal(minor_status, context_handle, input_token_buffer,
651 	  message_buffer, conf_state, qop_state, toktype)
652     OM_uint32 *minor_status;
653     gss_ctx_id_t context_handle;
654     gss_buffer_t input_token_buffer;
655     gss_buffer_t message_buffer;
656     int *conf_state;
657     int *qop_state;
658     int toktype;
659 {
660     krb5_gss_ctx_id_rec *ctx;
661     unsigned char *ptr;
662     int bodysize;
663     int err;
664     int toktype2;
665 
666     KRB5_LOG0(KRB5_INFO, "kg_unseal() start \n");
667 
668     /* validate the context handle */
669     if (! kg_validate_ctx_id(context_handle)) {
670 	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
671 
672 	KRB5_LOG0(KRB5_ERR, "kg_unseal() end, kg_validate_ctx_id() error "
673 		"G_VALIDATE_FAILED \n");
674 
675 	return(GSS_S_NO_CONTEXT);
676     }
677 
678     ctx = (krb5_gss_ctx_id_rec *) context_handle;
679 
680     if (! ctx->established) {
681 	*minor_status = KG_CTX_INCOMPLETE;
682 	KRB5_LOG0(KRB5_ERR, "kg_unseal() end, error ! ctx->established \n");
683 	return(GSS_S_NO_CONTEXT);
684     }
685 
686     /* parse the token, leave the data in message_buffer, setting conf_state */
687 
688     /* verify the header */
689     ptr = (unsigned char *) input_token_buffer->value;
690     if (ctx->proto)
691 	switch (toktype) {
692 	case KG_TOK_SIGN_MSG:
693 	    toktype2 = 0x0404;
694 	    break;
695 	case KG_TOK_SEAL_MSG:
696 	    toktype2 = 0x0504;
697 	    break;
698 	case KG_TOK_DEL_CTX:
699 	    toktype2 = 0x0405;
700 	    break;
701 	default:
702 	    toktype2 = toktype;
703 	    break;
704 	}
705     else
706 	toktype2 = toktype;
707     err = g_verify_token_header(ctx->mech_used,
708 				(uint32_t *)&bodysize, &ptr, toktype2,
709 				input_token_buffer->length,
710 				!ctx->proto);
711     if (err) {
712 	*minor_status = err;
713 	return GSS_S_DEFECTIVE_TOKEN;
714     }
715 
716     if (ctx->proto == 0) {
717 	err = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize,
718 			    message_buffer, conf_state, qop_state,
719 			    toktype);
720 
721     } else {
722 	err = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx,
723                                            ptr, bodysize, message_buffer,
724                                            conf_state, qop_state, toktype);
725     }
726 
727     *minor_status = err;
728 
729 #ifndef _KERNEL
730     if (err != 0)
731 	save_error_info (*minor_status, ctx->k5_context);
732 #endif
733 
734     KRB5_LOG(KRB5_INFO, "kg_unseal() end, err = %d", err);
735 
736     return(err);
737 }
738