mirror of https://lore.kernel.org/lkml/
 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 03/13] ima: invalidate unsupported PCR banks
Date: Sun, 23 Mar 2025 15:09:01 +0100	[thread overview]
Message-ID: <20250323140911.226137-4-nstange@suse.de> (raw)
In-Reply-To: <20250323140911.226137-1-nstange@suse.de>

Normally IMA would extend a template hash of each bank's associated
algorithm into a PCR. However, if a bank's hash algorithm is unavailable
to the kernel at IMA init time, it would fallback to extending padded SHA1
hashes instead.

That is, if e.g. SHA-256 was missing at IMA init, it would extend padded
SHA1 template hashes into a PCR's SHA-256 bank.

The ima_measurement command (marked as experimental) from ima-evm-utils
would accordingly try both variants when attempting to verify a measurement
list against PCRs. keylime OTOH doesn't seem to -- it expects the template
hash type to match the PCR bank algorithm. I would argue that for the
latter case, the fallback scheme could potentially cause hard to debug
verification failures.

There's another problem with the fallback scheme: right now, SHA-1
availability is a hard requirement for IMA, and it would be good for a
number of reasons to get rid of that. However, if SHA-1 is not available to
the kernel, it can hardly provide padded SHA-1 template hashes for PCR
banks with unsupported algos.

There are several more or less reasonable alternatives possible, among
them are:
a.) Instead of padded SHA-1, use padded/truncated ima_hash template
    hashes.
b.) Don't extend unsupported banks at all.
c.) Record every event as a violation, i.e. extend unsupported banks
    with 0xffs.
d.) Invalidate unsupported banks at least once by extending with a unique
    constant (e.g. with 0xfes).

a.) would make verification from tools like ima_measurement nearly
    impossible, as it would have to guess or somehow determine ima_hash.
b.) is a security risk, because the bank would validate an empty
    measurement list.
c.) isn't ideal security-wise either, because an unsupported bank would
    then validate an all-violations measurement log.
d.) is the only remaining viable option: extending unsupported PCR banks
    at least once with a unique constant of 0xfe ... fe not used for
    anything else would make those not to validate anything from that
    point on.

For this last alternative d.), there are some variations possible, which
differ in the number of times the magic 0xfe ... fe gets extended into
unsupported banks for invalidation purposes. Among the practical ones
are:
- invalidate each unsupported bank over and over again for each new
  measurement log entry or
- invalidate each unsupported bank exactly once.

The second option has the advantage over the first that it would enable
tools like ima-evm-utils' ima_measurement to recognize unsupported banks
in O(1) time, just by comparing the PCR bank value against the constant
HASH(00 .. 00 | fe .. fe). Note that if OTOH a bank got invalidated over
and over again for each single log entry, and assuming that it's desired
to report unsupported banks as such instead of just failing their
validation, ima_measurement would have to try to match yet another PCR
extension candidate sequence for the 0xfe .. fe over the complete
measurement list, as it's currently being done for the padded SHA1s
template hashes already.

As appealing as the scheme to invalidate each unsupported bank exactly once
might seem at first glance though, there's also the clear drawback of an
additional tracking burden with a significant complexity on the kernel
side: because IMA can't know ahead of time which out of all possible PCRs
would ever get referenced from some policy rules, it cannot simply run the
invalidation of unsupported banks upfront at __init, but would have to do
it lazily upon a given PCR's first extension. The need for carrying the
required state across kexecs, with the possibility of different kernels in
the kexec chain potentially supporting different sets of hash algorithms,
doesn't exactly make things easier either.

So, to move towards the original goal of disentangling IMA from its hard
dependency on SHA-1, go with the more straightforward route for now to
invalidate unsupported PCR banks over and over again for each new
measurement list entry recorded. The more sophisticated
"invalidate-exactly-once" scheme will be left to future patches, if to be
implemented at all.

As this potentially breaks existing userspace, i.e. the current
implementation of ima_measurement, put it behind a Kconfig option,
"IMA_COMPAT_FALLBACK_TPM_EXTEND". If set to "y", the original behavior of
extending with padded SHA-1 is retained. Otherwise the new scheme to
invalidate unsupported PCR banks by extending with constant 0xfe ... fe
will be effective. As ima_measurement is marked as experimental and I find
it unlikely that other existing tools depend on the padded SHA-1 fallback
scheme, make the IMA_COMPAT_FALLBACK_TPM_EXTEND Kconfig option default
to "n".

For IMA_COMPAT_FALLBACK_TPM_EXTEND=n,
- to cover PCR extensions for "regular" measurement list entries, make
  ima_calc_field_array_hash() to fill the digests corresponding to banks
  with unsupported hash algorithms with 0xfes,
- to cover PCR extensions for violation entries, make ima_init_digest() to
  likewise provision the digests[] elements corresponding to unsupported
  banks with 0xfes.

[1] https://github.com/linux-integrity/ima-evm-utils

Signed-off-by: Nicolai Stange <nstange@suse.de>
---
 security/integrity/ima/Kconfig      | 14 ++++++++++++++
 security/integrity/ima/ima_crypto.c | 19 ++++++++++++++++++-
 security/integrity/ima/ima_queue.c  | 12 ++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 475c32615006..c8f12a4a4edf 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -122,6 +122,20 @@ config IMA_DEFAULT_HASH
 	default "wp512" if IMA_DEFAULT_HASH_WP512
 	default "sm3" if IMA_DEFAULT_HASH_SM3
 
+config IMA_COMPAT_FALLBACK_TPM_EXTEND
+	bool "Enable compatibility TPM PCR extend for unsupported banks"
+	default n
+	help
+	  In case a TPM PCR hash algorithm is not supported by the kernel,
+	  retain the old behaviour to extend the bank with padded SHA1 template
+	  digests.
+
+	  If Y, IMA will be unavailable when SHA1 is missing from the kernel.
+	  If N, existing tools may fail to verify IMA measurement lists against
+	  TPM PCR banks corresponding to hashes not supported by the kernel.
+
+	  If unsure, say N.
+
 config IMA_WRITE_POLICY
 	bool "Enable multiple writes to the IMA policy"
 	default n
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 6f5696d999d0..a43080fb8edc 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -625,26 +625,43 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
 	u16 alg_id;
 	int rc, i;
 
+#if IS_ENABLED(CONFIG_IMA_COMPAT_FALLBACK_TPM_EXTEND)
 	rc = ima_calc_field_array_hash_tfm(field_data, entry, ima_sha1_idx);
 	if (rc)
 		return rc;
 
 	entry->digests[ima_sha1_idx].alg_id = TPM_ALG_SHA1;
+#endif
 
 	for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) {
+#if IS_ENABLED(CONFIG_IMA_COMPAT_FALLBACK_TPM_EXTEND)
 		if (i == ima_sha1_idx)
 			continue;
+#endif
 
 		if (i < NR_BANKS(ima_tpm_chip)) {
 			alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
 			entry->digests[i].alg_id = alg_id;
 		}
 
-		/* for unmapped TPM algorithms digest is still a padded SHA1 */
+		/*
+		 * For unmapped TPM algorithms, the digest is still a
+		 * padded SHA1 if backwards-compatibility fallback PCR
+		 * extension is enabled. Otherwise fill with
+		 * 0xfes. This is the value to invalidate unsupported
+		 * PCR banks with. Also, a non-all-zeroes value serves
+		 * as an indicator to kexec measurement restoration
+		 * that the entry is not a violation and all its
+		 * template digests need to get recomputed.
+		 */
 		if (!ima_algo_array[i].tfm) {
+#if IS_ENABLED(CONFIG_IMA_COMPAT_FALLBACK_TPM_EXTEND)
 			memcpy(entry->digests[i].digest,
 			       entry->digests[ima_sha1_idx].digest,
 			       TPM_DIGEST_SIZE);
+#else
+			memset(entry->digests[i].digest, 0xfe, TPM_DIGEST_SIZE);
+#endif
 			continue;
 		}
 
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 83d53824aa98..0cc1189446a8 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -274,11 +274,23 @@ int __init ima_init_digests(void)
 		digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
 		crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id;
 
+#if IS_ENABLED(CONFIG_IMA_COMPAT_FALLBACK_TPM_EXTEND)
 		/* for unmapped TPM algorithms digest is still a padded SHA1 */
 		if (crypto_id == HASH_ALGO__LAST)
 			digest_size = SHA1_DIGEST_SIZE;
 
 		memset(digests[i].digest, 0xff, digest_size);
+#else
+		if (ima_algo_array[i].tfm) {
+			memset(digests[i].digest, 0xff, digest_size);
+		} else {
+			/*
+			 * Unsupported banks are invalidated with 0xfe ... fe
+			 * to disambiguate from violations.
+			 */
+			memset(digests[i].digest, 0xfe, digest_size);
+		}
+#endif
 	}
 
 	return 0;
-- 
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 ` Nicolai Stange [this message]
2025-03-23 21:18   ` [RFC PATCH v2 03/13] ima: invalidate unsupported PCR banks 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 ` [RFC PATCH v2 11/13] ima: introduce ima_pcr_invalidated_banks() helper Nicolai Stange
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-4-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

all inboxes | Powered by JetHome®