[PATCH v3 6/6] tpm: pass multiple digests to tpm_pcr_extend()

Roberto Sassu
2017-06-21T14:35:17Z

This patch modifies the parameters of tpm_pcr_extend() by replacing the
SHA1 digest with an array of digests and the number of array elements.

This completes the changes necessary to correctly extend PCRs of a TPM 2.0.
Each PCR bank will be extended with a digest calculated with the PCR bank
algorithm. If the digest for a PCR bank has not been provided, the TPM
driver pads/truncates the first digest, to extend that bank. TPM users
should indicate in the event log in which sequence digests were passed
to the TPM driver (if they didn't provide all the digests), or should
pass to the driver a digest for each PCR bank.

Callers of tpm_pcr_extend(), pcrlock() and ima_pcr_extend(), have been
modified to pass the new arguments. They pass to tpm_pcr_extend() an array
with one element, containing the same SHA1 digest they were passing before
this patch.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
drivers/char/tpm/tpm-interface.c | 51 ++++++++++++++++++++++++++++++++------
drivers/char/tpm/tpm.h | 6 -----
include/linux/tpm.h | 12 +++++++--
security/integrity/ima/ima_queue.c | 4 ++-
security/keys/trusted.c | 6 ++---
5 files changed, 59 insertions(+), 20 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index cf0cdb2..3b0d7a2 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -871,44 +871,79 @@ static int tpm1_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash,
return rc;
}

+static u32 tpm_get_digest_size(struct tpm_chip *chip, enum tpm2_algorithms algo)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(chip->active_banks) &&
+ chip->active_banks[i].alg_id != TPM2_ALG_ERROR; i++)
+ if (chip->active_banks[i].alg_id == algo)
+ return chip->active_banks[i].digest_size;
+
+ /* Callers should have checked which algorithms the TPM supports,
+ * or should have provided a SHA1 digest, which is always supported.
+ * If the passed algorithm is unknown, return the size of SHA1.
+ */
+ return hash_digest_size[HASH_ALGO_SHA1];
+}
+
/**
* tpm_pcr_extend - extend pcr value with hash
* @chip_num: tpm idx # or AN&
* @pcr_idx: pcr idx to extend
- * @hash: hash value used to extend pcr value
+ * @count: number of digests
+ * @digests: array of digests
*
* The TPM driver should be built-in, but for whatever reason it
* isn't, protect against the chip disappearing, by incrementing
* the module usage count.
*/
-int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
+int tpm_pcr_extend(u32 chip_num, int pcr_idx, u32 count,
+ struct tpm2_digest *digests)
{
int rc;
struct tpm_chip *chip;
struct tpm2_digest digest_list[ARRAY_SIZE(chip->active_banks)];
- u32 count = 0;
- int i;
+ u32 first_digest_size;
+ int i, j;
+
+ if (count == 0)
+ return -EINVAL;

chip = tpm_chip_find_get(chip_num);
if (chip == NULL)
return -ENODEV;

+ first_digest_size = tpm_get_digest_size(chip, digests[0].alg_id);
+
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
memset(digest_list, 0, sizeof(digest_list));

for (i = 0; i < ARRAY_SIZE(chip->active_banks) &&
chip->active_banks[i].alg_id != TPM2_ALG_ERROR; i++) {
+ struct tpm_pcr_bank_info *bank = &chip->active_banks[i];
+ u8 *cur_digest = digests[0].digest;
+ u32 cur_digest_size = first_digest_size;
+
+ for (j = 0; j < count; j++) {
+ if (digests[j].alg_id == bank->alg_id) {
+ cur_digest = digests[j].digest;
+ cur_digest_size = bank->digest_size;
+ break;
+ }
+ }
+
digest_list[i].alg_id = chip->active_banks[i].alg_id;
- memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
- count++;
+ memcpy(digest_list[i].digest, cur_digest,
+ cur_digest_size);
}

- rc = tpm2_pcr_extend(chip, pcr_idx, count, digest_list);
+ rc = tpm2_pcr_extend(chip, pcr_idx, i, digest_list);
tpm_put_ops(chip);
return rc;
}

- rc = tpm1_pcr_extend(chip, pcr_idx, hash,
+ rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest,
"attempting extend a PCR value");
tpm_put_ops(chip);
return rc;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 75ec0d1..7c2f30b 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -34,7 +34,6 @@
#include <linux/acpi.h>
#include <linux/cdev.h>
#include <linux/highmem.h>
-#include <crypto/hash_info.h>

enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
@@ -386,11 +385,6 @@ struct tpm_cmd_t {
tpm_cmd_params params;
} __packed;

-struct tpm2_digest {
- u16 alg_id;
- u8 digest[SHA512_DIGEST_SIZE];
-} __packed;
-
/* A string buffer type for constructing TPM commands. This is based on the
* ideas of string buffer code in security/keys/trusted.h but is heap based
* in order to keep the stack usage minimal.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 49ec8fc..254d632 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -36,6 +36,11 @@ struct tpm_chip;
struct trusted_key_payload;
struct trusted_key_options;

+struct tpm2_digest {
+ u16 alg_id;
+ u8 digest[SHA512_DIGEST_SIZE];
+} __packed;
+
enum TPM_OPS_FLAGS {
TPM_OPS_AUTO_STARTUP = BIT(0),
};
@@ -76,7 +81,8 @@ struct tpm_pcr_bank_info {

extern int tpm_is_tpm2(u32 chip_num);
extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
-extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, u32 count,
+ struct tpm2_digest *digests);
extern int tpm_get_pcr_banks_info(u32 chip_num,
struct tpm_pcr_bank_info *active_banks);
extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
@@ -95,7 +101,9 @@ static inline int tpm_is_tpm2(u32 chip_num)
static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
return -ENODEV;
}
-static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
+static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, u32 count,
+ struct tpm2_digest *digests)
+{
return -ENODEV;
}
static inline int tpm_get_pcr_banks_info(u32 chip_num,
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index d9aa5ab..f628968 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -140,12 +140,14 @@ unsigned long ima_get_binary_runtime_size(void)

static int ima_pcr_extend(const u8 *hash, int pcr)
{
+ struct tpm2_digest digestarg = {.alg_id = TPM2_ALG_SHA1};
int result = 0;

if (!ima_used_chip)
return result;

- result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash);
+ memcpy(digestarg.digest, hash, IMA_DIGEST_SIZE);
+ result = tpm_pcr_extend(TPM_ANY_NUM, pcr, 1, &digestarg);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 435e86e..d6c0db8 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -377,15 +377,15 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
*/
static int pcrlock(const int pcrnum)
{
- unsigned char hash[SHA1_DIGEST_SIZE];
+ struct tpm2_digest digestarg = {.alg_id = TPM2_ALG_SHA1};
int ret;

if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
+ ret = tpm_get_random(TPM_ANY_NUM, digestarg.digest, SHA1_DIGEST_SIZE);
if (ret != SHA1_DIGEST_SIZE)
return ret;
- return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
+ return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, 1, &digestarg) ? -EINVAL : 0;
}

/*
--
2.9.3