@@ -274,6 +274,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
struct ima_iint_cache *iint, const char *op,
const char *cause);
int ima_init_crypto(void);
+unsigned long ima_pcr_invalidated_banks(u32 pcr);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
int template_desc_init_fields(const char *template_fmt,
@@ -900,3 +900,128 @@ int ima_calc_boot_aggregate(struct ima_digest_data *hash)
return rc;
}
+
+/*
+ * Expected values for unsupported PCR banks after invalidating once
+ * with 0xfe ... fe, i.e. HASH(0x00 ... 00 fe ... fe).
+ * The list might not be exhaustive as far as the set of recognized
+ * algorithms is concerned.
+ */
+static const struct {
+ u16 tpm_alg_id;
+ u8 digest_length;
+ const u8 *digest;
+} unsupported_pcr_bank_values[] = {
+ {
+ TPM_ALG_SHA1, 20,
+ (const u8[]) {
+ 0x74, 0x43, 0x5f, 0x39, 0xb5, 0x05, 0x21, 0x26,
+ 0x9d, 0xaa, 0xfd, 0x3e, 0x11, 0x1b, 0xf1, 0xd9,
+ 0x14, 0x1d, 0x9a, 0x5f,
+ },
+ },
+ {
+ TPM_ALG_SHA256, 32,
+ (const u8[]) {
+ 0x7a, 0x42, 0xe1, 0xf2, 0x6c, 0x07, 0x82, 0x7f,
+ 0xaa, 0x54, 0x87, 0x47, 0x62, 0xfd, 0x7f, 0xe7,
+ 0xa1, 0xdf, 0xbb, 0x8f, 0xfa, 0x51, 0xbf, 0x53,
+ 0x22, 0xa7, 0x71, 0xd2, 0xc8, 0x80, 0xc5, 0x86,
+ },
+ },
+ {
+ TPM_ALG_SHA384, 48,
+ (const u8[]) {
+ 0x68, 0xaa, 0xdf, 0xd3, 0x3e, 0x54, 0x15, 0x40,
+ 0x73, 0xc8, 0x6a, 0x95, 0x8d, 0x5d, 0x7b, 0xb2,
+ 0x68, 0xf3, 0x0c, 0x14, 0x9e, 0x19, 0x6d, 0x08,
+ 0x24, 0x7d, 0x51, 0x26, 0x05, 0xe5, 0x1c, 0x40,
+ 0xdd, 0xc8, 0x44, 0x4e, 0x93, 0x8a, 0x37, 0x05,
+ 0xfc, 0xd6, 0xa2, 0x80, 0xe3, 0x27, 0x0d, 0x71,
+ },
+ },
+ {
+ TPM_ALG_SHA512, 64,
+ (const u8[]) {
+ 0x58, 0x8c, 0x38, 0x64, 0x06, 0xdb, 0x9b, 0xcc,
+ 0x26, 0xa4, 0x13, 0x9c, 0x8a, 0xff, 0x6a, 0x10,
+ 0xf4, 0xe6, 0x5a, 0x92, 0xbd, 0xed, 0x9d, 0x62,
+ 0xbe, 0x92, 0x1b, 0x40, 0xf6, 0x7d, 0x9b, 0xc3,
+ 0x0d, 0x07, 0xc8, 0xfb, 0x1a, 0x8d, 0x56, 0xfa,
+ 0xa4, 0xf2, 0x05, 0xb6, 0x81, 0x29, 0x14, 0x5f,
+ 0xf6, 0x71, 0x32, 0xbb, 0x0d, 0x31, 0xca, 0xf3,
+ 0x5e, 0x8e, 0x95, 0xd9, 0xd8, 0x55, 0x28, 0x95,
+ },
+ },
+ {
+ TPM_ALG_SM3_256, 32,
+ (const u8[]) {
+ 0x05, 0xff, 0xaf, 0x59, 0x7e, 0x50, 0x39, 0x5b,
+ 0xaf, 0x69, 0xc0, 0xdc, 0x19, 0xb0, 0xe0, 0xfe,
+ 0x3f, 0x6b, 0x6f, 0x03, 0xcd, 0x04, 0xf6, 0x80,
+ 0x6c, 0x59, 0xdc, 0xd2, 0x06, 0xbf, 0x38, 0x78
+ },
+ },
+};
+
+/*
+ * Return true if the supplied PCR digest can get confirmed to match
+ * the expected value a bank with unsupported associated hash algorithm
+ * would have after invalidating it exactly once.
+ * Otherwise, return false.
+ *
+ * False negatives are tolerable from a soundness POV, but would
+ * potentially cause additional re-invalidations e.g. after kexec.
+ */
+static bool is_pcr_bank_invalidated(const struct tpm_digest * const digest)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(unsupported_pcr_bank_values); ++i) {
+ if (unsupported_pcr_bank_values[i].tpm_alg_id == digest->alg_id)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(unsupported_pcr_bank_values))
+ return false;
+
+ return memcmp(unsupported_pcr_bank_values[i].digest, digest->digest,
+ unsupported_pcr_bank_values[i].digest_length) == 0;
+}
+
+/*
+ * Read all of a PCR's banks and check which of those have a value
+ * matching the expected digest after invalidating once for
+ * unsupported algorithms.
+ *
+ * A bitmask of banks found to have been invalidated is getting
+ * returned. The set is not guaranteed to be complete.
+ */
+unsigned long ima_pcr_invalidated_banks(u32 pcr)
+{
+ int i, r;
+ struct tpm_digest d;
+ unsigned long invalidated_banks_mask = 0;
+
+ for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
+ if (i >= BITS_PER_LONG)
+ break;
+
+ d.alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
+ r = tpm_pcr_read(ima_tpm_chip, pcr, &d);
+ if (r) {
+ /*
+ * Failure to read is non-fatal, emit a
+ * warning only and move on to the next bank.
+ */
+ pr_warn("TPM PCR read failed %d, pcr=%d, bank=0x%02x\n",
+ r, pcr,
+ ima_tpm_chip->allocated_banks[i].alg_id);
+ }
+
+ if (is_pcr_bank_invalidated(&d))
+ invalidated_banks_mask |= BIT(i);
+ }
+
+ return invalidated_banks_mask;
+}
At the current stage, IMA would invalidate PCR banks corresponding to unsupported hash algorithms exactly once by extending with the special 0xfe ... fe from each kernel in a kexec chain. In order to work towards the goal of doing that only once for the overall chain, subsequent kernels must be able to recognize already invalidated PCR banks. PCR banks invalidated when in their initial reset state would have a value of HASH(0x00 ... 00 | fe ... fe). Introduce the ima_pcr_invalidated_banks() implementing this comparison for a couple of selected hash algorithms, namely the set the current TPM driver code knows about. Note that false positives would be fatal as far as soundness is concerned, as a future patch will make IMA to skip invalidations for banks reported to have been invalidated already. False negatives however will only cause superfluous re-invalidations, i.e. a PCR bank would not be recognizable as unsupported anymore, but any attempt to verify a measurement list against it would still fail. Thus, ima_pcr_invalidated_banks() doesn't necessarily need to support every hash algorithm possible and in particular, failure to keep it in sync with the TPM driver code, should the latter learn about some more hash algorithms in the future, would not be an issue. Let ima_pcr_invalidated_banks() read back all of a given PCR's bank digests from the TPM. Attempt to compare each against the well-known value of HASH(0x00 ... 00 | fe ... fe) and, in case of a match, set the corresponding bit in a bitmask returned eventually back to the caller. The type chosen for the returned bitmask is unsigned long. If the number of allocated banks exceeds its width, stop early after BITS_PER_LONG banks have been examined -- as mention earlier false negatives aren't fatal. In order to enable ima_pcr_invalidated_banks() to make comparisons against those well-known HASH(...) values from above, even in the scenario of interest here where the kernel's crypto API is lacking a usable implementation for some hash, provide them as pre-computed values in a lookup table for a number of selected hash algorithms, namely those recognized by the current TPM driver code. Lastly a word of caution towards the cherry-pickers among us: you will likely also want that other patch to the TPM driver code making tpm2_pcr_read() to authenticate the TPM response -- otherwise an interposer could potentially trick IMA to skip a needed PCR bank invalidation from a kexeced kernel even with CONFIG_TCG_TPM2_HMAC=y. Signed-off-by: Nicolai Stange <nstange@suse.de> --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_crypto.c | 125 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+)