Re: [PATCH v7 5/5] tpm: pass an array of tpm_extend_digest structures to tpm_pcr_extend()

From: Roberto Sassu
Date: Mon Jan 21 2019 - 03:11:36 EST


On 1/18/2019 4:12 PM, Jarkko Sakkinen wrote:
On Thu, Jan 17, 2019 at 08:59:00AM +0100, Roberto Sassu wrote:
On 12/20/2018 4:21 PM, Jarkko Sakkinen wrote:
On Thu, Dec 13, 2018 at 11:29:45AM +0100, Roberto Sassu wrote:
Currently, tpm_pcr_extend() accepts as an input only a SHA1 digest.

This patch modifies the definition of tpm_pcr_extend() to allow other
kernel subsystems to pass a digest for each algorithm supported by the TPM.
All digests are processed by the TPM in one operation.

If a tpm_pcr_extend() caller provides a subset of the supported algorithms,
the TPM driver extends the remaining PCR banks with the first digest
passed as an argument to the function.

The new tpm_extend digest structure has been preferred to the tpm_digest
structure, to let the caller specify the size of the digest (which may be
unknown to the TPM driver).

Due to the API change, ima_pcr_extend() and pcrlock() have been modified.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
drivers/char/tpm/tpm-interface.c | 24 +++++---------------
drivers/char/tpm/tpm.h | 5 +++--
drivers/char/tpm/tpm1-cmd.c | 13 ++++++++---
drivers/char/tpm/tpm2-cmd.c | 35 +++++++++++++++++++++---------
include/linux/tpm.h | 13 ++++++++---
security/integrity/ima/ima_queue.c | 5 ++++-
security/keys/trusted.c | 5 ++++-
7 files changed, 62 insertions(+), 38 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index eb7c79ca8a94..911fea19e408 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -478,42 +478,30 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
* tpm_pcr_extend - extend a PCR value in SHA1 bank.
* @chip: a &struct tpm_chip instance, %NULL for the default chip
* @pcr_idx: the PCR to be retrieved
- * @hash: the hash value used to extend the PCR value
+ * @count: number of tpm_extend_digest structures
+ * @digests: array of tpm_extend_digest structures used to extend PCRs
*
* Note: with TPM 2.0 extends also those banks for which no digest was
* specified in order to prevent malicious use of those PCR banks.
*
* Return: same as with tpm_transmit_cmd()
*/
-int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash)
+int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
+ const struct tpm_extend_digest *digests)

Remove const. Document how @digests is used like the special meaning
of the first index. I faintly remember asking this last time.

{
int rc;
- struct tpm_digest *digest_list;
- int i;
chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
- digest_list = kcalloc(chip->nr_allocated_banks,
- sizeof(*digest_list), GFP_KERNEL);
- if (!digest_list)
- return -ENOMEM;
-
- for (i = 0; i < chip->nr_allocated_banks; i++) {
- digest_list[i].alg_id = chip->allocated_banks[i].alg_id;
- memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
- }
-
- rc = tpm2_pcr_extend(chip, pcr_idx, chip->nr_allocated_banks,
- digest_list);
- kfree(digest_list);
+ rc = tpm2_pcr_extend(chip, pcr_idx, count, digests);
tpm_put_ops(chip);
return rc;
}
- rc = tpm1_pcr_extend(chip, pcr_idx, hash,
+ rc = tpm1_pcr_extend(chip, pcr_idx, count, digests,
"attempting extend a PCR value");

The validation is missing that the provided array has only one element
and the algorithm is SHA1. Could be done also inside tpm1_pcr_extend()
but what you are doing to that function does not make any sense so
better to do it here.

tpm_put_ops(chip);
return rc;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 64d93d26087f..6b446504d2fe 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -504,7 +504,8 @@ int tpm1_auto_startup(struct tpm_chip *chip);
int tpm1_do_selftest(struct tpm_chip *chip);
int tpm1_get_timeouts(struct tpm_chip *chip);
unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
-int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
+int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
+ const struct tpm_extend_digest *digests,
const char *log_msg);
int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf);
ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
@@ -551,7 +552,7 @@ int tpm2_get_timeouts(struct tpm_chip *chip);
int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digest, u16 *digest_size_ptr);
int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
- struct tpm_digest *digests);
+ const struct tpm_extend_digest *digests);
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
unsigned int flags);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index 8b70a7f884a7..04ee10284b8c 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -449,12 +449,20 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
}
#define TPM_ORD_PCR_EXTEND 20
-int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
+int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
+ const struct tpm_extend_digest *digests,
const char *log_msg)
{
struct tpm_buf buf;
+ u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
+ const u8 *hash;
int rc;
+ hash = dummy_hash;
+ if (count)
+ memcpy(dummy_hash, digests[0].data,
+ min(digests[0].size, (u16)sizeof(dummy_hash)));
+

You copy memory from one place to another without any good reason to do
so. My suggestion is just not to change tpm1_pcr_extend() at all.

rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
if (rc)
return rc;
@@ -743,7 +751,6 @@ int tpm1_auto_startup(struct tpm_chip *chip)
*/
int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
{
- u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
struct tpm_buf buf;
unsigned int try;
int rc;
@@ -751,7 +758,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
/* for buggy tpm, flush pcrs with extend to selected dummy */
if (tpm_suspend_pcr)
- rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
+ rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, 0, NULL,
"extending dummy pcr before suspend");
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 6ce5173cf0e5..77b5808270c6 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -247,21 +247,22 @@ struct tpm2_null_auth_area {
*
* @chip: TPM chip to use.
* @pcr_idx: index of the PCR.
- * @count: number of digests passed.
- * @digests: list of pcr banks and corresponding digest values to extend.
+ * @count: number of tpm_extend_digest passed.
+ * @digests: array of tpm_extend_digest with digest values to extend.
*
* Return: Same as with tpm_transmit_cmd.
*/

The documentation about @digests.

int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
- struct tpm_digest *digests)
+ const struct tpm_extend_digest *digests)
{
struct tpm_buf buf;
struct tpm2_null_auth_area auth_area;
+ const struct tpm_extend_digest *digest;
+ u8 dummy_hash[SHA512_DIGEST_SIZE] = { 0 };
+ const u8 *hash;
int rc;
int i;
-
- if (count > chip->nr_allocated_banks)
- return -EINVAL;
+ int j;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc)
@@ -277,11 +278,25 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
tpm_buf_append(&buf, (const unsigned char *)&auth_area,
sizeof(auth_area));
- tpm_buf_append_u32(&buf, count);
+ tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
+
+ if (count)
+ memcpy(dummy_hash, digests[0].data, digests[0].size);
+
+ for (i = 0; i < chip->nr_allocated_banks; i++) {
+ tpm_buf_append_u16(&buf, chip->allocated_banks[i].alg_id);
+
+ hash = dummy_hash;
+ for (j = 0; j < count; j++) {
+ digest = digests + j;
+
+ if (digest->alg_id == chip->allocated_banks[i].alg_id) {
+ hash = digest->data;
+ break;

I think the whole design is just wrong. I did re-read your response to
v6 again and I'm very sorry, but I just don't get this. Caller has all
the information (from struct tpm_chip) to give the correct data. This
function should validate that data (check algorithm ID and that's it).

The question is if checking tpm->allocated_banks is a strict
requirement, or we can allow callers to use the algorithm they are
currently using, without further modifications.

If you want to know what is available, you can use that array.

Extending with the dummy hash should be done by the caller when
preparing the array, not baked into this function. This kind of also
makes obvious that we don't need this new struct. There should never be
a local variable (whose size is BTW randomly chosen) called dummy_hash.

This means duplicating the code for each caller. Currently, this work is
done by the TPM driver.

It is better than fix the usage pattern like this. Not only that, but
this somewhat complicated behavior now completely undocumented in the
function description.

You are making strong assumptions how some other subsystem might use
extend operation. I will rather accept redundancy and consider
consolidation later when there are two clients. Rather keep the TPM
provided function simple and stupid.

Ok, if I understood it correctly, every caller of tpm_pcr_extend() will pass an array of tpm_digest structures.

I will not introduce a new structure, as the caller has to use
algorithms from chip->allocated_banks and passing the digest size is not
necessary.

tpm_pcr_extend() will reject incomplete arrays (one or more algorithms
are missing). To simplify even more tpm_pcr_extend(), I assume and check
that passed algorithms are in the same order as those in
chip->allocated_banks.

I will remove the 'count' parameter from tpm_pcr_extend(), as the number
of tpm_digest structures will be always chip->nr_allocated_banks.

Roberto


/Jarkko


--
HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Bo PENG, Jian LI, Yanli SHI