[PATCH v5 04/11] security: keys: trusted: Include TPM2 creation data
Eric Biggers
ebiggers at kernel.org
Sun Nov 13 21:20:06 UTC 2022
On Fri, Nov 11, 2022 at 03:16:29PM -0800, Evan Green wrote:
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad60068..608f8d9ca95fa8 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -7,5 +7,18 @@ TPMKey ::= SEQUENCE {
> emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
> parent INTEGER ({tpm2_key_parent}),
> pubkey OCTET STRING ({tpm2_key_pub}),
> - privkey OCTET STRING ({tpm2_key_priv})
> + privkey OCTET STRING ({tpm2_key_priv}),
> + ---
> + --- A TPM2B_CREATION_DATA struct as returned from the TPM2_Create command.
> + ---
> + creationData [1] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_data}),
> + ---
> + --- A TPM2B_DIGEST of the creationHash as returned from the TPM2_Create
> + --- command.
> + ---
> + creationHash [2] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_hash}),
> + ---
> + --- A TPMT_TK_CREATION ticket as returned from the TPM2_Create command.
> + ---
> + creationTk [3] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_tk})
> }
The commit that added this file claimed:
"The benefit of the ASN.1 format is that it's a standard and thus the
exported key can be used by userspace tools (openssl_tpm2_engine,
openconnect and tpm2-tss-engine"
Are these new fields in compliance with whatever standard that was referring to?
Or was that just referring to ASN.1 itself?
> +/* Helper function to advance past a __be16 length + buffer safely */
> +static const u8 *get_sized_section(const u8 *src, const u8 *end, u16 *len)
> +{
> + u32 length;
> +
> + if (src + sizeof(u16) > end)
> + return NULL;
'end - src < sizeof(u16)', so the pointer isn't advanced past the end.
> +
> + /* Include the size field in the returned section length. */
> + length = get_unaligned_be16(src) + sizeof(u16);
> + *len = length;
> + if (*len != length)
> + return NULL;
> +
> + src += *len;
> + if (src > end)
> + return NULL;
> +
> + return src;
Similarly:
if (end - src < *len)
return NULL;
return src + *len;
> + /*
> + * The creation ticket (TPMT_TK_CREATION) consists of a 2 byte
> + * tag, 4 byte handle, and then a TPM2B_DIGEST, which is a 2
> + * byte length followed by data.
> + */
> + if (src + 8 > end)
end - src < 8
And actually it really should be 6 instead of 8, to match the code below.
get_sized_section() already validates that there are at least 2 more bytes.
> + return -EINVAL;
> +
> + creation_tk = src;
> + src = get_sized_section(src + 6, end, &creation_tk_len);
> + if (!src)
> + return -EINVAL;
> +
> + creation_tk_len += 6;
> +
> + } else {
> + creation_data_len = 0;
> + creation_data = NULL;
> + }
>
> if (!scratch)
> return -ENOMEM;
> @@ -63,26 +125,81 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> }
>
> /*
> - * Assume both octet strings will encode to a 2 byte definite length
> + * Assume each octet string will encode to a 2 byte definite length.
> + * Each optional octet string consumes one extra byte.
> *
> - * Note: For a well behaved TPM, this warning should never
> - * trigger, so if it does there's something nefarious going on
> + * Note: For a well behaved TPM, this warning should never trigger, so
> + * if it does there's something nefarious going on
> */
> - if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
> - "BUG: scratch buffer is too small"))
> - return -EINVAL;
> + if (WARN(work - scratch + pub_len + priv_len + creation_data_len +
> + creation_hash_len + creation_tk_len + (7 * 5) + 3 >
> + SCRATCH_SIZE,
> + "BUG: scratch buffer is too small")) {
> + rc = -EINVAL;
> + goto err;
> + }
This appears to be fixing a memory leak in the error case.
The same memory leak also still appears above in:
if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
return PTR_ERR(w);
Maybe both should be fixed in a separate patch.
> + work2 = asn1_encode_octet_string(scratch2,
> + end_work2,
> + creation_data,
> + creation_data_len);
> +
> + work = asn1_encode_tag(work,
> + end_work,
> + 1,
> + scratch2,
> + work2 - scratch2);
There's no helper function to do these two steps together?
> +
> - if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
> - return PTR_ERR(work1);
> + if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) {
> + rc = PTR_ERR(work1);
> + goto err;
> + }
>
> return work1 - payload->blob;
> +err:
> + kfree(scratch);
> + return rc;
Is this another memory leak fix that is unrelated to the functionality added by
this patch?
Also, isn't 'scratch' still being leaked in the success case?
> static int tpm2_key_decode(struct trusted_key_payload *payload,
> - struct trusted_key_options *options,
> - u8 **buf)
> + struct trusted_key_options *options)
> {
> + u64 data_len;
> int ret;
> struct tpm2_key_context ctx;
> - u8 *blob;
> + u8 *blob, *buf;
>
> memset(&ctx, 0, sizeof(ctx));
>
> @@ -108,21 +231,57 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> if (ret < 0)
> return ret;
>
> - if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
> + data_len = ctx.priv_len + ctx.pub_len + ctx.creation_data_len +
> + ctx.creation_hash_len + ctx.creation_tk_len;
It's unclear why 'data_len' is a u64, given that the value assigned to it always
fits in a u32. Perhaps you intended to do the additions with 64-bit numbers so
that they can't overflow.
But shouldn't the lengths already be bounded by size of the ASN.1 blob before
even reaching here, anyway?
> +
> + if (data_len > MAX_BLOB_SIZE)
> return -EINVAL;
>
> - blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
> - if (!blob)
> + buf = kmalloc(data_len + 4, GFP_KERNEL);
> + if (!buf)
> return -ENOMEM;
It's unclear what the '+ 4' is for.
> @@ -229,6 +424,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> struct trusted_key_options *options)
> {
> int blob_len = 0;
> + unsigned int offset;
> struct tpm_buf buf;
> u32 hash;
> u32 flags;
> @@ -317,13 +513,14 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> rc = -E2BIG;
> goto out;
> }
> - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
> + offset = TPM_HEADER_SIZE + 4;
> + if (tpm_buf_length(&buf) < offset + blob_len) {
> rc = -EFAULT;
> goto out;
> }
>
> blob_len = tpm2_key_encode(payload, options,
> - &buf.data[TPM_HEADER_SIZE + 4],
> + &buf.data[offset],
> blob_len);
This hunk of the patch doesn't seem to serve any purpose.
- Eric
More information about the Linux-security-module-archive
mailing list