xref: /onnv-gate/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c (revision 13132:9615cdbf7b70)
10Sstevel@tonic-gate /*
2*13132SGlenn.Barry@oracle.com  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
30Sstevel@tonic-gate  */
40Sstevel@tonic-gate /*
50Sstevel@tonic-gate  * lib/gssapi/krb5/k5sealv3.c
60Sstevel@tonic-gate  *
70Sstevel@tonic-gate  * Copyright 2003,2004 by the Massachusetts Institute of Technology.
80Sstevel@tonic-gate  * All Rights Reserved.
90Sstevel@tonic-gate  *
100Sstevel@tonic-gate  * Export of this software from the United States of America may
110Sstevel@tonic-gate  *   require a specific license from the United States Government.
120Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
130Sstevel@tonic-gate  *   export to obtain such a license before exporting.
147934SMark.Phalan@Sun.COM  *
150Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
160Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
170Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
180Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
190Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
200Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
210Sstevel@tonic-gate  * to distribution of the software without specific, written prior
227934SMark.Phalan@Sun.COM  * permission.  Furthermore if you modify this software you must label
230Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
240Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
250Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
260Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
270Sstevel@tonic-gate  * or implied warranty.
287934SMark.Phalan@Sun.COM  *
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate /* draft-ietf-krb-wg-gssapi-cfx-05 */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #ifndef _KERNEL
340Sstevel@tonic-gate #include <assert.h>
350Sstevel@tonic-gate #include <stdarg.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #define ASSERT assert
380Sstevel@tonic-gate #endif
390Sstevel@tonic-gate 
407934SMark.Phalan@Sun.COM /* Solaris Kerberos */
417934SMark.Phalan@Sun.COM #include "k5-int.h"		/* for zap() */
427934SMark.Phalan@Sun.COM #include "k5-platform.h"
437934SMark.Phalan@Sun.COM 
447934SMark.Phalan@Sun.COM /* Solaris Kerberos */
457934SMark.Phalan@Sun.COM #include "k5-platform-store_16.h"
467934SMark.Phalan@Sun.COM #include "k5-platform-store_64.h"
477934SMark.Phalan@Sun.COM #include "k5-platform-load_16.h"
487934SMark.Phalan@Sun.COM #include "k5-platform-load_64.h"
497934SMark.Phalan@Sun.COM 
507934SMark.Phalan@Sun.COM #include "gssapiP_krb5.h"
510Sstevel@tonic-gate #include <sys/int_limits.h>
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static int
rotate_left(void * ptr,size_t bufsiz,size_t rc)540Sstevel@tonic-gate rotate_left (void *ptr, size_t bufsiz, size_t rc)
550Sstevel@tonic-gate {
560Sstevel@tonic-gate     /* Optimize for receiving.  After some debugging is done, the MIT
570Sstevel@tonic-gate        implementation won't do any rotates on sending, and while
580Sstevel@tonic-gate        debugging, they'll be randomly chosen.
590Sstevel@tonic-gate 
600Sstevel@tonic-gate        Return 1 for success, 0 for failure (ENOMEM).  */
610Sstevel@tonic-gate     void *tbuf;
620Sstevel@tonic-gate 
630Sstevel@tonic-gate     if (bufsiz == 0)
640Sstevel@tonic-gate 	return 1;
650Sstevel@tonic-gate     rc = rc % bufsiz;
660Sstevel@tonic-gate     if (rc == 0)
670Sstevel@tonic-gate 	return 1;
680Sstevel@tonic-gate 
690Sstevel@tonic-gate     tbuf = MALLOC(rc);
700Sstevel@tonic-gate     if (tbuf == 0)
710Sstevel@tonic-gate 	return 0;
720Sstevel@tonic-gate     (void) memcpy(tbuf, ptr, rc);
730Sstevel@tonic-gate     (void) memmove(ptr, (char *)ptr + rc, bufsiz - rc);
740Sstevel@tonic-gate     (void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
750Sstevel@tonic-gate     FREE(tbuf, rc);
760Sstevel@tonic-gate     return 1;
770Sstevel@tonic-gate }
780Sstevel@tonic-gate 
790Sstevel@tonic-gate static const gss_buffer_desc empty_message = { 0, 0 };
800Sstevel@tonic-gate 
810Sstevel@tonic-gate #define FLAG_SENDER_IS_ACCEPTOR	0x01
820Sstevel@tonic-gate #define FLAG_WRAP_CONFIDENTIAL	0x02
830Sstevel@tonic-gate #define FLAG_ACCEPTOR_SUBKEY	0x04
840Sstevel@tonic-gate 
850Sstevel@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)860Sstevel@tonic-gate gss_krb5int_make_seal_token_v3 (krb5_context context,
870Sstevel@tonic-gate 				krb5_gss_ctx_id_rec *ctx,
880Sstevel@tonic-gate 				const gss_buffer_desc * message,
890Sstevel@tonic-gate 				gss_buffer_t token,
900Sstevel@tonic-gate 				int conf_req_flag, int toktype)
910Sstevel@tonic-gate {
920Sstevel@tonic-gate     size_t bufsize = 16;
930Sstevel@tonic-gate     unsigned char *outbuf = 0;
940Sstevel@tonic-gate     krb5_error_code err;
950Sstevel@tonic-gate     int key_usage;
960Sstevel@tonic-gate     unsigned char acceptor_flag;
970Sstevel@tonic-gate     const gss_buffer_desc *message2 = message;
985053Sgtb #ifdef CFX_EXERCISE
995053Sgtb     size_t rrc;
1005053Sgtb #endif
1010Sstevel@tonic-gate     size_t ec;
1020Sstevel@tonic-gate     unsigned short tok_id;
1030Sstevel@tonic-gate     krb5_checksum sum;
1040Sstevel@tonic-gate     krb5_keyblock *key;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
1070Sstevel@tonic-gate     ASSERT(ctx->big_endian == 0);
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate     acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
1100Sstevel@tonic-gate     key_usage = (toktype == KG_TOK_WRAP_MSG
1110Sstevel@tonic-gate 		 ? (ctx->initiate
1120Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SEAL
1130Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SEAL)
1140Sstevel@tonic-gate 		 : (ctx->initiate
1150Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SIGN
1160Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SIGN));
1170Sstevel@tonic-gate     if (ctx->have_acceptor_subkey) {
1180Sstevel@tonic-gate 	key = ctx->acceptor_subkey;
1190Sstevel@tonic-gate     } else {
1200Sstevel@tonic-gate 	key = ctx->enc;
1210Sstevel@tonic-gate     }
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate #ifdef _KERNEL
1240Sstevel@tonic-gate     context->kef_cipher_mt = get_cipher_mech_type(context, key);
1250Sstevel@tonic-gate     context->kef_hash_mt = get_hash_mech_type(context, key);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
1280Sstevel@tonic-gate 	return (GSS_S_FAILURE);
1290Sstevel@tonic-gate     }
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate #endif /* _KERNEL */
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate #ifdef CFX_EXERCISE
1340Sstevel@tonic-gate     {
1350Sstevel@tonic-gate 	static int initialized = 0;
1360Sstevel@tonic-gate 	if (!initialized) {
1370Sstevel@tonic-gate 	    srand(time(0));
1380Sstevel@tonic-gate 	    initialized = 1;
1390Sstevel@tonic-gate 	}
1400Sstevel@tonic-gate     }
1410Sstevel@tonic-gate #endif
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
1440Sstevel@tonic-gate 	krb5_data plain;
1450Sstevel@tonic-gate 	krb5_enc_data cipher;
1460Sstevel@tonic-gate 	size_t ec_max;
1470Sstevel@tonic-gate 	size_t tlen;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 	/* 300: Adds some slop.  */
1500Sstevel@tonic-gate 	if (SIZE_MAX - 300 < message->length)
1510Sstevel@tonic-gate 	    return ENOMEM;
1520Sstevel@tonic-gate 	ec_max = SIZE_MAX - message->length - 300;
1530Sstevel@tonic-gate 	if (ec_max > 0xffff)
1540Sstevel@tonic-gate 	    ec_max = 0xffff;
1550Sstevel@tonic-gate 	/*
1560Sstevel@tonic-gate 	 * EC should really be a multiple (1) of the number of octets that
1570Sstevel@tonic-gate 	 * the cryptosystem would pad by if we didn't have the filler.
1580Sstevel@tonic-gate 	 *
1590Sstevel@tonic-gate 	 * For AES-CTS this will always be 0 and we expect no further
1600Sstevel@tonic-gate 	 * enctypes, so there should be no issue here.
1610Sstevel@tonic-gate 	 */
1620Sstevel@tonic-gate 	ec = 0;
1630Sstevel@tonic-gate 	plain.length = message->length + 16 + ec;
1640Sstevel@tonic-gate 	plain.data = MALLOC(plain.length);
1650Sstevel@tonic-gate 	if (plain.data == NULL)
1660Sstevel@tonic-gate 	    return ENOMEM;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	/* Get size of ciphertext.  */
1690Sstevel@tonic-gate 	if ((err = krb5_c_encrypt_length(context,
1700Sstevel@tonic-gate 		ctx->enc->enctype, plain.length, &tlen))) {
1710Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
1720Sstevel@tonic-gate 	    return (err);
1730Sstevel@tonic-gate         }
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	bufsize = 16 + tlen;
1760Sstevel@tonic-gate 	/* Allocate space for header plus encrypted data.  */
1770Sstevel@tonic-gate 	outbuf = MALLOC(bufsize);
1780Sstevel@tonic-gate 	if (outbuf == NULL) {
1790Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
1800Sstevel@tonic-gate 	    return ENOMEM;
1810Sstevel@tonic-gate 	}
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	/* TOK_ID */
1840Sstevel@tonic-gate 	store_16_be(0x0504, outbuf);
1850Sstevel@tonic-gate 	/* flags */
1860Sstevel@tonic-gate 	outbuf[2] = (acceptor_flag
1870Sstevel@tonic-gate 		     | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
1880Sstevel@tonic-gate 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
1890Sstevel@tonic-gate 	/* filler */
1900Sstevel@tonic-gate 	outbuf[3] = 0xff;
1910Sstevel@tonic-gate 	/* EC */
1920Sstevel@tonic-gate 	store_16_be(ec, outbuf+4);
1930Sstevel@tonic-gate 	/* RRC */
1940Sstevel@tonic-gate 	store_16_be(0, outbuf+6);
1950Sstevel@tonic-gate 	store_64_be(ctx->seq_send, outbuf+8);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	(void) memcpy(plain.data, message->value, message->length);
1980Sstevel@tonic-gate 	(void) memset(plain.data + message->length, 'x', ec);
1990Sstevel@tonic-gate 	(void) memcpy(plain.data + message->length + ec, outbuf, 16);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/* Should really use scatter/gather crypto interfaces */
2020Sstevel@tonic-gate 	cipher.ciphertext.data = (char *)outbuf + 16;
2030Sstevel@tonic-gate 	cipher.ciphertext.length = bufsize - 16;
2040Sstevel@tonic-gate 	cipher.enctype = key->enctype;
2050Sstevel@tonic-gate 	err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
2060Sstevel@tonic-gate 	(void) bzero(plain.data, plain.length);
2070Sstevel@tonic-gate 	FREE(plain.data, plain.length);
2080Sstevel@tonic-gate 	plain.data = 0;
2090Sstevel@tonic-gate 	if (err)
2100Sstevel@tonic-gate 	    goto error;
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	/* Now that we know we're returning a valid token....  */
2130Sstevel@tonic-gate 	ctx->seq_send++;
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate #ifdef CFX_EXERCISE
2160Sstevel@tonic-gate 	rrc = rand() & 0xffff;
2170Sstevel@tonic-gate 	if (rotate_left(outbuf+16, bufsize-16,
2180Sstevel@tonic-gate 			(bufsize-16) - (rrc % (bufsize - 16))))
2190Sstevel@tonic-gate 	    store_16_be(rrc, outbuf+6);
2200Sstevel@tonic-gate 	/* If the rotate fails, don't worry about it.  */
2210Sstevel@tonic-gate #endif
2220Sstevel@tonic-gate     } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
2230Sstevel@tonic-gate 	krb5_data plain;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/* Here, message is the application-supplied data; message2 is
2260Sstevel@tonic-gate 	   what goes into the output token.  They may be the same, or
2270Sstevel@tonic-gate 	   message2 may be empty (for MIC).  */
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	tok_id = 0x0504;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate     wrap_with_checksum:
2320Sstevel@tonic-gate 	plain.length = message->length + 16;
2330Sstevel@tonic-gate 	plain.data = MALLOC(message->length + 16);
2340Sstevel@tonic-gate 	if (plain.data == NULL)
2350Sstevel@tonic-gate 	    return ENOMEM;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	if (ctx->cksum_size > 0xffff) {
2380Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
2390Sstevel@tonic-gate 	    return EINVAL;
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	bufsize = 16 + message2->length + ctx->cksum_size;
2430Sstevel@tonic-gate 	outbuf = MALLOC(bufsize);
2440Sstevel@tonic-gate 	if (outbuf == NULL) {
2450Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
2460Sstevel@tonic-gate 	    plain.data = 0;
2470Sstevel@tonic-gate 	    err = ENOMEM;
2480Sstevel@tonic-gate 	    goto error;
2490Sstevel@tonic-gate 	}
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	/* TOK_ID */
2520Sstevel@tonic-gate 	store_16_be(tok_id, outbuf);
2530Sstevel@tonic-gate 	/* flags */
2540Sstevel@tonic-gate 	outbuf[2] = (acceptor_flag
2550Sstevel@tonic-gate 		     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
2560Sstevel@tonic-gate 	/* filler */
2570Sstevel@tonic-gate 	outbuf[3] = 0xff;
2580Sstevel@tonic-gate 	if (toktype == KG_TOK_WRAP_MSG) {
2590Sstevel@tonic-gate 	    /* Use 0 for checksum calculation, substitute
2600Sstevel@tonic-gate 	       checksum length later.  */
2610Sstevel@tonic-gate 	    /* EC */
2620Sstevel@tonic-gate 	    store_16_be(0, outbuf+4);
2630Sstevel@tonic-gate 	    /* RRC */
2640Sstevel@tonic-gate 	    store_16_be(0, outbuf+6);
2650Sstevel@tonic-gate 	} else {
2660Sstevel@tonic-gate 	    /* MIC and DEL store 0xFF in EC and RRC.  */
2670Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+4);
2680Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+6);
2690Sstevel@tonic-gate 	}
2700Sstevel@tonic-gate 	store_64_be(ctx->seq_send, outbuf+8);
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	(void) memcpy(plain.data, message->value, message->length);
2730Sstevel@tonic-gate 	(void) memcpy(plain.data + message->length, outbuf, 16);
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	/* Fill in the output token -- data contents, if any, and
2760Sstevel@tonic-gate 	   space for the checksum.  */
2770Sstevel@tonic-gate 	if (message2->length)
2780Sstevel@tonic-gate 	    (void) memcpy(outbuf + 16, message2->value, message2->length);
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	sum.contents = outbuf + 16 + message2->length;
2810Sstevel@tonic-gate 	sum.length = ctx->cksum_size;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	err = krb5_c_make_checksum(context, ctx->cksumtype, key,
2840Sstevel@tonic-gate 				   key_usage, &plain, &sum);
2850Sstevel@tonic-gate 	bzero(plain.data, plain.length);
2860Sstevel@tonic-gate 	FREE(plain.data, plain.length);
2870Sstevel@tonic-gate 	plain.data = 0;
2880Sstevel@tonic-gate 	if (err) {
2890Sstevel@tonic-gate 	    bzero(outbuf,bufsize);
2900Sstevel@tonic-gate 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2910Sstevel@tonic-gate 	    goto error;
2920Sstevel@tonic-gate 	}
2930Sstevel@tonic-gate 	if (sum.length != ctx->cksum_size) {
2940Sstevel@tonic-gate 	    err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2950Sstevel@tonic-gate 	    goto error;
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 	(void) memcpy(outbuf + 16 + message2->length, sum.contents,
2980Sstevel@tonic-gate 	    ctx->cksum_size);
2990Sstevel@tonic-gate 	krb5_free_checksum_contents(context, &sum);
3000Sstevel@tonic-gate 	sum.contents = 0;
3010Sstevel@tonic-gate 	/* Now that we know we're actually generating the token...  */
3020Sstevel@tonic-gate 	ctx->seq_send++;
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	if (toktype == KG_TOK_WRAP_MSG) {
3050Sstevel@tonic-gate #ifdef CFX_EXERCISE
3060Sstevel@tonic-gate 	    rrc = rand() & 0xffff;
3070Sstevel@tonic-gate 	    /* If the rotate fails, don't worry about it.  */
3080Sstevel@tonic-gate 	    if (rotate_left(outbuf+16, bufsize-16,
3090Sstevel@tonic-gate 			    (bufsize-16) - (rrc % (bufsize - 16))))
3100Sstevel@tonic-gate 		store_16_be(rrc, outbuf+6);
3110Sstevel@tonic-gate #endif
3120Sstevel@tonic-gate 	    /* Fix up EC field.  */
3130Sstevel@tonic-gate 	    store_16_be(ctx->cksum_size, outbuf+4);
3140Sstevel@tonic-gate 	} else {
3150Sstevel@tonic-gate 	    store_16_be(0xffff, outbuf+6);
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate     } else if (toktype == KG_TOK_MIC_MSG) {
3180Sstevel@tonic-gate 	tok_id = 0x0404;
3190Sstevel@tonic-gate 	message2 = &empty_message;
3200Sstevel@tonic-gate 	goto wrap_with_checksum;
3210Sstevel@tonic-gate     } else if (toktype == KG_TOK_DEL_CTX) {
3223376Smp153739 	/*
3233376Smp153739 	 * Solaris Kerberos:
3243376Smp153739 	 * No token should be generated for context deletion. Just
3253376Smp153739 	 * return.
3263376Smp153739 	 */
3273376Smp153739 	return 0;
3280Sstevel@tonic-gate     } else {
3290Sstevel@tonic-gate 	err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
3300Sstevel@tonic-gate 	goto error;
3310Sstevel@tonic-gate     }
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate     token->value = outbuf;
3340Sstevel@tonic-gate     token->length = bufsize;
3350Sstevel@tonic-gate     return 0;
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate error:
3380Sstevel@tonic-gate     FREE(outbuf, bufsize);
3390Sstevel@tonic-gate     token->value = NULL;
3400Sstevel@tonic-gate     token->length = 0;
3410Sstevel@tonic-gate     return err;
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
3450Sstevel@tonic-gate    conf_state is only valid if SEAL. */
3460Sstevel@tonic-gate 
3470Sstevel@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)3487934SMark.Phalan@Sun.COM gss_krb5int_unseal_token_v3(krb5_context *contextptr,
3490Sstevel@tonic-gate 			    OM_uint32 *minor_status,
3500Sstevel@tonic-gate 			    krb5_gss_ctx_id_rec *ctx,
3510Sstevel@tonic-gate 			    unsigned char *ptr, int bodysize,
3520Sstevel@tonic-gate 			    gss_buffer_t message_buffer,
3530Sstevel@tonic-gate 			    int *conf_state, int *qop_state, int toktype)
3540Sstevel@tonic-gate {
3557934SMark.Phalan@Sun.COM     krb5_context context = *contextptr;
3560Sstevel@tonic-gate     krb5_data plain;
3570Sstevel@tonic-gate     gssint_uint64 seqnum;
3580Sstevel@tonic-gate     size_t ec, rrc;
3590Sstevel@tonic-gate     int key_usage;
3600Sstevel@tonic-gate     unsigned char acceptor_flag;
3610Sstevel@tonic-gate     krb5_checksum sum;
3620Sstevel@tonic-gate     krb5_error_code err;
3630Sstevel@tonic-gate     krb5_boolean valid;
3640Sstevel@tonic-gate     krb5_keyblock *key;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate     ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
3670Sstevel@tonic-gate     ASSERT(ctx->big_endian == 0);
3680Sstevel@tonic-gate     ASSERT(ctx->proto == 1);
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate     if (qop_state)
3710Sstevel@tonic-gate 	*qop_state = GSS_C_QOP_DEFAULT;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate     acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
3740Sstevel@tonic-gate     key_usage = (toktype == KG_TOK_WRAP_MSG
3750Sstevel@tonic-gate 		 ? (!ctx->initiate
3760Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SEAL
3770Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SEAL)
3780Sstevel@tonic-gate 		 : (!ctx->initiate
3790Sstevel@tonic-gate 		    ? KG_USAGE_INITIATOR_SIGN
3800Sstevel@tonic-gate 		    : KG_USAGE_ACCEPTOR_SIGN));
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate     /* Oops.  I wrote this code assuming ptr would be at the start of
3830Sstevel@tonic-gate        the token header.  */
3840Sstevel@tonic-gate     ptr -= 2;
3850Sstevel@tonic-gate     bodysize += 2;
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate     if (bodysize < 16) {
3880Sstevel@tonic-gate     defective:
3890Sstevel@tonic-gate 	*minor_status = 0;
3900Sstevel@tonic-gate 	return GSS_S_DEFECTIVE_TOKEN;
3910Sstevel@tonic-gate     }
3920Sstevel@tonic-gate     if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
3930Sstevel@tonic-gate 	*minor_status = (OM_uint32)G_BAD_DIRECTION;
3940Sstevel@tonic-gate 	return GSS_S_BAD_SIG;
3950Sstevel@tonic-gate     }
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate     /* Two things to note here.
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate        First, we can't really enforce the use of the acceptor's subkey,
4000Sstevel@tonic-gate        if we're the acceptor; the initiator may have sent messages
4010Sstevel@tonic-gate        before getting the subkey.  We could probably enforce it if
4020Sstevel@tonic-gate        we're the initiator.
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate        Second, if someone tweaks the code to not set the flag telling
4050Sstevel@tonic-gate        the krb5 library to generate a new subkey in the AP-REP
4060Sstevel@tonic-gate        message, the MIT library may include a subkey anyways --
4070Sstevel@tonic-gate        namely, a copy of the AP-REQ subkey, if it was provided.  So
4080Sstevel@tonic-gate        the initiator may think we wanted a subkey, and set the flag,
4090Sstevel@tonic-gate        even though we weren't trying to set the subkey.  The "other"
4107934SMark.Phalan@Sun.COM        key, the one not asserted by the acceptor, will have the same
4110Sstevel@tonic-gate        value in that case, though, so we can just ignore the flag.  */
4120Sstevel@tonic-gate     if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
4130Sstevel@tonic-gate 	key = ctx->acceptor_subkey;
4140Sstevel@tonic-gate     } else {
4150Sstevel@tonic-gate 	key = ctx->enc;
4160Sstevel@tonic-gate     }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate #ifdef _KERNEL
4190Sstevel@tonic-gate     context->kef_cipher_mt = get_cipher_mech_type(context, key);
4200Sstevel@tonic-gate     context->kef_hash_mt = get_hash_mech_type(context, key);
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate     if ((err = init_key_kef(context->kef_cipher_mt, key))) {
4230Sstevel@tonic-gate 	return (GSS_S_FAILURE);
4240Sstevel@tonic-gate     }
4250Sstevel@tonic-gate #endif /* _KERNEL */
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate     if (toktype == KG_TOK_WRAP_MSG) {
4280Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0504)
4290Sstevel@tonic-gate 	    goto defective;
4300Sstevel@tonic-gate 	if (ptr[3] != 0xff)
4310Sstevel@tonic-gate 	    goto defective;
4320Sstevel@tonic-gate 	ec = load_16_be(ptr+4);
4330Sstevel@tonic-gate 	rrc = load_16_be(ptr+6);
4340Sstevel@tonic-gate 	seqnum = load_64_be(ptr+8);
4350Sstevel@tonic-gate 	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
4367934SMark.Phalan@Sun.COM 	no_mem:
4370Sstevel@tonic-gate 	    *minor_status = ENOMEM;
4380Sstevel@tonic-gate 	    return GSS_S_FAILURE;
4390Sstevel@tonic-gate 	}
4400Sstevel@tonic-gate 	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
4410Sstevel@tonic-gate 	    /* confidentiality */
4420Sstevel@tonic-gate 	    krb5_enc_data cipher;
4430Sstevel@tonic-gate 	    unsigned char *althdr;
4440Sstevel@tonic-gate             size_t plainlen;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	    if (conf_state)
4470Sstevel@tonic-gate 		*conf_state = 1;
4480Sstevel@tonic-gate 	    /* Do we have no decrypt_size function?
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	       For all current cryptosystems, the ciphertext size will
4510Sstevel@tonic-gate 	       be larger than the plaintext size.  */
4520Sstevel@tonic-gate 	    cipher.enctype = key->enctype;
4530Sstevel@tonic-gate 	    cipher.ciphertext.length = bodysize - 16;
4540Sstevel@tonic-gate 	    cipher.ciphertext.data = (char *)ptr + 16;
4550Sstevel@tonic-gate 	    plain.length = plainlen = bodysize - 16;
4560Sstevel@tonic-gate 	    plain.data = MALLOC(plain.length);
4570Sstevel@tonic-gate 	    if (plain.data == NULL)
4580Sstevel@tonic-gate 		goto no_mem;
4590Sstevel@tonic-gate 	    err = krb5_c_decrypt(context, key, key_usage, 0,
4600Sstevel@tonic-gate 				 &cipher, &plain);
4610Sstevel@tonic-gate 	    if (err) {
4620Sstevel@tonic-gate 		goto error;
4630Sstevel@tonic-gate 	    }
4640Sstevel@tonic-gate 	    /* Don't use bodysize here!  Use the fact that
4650Sstevel@tonic-gate 	       plain.length has been adjusted to the
4660Sstevel@tonic-gate 	       correct length.  */
4670Sstevel@tonic-gate 	    althdr = (uchar_t *)plain.data + plain.length - 16;
4680Sstevel@tonic-gate 	    if (load_16_be(althdr) != 0x0504
4690Sstevel@tonic-gate 		|| althdr[2] != ptr[2]
4700Sstevel@tonic-gate 		|| althdr[3] != ptr[3]
4710Sstevel@tonic-gate 		|| memcmp(althdr+8, ptr+8, 8)) {
4720Sstevel@tonic-gate 		FREE(plain.data, plainlen);
4730Sstevel@tonic-gate 		goto defective;
4740Sstevel@tonic-gate 	    }
4750Sstevel@tonic-gate 	    message_buffer->length = plain.length - ec - 16;
4760Sstevel@tonic-gate 	    message_buffer->value = MALLOC(message_buffer->length);
4770Sstevel@tonic-gate 	    if (message_buffer->value == NULL) {
4780Sstevel@tonic-gate 		FREE(plain.data, plainlen);
4790Sstevel@tonic-gate 		goto no_mem;
4800Sstevel@tonic-gate 	    }
4810Sstevel@tonic-gate 	    (void) memcpy(message_buffer->value, plain.data,
4820Sstevel@tonic-gate 			message_buffer->length);
4830Sstevel@tonic-gate 	    FREE(plain.data, plainlen);
4840Sstevel@tonic-gate 	} else {
4850Sstevel@tonic-gate 	    /* no confidentiality */
4860Sstevel@tonic-gate 	    if (conf_state)
4870Sstevel@tonic-gate 		*conf_state = 0;
4880Sstevel@tonic-gate 	    if (ec + 16 < ec)
4890Sstevel@tonic-gate 		/* overflow check */
4900Sstevel@tonic-gate 		goto defective;
4910Sstevel@tonic-gate 	    if (ec + 16 > bodysize)
4920Sstevel@tonic-gate 		goto defective;
4930Sstevel@tonic-gate 	    /* We have: header | msg | cksum.
4940Sstevel@tonic-gate 	       We need cksum(msg | header).
4950Sstevel@tonic-gate 	       Rotate the first two.  */
4960Sstevel@tonic-gate 	    store_16_be(0, ptr+4);
4970Sstevel@tonic-gate 	    store_16_be(0, ptr+6);
4987934SMark.Phalan@Sun.COM 	    plain.length = bodysize-ec;
4990Sstevel@tonic-gate 	    plain.data = (char *)ptr;
5000Sstevel@tonic-gate 	    if (!rotate_left(ptr, bodysize-ec, 16))
5010Sstevel@tonic-gate 		goto no_mem;
5020Sstevel@tonic-gate 	    sum.length = ec;
5030Sstevel@tonic-gate 	    if (sum.length != ctx->cksum_size) {
5040Sstevel@tonic-gate 		*minor_status = 0;
5050Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5060Sstevel@tonic-gate 	    }
5070Sstevel@tonic-gate 	    sum.contents = ptr+bodysize-ec;
5080Sstevel@tonic-gate 	    sum.checksum_type = ctx->cksumtype;
5090Sstevel@tonic-gate 	    err = krb5_c_verify_checksum(context, key, key_usage,
5100Sstevel@tonic-gate 					 &plain, &sum, &valid);
5110Sstevel@tonic-gate 	    if (err) {
5120Sstevel@tonic-gate 		*minor_status = err;
5130Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5140Sstevel@tonic-gate 	    }
5150Sstevel@tonic-gate 	    if (!valid) {
5160Sstevel@tonic-gate 		*minor_status = 0;
5170Sstevel@tonic-gate 		return GSS_S_BAD_SIG;
5180Sstevel@tonic-gate 	    }
5190Sstevel@tonic-gate 	    message_buffer->length = plain.length - 16;
5200Sstevel@tonic-gate 	    message_buffer->value = MALLOC(message_buffer->length);
5210Sstevel@tonic-gate 	    if (message_buffer->value == NULL)
5220Sstevel@tonic-gate 		goto no_mem;
5230Sstevel@tonic-gate 	    (void) memcpy(message_buffer->value,
5240Sstevel@tonic-gate 		plain.data, message_buffer->length);
5253376Smp153739 
5263376Smp153739 		/*
5273376Smp153739 		 * Solaris Kerberos: Restore the original token.
5283376Smp153739 		 * This allows the token to be detected as a duplicate if it
5293376Smp153739 		 * is passed in to gss_unwrap() again.
5303376Smp153739 		 */
5313376Smp153739 		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
5323376Smp153739 			goto no_mem;
5333376Smp153739 		store_16_be(ec, ptr+4);
5343376Smp153739 		store_16_be(rrc, ptr+6);
5350Sstevel@tonic-gate 	}
5360Sstevel@tonic-gate 	err = g_order_check(&ctx->seqstate, seqnum);
5370Sstevel@tonic-gate 	*minor_status = 0;
5380Sstevel@tonic-gate 	return err;
5390Sstevel@tonic-gate     } else if (toktype == KG_TOK_MIC_MSG) {
5400Sstevel@tonic-gate 	/* wrap token, no confidentiality */
5410Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0404)
5420Sstevel@tonic-gate 	    goto defective;
5430Sstevel@tonic-gate     verify_mic_1:
5440Sstevel@tonic-gate 	if (ptr[3] != 0xff)
5450Sstevel@tonic-gate 	    goto defective;
5460Sstevel@tonic-gate 	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
5470Sstevel@tonic-gate 	    goto defective;
5480Sstevel@tonic-gate 	seqnum = load_64_be(ptr+8);
5490Sstevel@tonic-gate 	plain.length = message_buffer->length + 16;
5500Sstevel@tonic-gate 	plain.data = MALLOC(plain.length);
5510Sstevel@tonic-gate 	if (plain.data == NULL)
5520Sstevel@tonic-gate 	    goto no_mem;
5530Sstevel@tonic-gate 	if (message_buffer->length)
5540Sstevel@tonic-gate 	    (void) memcpy(plain.data,
5550Sstevel@tonic-gate 		message_buffer->value, message_buffer->length);
5560Sstevel@tonic-gate 	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
5570Sstevel@tonic-gate 	sum.length = bodysize - 16;
5580Sstevel@tonic-gate 	sum.contents = ptr + 16;
5590Sstevel@tonic-gate 	sum.checksum_type = ctx->cksumtype;
5600Sstevel@tonic-gate 	err = krb5_c_verify_checksum(context, key, key_usage,
5610Sstevel@tonic-gate 				     &plain, &sum, &valid);
5620Sstevel@tonic-gate 	if (err) {
5637934SMark.Phalan@Sun.COM 	error:
5640Sstevel@tonic-gate 	    FREE(plain.data, plain.length);
5650Sstevel@tonic-gate 	    *minor_status = err;
566*13132SGlenn.Barry@oracle.com 	    save_error_info(*minor_status, context);
5670Sstevel@tonic-gate 	    return GSS_S_BAD_SIG; /* XXX */
5680Sstevel@tonic-gate 	}
5690Sstevel@tonic-gate 	FREE(plain.data, plain.length);
5700Sstevel@tonic-gate 	if (!valid) {
5710Sstevel@tonic-gate 	    *minor_status = 0;
5720Sstevel@tonic-gate 	    return GSS_S_BAD_SIG;
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate 	err = g_order_check(&ctx->seqstate, seqnum);
5750Sstevel@tonic-gate 	*minor_status = 0;
5760Sstevel@tonic-gate 	return err;
5770Sstevel@tonic-gate     } else if (toktype == KG_TOK_DEL_CTX) {
5780Sstevel@tonic-gate 	if (load_16_be(ptr) != 0x0405)
5790Sstevel@tonic-gate 	    goto defective;
5800Sstevel@tonic-gate 	message_buffer = (gss_buffer_t)&empty_message;
5810Sstevel@tonic-gate 	goto verify_mic_1;
5820Sstevel@tonic-gate     } else {
5830Sstevel@tonic-gate 	goto defective;
5840Sstevel@tonic-gate     }
5850Sstevel@tonic-gate }
586