[PATCH v2] Revert "tpm: pass an array of tpm_extend_digest structures to tpm_pcr_extend()"

From: Jarkko Sakkinen
Date: Thu Jul 04 2019 - 06:01:20 EST


From: Michal Suchanek <msuchanek@xxxxxxx>

This reverts commit 0b6cf6b97b7ef1fa3c7fefab0cac897a1c4a3400 to avoid
following crash:

BUG: Kernel NULL pointer dereference at 0x00000012
Faulting instruction address: 0xc000000000897908
Oops: Kernel access of bad area, sig: 11 [#1]
LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.2.0-rc6-2.gf99f70b-default #1 openSUSE Tumbleweed (unreleased)
NIP: c000000000897908 LR: c000000000897860 CTR: 0000000000000009
REGS: c000000004eb7550 TRAP: 0380 Not tainted (5.2.0-rc6-2.gf99f70b-default)
MSR: 800000010280b033 <SF,VEC,VSX,EE,FP,ME,IR,DR,RI,LE,TM[E]> CR: 88000224 XER: 20040001
CFAR: c000000000c4e964 IRQMASK: 0
GPR00: c000000000897860 c000000004eb77e0 c0000000015ced00 0000000000000000
GPR04: 0000000000000003 0000000000000001 0000000022000000 000000000000000e
GPR08: c0000005b46b0000 0000000000010000 0000000000000022 0000000010325476
GPR12: 0000000048000222 c0000000019a0000 c000000000010b70 0000000000000000
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR24: c000000000eded00 c0000000014ecb60 c000000000ccedf8 c0000005b5c9d000
GPR28: 000000000000000a 0000000000000012 c000000000f01180 c00c0000016d1ac0
NIP [c000000000897908] tpm1_pcr_extend+0x118/0x1d0
LR [c000000000897860] tpm1_pcr_extend+0x70/0x1d0
Call Trace:
[c000000004eb77e0] [c000000000897860] tpm1_pcr_extend+0x70/0x1d0 (unreliable)
[c000000004eb7890] [c0000000008964e4] tpm_pcr_extend+0xe4/0x170
[c000000004eb78d0] [c000000000667bac] ima_add_template_entry+0x1ac/0x350
[c000000004eb79b0] [c00000000066ad04] ima_store_template+0xc4/0x150
[c000000004eb7a30] [c000000001021a4c] ima_add_boot_aggregate+0xec/0x17c
[c000000004eb7b30] [c000000001021b70] ima_init+0x94/0xbc
[c000000004eb7b90] [c000000001021ce0] init_ima+0x44/0xec
[c000000004eb7c10] [c000000000010694] do_one_initcall+0x64/0x2b0
[c000000004eb7ce0] [c000000000fd445c] kernel_init_freeable+0x2e4/0x3cc
[c000000004eb7db0] [c000000000010b94] kernel_init+0x2c/0x148
[c000000004eb7e20] [c00000000000bc54] ret_from_kernel_thread+0x5c/0x68
Instruction dump:
90c80002 81410070 714a0001 e9010078 39480002 7d40542c 79470020 4082003c
394a0014 7c0a4840 41810090 5546c03e <e87d0000> e89d0008 80bd0010 7d283a14
---[ end trace 786ebab24be797a3 ]---

Cc: stable@xxxxxxxxxxxxxxx
Cc: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
Fixes: 0b6cf6b97b7e ("tpm: pass an array of tpm_extend_digest structures to tpm_pcr_extend()")
Signed-off-by: Michal Suchanek <msuchanek@xxxxxxx>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@xxxxxxxxxxxxxxx>
---
v2: Took Michal's patch and added the mandatory tags and fixed a trivial
checkpatch.pl error in the commit message.
drivers/char/tpm/tpm-interface.c | 30 +++++++++++++--------
drivers/char/tpm/tpm.h | 2 +-
drivers/char/tpm/tpm2-cmd.c | 10 ++++---
include/linux/tpm.h | 5 ++--
security/integrity/ima/ima.h | 1 -
security/integrity/ima/ima_init.c | 4 ---
security/integrity/ima/ima_queue.c | 27 +------------------
security/keys/trusted.c | 42 ++++++------------------------
8 files changed, 38 insertions(+), 83 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 1b4f95c13e00..dda742184ce2 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -303,34 +303,42 @@ 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
- * @digests: array of tpm_digest structures used to extend PCRs
+ * @hash: the hash value used to extend the PCR value
*
- * Note: callers must pass a digest for every allocated PCR bank, in the same
- * order of the banks in chip->allocated_banks.
+ * 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,
- struct tpm_digest *digests)
+int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash)
{
int rc;
+ struct tpm_digest *digest_list;
int i;

chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;

- for (i = 0; i < chip->nr_allocated_banks; i++)
- if (digests[i].alg_id != chip->allocated_banks[i].alg_id)
- return -EINVAL;
-
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
- rc = tpm2_pcr_extend(chip, pcr_idx, digests);
+ 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);
tpm_put_ops(chip);
return rc;
}

- rc = tpm1_pcr_extend(chip, pcr_idx, digests[0].digest,
+ rc = tpm1_pcr_extend(chip, pcr_idx, hash,
"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 e503ffc3aa39..3cf8ed290305 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -436,7 +436,7 @@ static inline u32 tpm2_rc_value(u32 rc)
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,
+int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
struct tpm_digest *digests);
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index d103545e4055..b700817ebd63 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -242,11 +242,12 @@ 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.
*
* Return: Same as with tpm_transmit_cmd.
*/
-int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
struct tpm_digest *digests)
{
struct tpm_buf buf;
@@ -254,6 +255,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
int rc;
int i;

+ if (count > chip->nr_allocated_banks)
+ return -EINVAL;
+
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc)
return rc;
@@ -268,9 +272,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
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, chip->nr_allocated_banks);
+ tpm_buf_append_u32(&buf, count);

- for (i = 0; i < chip->nr_allocated_banks; i++) {
+ for (i = 0; i < count; i++) {
tpm_buf_append_u16(&buf, digests[i].alg_id);
tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
chip->allocated_banks[i].digest_size);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 53c0ea9ec9df..d4516164dc9f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -166,8 +166,7 @@ struct tpm_chip {
extern int tpm_is_tpm2(struct tpm_chip *chip);
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_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash);
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 int tpm_seal_trusted(struct tpm_chip *chip,
@@ -190,7 +189,7 @@ static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx,
}

static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
- struct tpm_digest *digests)
+ const u8 *hash)
{
return -ENODEV;
}
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index ca10917b5f89..bf7178ef1619 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -149,7 +149,6 @@ int ima_measurements_show(struct seq_file *m, void *v);
unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);
-int __init ima_init_digests(void);

/*
* used to protect h_table and sha_table
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 1e47c1026471..cf7a2f58077e 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -119,12 +119,8 @@ int __init ima_init(void)
if (rc != 0)
return rc;

- /* It can be called before ima_init_digests(), it does not use TPM. */
ima_load_kexec_buffer();

- rc = ima_init_digests();
- if (rc != 0)
- return rc;
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 1ce8b1701566..03afa67c2e04 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -23,9 +23,6 @@

#define AUDIT_CAUSE_LEN_MAX 32

-/* pre-allocated array of tpm_digest structures to extend a PCR */
-static struct tpm_digest *digests;
-
LIST_HEAD(ima_measurements); /* list of all measurements */
#ifdef CONFIG_IMA_KEXEC
static unsigned long binary_runtime_size;
@@ -139,15 +136,11 @@ unsigned long ima_get_binary_runtime_size(void)
static int ima_pcr_extend(const u8 *hash, int pcr)
{
int result = 0;
- int i;

if (!ima_tpm_chip)
return result;

- for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
- memcpy(digests[i].digest, hash, TPM_DIGEST_SIZE);
-
- result = tpm_pcr_extend(ima_tpm_chip, pcr, digests);
+ result = tpm_pcr_extend(ima_tpm_chip, pcr, hash);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
@@ -214,21 +207,3 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry)
mutex_unlock(&ima_extend_list_mutex);
return result;
}
-
-int __init ima_init_digests(void)
-{
- int i;
-
- if (!ima_tpm_chip)
- return 0;
-
- digests = kcalloc(ima_tpm_chip->nr_allocated_banks, sizeof(*digests),
- GFP_NOFS);
- if (!digests)
- return -ENOMEM;
-
- for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
- digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
-
- return 0;
-}
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 9a94672e7adc..287553039f0f 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -32,7 +32,6 @@
static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
static struct tpm_chip *chip;
-static struct tpm_digest *digests;

struct sdesc {
struct shash_desc shash;
@@ -386,10 +385,15 @@ EXPORT_SYMBOL_GPL(trusted_tpm_send);
*/
static int pcrlock(const int pcrnum)
{
+ unsigned char hash[SHA1_DIGEST_SIZE];
+ int ret;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
-
- return tpm_pcr_extend(chip, pcrnum, digests) ? -EINVAL : 0;
+ ret = tpm_get_random(chip, hash, SHA1_DIGEST_SIZE);
+ if (ret != SHA1_DIGEST_SIZE)
+ return ret;
+ return tpm_pcr_extend(chip, pcrnum, hash) ? -EINVAL : 0;
}

/*
@@ -1226,29 +1230,6 @@ static int __init trusted_shash_alloc(void)
return ret;
}

-static int __init init_digests(void)
-{
- u8 digest[TPM_MAX_DIGEST_SIZE];
- int ret;
- int i;
-
- ret = tpm_get_random(chip, digest, TPM_MAX_DIGEST_SIZE);
- if (ret < 0)
- return ret;
- if (ret < TPM_MAX_DIGEST_SIZE)
- return -EFAULT;
-
- digests = kcalloc(chip->nr_allocated_banks, sizeof(*digests),
- GFP_KERNEL);
- if (!digests)
- return -ENOMEM;
-
- for (i = 0; i < chip->nr_allocated_banks; i++)
- memcpy(digests[i].digest, digest, TPM_MAX_DIGEST_SIZE);
-
- return 0;
-}
-
static int __init init_trusted(void)
{
int ret;
@@ -1259,21 +1240,15 @@ static int __init init_trusted(void)
chip = tpm_default_chip();
if (!chip)
return 0;
-
- ret = init_digests();
- if (ret < 0)
- goto err_put;
ret = trusted_shash_alloc();
if (ret < 0)
- goto err_free;
+ goto err_put;
ret = register_key_type(&key_type_trusted);
if (ret < 0)
goto err_release;
return 0;
err_release:
trusted_shash_release();
-err_free:
- kfree(digests);
err_put:
put_device(&chip->dev);
return ret;
@@ -1283,7 +1258,6 @@ static void __exit cleanup_trusted(void)
{
if (chip) {
put_device(&chip->dev);
- kfree(digests);
trusted_shash_release();
unregister_key_type(&key_type_trusted);
}
--
2.20.1