17c478bd9Sstevel@tonic-gate /*
25e01956fSGlenn Barry  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate  */
47c478bd9Sstevel@tonic-gate /*
57c478bd9Sstevel@tonic-gate  * lib/gssapi/krb5/k5sealv3.c
67c478bd9Sstevel@tonic-gate  *
77c478bd9Sstevel@tonic-gate  * Copyright 2003,2004 by the Massachusetts Institute of Technology.
87c478bd9Sstevel@tonic-gate  * All Rights Reserved.
97c478bd9Sstevel@tonic-gate  *
107c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
117c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
127c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
137c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
14*55fea89dSDan Cross  *
157c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
167c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
177c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
187c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
197c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
207c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
217c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
22159d09a2SMark Phalan  * permission.  Furthermore if you modify this software you must label
237c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
247c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
257c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
267c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
277c478bd9Sstevel@tonic-gate  * or implied warranty.
28*55fea89dSDan Cross  *
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate /* draft-ietf-krb-wg-gssapi-cfx-05 */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #ifndef _KERNEL
347c478bd9Sstevel@tonic-gate #include <assert.h>
357c478bd9Sstevel@tonic-gate #include <stdarg.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #define ASSERT assert
387c478bd9Sstevel@tonic-gate #endif
397c478bd9Sstevel@tonic-gate 
40159d09a2SMark Phalan /* Solaris Kerberos */
41159d09a2SMark Phalan #include "k5-int.h"		/* for zap() */
42159d09a2SMark Phalan #include "k5-platform.h"
43159d09a2SMark Phalan 
44159d09a2SMark Phalan /* Solaris Kerberos */
45159d09a2SMark Phalan #include "k5-platform-store_16.h"
46159d09a2SMark Phalan #include "k5-platform-store_64.h"
47159d09a2SMark Phalan #include "k5-platform-load_16.h"
48159d09a2SMark Phalan #include "k5-platform-load_64.h"
49159d09a2SMark Phalan 
50159d09a2SMark Phalan #include "gssapiP_krb5.h"
517c478bd9Sstevel@tonic-gate #include <sys/int_limits.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static int
rotate_left(void * ptr,size_t bufsiz,size_t rc)547c478bd9Sstevel@tonic-gate rotate_left (void *ptr, size_t bufsiz, size_t rc)
557c478bd9Sstevel@tonic-gate {
567c478bd9Sstevel@tonic-gate     /* Optimize for receiving.  After some debugging is done, the MIT
577c478bd9Sstevel@tonic-gate        implementation won't do any rotates on sending, and while
587c478bd9Sstevel@tonic-gate        debugging, they'll be randomly chosen.
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate        Return 1 for success, 0 for failure (ENOMEM).  */
617c478bd9Sstevel@tonic-gate     void *tbuf;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate     if (bufsiz == 0)
647c478bd9Sstevel@tonic-gate 	return 1;
657c478bd9Sstevel@tonic-gate     rc = rc % bufsiz;
667c478bd9Sstevel@tonic-gate     if (rc == 0)
677c478bd9Sstevel@tonic-gate 	return 1;
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate     tbuf = MALLOC(rc);
707c478bd9Sstevel@tonic-gate     if (tbuf == 0)
717c478bd9Sstevel@tonic-gate 	return 0;
727c478bd9Sstevel@tonic-gate     (void) memcpy(tbuf, ptr, rc);
737c478bd9Sstevel@tonic-gate     (void) memmove(ptr, (char *)ptr + rc, bufsiz - rc);
747c478bd9Sstevel@tonic-gate     (void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
757c478bd9Sstevel@tonic-gate     FREE(tbuf, rc);
767c478bd9Sstevel@tonic-gate     return 1;
777c478bd9Sstevel@tonic-gate }
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static const gss_buffer_desc empty_message = { 0, 0 };
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #define FLAG_SENDER_IS_ACCEPTOR	0x01
827c478bd9Sstevel@tonic-gate #define FLAG_WRAP_CONFIDENTIAL	0x02
837c478bd9Sstevel@tonic-gate #define FLAG_ACCEPTOR_SUBKEY	0x04
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate krb5_error_code
gss_krb5int_make_seal_token_v3(krb5_context context,krb5_gss_ctx_id_rec * ctx,const gss_buffer_desc * message,gss_buffer_t token,int conf_req_flag,int toktype)867c478bd9Sstevel@tonic-gate gss_krb5int_make_seal_token_v3 (krb5_context context,
877c478bd9Sstevel@tonic-gate 				krb5_gss_ctx_id_rec *ctx,
887c478bd9Sstevel@tonic-gate 				const gss_buffer_desc * message,
897c478bd9Sstevel@tonic-gate 				gss_buffer_t token,
907c478bd9Sstevel@tonic-gate 				int conf_req_flag, int toktype)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate     size_t bufsize = 16;
937c478bd9Sstevel@tonic-gate     unsigned char *outbuf = 0;
947c478bd9Sstevel@tonic-gate     krb5_error_code err;
957c478bd9Sstevel@tonic-gate     int key_usage;
967c478bd9Sstevel@tonic-gate     unsigned char acceptor_flag;
977c478bd9Sstevel@tonic-gate     const gss_buffer_desc *message2 = message;
98ab9b2e15Sgtb #ifdef CFX_EXERCISE
99ab9b2e15Sgtb     size_t rrc;
100ab9b2e15Sgtb #endif
1017c478bd9Sstevel@tonic-gate     size_t ec;
1027c478bd9Sstevel@tonic-gate     unsigned short tok_id;
1037c478bd9Sstevel@tonic-gate     krb5_checksum sum;
1047c478bd9Sstevel@tonic-gate     krb5_keyblock *key;
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
1077c478bd9Sstevel@tonic-gate     ASSERT(ctx->big_endian == 0);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate     acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
1107c478bd9Sstevel@tonic-gate     key_usage = (toktype == KG_TOK_WRAP_MSG
1117c478bd9Sstevel@tonic-gate 		 ? (ctx->initiate
1127c478bd9Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SEAL
1137c478bd9Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SEAL)
1147c478bd9Sstevel@tonic-gate 		 : (ctx->initiate
1157c478bd9Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SIGN
1167c478bd9Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SIGN));
1177c478bd9Sstevel@tonic-gate     if (ctx->have_acceptor_subkey) {
1187c478bd9Sstevel@tonic-gate 	key = ctx->acceptor_subkey;
1197c478bd9Sstevel@tonic-gate     } else {
1207c478bd9Sstevel@tonic-gate 	key = ctx->enc;
1217c478bd9Sstevel@tonic-gate     }
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate #ifdef _KERNEL
1247c478bd9Sstevel@tonic-gate     context->kef_cipher_mt = get_cipher_mech_type(context, key);
1257c478bd9Sstevel@tonic-gate     context->kef_hash_mt = get_hash_mech_type(context, key);
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
1287c478bd9Sstevel@tonic-gate 	return (GSS_S_FAILURE);
1297c478bd9Sstevel@tonic-gate     }
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate #endif /* _KERNEL */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate #ifdef CFX_EXERCISE
1347c478bd9Sstevel@tonic-gate     {
1357c478bd9Sstevel@tonic-gate 	static int initialized = 0;
1367c478bd9Sstevel@tonic-gate 	if (!initialized) {
1377c478bd9Sstevel@tonic-gate 	    srand(time(0));
1387c478bd9Sstevel@tonic-gate 	    initialized = 1;
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate     }
1417c478bd9Sstevel@tonic-gate #endif
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
1447c478bd9Sstevel@tonic-gate 	krb5_data plain;
1457c478bd9Sstevel@tonic-gate 	krb5_enc_data cipher;
1467c478bd9Sstevel@tonic-gate 	size_t ec_max;
1477c478bd9Sstevel@tonic-gate 	size_t tlen;
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	/* 300: Adds some slop.  */
1507c478bd9Sstevel@tonic-gate 	if (SIZE_MAX - 300 < message->length)
1517c478bd9Sstevel@tonic-gate 	    return ENOMEM;
1527c478bd9Sstevel@tonic-gate 	ec_max = SIZE_MAX - message->length - 300;
1537c478bd9Sstevel@tonic-gate 	if (ec_max > 0xffff)
1547c478bd9Sstevel@tonic-gate 	    ec_max = 0xffff;
1557c478bd9Sstevel@tonic-gate 	/*
1567c478bd9Sstevel@tonic-gate 	 * EC should really be a multiple (1) of the number of octets that
1577c478bd9Sstevel@tonic-gate 	 * the cryptosystem would pad by if we didn't have the filler.
1587c478bd9Sstevel@tonic-gate 	 *
1597c478bd9Sstevel@tonic-gate 	 * For AES-CTS this will always be 0 and we expect no further
1607c478bd9Sstevel@tonic-gate 	 * enctypes, so there should be no issue here.
1617c478bd9Sstevel@tonic-gate 	 */
1627c478bd9Sstevel@tonic-gate 	ec = 0;
1637c478bd9Sstevel@tonic-gate 	plain.length = message->length + 16 + ec;
1647c478bd9Sstevel@tonic-gate 	plain.data = MALLOC(plain.length);
1657c478bd9Sstevel@tonic-gate 	if (plain.data == NULL)
1667c478bd9Sstevel@tonic-gate 	    return ENOMEM;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	/* Get size of ciphertext.  */
1697c478bd9Sstevel@tonic-gate 	if ((err = krb5_c_encrypt_length(context,
1707c478bd9Sstevel@tonic-gate 		ctx->enc->enctype, plain.length, &tlen))) {
1717c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
1727c478bd9Sstevel@tonic-gate 	    return (err);
1737c478bd9Sstevel@tonic-gate         }
174*55fea89dSDan Cross 
1757c478bd9Sstevel@tonic-gate 	bufsize = 16 + tlen;
1767c478bd9Sstevel@tonic-gate 	/* Allocate space for header plus encrypted data.  */
1777c478bd9Sstevel@tonic-gate 	outbuf = MALLOC(bufsize);
1787c478bd9Sstevel@tonic-gate 	if (outbuf == NULL) {
1797c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
1807c478bd9Sstevel@tonic-gate 	    return ENOMEM;
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	/* TOK_ID */
1847c478bd9Sstevel@tonic-gate 	store_16_be(0x0504, outbuf);
1857c478bd9Sstevel@tonic-gate 	/* flags */
1867c478bd9Sstevel@tonic-gate 	outbuf[2] = (acceptor_flag
1877c478bd9Sstevel@tonic-gate 		     | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
1887c478bd9Sstevel@tonic-gate 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
1897c478bd9Sstevel@tonic-gate 	/* filler */
1907c478bd9Sstevel@tonic-gate 	outbuf[3] = 0xff;
1917c478bd9Sstevel@tonic-gate 	/* EC */
1927c478bd9Sstevel@tonic-gate 	store_16_be(ec, outbuf+4);
1937c478bd9Sstevel@tonic-gate 	/* RRC */
1947c478bd9Sstevel@tonic-gate 	store_16_be(0, outbuf+6);
1957c478bd9Sstevel@tonic-gate 	store_64_be(ctx->seq_send, outbuf+8);
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	(void) memcpy(plain.data, message->value, message->length);
1987c478bd9Sstevel@tonic-gate 	(void) memset(plain.data + message->length, 'x', ec);
1997c478bd9Sstevel@tonic-gate 	(void) memcpy(plain.data + message->length + ec, outbuf, 16);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/* Should really use scatter/gather crypto interfaces */
2027c478bd9Sstevel@tonic-gate 	cipher.ciphertext.data = (char *)outbuf + 16;
2037c478bd9Sstevel@tonic-gate 	cipher.ciphertext.length = bufsize - 16;
2047c478bd9Sstevel@tonic-gate 	cipher.enctype = key->enctype;
2057c478bd9Sstevel@tonic-gate 	err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
2067c478bd9Sstevel@tonic-gate 	(void) bzero(plain.data, plain.length);
2077c478bd9Sstevel@tonic-gate 	FREE(plain.data, plain.length);
2087c478bd9Sstevel@tonic-gate 	plain.data = 0;
2097c478bd9Sstevel@tonic-gate 	if (err)
2107c478bd9Sstevel@tonic-gate 	    goto error;
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	/* Now that we know we're returning a valid token....  */
2137c478bd9Sstevel@tonic-gate 	ctx->seq_send++;
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate #ifdef CFX_EXERCISE
2167c478bd9Sstevel@tonic-gate 	rrc = rand() & 0xffff;
2177c478bd9Sstevel@tonic-gate 	if (rotate_left(outbuf+16, bufsize-16,
2187c478bd9Sstevel@tonic-gate 			(bufsize-16) - (rrc % (bufsize - 16))))
2197c478bd9Sstevel@tonic-gate 	    store_16_be(rrc, outbuf+6);
2207c478bd9Sstevel@tonic-gate 	/* If the rotate fails, don't worry about it.  */
2217c478bd9Sstevel@tonic-gate #endif
2227c478bd9Sstevel@tonic-gate     } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
2237c478bd9Sstevel@tonic-gate 	krb5_data plain;
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/* Here, message is the application-supplied data; message2 is
2267c478bd9Sstevel@tonic-gate 	   what goes into the output token.  They may be the same, or
2277c478bd9Sstevel@tonic-gate 	   message2 may be empty (for MIC).  */
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	tok_id = 0x0504;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate     wrap_with_checksum:
2327c478bd9Sstevel@tonic-gate 	plain.length = message->length + 16;
2337c478bd9Sstevel@tonic-gate 	plain.data = MALLOC(message->length + 16);
2347c478bd9Sstevel@tonic-gate 	if (plain.data == NULL)
2357c478bd9Sstevel@tonic-gate 	    return ENOMEM;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	if (ctx->cksum_size > 0xffff) {
2387c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
2397c478bd9Sstevel@tonic-gate 	    return EINVAL;
2407c478bd9Sstevel@tonic-gate 	}
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	bufsize = 16 + message2->length + ctx->cksum_size;
2437c478bd9Sstevel@tonic-gate 	outbuf = MALLOC(bufsize);
2447c478bd9Sstevel@tonic-gate 	if (outbuf == NULL) {
2457c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
2467c478bd9Sstevel@tonic-gate 	    plain.data = 0;
2477c478bd9Sstevel@tonic-gate 	    err = ENOMEM;
2487c478bd9Sstevel@tonic-gate 	    goto error;
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/* TOK_ID */
2527c478bd9Sstevel@tonic-gate 	store_16_be(tok_id, outbuf);
2537c478bd9Sstevel@tonic-gate 	/* flags */
2547c478bd9Sstevel@tonic-gate 	outbuf[2] = (acceptor_flag
2557c478bd9Sstevel@tonic-gate 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
2567c478bd9Sstevel@tonic-gate 	/* filler */
2577c478bd9Sstevel@tonic-gate 	outbuf[3] = 0xff;
2587c478bd9Sstevel@tonic-gate 	if (toktype == KG_TOK_WRAP_MSG) {
2597c478bd9Sstevel@tonic-gate 	    /* Use 0 for checksum calculation, substitute
2607c478bd9Sstevel@tonic-gate 	       checksum length later.  */
2617c478bd9Sstevel@tonic-gate 	    /* EC */
2627c478bd9Sstevel@tonic-gate 	    store_16_be(0, outbuf+4);
2637c478bd9Sstevel@tonic-gate 	    /* RRC */
2647c478bd9Sstevel@tonic-gate 	    store_16_be(0, outbuf+6);
2657c478bd9Sstevel@tonic-gate 	} else {
2667c478bd9Sstevel@tonic-gate 	    /* MIC and DEL store 0xFF in EC and RRC.  */
2677c478bd9Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+4);
2687c478bd9Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+6);
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 	store_64_be(ctx->seq_send, outbuf+8);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	(void) memcpy(plain.data, message->value, message->length);
2737c478bd9Sstevel@tonic-gate 	(void) memcpy(plain.data + message->length, outbuf, 16);
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	/* Fill in the output token -- data contents, if any, and
2767c478bd9Sstevel@tonic-gate 	   space for the checksum.  */
2777c478bd9Sstevel@tonic-gate 	if (message2->length)
2787c478bd9Sstevel@tonic-gate 	    (void) memcpy(outbuf + 16, message2->value, message2->length);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	sum.contents = outbuf + 16 + message2->length;
2817c478bd9Sstevel@tonic-gate 	sum.length = ctx->cksum_size;
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	err = krb5_c_make_checksum(context, ctx->cksumtype, key,
2847c478bd9Sstevel@tonic-gate 				   key_usage, &plain, &sum);
2857c478bd9Sstevel@tonic-gate 	bzero(plain.data, plain.length);
2867c478bd9Sstevel@tonic-gate 	FREE(plain.data, plain.length);
2877c478bd9Sstevel@tonic-gate 	plain.data = 0;
2887c478bd9Sstevel@tonic-gate 	if (err) {
2897c478bd9Sstevel@tonic-gate 	    bzero(outbuf,bufsize);
2907c478bd9Sstevel@tonic-gate 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2917c478bd9Sstevel@tonic-gate 	    goto error;
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 	if (sum.length != ctx->cksum_size) {
2947c478bd9Sstevel@tonic-gate 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2957c478bd9Sstevel@tonic-gate 	    goto error;
2967c478bd9Sstevel@tonic-gate 	}
2977c478bd9Sstevel@tonic-gate 	(void) memcpy(outbuf + 16 + message2->length, sum.contents,
2987c478bd9Sstevel@tonic-gate 	    ctx->cksum_size);
2997c478bd9Sstevel@tonic-gate 	krb5_free_checksum_contents(context, &sum);
3007c478bd9Sstevel@tonic-gate 	sum.contents = 0;
3017c478bd9Sstevel@tonic-gate 	/* Now that we know we're actually generating the token...  */
3027c478bd9Sstevel@tonic-gate 	ctx->seq_send++;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	if (toktype == KG_TOK_WRAP_MSG) {
3057c478bd9Sstevel@tonic-gate #ifdef CFX_EXERCISE
3067c478bd9Sstevel@tonic-gate 	    rrc = rand() & 0xffff;
3077c478bd9Sstevel@tonic-gate 	    /* If the rotate fails, don't worry about it.  */
3087c478bd9Sstevel@tonic-gate 	    if (rotate_left(outbuf+16, bufsize-16,
3097c478bd9Sstevel@tonic-gate 			    (bufsize-16) - (rrc % (bufsize - 16))))
3107c478bd9Sstevel@tonic-gate 		store_16_be(rrc, outbuf+6);
3117c478bd9Sstevel@tonic-gate #endif
3127c478bd9Sstevel@tonic-gate 	    /* Fix up EC field.  */
3137c478bd9Sstevel@tonic-gate 	    store_16_be(ctx->cksum_size, outbuf+4);
3147c478bd9Sstevel@tonic-gate 	} else {
3157c478bd9Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+6);
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate     } else if (toktype == KG_TOK_MIC_MSG) {
3187c478bd9Sstevel@tonic-gate 	tok_id = 0x0404;
3197c478bd9Sstevel@tonic-gate 	message2 = &empty_message;
3207c478bd9Sstevel@tonic-gate 	goto wrap_with_checksum;
3217c478bd9Sstevel@tonic-gate     } else if (toktype == KG_TOK_DEL_CTX) {
3223dba6097Smp 	/*
3233dba6097Smp 	 * Solaris Kerberos:
3243dba6097Smp 	 * No token should be generated for context deletion. Just
3253dba6097Smp 	 * return.
3263dba6097Smp 	 */
3273dba6097Smp 	return 0;
3287c478bd9Sstevel@tonic-gate     } else {
3297c478bd9Sstevel@tonic-gate 	err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
3307c478bd9Sstevel@tonic-gate 	goto error;
3317c478bd9Sstevel@tonic-gate     }
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate     token->value = outbuf;
3347c478bd9Sstevel@tonic-gate     token->length = bufsize;
3357c478bd9Sstevel@tonic-gate     return 0;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate error:
3387c478bd9Sstevel@tonic-gate     FREE(outbuf, bufsize);
3397c478bd9Sstevel@tonic-gate     token->value = NULL;
3407c478bd9Sstevel@tonic-gate     token->length = 0;
3417c478bd9Sstevel@tonic-gate     return err;
3427c478bd9Sstevel@tonic-gate }
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
3457c478bd9Sstevel@tonic-gate    conf_state is only valid if SEAL. */
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate OM_uint32
gss_krb5int_unseal_token_v3(krb5_context * contextptr,OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,unsigned char * ptr,int bodysize,gss_buffer_t message_buffer,int * conf_state,int * qop_state,int toktype)348159d09a2SMark Phalan gss_krb5int_unseal_token_v3(krb5_context *contextptr,
3497c478bd9Sstevel@tonic-gate 			    OM_uint32 *minor_status,
3507c478bd9Sstevel@tonic-gate 			    krb5_gss_ctx_id_rec *ctx,
3517c478bd9Sstevel@tonic-gate 			    unsigned char *ptr, int bodysize,
3527c478bd9Sstevel@tonic-gate 			    gss_buffer_t message_buffer,
3537c478bd9Sstevel@tonic-gate 			    int *conf_state, int *qop_state, int toktype)
3547c478bd9Sstevel@tonic-gate {
355159d09a2SMark Phalan     krb5_context context = *contextptr;
3567c478bd9Sstevel@tonic-gate     krb5_data plain;
3577c478bd9Sstevel@tonic-gate     gssint_uint64 seqnum;
3587c478bd9Sstevel@tonic-gate     size_t ec, rrc;
3597c478bd9Sstevel@tonic-gate     int key_usage;
3607c478bd9Sstevel@tonic-gate     unsigned char acceptor_flag;
3617c478bd9Sstevel@tonic-gate     krb5_checksum sum;
3627c478bd9Sstevel@tonic-gate     krb5_error_code err;
3637c478bd9Sstevel@tonic-gate     krb5_boolean valid;
3647c478bd9Sstevel@tonic-gate     krb5_keyblock *key;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
3677c478bd9Sstevel@tonic-gate     ASSERT(ctx->big_endian == 0);
3687c478bd9Sstevel@tonic-gate     ASSERT(ctx->proto == 1);
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate     if (qop_state)
3717c478bd9Sstevel@tonic-gate 	*qop_state = GSS_C_QOP_DEFAULT;
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
3747c478bd9Sstevel@tonic-gate     key_usage = (toktype == KG_TOK_WRAP_MSG
3757c478bd9Sstevel@tonic-gate 		 ? (!ctx->initiate
3767c478bd9Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SEAL
3777c478bd9Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SEAL)
3787c478bd9Sstevel@tonic-gate 		 : (!ctx->initiate
3797c478bd9Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SIGN
3807c478bd9Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SIGN));
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate     /* Oops.  I wrote this code assuming ptr would be at the start of
3837c478bd9Sstevel@tonic-gate        the token header.  */
3847c478bd9Sstevel@tonic-gate     ptr -= 2;
3857c478bd9Sstevel@tonic-gate     bodysize += 2;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate     if (bodysize < 16) {
3887c478bd9Sstevel@tonic-gate     defective:
3897c478bd9Sstevel@tonic-gate 	*minor_status = 0;
3907c478bd9Sstevel@tonic-gate 	return GSS_S_DEFECTIVE_TOKEN;
3917c478bd9Sstevel@tonic-gate     }
3927c478bd9Sstevel@tonic-gate     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
3937c478bd9Sstevel@tonic-gate 	*minor_status = (OM_uint32)G_BAD_DIRECTION;
3947c478bd9Sstevel@tonic-gate 	return GSS_S_BAD_SIG;
3957c478bd9Sstevel@tonic-gate     }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate     /* Two things to note here.
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate        First, we can't really enforce the use of the acceptor's subkey,
4007c478bd9Sstevel@tonic-gate        if we're the acceptor; the initiator may have sent messages
4017c478bd9Sstevel@tonic-gate        before getting the subkey.  We could probably enforce it if
4027c478bd9Sstevel@tonic-gate        we're the initiator.
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate        Second, if someone tweaks the code to not set the flag telling
4057c478bd9Sstevel@tonic-gate        the krb5 library to generate a new subkey in the AP-REP
4067c478bd9Sstevel@tonic-gate        message, the MIT library may include a subkey anyways --
4077c478bd9Sstevel@tonic-gate        namely, a copy of the AP-REQ subkey, if it was provided.  So
4087c478bd9Sstevel@tonic-gate        the initiator may think we wanted a subkey, and set the flag,
4097c478bd9Sstevel@tonic-gate        even though we weren't trying to set the subkey.  The "other"
410159d09a2SMark Phalan        key, the one not asserted by the acceptor, will have the same
4117c478bd9Sstevel@tonic-gate        value in that case, though, so we can just ignore the flag.  */
4127c478bd9Sstevel@tonic-gate     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
4137c478bd9Sstevel@tonic-gate 	key = ctx->acceptor_subkey;
4147c478bd9Sstevel@tonic-gate     } else {
4157c478bd9Sstevel@tonic-gate 	key = ctx->enc;
4167c478bd9Sstevel@tonic-gate     }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate #ifdef _KERNEL
4197c478bd9Sstevel@tonic-gate     context->kef_cipher_mt = get_cipher_mech_type(context, key);
4207c478bd9Sstevel@tonic-gate     context->kef_hash_mt = get_hash_mech_type(context, key);
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
4237c478bd9Sstevel@tonic-gate 	return (GSS_S_FAILURE);
4247c478bd9Sstevel@tonic-gate     }
4257c478bd9Sstevel@tonic-gate #endif /* _KERNEL */
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate     if (toktype == KG_TOK_WRAP_MSG) {
4287c478bd9Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0504)
4297c478bd9Sstevel@tonic-gate 	    goto defective;
4307c478bd9Sstevel@tonic-gate 	if (ptr[3] != 0xff)
4317c478bd9Sstevel@tonic-gate 	    goto defective;
4327c478bd9Sstevel@tonic-gate 	ec = load_16_be(ptr+4);
4337c478bd9Sstevel@tonic-gate 	rrc = load_16_be(ptr+6);
4347c478bd9Sstevel@tonic-gate 	seqnum = load_64_be(ptr+8);
4357c478bd9Sstevel@tonic-gate 	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
436159d09a2SMark Phalan 	no_mem:
4377c478bd9Sstevel@tonic-gate 	    *minor_status = ENOMEM;
4387c478bd9Sstevel@tonic-gate 	    return GSS_S_FAILURE;
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
4417c478bd9Sstevel@tonic-gate 	    /* confidentiality */
4427c478bd9Sstevel@tonic-gate 	    krb5_enc_data cipher;
4437c478bd9Sstevel@tonic-gate 	    unsigned char *althdr;
4447c478bd9Sstevel@tonic-gate             size_t plainlen;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	    if (conf_state)
4477c478bd9Sstevel@tonic-gate 		*conf_state = 1;
4487c478bd9Sstevel@tonic-gate 	    /* Do we have no decrypt_size function?
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	       For all current cryptosystems, the ciphertext size will
4517c478bd9Sstevel@tonic-gate 	       be larger than the plaintext size.  */
4527c478bd9Sstevel@tonic-gate 	    cipher.enctype = key->enctype;
4537c478bd9Sstevel@tonic-gate 	    cipher.ciphertext.length = bodysize - 16;
4547c478bd9Sstevel@tonic-gate 	    cipher.ciphertext.data = (char *)ptr + 16;
4557c478bd9Sstevel@tonic-gate 	    plain.length = plainlen = bodysize - 16;
4567c478bd9Sstevel@tonic-gate 	    plain.data = MALLOC(plain.length);
4577c478bd9Sstevel@tonic-gate 	    if (plain.data == NULL)
4587c478bd9Sstevel@tonic-gate 		goto no_mem;
4597c478bd9Sstevel@tonic-gate 	    err = krb5_c_decrypt(context, key, key_usage, 0,
4607c478bd9Sstevel@tonic-gate 				 &cipher, &plain);
4617c478bd9Sstevel@tonic-gate 	    if (err) {
4627c478bd9Sstevel@tonic-gate 		goto error;
4637c478bd9Sstevel@tonic-gate 	    }
4647c478bd9Sstevel@tonic-gate 	    /* Don't use bodysize here!  Use the fact that
4657c478bd9Sstevel@tonic-gate 	       plain.length has been adjusted to the
4667c478bd9Sstevel@tonic-gate 	       correct length.  */
4677c478bd9Sstevel@tonic-gate 	    althdr = (uchar_t *)plain.data + plain.length - 16;
4687c478bd9Sstevel@tonic-gate 	    if (load_16_be(althdr) != 0x0504
4697c478bd9Sstevel@tonic-gate 		|| althdr[2] != ptr[2]
4707c478bd9Sstevel@tonic-gate 		|| althdr[3] != ptr[3]
4717c478bd9Sstevel@tonic-gate 		|| memcmp(althdr+8, ptr+8, 8)) {
4727c478bd9Sstevel@tonic-gate 		FREE(plain.data, plainlen);
4737c478bd9Sstevel@tonic-gate 		goto defective;
4747c478bd9Sstevel@tonic-gate 	    }
4757c478bd9Sstevel@tonic-gate 	    message_buffer->length = plain.length - ec - 16;
4767c478bd9Sstevel@tonic-gate 	    message_buffer->value = MALLOC(message_buffer->length);
4777c478bd9Sstevel@tonic-gate 	    if (message_buffer->value == NULL) {
4787c478bd9Sstevel@tonic-gate 		FREE(plain.data, plainlen);
4797c478bd9Sstevel@tonic-gate 		goto no_mem;
4807c478bd9Sstevel@tonic-gate 	    }
4817c478bd9Sstevel@tonic-gate 	    (void) memcpy(message_buffer->value, plain.data,
4827c478bd9Sstevel@tonic-gate 			message_buffer->length);
4837c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plainlen);
4847c478bd9Sstevel@tonic-gate 	} else {
4857c478bd9Sstevel@tonic-gate 	    /* no confidentiality */
4867c478bd9Sstevel@tonic-gate 	    if (conf_state)
4877c478bd9Sstevel@tonic-gate 		*conf_state = 0;
4887c478bd9Sstevel@tonic-gate 	    if (ec + 16 < ec)
4897c478bd9Sstevel@tonic-gate 		/* overflow check */
4907c478bd9Sstevel@tonic-gate 		goto defective;
4917c478bd9Sstevel@tonic-gate 	    if (ec + 16 > bodysize)
4927c478bd9Sstevel@tonic-gate 		goto defective;
4937c478bd9Sstevel@tonic-gate 	    /* We have: header | msg | cksum.
4947c478bd9Sstevel@tonic-gate 	       We need cksum(msg | header).
4957c478bd9Sstevel@tonic-gate 	       Rotate the first two.  */
4967c478bd9Sstevel@tonic-gate 	    store_16_be(0, ptr+4);
4977c478bd9Sstevel@tonic-gate 	    store_16_be(0, ptr+6);
498159d09a2SMark Phalan 	    plain.length = bodysize-ec;
4997c478bd9Sstevel@tonic-gate 	    plain.data = (char *)ptr;
5007c478bd9Sstevel@tonic-gate 	    if (!rotate_left(ptr, bodysize-ec, 16))
5017c478bd9Sstevel@tonic-gate 		goto no_mem;
5027c478bd9Sstevel@tonic-gate 	    sum.length = ec;
5037c478bd9Sstevel@tonic-gate 	    if (sum.length != ctx->cksum_size) {
5047c478bd9Sstevel@tonic-gate 		*minor_status = 0;
5057c478bd9Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5067c478bd9Sstevel@tonic-gate 	    }
5077c478bd9Sstevel@tonic-gate 	    sum.contents = ptr+bodysize-ec;
5087c478bd9Sstevel@tonic-gate 	    sum.checksum_type = ctx->cksumtype;
5097c478bd9Sstevel@tonic-gate 	    err = krb5_c_verify_checksum(context, key, key_usage,
5107c478bd9Sstevel@tonic-gate 					 &plain, &sum, &valid);
5117c478bd9Sstevel@tonic-gate 	    if (err) {
5127c478bd9Sstevel@tonic-gate 		*minor_status = err;
5137c478bd9Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5147c478bd9Sstevel@tonic-gate 	    }
5157c478bd9Sstevel@tonic-gate 	    if (!valid) {
5167c478bd9Sstevel@tonic-gate 		*minor_status = 0;
5177c478bd9Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5187c478bd9Sstevel@tonic-gate 	    }
5197c478bd9Sstevel@tonic-gate 	    message_buffer->length = plain.length - 16;
5207c478bd9Sstevel@tonic-gate 	    message_buffer->value = MALLOC(message_buffer->length);
5217c478bd9Sstevel@tonic-gate 	    if (message_buffer->value == NULL)
5227c478bd9Sstevel@tonic-gate 		goto no_mem;
5237c478bd9Sstevel@tonic-gate 	    (void) memcpy(message_buffer->value,
5247c478bd9Sstevel@tonic-gate 		plain.data, message_buffer->length);
5253dba6097Smp 
5263dba6097Smp 		/*
5273dba6097Smp 		 * Solaris Kerberos: Restore the original token.
5283dba6097Smp 		 * This allows the token to be detected as a duplicate if it
5293dba6097Smp 		 * is passed in to gss_unwrap() again.
5303dba6097Smp 		 */
5313dba6097Smp 		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
5323dba6097Smp 			goto no_mem;
5333dba6097Smp 		store_16_be(ec, ptr+4);
5343dba6097Smp 		store_16_be(rrc, ptr+6);
5357c478bd9Sstevel@tonic-gate 	}
5367c478bd9Sstevel@tonic-gate 	err = g_order_check(&ctx->seqstate, seqnum);
5377c478bd9Sstevel@tonic-gate 	*minor_status = 0;
5387c478bd9Sstevel@tonic-gate 	return err;
5397c478bd9Sstevel@tonic-gate     } else if (toktype == KG_TOK_MIC_MSG) {
5407c478bd9Sstevel@tonic-gate 	/* wrap token, no confidentiality */
5417c478bd9Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0404)
5427c478bd9Sstevel@tonic-gate 	    goto defective;
5437c478bd9Sstevel@tonic-gate     verify_mic_1:
5447c478bd9Sstevel@tonic-gate 	if (ptr[3] != 0xff)
5457c478bd9Sstevel@tonic-gate 	    goto defective;
5467c478bd9Sstevel@tonic-gate 	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
5477c478bd9Sstevel@tonic-gate 	    goto defective;
5487c478bd9Sstevel@tonic-gate 	seqnum = load_64_be(ptr+8);
5497c478bd9Sstevel@tonic-gate 	plain.length = message_buffer->length + 16;
5507c478bd9Sstevel@tonic-gate 	plain.data = MALLOC(plain.length);
5517c478bd9Sstevel@tonic-gate 	if (plain.data == NULL)
5527c478bd9Sstevel@tonic-gate 	    goto no_mem;
5537c478bd9Sstevel@tonic-gate 	if (message_buffer->length)
5547c478bd9Sstevel@tonic-gate 	    (void) memcpy(plain.data,
5557c478bd9Sstevel@tonic-gate 		message_buffer->value, message_buffer->length);
5567c478bd9Sstevel@tonic-gate 	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
5577c478bd9Sstevel@tonic-gate 	sum.length = bodysize - 16;
5587c478bd9Sstevel@tonic-gate 	sum.contents = ptr + 16;
5597c478bd9Sstevel@tonic-gate 	sum.checksum_type = ctx->cksumtype;
5607c478bd9Sstevel@tonic-gate 	err = krb5_c_verify_checksum(context, key, key_usage,
5617c478bd9Sstevel@tonic-gate 				     &plain, &sum, &valid);
5627c478bd9Sstevel@tonic-gate 	if (err) {
563159d09a2SMark Phalan 	error:
5647c478bd9Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
5657c478bd9Sstevel@tonic-gate 	    *minor_status = err;
5665e01956fSGlenn Barry 	    save_error_info(*minor_status, context);
5677c478bd9Sstevel@tonic-gate 	    return GSS_S_BAD_SIG; /* XXX */
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate 	FREE(plain.data, plain.length);
5707c478bd9Sstevel@tonic-gate 	if (!valid) {
5717c478bd9Sstevel@tonic-gate 	    *minor_status = 0;
5727c478bd9Sstevel@tonic-gate 	    return GSS_S_BAD_SIG;
5737c478bd9Sstevel@tonic-gate 	}
5747c478bd9Sstevel@tonic-gate 	err = g_order_check(&ctx->seqstate, seqnum);
5757c478bd9Sstevel@tonic-gate 	*minor_status = 0;
5767c478bd9Sstevel@tonic-gate 	return err;
5777c478bd9Sstevel@tonic-gate     } else if (toktype == KG_TOK_DEL_CTX) {
5787c478bd9Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0405)
5797c478bd9Sstevel@tonic-gate 	    goto defective;
5807c478bd9Sstevel@tonic-gate 	message_buffer = (gss_buffer_t)&empty_message;
5817c478bd9Sstevel@tonic-gate 	goto verify_mic_1;
5827c478bd9Sstevel@tonic-gate     } else {
5837c478bd9Sstevel@tonic-gate 	goto defective;
5847c478bd9Sstevel@tonic-gate     }
5857c478bd9Sstevel@tonic-gate }
586