linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Nicolai Stange <nstange@suse.de>
To: Mimi Zohar <zohar@linux.ibm.com>,
	Roberto Sassu <roberto.sassu@huawei.com>,
	Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Cc: Eric Snowberg <eric.snowberg@oracle.com>,
	Jarkko Sakkinen <jarkko@kernel.org>,
	James Bottomley <James.Bottomley@HansenPartnership.com>,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org, Nicolai Stange <nstange@suse.de>
Subject: [RFC PATCH v2 11/13] ima: introduce ima_pcr_invalidated_banks() helper
Date: Sun, 23 Mar 2025 15:09:09 +0100	[thread overview]
Message-ID: <20250323140911.226137-12-nstange@suse.de> (raw)
In-Reply-To: <20250323140911.226137-1-nstange@suse.de>

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(+)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7ad4a1eefd94..67b78f5512f1 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -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,
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index c78bf4872b6a..c1d9cd85a66d 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -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;
+}
-- 
2.49.0


  parent reply	other threads:[~2025-03-23 14:10 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-23 14:08 [RFC PATCH v2 00/13] ima: get rid of hard dependency on SHA-1 Nicolai Stange
2025-03-23 14:08 ` [RFC PATCH v2 01/13] ima: don't expose runtime_measurements for unsupported hashes Nicolai Stange
2025-03-25 14:26   ` Mimi Zohar
2025-03-26  7:44     ` Nicolai Stange
2025-03-26 13:28       ` Mimi Zohar
2025-03-23 14:09 ` [RFC PATCH v2 02/13] ima: always create runtime_measurements sysfs file for ima_hash Nicolai Stange
2025-03-24 14:31   ` Mimi Zohar
2025-03-26  8:21     ` Nicolai Stange
2025-03-26 13:17       ` Mimi Zohar
2025-03-26 13:46         ` Nicolai Stange
2025-03-26 14:48           ` Mimi Zohar
2025-03-23 14:09 ` [RFC PATCH v2 03/13] ima: invalidate unsupported PCR banks Nicolai Stange
2025-03-23 21:18   ` James Bottomley
2025-03-25  1:03     ` Mimi Zohar
2025-03-25 15:44       ` James Bottomley
2025-03-26  8:45         ` Nicolai Stange
2025-03-24 15:05   ` Mimi Zohar
2025-03-26  9:01     ` Nicolai Stange
2025-03-26 14:18       ` Mimi Zohar
2025-03-26 14:31         ` Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 04/13] ima: make SHA1 non-mandatory Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 05/13] ima: select CRYPTO_SHA256 from Kconfig Nicolai Stange
2025-03-25 15:17   ` Mimi Zohar
2025-03-23 14:09 ` [RFC PATCH v2 06/13] ima: move INVALID_PCR() to ima.h Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 07/13] tpm: enable bank selection for PCR extend Nicolai Stange
2025-03-23 20:41   ` Jarkko Sakkinen
2025-03-26  9:45     ` Nicolai Stange
2025-03-26  1:18   ` Mimi Zohar
2025-03-26  9:41     ` Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 08/13] ima: track the set of PCRs ever extended Nicolai Stange
2025-03-25 17:09   ` Mimi Zohar
2025-03-26  9:56     ` Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 09/13] ima: invalidate unsupported PCR banks only once Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 10/13] tpm: authenticate tpm2_pcr_read() Nicolai Stange
2025-03-23 17:25   ` James Bottomley
2025-03-26  6:34     ` Nicolai Stange
2025-03-23 20:35   ` Jarkko Sakkinen
2025-03-23 14:09 ` Nicolai Stange [this message]
2025-03-23 14:09 ` [RFC PATCH v2 12/13] ima: make ima_free_tfm()'s linkage extern Nicolai Stange
2025-03-23 14:09 ` [RFC PATCH v2 13/13] ima: don't re-invalidate unsupported PCR banks after kexec Nicolai Stange
2025-03-26  1:58 ` [RFC PATCH v2 00/13] ima: get rid of hard dependency on SHA-1 Mimi Zohar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250323140911.226137-12-nstange@suse.de \
    --to=nstange@suse.de \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=eric.snowberg@oracle.com \
    --cc=jarkko@kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=roberto.sassu@huawei.com \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).