linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Evan Green <evgreen@chromium.org>
Cc: linux-kernel@vger.kernel.org, gwendal@chromium.org,
	Eric Biggers <ebiggers@kernel.org>,
	Matthew Garrett <mgarrett@aurora.tech>,
	jarkko@kernel.org, zohar@linux.ibm.com,
	linux-integrity@vger.kernel.org, Pavel Machek <pavel@ucw.cz>,
	apronin@chromium.org, dlunev@google.com, rjw@rjwysocki.net,
	linux-pm@vger.kernel.org, corbet@lwn.net, jejb@linux.ibm.com,
	David Howells <dhowells@redhat.com>,
	James Morris <jmorris@namei.org>,
	Paul Moore <paul@paul-moore.com>,
	"Serge E. Hallyn" <serge@hallyn.com>,
	keyrings@vger.kernel.org, linux-security-module@vger.kernel.org
Subject: Re: [PATCH v2 03/10] security: keys: trusted: Include TPM2 creation data
Date: Tue, 20 Sep 2022 16:04:25 -0700	[thread overview]
Message-ID: <202209201552.DF8C511D23@keescook> (raw)
In-Reply-To: <20220823152108.v2.3.Ieb1215f598bc9df56b0e29e5977eae4fcca25e15@changeid>

On Tue, Aug 23, 2022 at 03:25:19PM -0700, Evan Green wrote:
> In addition to the private key and public key, the TPM2_Create
> command may also return creation data, a creation hash, and a creation
> ticket. These fields allow the TPM to attest to the contents of a
> specified set of PCRs at the time the trusted key was created. Encrypted
> hibernation will use this to ensure that PCRs settable only by the
> kernel were set properly at the time of creation, indicating this is an
> authentic hibernate key.
> 
> Encode these additional parameters into the ASN.1 created to represent
> the key blob. The new fields are made optional so that they don't bloat
> key blobs which don't need them, and to ensure interoperability with
> old blobs.
> 
> ---
> 
> (no changes since v1)
> 
> This is a replacement for Matthew's original patch here:
> https://patchwork.kernel.org/patch/12096489/
> 
> That patch was written before the exported key format was switched to
> ASN.1. This patch accomplishes the same thing (saving, loading, and
> getting pointers to the creation data) while utilizing the new ASN.1
> format.

This part (between your S-o-b and the "---") should got below the "---"
after your S-o-b, otherwise tooling will include it in the commit log
(or lose your S-o-b).

> 
> Signed-off-by: Evan Green <evgreen@chromium.org>
> ---
>  include/keys/trusted-type.h               |   8 +
>  security/keys/trusted-keys/tpm2key.asn1   |   5 +-
>  security/keys/trusted-keys/trusted_tpm2.c | 202 +++++++++++++++++++---
>  3 files changed, 190 insertions(+), 25 deletions(-)
> 
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 4eb64548a74f1a..209086fed240a5 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -22,15 +22,23 @@
>  #define MAX_BLOB_SIZE			512
>  #define MAX_PCRINFO_SIZE		64
>  #define MAX_DIGEST_SIZE			64
> +#define MAX_CREATION_DATA		412
> +#define MAX_TK				76
>  
>  struct trusted_key_payload {
>  	struct rcu_head rcu;
>  	unsigned int key_len;
>  	unsigned int blob_len;
> +	unsigned int creation_len;
> +	unsigned int creation_hash_len;
> +	unsigned int tk_len;
>  	unsigned char migratable;
>  	unsigned char old_format;
>  	unsigned char key[MAX_KEY_SIZE + 1];
>  	unsigned char blob[MAX_BLOB_SIZE];
> +	unsigned char *creation;
> +	unsigned char *creation_hash;
> +	unsigned char *tk;
>  };
>  
>  struct trusted_key_options {
> diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
> index f57f869ad60068..1bfbf290e523a3 100644
> --- a/security/keys/trusted-keys/tpm2key.asn1
> +++ b/security/keys/trusted-keys/tpm2key.asn1
> @@ -7,5 +7,8 @@ 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}),
> +	creationData	[1] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_data}),
> +	creationHash	[2] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_hash}),
> +	creationTk	[3] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_creation_tk})
>  	}

Maybe include a link (or named reference) to these fields from the TPM
spec?

> [...]
> @@ -46,6 +49,26 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
>  
>  	pub_len = get_unaligned_be16(src) + 2;
>  	pub = src;
> +	src += pub_len;
> +
> +	creation_data_len = get_unaligned_be16(src);
> +	if (creation_data_len) {
> +		creation_data_len += 2;
> +		creation_data = src;
> +		src += creation_data_len;
> +
> +		creation_hash_len = get_unaligned_be16(src) + 2;
> +		creation_hash = src;
> +		src += creation_hash_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.
> +		 */
> +		creation_tk_len = get_unaligned_be16(src + 6) + 8;
> +		creation_tk = src;
> +	}
>  
>  	if (!scratch)
>  		return -ENOMEM;

I don't see anything in this code (even before your patch) actually
checking length against the "len" argument to tpm2_key_encode(). I think
that needs to be fixed so proper bounds checking can be done here.
Otherwise how do we know if we're running off the end of "src"?

Yes, I realize if we have a malicious TPM everything goes out the
window, but TPMs don't always behave -- this code should likely be more
defensive. Or, I've misunderstood where "src" is coming from.
Regardless, my question stands: what is checking "len"?

> @@ -63,26 +86,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;
> +	}
>  
>  	work = asn1_encode_integer(work, end_work, options->keyhandle);
>  	work = asn1_encode_octet_string(work, end_work, pub, pub_len);
>  	work = asn1_encode_octet_string(work, end_work, priv, priv_len);
> +	if (creation_data_len) {
> +		u8 *scratch2 = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> +		u8 *work2;
> +		u8 *end_work2 = scratch2 + SCRATCH_SIZE;
> +
> +		if (!scratch2) {
> +			rc = -ENOMEM;
> +			goto err;
> +		}
> +
> +		work2 = asn1_encode_octet_string(scratch2,
> +						 end_work2,
> +						 creation_data,
> +						 creation_data_len);
> +
> +		work = asn1_encode_tag(work,
> +				       end_work,
> +				       1,
> +				       scratch2,
> +				       work2 - scratch2);
> +
> +		work2 = asn1_encode_octet_string(scratch2,
> +						 end_work2,
> +						 creation_hash,
> +						 creation_hash_len);
> +
> +		work = asn1_encode_tag(work,
> +				       end_work,
> +				       2,
> +				       scratch2,
> +				       work2 - scratch2);
> +
> +		work2 = asn1_encode_octet_string(scratch2,
> +						 end_work2,
> +						 creation_tk,
> +						 creation_tk_len);
> +
> +		work = asn1_encode_tag(work,
> +				       end_work,
> +				       3,
> +				       scratch2,
> +				       work2 - scratch2);
> +
> +		kfree(scratch2);
> +	}
>  
>  	work1 = payload->blob;
>  	work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob),
>  				     scratch, work - scratch);
> -	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;

I find the addition of the word "BUG" in a WARN() to be confusing. :) I
realize this is just copying the existing style, though.

> +	}
>  
>  	return work1 - payload->blob;
> +err:
> +	kfree(scratch);
> +	return rc;
>  }
>  
>  struct tpm2_key_context {
> @@ -91,15 +169,21 @@ struct tpm2_key_context {
>  	u32 pub_len;
>  	const u8 *priv;
>  	u32 priv_len;
> +	const u8 *creation_data;
> +	u32 creation_data_len;
> +	const u8 *creation_hash;
> +	u32 creation_hash_len;
> +	const u8 *creation_tk;
> +	u32 creation_tk_len;
>  };
>  
>  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 +192,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;
> +
> +	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;
>  
> -	*buf = blob;
> +	blob = buf;
>  	options->keyhandle = ctx.parent;
>  
>  	memcpy(blob, ctx.priv, ctx.priv_len);
>  	blob += ctx.priv_len;
>  
>  	memcpy(blob, ctx.pub, ctx.pub_len);
> +	blob += ctx.pub_len;
> +	if (ctx.creation_data_len) {
> +		memcpy(blob, ctx.creation_data, ctx.creation_data_len);
> +		blob += ctx.creation_data_len;
> +	}
> +
> +	if (ctx.creation_hash_len) {
> +		memcpy(blob, ctx.creation_hash, ctx.creation_hash_len);
> +		blob += ctx.creation_hash_len;
> +	}
>  
> +	if (ctx.creation_tk_len) {
> +		memcpy(blob, ctx.creation_tk, ctx.creation_tk_len);
> +		blob += ctx.creation_tk_len;
> +	}
> +
> +	/*
> +	 * Copy the buffer back into the payload blob since the creation
> +	 * info will be used after loading.
> +	 */
> +	payload->blob_len = blob - buf;
> +	memcpy(payload->blob, buf, payload->blob_len);
> +	if (ctx.creation_data_len) {
> +		payload->creation = payload->blob + ctx.priv_len + ctx.pub_len;
> +		payload->creation_len = ctx.creation_data_len;
> +		payload->creation_hash = payload->creation + ctx.creation_data_len;
> +		payload->creation_hash_len = ctx.creation_hash_len;
> +		payload->tk = payload->creation_hash +
> +			      payload->creation_hash_len;
> +
> +		payload->tk_len = ctx.creation_tk_len;
> +	}
> +
> +	kfree(buf);
>  	return 0;
>  }
>  
> @@ -185,6 +305,42 @@ int tpm2_key_priv(void *context, size_t hdrlen,
>  	return 0;
>  }
>  
> +int tpm2_key_creation_data(void *context, size_t hdrlen,
> +			   unsigned char tag,
> +			   const void *value, size_t vlen)
> +{
> +	struct tpm2_key_context *ctx = context;
> +
> +	ctx->creation_data = value;
> +	ctx->creation_data_len = vlen;
> +
> +	return 0;
> +}

What is hdrlen here? Or rather, what kinds of bounds checking is needed
here?

> +
> +int tpm2_key_creation_hash(void *context, size_t hdrlen,
> +			   unsigned char tag,
> +			   const void *value, size_t vlen)
> +{
> +	struct tpm2_key_context *ctx = context;
> +
> +	ctx->creation_hash = value;
> +	ctx->creation_hash_len = vlen;
> +
> +	return 0;
> +}
> +
> +int tpm2_key_creation_tk(void *context, size_t hdrlen,
> +			 unsigned char tag,
> +			 const void *value, size_t vlen)
> +{
> +	struct tpm2_key_context *ctx = context;
> +
> +	ctx->creation_tk = value;
> +	ctx->creation_tk_len = vlen;
> +
> +	return 0;
> +}
> +
>  /**
>   * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
>   *
> @@ -229,6 +385,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 +474,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);
>  
>  out:
> @@ -370,13 +528,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  	int rc;
>  	u32 attrs;
>  
> -	rc = tpm2_key_decode(payload, options, &blob);
> -	if (rc) {
> -		/* old form */
> -		blob = payload->blob;
> +	rc = tpm2_key_decode(payload, options);
> +	if (rc)
>  		payload->old_format = 1;
> -	}
>  
> +	blob = payload->blob;
>  	/* new format carries keyhandle but old format doesn't */
>  	if (!options->keyhandle)
>  		return -EINVAL;
> @@ -433,8 +589,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  			(__be32 *) &buf.data[TPM_HEADER_SIZE]);
>  
>  out:
> -	if (blob != payload->blob)
> -		kfree(blob);
>  	tpm_buf_destroy(&buf);
>  
>  	if (rc > 0)
> -- 
> 2.31.0
> 

Otherwise looks good!

-Kees

-- 
Kees Cook

  reply	other threads:[~2022-09-20 23:04 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-23 22:25 [PATCH v2 00/10] Encrypted Hibernation Evan Green
2022-08-23 22:25 ` [PATCH v2 01/10] tpm: Add support for in-kernel resetting of PCRs Evan Green
2022-08-26  2:59   ` Jarkko Sakkinen
2022-09-07 17:02     ` Evan Green
2022-09-08  5:22       ` Jarkko Sakkinen
2022-08-23 22:25 ` [PATCH v2 02/10] tpm: Allow PCR 23 to be restricted to kernel-only use Evan Green
2022-08-26  3:02   ` Jarkko Sakkinen
2022-09-07 17:03     ` Evan Green
2022-09-13 12:26   ` Stefan Berger
2022-09-20  4:50     ` Jarkko Sakkinen
2022-09-21 15:35       ` Evan Green
2022-09-21 18:02         ` Jarkko Sakkinen
2022-09-21 18:05           ` Jarkko Sakkinen
2022-09-21 19:02             ` Evan Green
2022-08-23 22:25 ` [PATCH v2 03/10] security: keys: trusted: Include TPM2 creation data Evan Green
2022-09-20 23:04   ` Kees Cook [this message]
2022-09-23 22:22     ` Evan Green
2022-08-23 22:25 ` [PATCH v2 04/10] security: keys: trusted: Allow storage of PCR values in " Evan Green
2022-08-24 11:56   ` Ben Boeckel
2022-08-24 17:34     ` Evan Green
2022-08-23 22:25 ` [PATCH v2 05/10] security: keys: trusted: Verify " Evan Green
2022-09-20 23:06   ` Kees Cook
2022-09-23 22:23     ` Evan Green
2022-08-23 22:25 ` [PATCH v2 06/10] PM: hibernate: Add kernel-based encryption Evan Green
2022-09-20 23:09   ` Kees Cook
2022-08-23 22:25 ` [PATCH v2 07/10] PM: hibernate: Use TPM-backed keys to encrypt image Evan Green
2022-09-20 23:16   ` Kees Cook
2022-09-23 22:23     ` Evan Green
2022-09-24  4:31       ` Kees Cook
2022-08-23 22:25 ` [PATCH v2 08/10] PM: hibernate: Mix user key in encrypted hibernate Evan Green
2022-08-23 22:25 ` [PATCH v2 09/10] PM: hibernate: Verify the digest encryption key Evan Green
2022-08-23 22:25 ` [PATCH v2 10/10] PM: hibernate: seal the encryption key with a PCR policy Evan Green
2022-09-20 23:24   ` Kees Cook
2022-08-31 18:34 ` [PATCH v2 00/10] Encrypted Hibernation Limonciello, Mario
2022-09-07 17:03   ` Evan Green
2022-09-20  8:46 ` Pavel Machek
2022-09-20 16:39   ` Evan Green
2022-09-21 18:09   ` Jason Gunthorpe
2022-09-20 22:52 ` Kees Cook

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=202209201552.DF8C511D23@keescook \
    --to=keescook@chromium.org \
    --cc=apronin@chromium.org \
    --cc=corbet@lwn.net \
    --cc=dhowells@redhat.com \
    --cc=dlunev@google.com \
    --cc=ebiggers@kernel.org \
    --cc=evgreen@chromium.org \
    --cc=gwendal@chromium.org \
    --cc=jarkko@kernel.org \
    --cc=jejb@linux.ibm.com \
    --cc=jmorris@namei.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mgarrett@aurora.tech \
    --cc=paul@paul-moore.com \
    --cc=pavel@ucw.cz \
    --cc=rjw@rjwysocki.net \
    --cc=serge@hallyn.com \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).