linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.ibm.com>
To: Patrick Uiterwijk <patrick@puiterwijk.org>,
	peterhuewe@gmx.de, jarkko@kernel.org, jgg@ziepe.ca,
	zohar@linux.ibm.com, dmitry.kasatkin@gmail.com,
	linux-integrity@vger.kernel.org
Cc: pbrobinson@gmail.com, kgold@linux.ibm.com
Subject: Re: [PATCH 1/3] tpm: Add support for reading a TPM NV Index
Date: Thu, 25 Feb 2021 16:50:13 -0500	[thread overview]
Message-ID: <cf2339c2-2fed-2cc0-d215-658606abf146@linux.ibm.com> (raw)
In-Reply-To: <20210225203229.363302-2-patrick@puiterwijk.org>

On 2/25/21 3:32 PM, Patrick Uiterwijk wrote:
> Add support to read contents from a TPM2 Non-Volatile Index location,
> allowing the kernel to retrieve contents and attributes of NV indexes.
>
> Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
> ---
>   drivers/char/tpm/tpm-interface.c |  30 ++++++
>   drivers/char/tpm/tpm.h           |   5 +
>   drivers/char/tpm/tpm2-cmd.c      | 163 +++++++++++++++++++++++++++++++
>   include/linux/tpm.h              |  65 ++++++++++++
>   4 files changed, 263 insertions(+)
>
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index 1621ce818705..9d81c11181d4 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -342,6 +342,36 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>   }
>   EXPORT_SYMBOL_GPL(tpm_pcr_extend);
>   
> +/**
> + * tpm_nv_read - Read an NV Index from the TPM
> + * @chip:	A &struct tpm_chip instance, %NULL for the default chip
> + * @nv_idx:	The NV Index to be retrieved
> + * @attr_out:	A place to store returned attributes if a TPM 2 was used
> + * @out:	A pointer where to store the return buffer
> + *
> + * Return: number of bytes read or a negative error value
> + */
> +int tpm_nv_read(struct tpm_chip *chip, u32 nv_idx, u32 *attr_out, void **out)
> +{
> +	int rc;
> +
> +	chip = tpm_find_get_ops(chip);
> +	if (!chip)
> +		return -ENODEV;
> +
> +	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
> +		rc = tpm2_nv_read(chip, nv_idx, attr_out, out);
> +		goto out;
> +	}
> +
> +	rc = -ENODEV;
> +
> +out:
> +	tpm_put_ops(chip);
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(tpm_nv_read);
> +
>   /**
>    * tpm_send - send a TPM command
>    * @chip:	a &struct tpm_chip instance, %NULL for the default chip
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index 947d1db0a5cc..d4dfc5148adb 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -56,9 +56,12 @@ enum tpm_addr {
>   #define TPM_ERR_DEACTIVATED     0x6
>   #define TPM_ERR_DISABLED        0x7
>   #define TPM_ERR_INVALID_POSTINIT 38
> +#define TPM_ERR_INVALID_HANDLE_1 0x18b
>   
>   #define TPM_TAG_RQU_COMMAND 193
>   
> +#define TPM2_HR_NV_INDEX 0x1000000
> +
>   /* TPM2 specific constants. */
>   #define TPM2_SPACE_BUFFER_SIZE		16384 /* 16 kB */
>   
> @@ -224,6 +227,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
>   ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
>   			u32 *value, const char *desc);
>   
> +int tpm2_nv_read(struct tpm_chip *chip, u32 nvindex, u32 *attr_out, void **dest);
> +int tpm2_nv_readpublic(struct tpm_chip *chip, u32 nvindex, struct tpm2_nv_public *info);
>   ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
>   int tpm2_auto_startup(struct tpm_chip *chip);
>   void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index eff1f12d981a..ba1026123464 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -269,6 +269,169 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>   	return rc;
>   }
>   
> +struct tpm2_buffer_out {
> +	__be16	size;
> +	u8	data[];
> +} __packed;
> +
> +struct tpm2_nv_public_out {
> +	__be32	nvIndex;
> +	__be16	nameAlg;
> +	__be32	attributes;
> +	__be16	authPolicySize;
> +	u8	data[];
> +} __packed;
> +
> +int tpm2_nv_readpublic(struct tpm_chip *chip, u32 nvindex, struct tpm2_nv_public *info)
> +{
> +	struct tpm_buf buf;
> +	int rc;
> +	u16 recd;
> +	u32 resp_header_length;
> +	struct tpm2_buffer_out *out;
> +	struct tpm2_nv_public_out *publicout;
> +	u32 nvhandle;
> +	u16 auth_policy_size;
> +
> +	if ((nvindex & ~TPM2_HR_NV_INDEX) > 0x00FFFFFF)
> +		return -EINVAL;
> +
> +	/* HR_NV_INDEX = TPM_HT_NV_INDEX << HR_SHIFT */
> +	nvhandle = TPM2_HR_NV_INDEX | nvindex;
> +
> +	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_NV_READPUBLIC);
> +	if (rc)
> +		return rc;
> +	tpm_buf_append_u32(&buf, nvhandle);
> +	rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> +	if (rc) {
> +		if (rc != TPM_ERR_DISABLED && rc != TPM_ERR_DEACTIVATED

These are TPM 1.2 error codes. I don't think TPM 2 returns those, does 
it? Ah, you took that from tpm_transmit_cmd... I think you can remove those.


> +		    && rc != TPM2_RC_TESTING && rc != TPM_ERR_INVALID_HANDLE_1)
> +			dev_err(&chip->dev, "A TPM error (%d) occurred attempting to read an NV Index public\n", rc);
> +		if (rc == TPM_ERR_INVALID_HANDLE_1)
> +			rc = -ENOENT;
> +		else if (rc > 0)
> +			rc = -EIO;
> +		goto out;
> +	}
> +	resp_header_length = tpm_buf_response_header_length(&buf, 0);
> +	out = (struct tpm2_buffer_out *)&buf.data[resp_header_length];
> +	publicout = (struct tpm2_nv_public_out *)&out->data;
> +	recd = be16_to_cpu(out->size);
> +
> +	info->nv_index = be32_to_cpu(publicout->nvIndex);
> +	info->name_alg = be16_to_cpu(publicout->nameAlg);
> +	info->attributes = be32_to_cpu(publicout->attributes);
> +
> +	/* Determine the size of the authPolicy, so we can skip over that to grab the data size */
> +	auth_policy_size = be16_to_cpu(publicout->authPolicySize);
> +
> +	info->data_size = be16_to_cpu((publicout->data[auth_policy_size]) | (publicout->data[auth_policy_size+1] << 8));


I don't think this is correct. The way you read the 2 bytes they are 
already in native format then due to the shifting. So be16_to_cpu should 
return the wrong result on little endian machines.


> +
> +out:
> +	tpm_buf_destroy(&buf);
> +	return rc;
> +}
> +
> +int tpm2_nv_read(struct tpm_chip *chip, u32 nvindex, u32 *attr_out, void **dest)
> +{
> +	struct tpm_buf buf;
> +	int rc;
> +	struct tpm2_buffer_out *out;
> +	u16 recd;
> +	u16 copied;
> +	u32 nvhandle;
> +	u32 resp_header_length;
> +	struct tpm2_null_auth_area auth_area;
> +	u16 size;
> +	struct tpm2_nv_public public;
> +
> +	copied = 0;
> +
> +	if ((nvindex & ~TPM2_HR_NV_INDEX) > 0x00FFFFFF)
> +		return -EINVAL;
> +
> +	/* HR_NV_INDEX = TPM_HT_NV_INDEX << HR_SHIFT */
> +	nvhandle = TPM2_HR_NV_INDEX | nvindex;
> +
> +	/* Determine the size of the NV Index contents */
> +	rc = tpm2_nv_readpublic(chip, nvindex, &public);
> +	if (rc < 0)
> +		return rc;
> +	if (attr_out != NULL)
> +		*attr_out = public.attributes;
> +	size = public.data_size;
> +	*dest = kzalloc(size, GFP_KERNEL);
> +	if (!*dest) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +
> +	/* Retrieve the actual NV Index contents */
> +	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_NV_READ);
> +	if (rc)
> +		goto out_free;
> +
> +	while (copied < size) {
> +		tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_NV_READ);
> +
> +		tpm_buf_append_u32(&buf, TPM2_RH_OWNER);
> +		tpm_buf_append_u32(&buf, nvhandle);
> +
> +		auth_area.handle = cpu_to_be32(TPM2_RS_PW);
> +		auth_area.nonce_size = 0;
> +		auth_area.attributes = 0;
> +		auth_area.auth_size = 0;
> +
> +		tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
> +		tpm_buf_append(&buf, (const unsigned char *)&auth_area,
> +			       sizeof(auth_area));
> +
> +		/* Size to request: at most 512 bytes at a time */
> +		tpm_buf_append_u16(&buf, min_t(u16, 512, size-copied));
> +		/* Offset: start at where we ended up */
> +		tpm_buf_append_u16(&buf, copied);
> +
> +		rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read NV index");
> +		if (rc) {
> +			if (rc > 0)
> +				rc = -EIO;
> +			goto out_free;
> +		}
> +		resp_header_length = tpm_buf_response_header_length(&buf, 0);
> +		out = (struct tpm2_buffer_out *)&buf.data[resp_header_length];
> +		recd = be16_to_cpu(out->size);
> +
> +		if (recd == 0) {
> +			rc = -EIO;
> +			goto out_free;
> +		}
> +		if (recd > size-copied) {
> +			rc = -EIO;
> +			goto out_free;
> +		}

You could add this to the above.

if (recd == 0 || recd > size - copied) ...

This address the case where the TPM is actually returning more bytes 
than requested.

> +
> +		memcpy(*dest + copied, out->data, recd);
> +		copied += recd;
> +	};
> +
> +out_free:
> +	if ((rc < 0) || (copied != size)) {
> +		kvfree(*dest);


kfree ?


> +		*dest = NULL;
> +	}
> +
> +out:
> +	tpm_buf_destroy(&buf);
> +
> +	if (rc < 0)
> +		return rc;
> +	else if (copied != size)
> +		return -EIO;
> +	else
> +		return size;
> +}
> +
>   struct tpm2_get_random_out {
>   	__be16 size;
>   	u8 buffer[TPM_MAX_RNG_DATA];
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 8f4ff39f51e7..b812236b9955 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -53,6 +53,40 @@ struct tpm_bank_info {
>   	u16 crypto_id;
>   };
>   
> +enum tpm_nv_public_attrs {
> +	TPM2_ATTR_NV_PPWRITE = 0x00000001,
> +	TPM2_ATTR_NV_OWNERWRITE = 0x00000002,
> +	TPM2_ATTR_NV_AUTHWRITE = 0x00000004,
> +	TPM2_ATTR_NV_POLICYWRITE = 0x00000008,
> +	/* Bits 4-7 TPM_NT */
> +	/* Bits 8-9 reserved */
> +	TPM2_ATTR_NV_POLICY_DELETE = 0x00000400,
> +	TPM2_ATTR_NV_WRITELOCKED = 0x00000800,
> +	TPM2_ATTR_NV_WRITEALL = 0x00001000,
> +	TPM2_ATTR_NV_WRITE_DEFINE = 0x00002000,
> +	TPM2_ATTR_NV_WRITE_STCLEAR = 0x00004000,
> +	TPM2_ATTR_NV_GLOBALLOCK = 0x00008000,
> +	TPM2_ATTR_NV_PPREAD = 0x00010000,
> +	TPM2_ATTR_NV_OWNERREAD = 0x00020000,
> +	TPM2_ATTR_NV_AUTHREAD = 0x00040000,
> +	TPM2_ATTR_NV_POLICYREAD = 0x00080000,
> +	/* Bits 20-24 reserved */
> +	TPM2_ATTR_NV_NO_DA = 0x02000000,
> +	TPM2_ATTR_NV_ORDERLY = 0x04000000,
> +	TPM2_ATTR_NV_CLEAR_STCLEAR = 0x08000000,
> +	TPM2_ATTR_NV_READLOCKED = 0x10000000,
> +	TPM2_ATTR_NV_WRITTEN = 0x20000000,
> +	TPM2_ATTR_NV_PLATFORMCREATE = 0x40000000,
> +	TPM2_ATTR_NV_READ_STCLEAR = 0x80000000,
> +};
> +
> +struct tpm2_nv_public {
> +	u32 nv_index;
> +	u16 name_alg;
> +	u32 attributes;
> +	u16 data_size;
> +};
> +
>   enum TPM_OPS_FLAGS {
>   	TPM_OPS_AUTO_STARTUP = BIT(0),
>   };
> @@ -189,6 +223,10 @@ enum tpm2_structures {
>   	TPM2_ST_SESSIONS	= 0x8002,
>   };
>   
> +enum tpm2_root_handles {
> +	TPM2_RH_OWNER		= 0x40000001,
> +};
> +
>   /* Indicates from what layer of the software stack the error comes from */
>   #define TSS2_RC_LAYER_SHIFT	 16
>   #define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT)
> @@ -223,6 +261,7 @@ enum tpm2_command_codes {
>   	TPM2_CC_CONTEXT_LOAD	        = 0x0161,
>   	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
>   	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
> +	TPM2_CC_NV_READPUBLIC		= 0x0169,
>   	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
>   	TPM2_CC_GET_CAPABILITY	        = 0x017A,
>   	TPM2_CC_GET_RANDOM	        = 0x017B,
> @@ -389,6 +428,21 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
>   	tpm_buf_append(buf, (u8 *) &value2, 4);
>   }
>   
> +static inline u32 tpm_buf_response_header_length(struct tpm_buf *buf, bool has_shielded_locations)
> +{
> +	u32 header_length = TPM_HEADER_SIZE;
> +
> +	/* Possibly a handle for a Shielded Location */
> +	if (has_shielded_locations)
> +		header_length += 4;
> +
> +	/* Possibly the 32-bit parameter area size */
> +	if (tpm_buf_tag(buf) == TPM2_ST_SESSIONS)
> +		header_length += 4;
> +
> +	return header_length;
> +}
> +
>   static inline u32 tpm2_rc_value(u32 rc)
>   {
>   	return (rc & BIT(7)) ? rc & 0xff : rc;
> @@ -401,6 +455,7 @@ extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
>   			struct tpm_digest *digest);
>   extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>   			  struct tpm_digest *digests);
> +extern int tpm_nv_read(struct tpm_chip *chip, u32 nv_idx, u32 *attrs_out, void **out);
>   extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
>   extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
>   extern struct tpm_chip *tpm_default_chip(void);
> @@ -423,6 +478,16 @@ static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>   	return -ENODEV;
>   }
>   
> +static inline int tpm2_nv_readpublic(struct tpm_chip *chip, u32 nvindex, struct tpm2_nv_public *info)
> +{
> +	return -ENODEV;
> +}
> +
> +static inline int tpm_nv_read(struct tpm_chip *chip, u32 nv_idx, u8 *out, size_t max)
> +{


tpm2_nv_read


> +	return -ENODEV;
> +}
> +
>   static inline int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
>   {
>   	return -ENODEV;



  reply	other threads:[~2021-02-25 21:51 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-25 20:32 [PATCH 0/3] Load keys from TPM2 NV Index on IMA keyring Patrick Uiterwijk
2021-02-25 20:32 ` [PATCH 1/3] tpm: Add support for reading a TPM NV Index Patrick Uiterwijk
2021-02-25 21:50   ` Stefan Berger [this message]
2021-02-26  1:09   ` Jarkko Sakkinen
2021-02-25 20:32 ` [PATCH 2/3] integrity: Allow specifying flags in integrity_load_cert Patrick Uiterwijk
2021-02-26 21:04   ` Stefan Berger
2021-02-25 20:32 ` [PATCH 3/3] integrity: Load keys from TPM NV onto IMA keyring Patrick Uiterwijk
2021-02-26 21:47   ` Stefan Berger
2021-02-26 21:51     ` Stefan Berger
2021-02-25 21:50 ` [PATCH 0/3] Load keys from TPM2 NV Index on " James Bottomley
2021-02-26 21:45   ` Ken Goldman

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=cf2339c2-2fed-2cc0-d215-658606abf146@linux.ibm.com \
    --to=stefanb@linux.ibm.com \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=jarkko@kernel.org \
    --cc=jgg@ziepe.ca \
    --cc=kgold@linux.ibm.com \
    --cc=linux-integrity@vger.kernel.org \
    --cc=patrick@puiterwijk.org \
    --cc=pbrobinson@gmail.com \
    --cc=peterhuewe@gmx.de \
    --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).