Re: [PATCH v7 5/5] tpm: pass an array of tpm_extend_digest structures to tpm_pcr_extend()
From: Roberto Sassu <roberto.sassu@huawei.com>
Date: 2019-01-21 08:11:36
Also in:
linux-integrity, lkml
On 1/18/2019 4:12 PM, Jarkko Sakkinen wrote:
On Thu, Jan 17, 2019 at 08:59:00AM +0100, Roberto Sassu wrote:quoted
On 12/20/2018 4:21 PM, Jarkko Sakkinen wrote:quoted
On Thu, Dec 13, 2018 at 11:29:45AM +0100, Roberto Sassu wrote:quoted
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@huawei.com> --- 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.quoted
{ 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.quoted
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.quoted
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.quoted
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.quoted
quoted
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