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;
next prev parent 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).