Re: [PATCH 6/6] KEYS: trusted: add support for TPM keys with signed policy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri May 24, 2024 at 4:04 PM EEST, James Bottomley wrote:
> Signed policy allows key policies to be modified after the TPM key is
> created, provided the new policy is signed with a private key whose
> public part is encoded into the initial policy.  How this works is
> described in
>
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
>
> Since this means that the key structure now contains public keys and
> signatures, the maximum blob size has to be expanded simply to
> accommodate a key with several policies.  The code assumes (as does
> all other signed policy engines) that the first policy is the most
> likely one to succeed, so it tries all the available signed policies
> in first to last order and loads the key when one of them succeeds.
>
> This code allows the kernel to process signed policy keys correctly,
> it does not allow the kernel to create them.
>
> Signed-off-by: James Bottomley <James.Bottomley@xxxxxxxxxxxxxxxxxxxxx>
> ---
>  drivers/char/tpm/tpm2-sessions.c          |   6 +
>  include/keys/trusted-type.h               |   3 +-
>  include/linux/tpm.h                       |   6 +
>  security/keys/trusted-keys/tpm2-policy.c  | 473 ++++++++++++++++------
>  security/keys/trusted-keys/tpm2-policy.h  |  19 +-
>  security/keys/trusted-keys/tpm2key.asn1   |  12 +-
>  security/keys/trusted-keys/trusted_tpm2.c |  63 +--
>  7 files changed, 434 insertions(+), 148 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 63c175b2165c..c2b73283c46f 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -1365,3 +1365,9 @@ int tpm2_sessions_init(struct tpm_chip *chip)
>  
>  	return rc;
>  }
> +
> +struct crypto_shash *tpm2_get_policy_hash(struct tpm_chip *chip)
> +{
> +	return chip->auth->tfm;
> +}

Please open code.


> +EXPORT_SYMBOL(tpm2_get_policy_hash);
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 5d1d481a8a19..a6999800055e 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -19,11 +19,12 @@
>  
>  #define MIN_KEY_SIZE			32
>  #define MAX_KEY_SIZE			128
> -#define MAX_BLOB_SIZE			512
> +#define MAX_BLOB_SIZE			4096
>  #define MAX_PCRINFO_SIZE		128
>  #define MAX_DIGEST_SIZE			64
>  
>  #define TPM2_MAX_POLICIES		16
> +#define TPM2_MAX_AUTHS			8
>  
>  struct trusted_key_payload {
>  	struct rcu_head rcu;
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 894e51a7fe3a..09f14482675b 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -290,6 +290,8 @@ enum tpm2_command_codes {
>  	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
>  	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
>  	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
> +	TPM2_CC_LOAD_EXTERNAL		= 0x0167,
> +	TPM2_CC_POLICY_AUTHORIZE	= 0x016A,
>  	TPM2_CC_POLICY_AUTHVALUE	= 0x016B,
>  	TPM2_CC_POLICY_COUNTER_TIMER	= 0x016D,
>  	TPM2_CC_READ_PUBLIC		= 0x0173,
> @@ -299,14 +301,17 @@ enum tpm2_command_codes {
>  	TPM2_CC_GET_RANDOM	        = 0x017B,
>  	TPM2_CC_PCR_READ	        = 0x017E,
>  	TPM2_CC_POLICY_PCR		= 0x017F,
> +	TPM2_CC_POLICY_RESTART		= 0x0180,
>  	TPM2_CC_PCR_EXTEND	        = 0x0182,
>  	TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185,
>  	TPM2_CC_HASH_SEQUENCE_START     = 0x0186,
> +	TPM2_CC_POLICY_GET_DIGEST	= 0x0189,
>  	TPM2_CC_CREATE_LOADED           = 0x0191,
>  	TPM2_CC_LAST		        = 0x0193, /* Spec 1.36 */
>  };
>  
>  enum tpm2_permanent_handles {
> +	TPM2_RH_OWNER		= 0x40000001,
>  	TPM2_RH_NULL		= 0x40000007,
>  	TPM2_RS_PW		= 0x40000009,
>  };
> @@ -578,6 +583,7 @@ static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
>  
>  int tpm2_start_auth_session(struct tpm_chip *chip);
>  int tpm2_start_policy_session(struct tpm_chip *chip, u32 *handle, u8 hash);
> +struct crypto_shash *tpm2_get_policy_hash(struct tpm_chip *chip);
>  void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>  			 u32 handle, u8 *name);
>  void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
> index c0508cb95923..f9ee57fed8fe 100644
> --- a/security/keys/trusted-keys/tpm2-policy.c
> +++ b/security/keys/trusted-keys/tpm2-policy.c
> @@ -28,13 +28,28 @@ int tpm2_key_code(void *context, size_t hdrlen,
>  	u32 code = 0;
>  	const u8 *v = value;
>  	int i;
> +	u8 a = ctx->auth_policies;
>  
>  	for (i = 0; i < vlen; i++) {
>  		code <<= 8;
>  		code |= v[i];
>  	}
>  
> -	ctx->policy_code[ctx->policy_count] = code;
> +	ctx->policy_code[a][ctx->policy_count[a]] = code;
> +
> +	return 0;
> +}
> +
> +int tpm2_pol_seq(void *context, size_t hdrlen,
> +		 unsigned char tag,
> +		 const void *value, size_t vlen)
> +{
> +	struct tpm2_key_context *ctx = context;
> +
> +	ctx->auth_policies++;
> +
> +	if (ctx->auth_policies > TPM2_MAX_AUTHS)
> +		return -EINVAL;
>  
>  	return 0;
>  }
> @@ -44,9 +59,15 @@ int tpm2_key_policy(void *context, size_t hdrlen,
>  		  const void *value, size_t vlen)
>  {
>  	struct tpm2_key_context *ctx = context;
> +	u8 a = ctx->auth_policies;
> +	u8 policy_count = ctx->policy_count[a]++;
> +
> +	if (policy_count >= TPM2_MAX_POLICIES)
> +		return -EINVAL;
>  
> -	ctx->policies[ctx->policy_count] = value;
> -	ctx->policy_len[ctx->policy_count++] = vlen;
> +	ctx->policies[a][policy_count] = value;
> +	ctx->policy_len[a][policy_count] = vlen;
> +	ctx->policy_tot_len += vlen;
>  
>  	return 0;
>  }
> @@ -56,21 +77,24 @@ int tpm2_key_policy(void *context, size_t hdrlen,
>   */
>  static int tpm2_validate_policy(struct tpm2_policies *pols)
>  {
> -	int i;
> +	int i, j;
>  
> -	if (pols->count == 0)
> +	if (pols->auths == 0)
>  		return 0;
>  
> -	for (i = 0; i < pols->count; i++) {
> -		switch (pols->code[i]) {
> -		case TPM2_CC_POLICY_COUNTER_TIMER:
> -		case TPM2_CC_POLICY_PCR:
> -		case TPM2_CC_POLICY_AUTHVALUE:
> -			break;
> -		default:
> -			pr_info("tpm2 policy 0x%x is unsupported\n",
> -				pols->code[i]);
> +	for (i = 0; i < pols->auths; i++) {
> +		for (j = 0; j < pols->count[i]; j++) {
> +			switch (pols->code[i][j]) {
> +			case TPM2_CC_POLICY_COUNTER_TIMER:
> +			case TPM2_CC_POLICY_PCR:
> +			case TPM2_CC_POLICY_AUTHVALUE:
> +			case TPM2_CC_POLICY_AUTHORIZE:
> +				break;
> +			default:
> +				pr_info("tpm2 policy 0x%x is unsupported\n",
> +					pols->code[i][j]);
>  			return -EINVAL;
> +			}
>  		}
>  	}
>  
> @@ -89,36 +113,35 @@ static int tpm2_validate_policy(struct tpm2_policies *pols)
>  int tpm2_key_policy_process(struct tpm2_key_context *ctx,
>  			    struct trusted_key_payload *payload)
>  {
> -	int tot_len = 0;
>  	u8 *buf;
> -	int i, ret, len = 0;
> +	int i, j, ret, len = 0;
>  	struct tpm2_policies *pols;
>  	u16 name_alg;
>  
> -	if (ctx->policy_count == 0)
> +	if (ctx->policy_tot_len == 0)
>  		return 0;
>  
> -	for (i = 0; i < ctx->policy_count; i++)
> -		tot_len += ctx->policy_len[i];
> -	tot_len += sizeof(*pols);
> -
> -	pols = kmalloc(tot_len, GFP_KERNEL);
> +	pols = kmalloc(ctx->policy_tot_len + sizeof(*pols), GFP_KERNEL);
>  	if (!pols)
>  		return -ENOMEM;
>  
>  	payload->policies = pols;
>  	buf = (u8 *)(pols + 1);
>  
> -	for (i = 0; i < ctx->policy_count; i++) {
> -		pols->policies[i] = &buf[len];
> -		pols->len[i] = ctx->policy_len[i];
> -		pols->code[i] = ctx->policy_code[i];
> -		if (pols->len[i])
> -			memcpy(pols->policies[i], ctx->policies[i],
> -			       ctx->policy_len[i]);
> -		len += ctx->policy_len[i];
> +	for (i = 0; i < ctx->auth_policies; i++) {
> +		for (j = 0; j < ctx->policy_count[i]; j++) {
> +			pols->policies[i][j] = &buf[len];
> +			pols->len[i][j] = ctx->policy_len[i][j];
> +			pols->code[i][j] = ctx->policy_code[i][j];
> +			if (pols->len[i][j])
> +				memcpy(pols->policies[i][j],
> +				       ctx->policies[i][j],
> +				       ctx->policy_len[i][j]);
> +			len += ctx->policy_len[i][j];
> +		}
> +		pols->count[i] = ctx->policy_count[i];
>  	}
> -	pols->count = ctx->policy_count;
> +	pols->auths = ctx->auth_policies;
>  
>  	ret = tpm2_validate_policy(pols);
>  	if (ret)
> @@ -148,7 +171,7 @@ int tpm2_key_policy_process(struct tpm2_key_context *ctx,
>  int tpm2_generate_policy_digest(struct tpm2_policies *pols,
>  				u32 hash, u8 *policydigest, u32 *plen)
>  {
> -	int i;
> +	int i, j;
>  	struct crypto_shash *tfm;
>  	int rc;
>  
> @@ -174,45 +197,47 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols,
>  	/* policy digests always start out all zeros */
>  	memset(policydigest, 0, rc);
>  
> -	for (i = 0; i < pols->count; i++) {
> -		u8 *policy = pols->policies[i];
> -		int len = pols->len[i];
> -		u32 cmd = pols->code[i];
> -		u8 digest[MAX_DIGEST_SIZE];
> -		u8 code[4];
> -		SHASH_DESC_ON_STACK(sdesc, tfm);
> +	for (i = 0; i < pols->auths; i++) {
> +		for (j = 0; j < pols->count[i]; j++) {
> +			u8 *policy = pols->policies[i][j];
> +			int len = pols->len[i][j];
> +			u32 cmd = pols->code[i][j];
> +			u8 digest[MAX_DIGEST_SIZE];
> +			u8 code[4];
> +			SHASH_DESC_ON_STACK(sdesc, tfm);
>  
> -		sdesc->tfm = tfm;
> -		rc = crypto_shash_init(sdesc);
> -		if (rc)
> -			goto err;
> +			sdesc->tfm = tfm;
> +			rc = crypto_shash_init(sdesc);
> +			if (rc)
> +				goto err;
>  
> -		/* first hash the previous digest */
> -		crypto_shash_update(sdesc, policydigest, *plen);
> +			/* first hash the previous digest */
> +			crypto_shash_update(sdesc, policydigest, *plen);
>  
> -		/* then hash the command code */
> -		put_unaligned_be32(cmd, code);
> -		crypto_shash_update(sdesc, code, 4);
> +			/* then hash the command code */
> +			put_unaligned_be32(cmd, code);
> +			crypto_shash_update(sdesc, code, 4);
>  
> -		/* commands that need special handling */
> -		if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
> -			SHASH_DESC_ON_STACK(sdesc1, tfm);
> +			/* commands that need special handling */
> +			if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
> +				SHASH_DESC_ON_STACK(sdesc1, tfm);
>  
> -			sdesc1->tfm = tfm;
> +				sdesc1->tfm = tfm;
>  
> -			/* counter timer policies are double hashed */
> -			crypto_shash_digest(sdesc1, policy, len,
> -					    digest);
> -			policy = digest;
> -			len = *plen;
> -		}
> +				/* counter timer policies are double hashed */
> +				crypto_shash_digest(sdesc1, policy, len,
> +						    digest);
> +				policy = digest;
> +				len = *plen;
> +			}
>  
> -		if (len)
> -			crypto_shash_update(sdesc, policy, len);
> +			if (len)
> +				crypto_shash_update(sdesc, policy, len);
>  
> -		/* now output the intermediate to the policydigest */
> -		crypto_shash_final(sdesc, policydigest);
> +			/* now output the intermediate to the policydigest */
> +			crypto_shash_final(sdesc, policydigest);
>  
> +		}
>  	}
>  	rc = 0;
>  
> @@ -228,41 +253,45 @@ int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len)
>  	u8 *work = buf + SCRATCH_SIZE;
>  	u8 *ptr;
>  	u8 *end_work = work + SCRATCH_SIZE;
> -	int i, ret;
> +	int i, j, ret;
>  
>  	if (!buf)
>  		return -ENOMEM;
>  
> -	for (i = 0; i < pols->count; i++) {
> -		u8 *seq, *tag;
> -		u32 cmd = pols->code[i];
> +	for (i = 0; i < pols->auths; i++) {
> +		for (j = 0; j < pols->count[i]; j++) {
> +			u8 *seq, *tag;
> +			u32 cmd = pols->code[i][j];
>  
> -		if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE,
> -			 "BUG: scratch buffer is too small"))
> -			return -EINVAL;
> +			if (WARN(work - buf + 14 + pols->len[i][j] >
> +				 2 * SCRATCH_SIZE,
> +				 "BUG: scratch buffer is too small"))
> +				return -EINVAL;
>  
> -		work = asn1_encode_sequence(work, end_work, NULL, -1);
> -		seq = work;
> +			work = asn1_encode_sequence(work, end_work, NULL, -1);
> +			seq = work;
>  
> -		work = asn1_encode_tag(work, end_work, 0, NULL, -1);
> -		tag = work;
> +			work = asn1_encode_tag(work, end_work, 0, NULL, -1);
> +			tag = work;
>  
> -		work = asn1_encode_integer(work, end_work, cmd);
> -		asn1_encode_tag(tag, end_work, 0, NULL, work - tag);
> +			work = asn1_encode_integer(work, end_work, cmd);
> +			asn1_encode_tag(tag, end_work, 0, NULL, work - tag);
>  
> -		work = asn1_encode_tag(work, end_work, 1, NULL, -1);
> -		tag = work;
> +			work = asn1_encode_tag(work, end_work, 1, NULL, -1);
> +			tag = work;
>  
> -		work = asn1_encode_octet_string(work, end_work,
> -						pols->policies[i],
> -						pols->len[i]);
> +			work = asn1_encode_octet_string(work, end_work,
> +							pols->policies[i][j],
> +							pols->len[i][j]);
>  
> -		asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
> +			asn1_encode_tag(tag, end_work, 1, NULL, work - tag);
>  
> -		seq = asn1_encode_sequence(seq, end_work, NULL, work - seq);
> -		if (IS_ERR(seq)) {
> -			ret = PTR_ERR(seq);
> -			goto err;
> +			seq = asn1_encode_sequence(seq, end_work, NULL,
> +						   work - seq);
> +			if (IS_ERR(seq)) {
> +				ret = PTR_ERR(seq);
> +				goto err;
> +			}
>  		}
>  	}
>  	ptr = asn1_encode_sequence(buf, buf + SCRATCH_SIZE, buf + PAGE_SIZE,
> @@ -282,18 +311,131 @@ int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len)
>  	return ret;
>  }
>  
> -int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
> +static int tpm2_policy_restart(struct tpm_chip *chip, u32 handle)
>  {
> -	int i, rc;
> -	u32 handle;
> -	const char *failure;
> +	struct tpm_buf buf;
> +	int rc;
>  
> -	rc = tpm2_start_policy_session(chip, &handle, pols->hash);
> +	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_RESTART);
> +	if (rc)
> +		return rc;
> +
> +	tpm_buf_append_u32(&buf, handle);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 0, "restarting policy");
> +	tpm_buf_destroy(&buf);
> +	if (rc) {
> +		pr_notice("TPM policy restart failed, rc=%d\n", rc);
> +		return -EPERM;
> +	}
> +	return 0;
> +}
> +
> +static int tpm2_policy_gettkhash(struct tpm_chip *chip, u8 *pubkey, u8 *nonce,
> +				 u8 *signature, int siglen,
> +				 u32 handle, u8 **hash, u8 **name,
> +				 u8 **ticket)
> +{
> +	struct tpm_buf buf;
> +	int rc;
> +	int len;
> +	u32 sigkey;
> +	off_t offset;
> +	SHASH_DESC_ON_STACK(sdesc, tpm2_get_policy_hash(chip));
> +
> +	sdesc->tfm = tpm2_get_policy_hash(chip);
> +
> +	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_POLICY_GET_DIGEST);
>  	if (rc)
>  		return rc;
>  
> -	for (i = 0; i < pols->count; i++) {
> -		u32 cmd = pols->code[i];
> +	tpm_buf_append_u32(&buf, handle);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 0, "getting policy hash");
> +	if (rc)
> +		goto out;
> +	len = get_unaligned_be16(&buf.data[TPM_HEADER_SIZE]) + 2;
> +	rc = -ENOMEM;
> +	*hash = kmalloc(len, GFP_KERNEL);
> +	if (!*hash)
> +		goto out;
> +	memcpy(*hash, &buf.data[TPM_HEADER_SIZE], len);
> +
> +	tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_LOAD_EXTERNAL);
> +
> +	/* empty sensitive */
> +	tpm_buf_append_u16(&buf, 0);
> +	/* TPM2B_PUBLIC */
> +	len = get_unaligned_be16(pubkey) + 2;
> +	tpm_buf_append(&buf, pubkey, len);
> +	/* hierarchy (use null because never a password) */
> +	tpm_buf_append_u32(&buf, TPM2_RH_OWNER);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 4, "loading external key");
> +	if (rc)
> +		goto out;
> +
> +	offset = TPM_HEADER_SIZE;
> +	sigkey = tpm_buf_read_u32(&buf, &offset);
> +	len = get_unaligned_be16(&buf.data[offset]) + 2;
> +	*name = kmalloc(len, GFP_KERNEL);
> +	if (!*name) {
> +		tpm2_flush_context(chip, sigkey);
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +	memcpy(*name, &buf.data[offset], len);
> +
> +	tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_VERIFY_SIGNATURE);
> +	/* handle of public key to verify with */
> +	tpm_buf_append_u32(&buf, sigkey);
> +	/* digest hash(policy||nonce) */
> +	len = get_unaligned_be16(*hash);
> +	tpm_buf_append_u16(&buf, len);
> +	/* now compute the signed data which is hash(policy||nonce) */
> +	crypto_shash_init(sdesc);
> +	len = get_unaligned_be16(*hash); /* better be the tfm hash size */
> +	crypto_shash_update(sdesc, *hash + 2, len);
> +	len = get_unaligned_be16(nonce);
> +	crypto_shash_update(sdesc, nonce + 2, len);
> +	crypto_shash_final(sdesc, &buf.data[buf.length]);
> +	buf.length += len;
> +	/* signature */
> +	tpm_buf_append(&buf, signature, siglen);
> +
> +	rc = tpm_transmit_cmd(chip, &buf, 4, "verifying signature");
> +	tpm2_flush_context(chip, sigkey);
> +	if (rc)
> +		goto out;
> +
> +	len = tpm_buf_length(&buf) - TPM_HEADER_SIZE;
> +	*ticket = kmalloc(len, GFP_KERNEL);
> +	if (!*ticket) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +	memcpy(*ticket, &buf.data[TPM_HEADER_SIZE], len);
> +
> + out:
> +	if (rc) {
> +		kfree(*hash);
> +		*hash = NULL;
> +		kfree(*name);
> +		*name = NULL;
> +	}
> +	tpm_buf_destroy(&buf);
> +	return rc;
> +}
> +
> +
> +static int tpm2_try_policy(struct tpm_chip *chip, struct tpm2_policies *pols,
> +			   u32 handle, int p)
> +{
> +	int i, rc;
> +	const char *failure;
> +
> +	for (i = 0; i < pols->count[p]; i++) {
> +		u32 cmd = pols->code[p][i];
>  		struct tpm_buf buf;
>  
>  		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd);
> @@ -311,10 +453,11 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
>  			 * policy command
>  			 */
>  			tpm_buf_append_u16(&buf, pols->hash_size);
> -			tpm_buf_append(&buf, pols->policies[i] + pols->len[i] -
> +			tpm_buf_append(&buf, pols->policies[p][i]
> +				       + pols->len[p][i] -
>  				       pols->hash_size, pols->hash_size);
> -			tpm_buf_append(&buf, pols->policies[i],
> -				       pols->len[i] - pols->hash_size);
> +			tpm_buf_append(&buf, pols->policies[p][i],
> +				       pols->len[p][i] - pols->hash_size);
>  			break;
>  
>  		case TPM2_CC_POLICY_COUNTER_TIMER: {
> @@ -324,26 +467,82 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
>  			 * respectively.  The rest is operandB which
>  			 * must be zero padded in a hash digest
>  			 */
> -			u16 opb_len = pols->len[i] - 4;
> +			u16 opb_len = pols->len[p][i] - 4;
>  
>  			if (opb_len > pols->hash_size)
>  				return -EINVAL;
>  
>  			tpm_buf_append_u16(&buf, opb_len);
> -			tpm_buf_append(&buf, pols->policies[i], opb_len);
> +			tpm_buf_append(&buf, pols->policies[p][i], opb_len);
>  
>  			/* offset and operand*/
> -			tpm_buf_append(&buf, pols->policies[i] + opb_len, 4);
> +			tpm_buf_append(&buf, pols->policies[p][i] + opb_len, 4);
>  			failure = "Counter Timer";
>  
>  			break;
>  		}
> +		case TPM2_CC_POLICY_AUTHORIZE: {
> +			u8 *pubkey = pols->policies[p][i];
> +			u8 *nonce;
> +			u8 *signature;
> +			u8 *ticket = NULL;
> +			u8 *hash = NULL;
> +			u8 *name = NULL;
> +			int len, siglen;
> +			const int maxlen = pols->len[p][i];
> +
> +			if (i == 0)
> +				/*
> +				 * If this is the first policy then skip
> +				 * because we should already have executed
> +				 * a successful PolicyAuthorize before getting
> +				 * here
> +				 */
> +				continue;
> +
> +			len = get_unaligned_be16(pubkey);
> +			if (len + 2 > maxlen) {
> +				failure = "malformed policy";
> +				break;
> +			}
> +			nonce = pubkey + len + 2;
> +			len = get_unaligned_be16(nonce);
> +			if (len + 2 > maxlen) {
> +				failure = "malformed policy";
> +				break;
> +			}
> +			signature = nonce + len + 2;
> +			siglen = pubkey + maxlen - signature;
> +			failure = "policy authorize";
> +			rc = tpm2_policy_gettkhash(chip, pubkey, nonce,
> +						   signature, siglen,
> +						   handle, &hash,
> +						   &name, &ticket);
> +			if (rc)
> +				break;
> +			len = get_unaligned_be16(hash);
> +			tpm_buf_append(&buf, hash, len + 2);
> +			kfree(hash);
> +
> +			len = get_unaligned_be16(nonce);
> +			tpm_buf_append(&buf, nonce, len + 2);
> +
> +			len = get_unaligned_be16(name);
> +			tpm_buf_append(&buf, name, len + 2);
> +			kfree(name);
> +
> +			len = get_unaligned_be16(ticket + 6) + 8;
> +			tpm_buf_append(&buf, ticket, len);
> +			kfree(ticket);
> +
> +			break;
> +		}
>  
>  		default:
>  			failure = "unknown policy";
> -			if (pols->len[i])
> -				tpm_buf_append(&buf, pols->policies[i],
> -					       pols->len[i]);
> +			if (pols->len[p][i])
> +				tpm_buf_append(&buf, pols->policies[p][i],
> +					       pols->len[p][i]);
>  
>  			break;
>  		}
> @@ -353,14 +552,62 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
>  		if (rc) {
>  			pr_notice("TPM policy %s failed, rc=%d\n",
>  				  failure, rc);
> -			tpm2_end_auth_session(chip);
> -			return -EPERM;
> +			return rc;
>  		}
>  	}
> -
>  	return 0;
>  }
>  
> +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
> +{
> +	int rc;
> +	u32 handle;
> +
> +	if (pols->code[0][0] == TPM2_CC_POLICY_AUTHORIZE &&
> +	    pols->auths == 1) {
> +		pr_notice("TPM Key requires signed policies but has none\n");
> +		return -EINVAL;
> +	}
> +
> +	rc = tpm2_start_policy_session(chip, &handle, pols->hash);
> +	if (rc)
> +		return rc;
> +
> +	if (pols->code[0][0] == TPM2_CC_POLICY_AUTHORIZE) {
> +		int p;
> +
> +		for (p = 1; p < pols->auths; p++) {
> +			if (p != 1) {
> +				/* restart policy if looping */
> +				rc = tpm2_policy_restart(chip, handle);
> +				if (rc)
> +					goto out;
> +			}
> +
> +			rc = tpm2_try_policy(chip, pols, handle, p);
> +			if (rc) {
> +				pr_notice("TPM signed policy %d failed\n", p);
> +			} else {
> +				pr_notice("TPM signed policy %d succeeded\n",
> +					  p);
> +				break;
> +			}
> +		}
> +		if (rc)
> +			/* no signed policies succeeded */
> +			goto out;
> +	}
> +
> +	rc = tpm2_try_policy(chip, pols, handle, 0);
> + out:
> +	if (rc) {
> +		rc = -EPERM;
> +		tpm2_end_auth_session(chip);
> +	}
> +
> +	return rc;
> +}
> +
>  int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
>  {
>  	struct tpm2_policies *pols;
> @@ -379,22 +626,22 @@ int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
>  		if (*p == '\0' || *p == '\n')
>  			continue;
>  
> -		pols->len[i] = strlen(p)/2;
> -		if (pols->len[i] > left) {
> +		pols->len[0][i] = strlen(p)/2;
> +		if (pols->len[0][i] > left) {
>  			res = -E2BIG;
>  			goto err;
>  		}
>  
> -		res = hex2bin(ptr, p, pols->len[i]);
> +		res = hex2bin(ptr, p, pols->len[0][i]);
>  		if (res)
>  			goto err;
>  
>  		/* get command code and skip past */
> -		pols->code[i] = get_unaligned_be32(ptr);
> -		pols->policies[i] = ptr + 4;
> -		ptr += pols->len[i];
> -		left -= pols->len[i];
> -		pols->len[i] -= 4;
> +		pols->code[0][i] = get_unaligned_be32(ptr);
> +		pols->policies[0][i] = ptr + 4;
> +		ptr += pols->len[0][i];
> +		left -= pols->len[0][i];
> +		pols->len[0][i] -= 4;
>  
>  		/*
>  		 * FIXME: this does leave the code embedded in dead
> @@ -404,7 +651,7 @@ int tpm2_parse_policies(struct tpm2_policies **ppols, char *str)
>  		i++;
>  	}
>  
> -	pols->count = i;
> +	pols->count[0] = i;
>  	*ppols = pols;
>  
>  	return 0;
> diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h
> index 8ddf235b3fec..da0ab99078b8 100644
> --- a/security/keys/trusted-keys/tpm2-policy.h
> +++ b/security/keys/trusted-keys/tpm2-policy.h
> @@ -6,17 +6,20 @@ struct tpm2_key_context {
>  	u32 pub_len;
>  	const u8 *priv;
>  	u32 priv_len;
> -	const u8 *policies[TPM2_MAX_POLICIES];
> -	u32 policy_code[TPM2_MAX_POLICIES];
> -	u16 policy_len[TPM2_MAX_POLICIES];
> -	u8 policy_count;
> +	const u8 *policies[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u32 policy_code[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u16 policy_len[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u8 policy_count[TPM2_MAX_AUTHS];
> +	u8 auth_policies;
> +	int policy_tot_len;
>  };
>  
>  struct tpm2_policies {
> -	u32 code[TPM2_MAX_POLICIES];
> -	u8 *policies[TPM2_MAX_POLICIES];
> -	u16 len[TPM2_MAX_POLICIES];
> -	u8 count;
> +	u32 code[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u8 *policies[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u16 len[TPM2_MAX_AUTHS][TPM2_MAX_POLICIES];
> +	u8 count[TPM2_MAX_AUTHS];
> +	u8 auths;
>  	int hash;		/* crypto not TPM hash algorithm */
>  	u16 hash_size;
>  };
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index 1684bd8f725e..c5a68b3e354f 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -5,12 +5,13 @@
>  ---       However, the Linux asn.1 parser doesn't understand
>  ---       [2] EXPLICIT SEQUENCE OF OPTIONAL
>  ---       So there's an extra intermediate TPMPolicySequence
> ----       definition to work around this
> +---       and TPMAuthPolicySequence definitions to work around this
>  
>  TPMKey ::= SEQUENCE {
>  	type		OBJECT IDENTIFIER ({tpm2_key_type}),
>  	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
> -	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL,
> +	policy		[1] EXPLICIT TPMPolicySequence OPTIONAL ({tpm2_pol_seq}),
> +	authPolicy	[3] EXPLICIT TPMAuthPolicySequence OPTIONAL,
>  	parent		INTEGER ({tpm2_key_parent}),
>  	pubkey		OCTET STRING ({tpm2_key_pub}),
>  	privkey		OCTET STRING ({tpm2_key_priv})
> @@ -22,3 +23,10 @@ TPMPolicy ::= SEQUENCE {
>  	commandCode		[0] EXPLICIT INTEGER ({tpm2_key_code}),
>  	commandPolicy		[1] EXPLICIT OCTET STRING ({tpm2_key_policy})
>  	}
> +
> +TPMAuthPolicySequence ::= SEQUENCE OF TPMAuthPolicy ({tpm2_pol_seq})
> +
> +TPMAuthPolicy ::= SEQUENCE {
> +	name		[0] EXPLICIT UTF8String OPTIONAL,
> +	policy		[1] EXPLICIT TPMPolicySequence
> +	}
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 64c922bbc36c..e2d937e44274 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -98,38 +98,45 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
>  			   u8 **buf)
>  {
>  	int ret;
> -	struct tpm2_key_context ctx;
> +	struct tpm2_key_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
>  	u8 *blob;
>  
> -	memset(&ctx, 0, sizeof(ctx));
> +	if (!ctx)
> +		return -ENOMEM;
>  
> -	ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
> +	ret = asn1_ber_decoder(&tpm2key_decoder, ctx, payload->blob,
>  			       payload->blob_len);
>  	if (ret < 0)
> -		return ret;
> +		goto out;
>  
> -	if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> -		return -EINVAL;
> +	if (ctx->priv_len + ctx->pub_len > MAX_BLOB_SIZE) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
>  
> -	blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
> -	if (!blob)
> -		return -ENOMEM;
> +	blob = kmalloc(ctx->priv_len + ctx->pub_len + 4, GFP_KERNEL);
> +	if (!blob) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
>  
> -	ret = tpm2_key_policy_process(&ctx, payload);
> +	ret = tpm2_key_policy_process(ctx, payload);
>  	if (ret) {
>  		kfree(blob);
> -		return ret;
> +		goto out;
>  	}
>  
>  	*buf = blob;
> -	options->keyhandle = ctx.parent;
> +	options->keyhandle = ctx->parent;
>  
> -	memcpy(blob, ctx.priv, ctx.priv_len);
> -	blob += ctx.priv_len;
> +	memcpy(blob, ctx->priv, ctx->priv_len);
> +	blob += ctx->priv_len;
>  
> -	memcpy(blob, ctx.pub, ctx.pub_len);
> +	memcpy(blob, ctx->pub, ctx->pub_len);
>  
> -	return 0;
> + out:
> +	kfree(ctx);
> +	return ret;
>  }
>  
>  int tpm2_key_parent(void *context, size_t hdrlen,
> @@ -264,7 +271,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		pols = kmalloc(POLICY_SIZE, GFP_KERNEL);
>  		if (!pols)
>  			return -ENOMEM;
> -		pols->count = 0;
> +		pols->count[0] = 0;
> +		pols->auths = 1;
>  		payload->policies = pols;
>  		scratch = (u8 *)(pols + 1);
>  	}
> @@ -275,10 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		/* 4 array len */
>  		const int len = 4 + options->pcrinfo_len;
>  
> -		i = pols->count++;
> -		pols->len[i] = len;
> -		pols->policies[i] = scratch;
> -		pols->code[i] = TPM2_CC_POLICY_PCR;
> +		i = pols->count[0]++;
> +		pols->len[0][i] = len;
> +		pols->policies[0][i] = scratch;
> +		pols->code[0][i] = TPM2_CC_POLICY_PCR;
>  
>  		/* only a single TPMS_PCR_SELECTION */
>  		put_unaligned_be32(1, &scratch[0]);
> @@ -300,11 +308,11 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		int i;
>  
>  		pols = payload->policies;
> -		i = pols->count++;
> +		i = pols->count[0]++;
>  
>  		/* the TPM2_PolicyPassword command has no payload */
> -		pols->len[i] = 0;
> -		pols->code[i] = TPM2_CC_POLICY_AUTHVALUE;
> +		pols->len[0][i] = 0;
> +		pols->code[0][i] = TPM2_CC_POLICY_AUTHVALUE;
>  	}
>  
>  	if (payload->policies) {
> @@ -685,6 +693,13 @@ int __weak tpm2_key_code(void *context, size_t hdrlen,
>  	return -EINVAL;
>  }
>  
> +int __weak tpm2_pol_seq(void *context, size_t hdrlen,
> +			unsigned char tag,
> +			const void *value, size_t vlen)
> +{
> +	return -EINVAL;
> +}
> +
>  int __weak tpm2_key_policy(void *context, size_t hdrlen,
>  		  unsigned char tag,
>  		  const void *value, size_t vlen)

BR, Jarkko





[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux