linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v1 0/1] Implement IMA Event Log Trimming
@ 2025-11-19 21:33 Anirudh Venkataramanan
  2025-11-19 21:33 ` [RFC v1 1/1] ima: Implement IMA event log trimming Anirudh Venkataramanan
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Anirudh Venkataramanan @ 2025-11-19 21:33 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
	Anirudh Venkataramanan, Steven Chen, Gregory Lumen,
	Lakshmi Ramasubramanian, Sush Shringarputale

==========================================================================
| A. Introduction                                                        |
==========================================================================

IMA events are kept in kernel memory and preserved across kexec soft
reboots. This can lead to increased kernel memory usage over time,
especially with aggressive IMA policies that measure everything. To reduce
memory pressure, it becomes necessary to discard IMA events but given that
IMA events are extended into PCRs in the TPM, just discarding events will
break the PCR extension chain, making future verification of the IMA event
log impossible.

This patch series proposes a method to discard IMA events while keeping the
log verifiable. While reducing memory pressure is the primary objective,
the second order benefit of trimming the IMA log is that IMA log verifiers
(local userspace daemon or a remote cloud service) can process smaller IMA
logs on a rolling basis, thus avoiding re-verification of previously
verified events.

The method has other advantages too:

 1. It provides a userspace interface that can be used to precisely control
    trim point, allowing for trim points to be optionally aligned with
    userspace IMA event log validation.

 2. It ensures that all necessary information required for continued IMA
    log validation is made available via the userspace interface at all
    times.

 3. It provides a simple mechanism for userspace applications to determine
    if the event log has been unexpectedly trimmed.

 4. The duration for which the IMA Measurement list mutex must be held (for
    trimming) is minimal.

==========================================================================
| B. Solution                                                            |
==========================================================================

--------------------------------------------------------------------------
| B.1 Overview                                                           |
--------------------------------------------------------------------------

The kernel trims the IMA event log based on PCR values supplied by userspace.
The core principles leveraged are as follows:

 - Given an IMA event log, PCR values for each IMA event can be obtained by
   recalulating the PCR extension for each event. Thus processing N events
   from the start will yield PCR values as of event N. This is referred to
   as "IMA event log replay".

 - To get the PCR value for event N + 1, only the PCR value as of event N
   is needed. If this can be known, events till and including N can be
   safely purged.

Putting it all together, we get the following userspace + kernel flow:

 1. A userspace application replays the IMA event log to generate PCR
    values and then triggers a trim by providing these values to the kernel
    (by writing to a pseudo file). 

    Optionally, the userspace application may verify these PCR values
    against the corresponding TPM quote, and trigger trimming only if
    the calculated PCR values match up to the expectations in the quote's
    PCR digest.

 2. The kernel uses the userspace supplied PCR values to trim the IMA
    measurements list at a specific point, and so these are referred to as
    "trim-to PCR values" in this context.

    Note that the kernel doesn't really understand what these userspace
    provided PCR values mean or what IMA event they correspond to, and so
    it does its own IMA event replay till either the replayed PCR values
    match with the userspace provided ones, or it runs out of events.

    If a match is found, the kernel can proceed with trimming the IMA
    measurements list. This is done in two steps, to keep locking context
    minimal.

    step 1: Find and return the list entry (as a count from head) of exact
            match. This does not lock the measurements list mutex, ensuring
            new events can be appended to the log.

    step 2: Lock the measurements list mutex and trim the measurements list
            at the previously identified list entry.

   If the trim is successful, the trim-to PCR values are saved as "starting
   PCR values". The next time userspace wants to replay the IMA event log,
   it will use the starting PCR values as the base for the IMA event log
   replay.

--------------------------------------------------------------------------
| B.2 Kernel Interfaces                                                  |
--------------------------------------------------------------------------

A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
following operations is exposed.

  read: returns starting PCR values stored in the kernel (within IMA
        specifically).

 write: writes trim-to PCR values to trigger trimming. If trimming is
        successful, trim-to PCR values are stored as starting PCR values.
        requires root privileges.

--------------------------------------------------------------------------
| B.3 Walk-through with a real example                                   |
--------------------------------------------------------------------------

This is a real example from a test run.

Suppose this IMA policy is deployed:

  measure func=FILE_CHECK mask=MAY_READ pcr=10
  measure func=FILE_CHECK mask=MAY_WRITE pcr=11

When the policy is deployed, a zero digest starting PCR value will be set
for each PCR used. If the TPM supports multiple hashbanks, there will be
one starting PCR value per PCR, per TPM hashbank. This can be seen in the
following hexdump:

$ sudo hexdump -vC /sys/kernel/config/ima/pcrs
00000000  70 63 72 31 30 3a 73 68  61 31 3a 00 00 00 00 00  |pcr10:sha1:.....|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 70  |...............p|
00000020  63 72 31 31 3a 73 68 61  31 3a 00 00 00 00 00 00  |cr11:sha1:......|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 70 63  |..............pc|
00000040  72 31 30 3a 73 68 61 32  35 36 3a 00 00 00 00 00  |r10:sha256:.....|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 70 63 72 31 31  |...........pcr11|
00000070  3a 73 68 61 32 35 36 3a  00 00 00 00 00 00 00 00  |:sha256:........|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  70 63 72 31 30 3a 73 68  |........pcr10:sh|
000000a0  61 33 38 34 3a 00 00 00  00 00 00 00 00 00 00 00  |a384:...........|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  00 00 00 00 00 70 63 72  31 31 3a 73 68 61 33 38  |.....pcr11:sha38|
000000e0  34 3a 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |4:..............|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000110  00 00                                             |..|
00000112

Let's say that a userspace utility replays the IMA event log, and triggers
trimming by writing the following PCR values (i.e. trim-to PCR values) to the
pseudo file:

pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41

The trim is successful, 

1. Some number of entries from the measurements log will disappear. This
   can be verified by reading out the ASCII or binary IMA measurements
   file.

2. The trim-to PCR values are saved as starting PCR values. This can be
   verified by reading out the pseudo file again as shown below. Note that
   even through only sha256 PCR values were written, the kernel populated
   sha1 and sha384 starting values as well.

$ sudo hexdump -vC /sys/kernel/config/ima/pcrs

00000000  70 63 72 31 30 3a 73 68  61 31 3a c4 7f 9d 00 68  |pcr10:sha1:....h|
00000010  e4 86 71 bf bc ae f0 10  12 ff 68 e2 9e 74 e4 70  |..q.......h..t.p|
00000020  63 72 31 31 3a 73 68 61  31 3a 90 d7 17 ac 60 4d  |cr11:sha1:....`M|
00000030  c8 25 ce 77 7d 9d 94 cf  44 7b b2 2e 2e e2 70 63  |.%.w}...D{....pc|
00000040  72 31 30 3a 73 68 61 32  35 36 3a 82 68 78 29 06  |r10:sha256:.hx).|
00000050  55 5c f3 ae fc 17 9f 81  5c 87 85 27 dd 4e 67 ea  |U\......\..'.Ng.|
00000060  a8 36 57 2e ba ba b3 19  77 92 2c 70 63 72 31 31  |.6W.....w.,pcr11|
00000070  3a 73 68 61 32 35 36 3a  4c 7f 31 92 71 83 ea cb  |:sha256:L.1.q...|
00000080  53 d5 1d 95 b0 16 29 16  fd 3f ca 51 a8 d1 ef c6  |S.....)..?.Q....|
00000090  dd e3 80 5e b8 91 fe 41  70 63 72 31 30 3a 73 68  |...^...Apcr10:sh|
000000a0  61 33 38 34 3a 8e d6 12  18 b1 d6 cd 95 16 98 33  |a384:..........3|
000000b0  2b 7d a2 d6 d9 05 c7 e8  5b 15 b0 91 c5 fc 23 d1  |+}......[.....#.|
000000c0  f9 a8 8d 60 50 5c e9 64  5f d7 b3 b2 f1 9c 90 0a  |...`P\.d_.......|
000000d0  45 53 5d b2 57 70 63 72  31 31 3a 73 68 61 33 38  |ES].Wpcr11:sha38|
000000e0  34 3a 25 fc 21 28 31 5a  f7 c6 fb 0f 40 c9 06 e6  |4:%.!(1Z....@...|
000000f0  c5 da ed 20 61 a1 03 54  4f 67 18 88 82 0f 48 d1  |... a..TOg....H.|
00000100  2f e0 3d 36 46 5e 94 a4  88 51 f8 91 39 7e e5 97  |/.=6F^...Q..9~..|
00000110  2c c5                                             |,.|
00000112

--------------------------------------------------------------------------
| C. Footnotes                                                           |
--------------------------------------------------------------------------

1. The 'pcrs' pseudo file is currently part of configfs. This was due to
   some early internal feedback in a different context. This can as well be
   in securityfs with the rest of the IMA pseudo files.

2. PCR values are never read out of the TPM at any point. All PCR values
   used are derived from IMA event log replay.

3. Code is "RFC quality". Refinements can be made if the method is accepted.

4. For functional validation, base kernel version was 6.17 stable, with the
   most recent tested version being 6.17.8.

5. Code has been validated to some degree using a python-based internal test
   tool. This can be published if there is community interest. 

Steven Chen (1):
  ima: Implement IMA event log trimming

 drivers/Kconfig                       |   2 +
 drivers/Makefile                      |   1 +
 drivers/ima/Kconfig                   |  13 +
 drivers/ima/Makefile                  |   2 +
 drivers/ima/ima_config_pcrs.c         | 291 ++++++++++++++++++
 include/linux/ima.h                   |  27 ++
 security/integrity/ima/Makefile       |   4 +
 security/integrity/ima/ima.h          |   8 +
 security/integrity/ima/ima_init.c     |  44 +++
 security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
 security/integrity/ima/ima_policy.c   |   7 +-
 security/integrity/ima/ima_queue.c    |   5 +-
 12 files changed, 821 insertions(+), 4 deletions(-)
 create mode 100644 drivers/ima/Kconfig
 create mode 100644 drivers/ima/Makefile
 create mode 100644 drivers/ima/ima_config_pcrs.c
 create mode 100644 security/integrity/ima/ima_log_trim.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC v1 1/1] ima: Implement IMA event log trimming
  2025-11-19 21:33 [RFC v1 0/1] Implement IMA Event Log Trimming Anirudh Venkataramanan
@ 2025-11-19 21:33 ` Anirudh Venkataramanan
  2025-11-20 11:02 ` [RFC v1 0/1] Implement IMA Event Log Trimming Roberto Sassu
  2025-11-20 22:58 ` Mimi Zohar
  2 siblings, 0 replies; 10+ messages in thread
From: Anirudh Venkataramanan @ 2025-11-19 21:33 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
	Anirudh Venkataramanan, Steven Chen, Gregory Lumen,
	Lakshmi Ramasubramanian, Sush Shringarputale

From: Steven Chen <chenste@linux.microsoft.com>

Implement a PCR value based method for IMA event log trimming by
exposing a new pseudo file /sys/kernel/config/ima/pcrs. This
pseudo file allows userspace to:

 - read IMA starting PCR values.
 - write trim-to PCR values to trigger IMA log trimming.

If a trimming operation is successful, one or more entries will be
purged from the IMA measurements list in the kernel.

Signed-off-by: Steven Chen <chenste@linux.microsoft.com>
Signed-off-by: Anirudh Venkataramanan <anirudhve@linux.microsoft.com>
---
 drivers/Kconfig                       |   2 +
 drivers/Makefile                      |   1 +
 drivers/ima/Kconfig                   |  13 +
 drivers/ima/Makefile                  |   2 +
 drivers/ima/ima_config_pcrs.c         | 291 ++++++++++++++++++
 include/linux/ima.h                   |  27 ++
 security/integrity/ima/Makefile       |   4 +
 security/integrity/ima/ima.h          |   8 +
 security/integrity/ima/ima_init.c     |  44 +++
 security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
 security/integrity/ima/ima_policy.c   |   7 +-
 security/integrity/ima/ima_queue.c    |   5 +-
 12 files changed, 821 insertions(+), 4 deletions(-)
 create mode 100644 drivers/ima/Kconfig
 create mode 100644 drivers/ima/Makefile
 create mode 100644 drivers/ima/ima_config_pcrs.c
 create mode 100644 security/integrity/ima/ima_log_trim.c

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4915a63866b0..35b83be86c42 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -251,4 +251,6 @@ source "drivers/hte/Kconfig"
 
 source "drivers/cdx/Kconfig"
 
+source "drivers/ima/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 8e1ffa4358d5..3aad6096d416 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -197,3 +197,4 @@ obj-$(CONFIG_DPLL)		+= dpll/
 
 obj-$(CONFIG_DIBS)		+= dibs/
 obj-$(CONFIG_S390)		+= s390/
+obj-$(CONFIG_IMA_PCRS)		+= ima/
diff --git a/drivers/ima/Kconfig b/drivers/ima/Kconfig
new file mode 100644
index 000000000000..9e465fbe3adb
--- /dev/null
+++ b/drivers/ima/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config IMA_PCRS
+	bool "IMA Event Log Trimming"
+	default n
+	help
+	  Say Y here if you want support for IMA Event Log Trimming.
+
+	  This creates the configfs file /sys/kernel/config/ima/pcrs.
+	  Userspace
+	  - writes to this file to trigger IMA event log trimming
+	  - reads this file to get IMA starting PCR values
+
+	  If unsure, say N.
diff --git a/drivers/ima/Makefile b/drivers/ima/Makefile
new file mode 100644
index 000000000000..ac9b9b96b5cb
--- /dev/null
+++ b/drivers/ima/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_IMA_PCRS)	+= ima_config_pcrs.o
diff --git a/drivers/ima/ima_config_pcrs.c b/drivers/ima/ima_config_pcrs.c
new file mode 100644
index 000000000000..f329665f2b90
--- /dev/null
+++ b/drivers/ima/ima_config_pcrs.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Microsoft Corporation
+ *
+ * Authors:
+ * Steven Chen <chenste@linux.microsoft.com>
+ *
+ * File: ima_config_pcrs.c
+ *
+ * Implements IMA interface to allow userspace to read IMA starting PCR
+ * values and trigger IMA event log trimming.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/configfs.h>
+#include <linux/printk.h>
+#include <linux/tpm.h>
+#include <linux/ima.h>
+/*
+ * The IMA PCR configfs is a childless subsystem.  It cannot create
+ * any config_items.  It just has attribute pcrs for read and write.
+ */
+
+#define MAX_HASH_ALGO_NAME_LENGTH 16 /*sha1, sha256, ...*/
+#define PCR_NAME_LEN 6  /*pcrxx:*/
+#define MIN_PCR_RECORD_LENGTH 20
+#define OFFSET_IDX_1 3
+#define OFFSET_IDX_2 4
+#define OFFSET_COLON 5
+
+/*
+ * mutex protects atomicity of trimming measurement list
+ * and updating ima_start_point_pcr_values
+ * protects concurrent writes to the IMA PCR values
+ * This also not allow memory allocation while waiting the mutex
+ */
+static DEFINE_MUTEX(ima_pcr_write_mutex);
+
+/* show current IMA Start Point PCR values in ascii format */
+static ssize_t ima_config_pcrs_ascii_show(struct config_item *item, char *page)
+{
+	int len = 0;
+
+	for (int i = 0; i < num_tpm_banks; i++) {
+		for (int j = 0; j < num_pcr_configured; j++) {
+			int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+
+			/*
+			 * Write "pcrXX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+			 * where XX is the PCR index (00-23),
+			 * where AAA is the algorithm name, such as sha1, sha256, sha384..
+			 * and x...x is the digest data in hex format
+			 */
+			len += sprintf(page + len, "pcr%02d:", digests_pcr_map[j]);
+			len += sprintf(page + len, "%s:", hash_algo_name[algorithm_id]);
+			for (int k = 0; k < hash_digest_size[algorithm_id]; k++)
+				len += sprintf(page + len, "%02x",
+					ima_start_point_pcr_values[i].digests[j][k]);
+			len += sprintf(page + len, "\n");
+		}
+	}
+	return len;
+}
+
+/* show current IMA Start Point PCR values */
+static ssize_t ima_config_pcrs_show(struct config_item *item, char *page)
+{
+	int len = 0;
+
+	for (int i = 0; i < num_tpm_banks; i++) {
+		int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+
+		for (int j = 0; j < TPM2_PLATFORM_PCR; ++j) {
+			int digest_index = pcr_digests_map[j];
+
+			if (digest_index == -1)
+				continue;
+			/*
+			 * Write "pcrXX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+			 * where XX is the PCR index (00-23),
+			 * where AAA is the algorithm name, such as sha1, sha256, sha384..
+			 * and x...x is the digest data in raw format
+			 */
+			len += sprintf(page + len, "pcr%02d:", j);
+			len += sprintf(page + len, "%s:", hash_algo_name[algorithm_id]);
+
+			memcpy(page + len, ima_start_point_pcr_values[i].digests[digest_index],
+			       hash_digest_size[algorithm_id]);
+
+			len += hash_digest_size[algorithm_id];
+		}
+	}
+
+	return len;
+}
+
+/* get PCR index from input string */
+static int get_pcr_idx(char *p, int offset)
+{
+	/* validate digits first */
+	if (!isdigit((unsigned char)p[offset + OFFSET_IDX_1]) ||
+	    !isdigit((unsigned char)p[offset + OFFSET_IDX_2]))
+		return -EINVAL;
+
+	return (p[offset + OFFSET_IDX_1] - '0') * 10 + (p[offset + OFFSET_IDX_2] - '0');
+}
+
+/*
+ * Parse the input data
+ * Get new PCR values and trim IMA event log
+ */
+static ssize_t ima_config_pcrs_store(struct config_item *item, const char *page, size_t count)
+{
+	struct ima_pcr_value *ima_pcr_values;
+	unsigned long pcr_found = 0;
+	char *p = (char *)page;
+	int pcr_count;
+	int offset = 0;
+	int ret = -EINVAL;
+
+	if (count <= 0)
+		return ret;
+
+	if (num_pcr_configured <= 0)
+		return -ENOMEM;
+
+	/*
+	 * Only one thread can write to the PCRs at a time
+	 * Not allocate memory while waiting the mutex
+	 */
+
+	mutex_lock(&ima_pcr_write_mutex);
+
+	pcr_count = num_pcr_configured;
+
+	ima_pcr_values = kcalloc(num_pcr_configured, sizeof(*ima_pcr_values), GFP_KERNEL);
+
+	if (!ima_pcr_values) {
+		ret = -ENOMEM;
+		goto out_notrim_unlock;
+	}
+
+	/*
+	 * parse the input data
+	 * each entry is like: pcrX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+	 * where XX is the PCR index (00-23),
+	 * where AAA is the algorithm name, e.g. sha1, sha256, sha384..
+	 * and x...x is the digest data in binary format
+	 * multiple entries are concatenated together
+	 */
+	while (offset < count) {
+		int index, mapped_index, algo_id, algo_name_len = 0;
+		char algo_name[MAX_HASH_ALGO_NAME_LENGTH];
+		u8 digest_len;
+
+		/* Need at least "pcrXX:AAA:" */
+		if (offset + MIN_PCR_RECORD_LENGTH > count)
+			goto out_nottrim;
+
+		if (memcmp(p + offset, "pcr", 3) != 0 || p[offset + OFFSET_COLON] != ':') {
+			pr_alert("invalid input format\n");
+			goto out_nottrim;
+		}
+
+		/* Get the PCR index*/
+		index = get_pcr_idx(p, offset);
+		if (index < 0 || index >= TPM2_PLATFORM_PCR) {
+			pr_alert("invalid PCR index: %d\n", index);
+			goto out_nottrim;
+		}
+
+		offset += PCR_NAME_LEN;
+
+		while (offset + algo_name_len < count && p[offset + algo_name_len] != ':')
+			++algo_name_len;
+
+		/* Check actual algo name length */
+		if (algo_name_len == 0 || algo_name_len >= MAX_HASH_ALGO_NAME_LENGTH) {
+			pr_err("ima pcr configuration: invalid algorithm name length\n");
+			goto out_nottrim;
+		}
+
+		memcpy(algo_name, p + offset, algo_name_len);
+		algo_name[algo_name_len] = '\0';
+
+		algo_id = match_string(hash_algo_name, HASH_ALGO__LAST, algo_name);
+		/* validate algo_id */
+		if (algo_id < HASH_ALGO_SHA1 || algo_id >= HASH_ALGO__LAST) {
+			pr_err("ima pcr configuration: invalid algorithm ID\n");
+			goto out_nottrim;
+		}
+		digest_len = hash_digest_size[algo_id];
+		offset += algo_name_len + 1;
+		/* validate we have enough data for the digest */
+		if (offset + digest_len > count)
+			goto out_nottrim;
+
+		mapped_index = pcr_digests_map[index];
+
+		if (pcr_digests_map[index] != -1 && !test_bit(index, &pcr_found)) {
+			ima_pcr_values[mapped_index].algo_id = algo_id;
+			set_bit(index, &pcr_found);
+			memcpy(ima_pcr_values[mapped_index].digest, p + offset, digest_len);
+			--pcr_count;
+		} else {
+			pr_alert("invalid PCR index or duplicate PCR set index: %d\n", index);
+			goto out_nottrim;
+		}
+		offset += digest_len;
+	}
+
+	if (pcr_count != 0) {
+		/* not all PCRs are provided */
+		pr_alert("not all PCRs are provided\n");
+		goto out_nottrim;
+	}
+
+	/*
+	 * all configured PCRs values are provided, now recalculate the IMA PCRs
+	 * and check if they can match the these PCR values
+	 * Trim the IMA event logs if the given PCR values match
+	 */
+	ret = ima_trim_event_log((const void *)ima_pcr_values);
+	if (ret >= 0)
+		ret = count;
+
+out_nottrim:
+	kfree(ima_pcr_values);
+out_notrim_unlock:
+	mutex_unlock(&ima_pcr_write_mutex);
+	return ret;
+}
+
+CONFIGFS_ATTR(ima_config_, pcrs);
+CONFIGFS_ATTR_RO(ima_config_, pcrs_ascii);
+
+static struct configfs_attribute *ima_config_pcr_attrs[] = {
+	&ima_config_attr_pcrs,
+	&ima_config_attr_pcrs_ascii,
+	NULL,
+};
+
+static const struct config_item_type ima_config_pcr_type = {
+	.ct_attrs	= ima_config_pcr_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct configfs_subsystem config_ima_pcrs = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "ima",
+			.ci_type = &ima_config_pcr_type,
+		},
+	},
+};
+
+static int __init configfs_ima_pcrs_init(void)
+{
+	struct tpm_chip *tpm_chip;
+	int ret;
+
+	tpm_chip = tpm_default_chip();
+	if (!tpm_chip) {
+		pr_err("No TPM chip found, IMA PCR configfs disabled\n");
+		return -ENODEV;
+	}
+
+	config_group_init(&config_ima_pcrs.su_group);
+	mutex_init(&config_ima_pcrs.su_mutex);
+	ret = configfs_register_subsystem(&config_ima_pcrs);
+	if (ret) {
+		pr_err("Error %d while registering subsystem %s\n",
+		       ret, config_ima_pcrs.su_group.cg_item.ci_namebuf);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit configfs_ima_pcrs_exit(void)
+{
+	configfs_unregister_subsystem(&config_ima_pcrs);
+}
+
+module_init(configfs_ima_pcrs_init);
+module_exit(configfs_ima_pcrs_exit);
+MODULE_DESCRIPTION("IMA PCRS configfs module");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 8e29cb4e6a01..f8a6209b9423 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -12,6 +12,8 @@
 #include <linux/security.h>
 #include <linux/kexec.h>
 #include <crypto/hash_info.h>
+#include <linux/tpm.h>
+
 struct linux_binprm;
 
 #ifdef CONFIG_IMA
@@ -24,6 +26,31 @@ extern int ima_measure_critical_data(const char *event_label,
 				     const void *buf, size_t buf_len,
 				     bool hash, u8 *digest, size_t digest_len);
 
+#ifdef CONFIG_IMA_PCRS
+struct ima_pcr_value {
+	u8 digest[HASH_MAX_DIGESTSIZE];	/* PCR value */
+	u8 algo_id;                     /* Hash algorithm ID */
+};
+
+struct tpm_bank_pcr_values {
+	u16 alg_id;
+	u8 **digests; /* Array of pointers, one per configured PCR */
+};
+
+extern u8 num_tpm_banks;
+extern u8 num_pcr_configured;
+
+extern signed char algo_pcr_bank_map[HASH_ALGO__LAST];
+extern signed char pcr_digests_map[TPM2_PLATFORM_PCR];
+extern signed char digests_pcr_map[TPM2_PLATFORM_PCR];
+
+extern struct tpm_bank_pcr_values *ima_start_point_pcr_values;
+
+extern int ima_trim_event_log(const void *bin);
+#else
+static inline int ima_trim_event_log(const void *bin) { return 0; }
+#endif /* CONFIG_IMA_PCRS */
+
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
 extern void ima_appraise_parse_cmdline(void);
 #else
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b376d38b4ee6..ae9ce210638d 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -18,3 +18,7 @@ ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
 ifeq ($(CONFIG_EFI),y)
 ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
 endif
+
+ifeq ($(CONFIG_IMA_PCRS),y)
+ima-y += ima_log_trim.o
+endif
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e3d71d8d56e3..d9accf504e42 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -55,6 +55,8 @@ struct ima_algo_desc {
 	enum hash_algo algo;
 };
 
+extern struct mutex ima_extend_list_mutex; /* protects extending measurement list */
+
 /* set during initialization */
 extern int ima_hash_algo __ro_after_init;
 extern int ima_sha1_idx __ro_after_init;
@@ -250,6 +252,12 @@ void ima_measure_kexec_event(const char *event_name);
 static inline void ima_measure_kexec_event(const char *event_name) {}
 #endif
 
+#ifdef CONFIG_IMA_PCRS
+extern int ima_add_configured_pcr(int new_pcr_index);
+#else
+static inline int ima_add_configured_pcr(int new_pcr_index) { return 0; }
+#endif /* CONFIG_IMA_PCRS */
+
 /*
  * The default binary_runtime_measurements list format is defined as the
  * platform native format.  The canonical format is defined as little-endian.
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index a2f34f2d8ad7..1102d2073336 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -24,6 +24,16 @@
 const char boot_aggregate_name[] = "boot_aggregate";
 struct tpm_chip *ima_tpm_chip;
 
+#ifdef CONFIG_IMA_PCRS
+u8 num_tpm_banks;
+u8 num_pcr_configured;
+signed char algo_pcr_bank_map[HASH_ALGO__LAST];
+signed char digests_pcr_map[TPM2_PLATFORM_PCR];
+signed char pcr_digests_map[TPM2_PLATFORM_PCR];
+
+struct tpm_bank_pcr_values *ima_start_point_pcr_values;
+#endif
+
 /* Add the boot aggregate to the IMA measurement list and extend
  * the PCR register.
  *
@@ -134,6 +144,40 @@ int __init ima_init(void)
 	if (rc != 0)
 		return rc;
 
+#ifdef CONFIG_IMA_PCRS
+
+	num_tpm_banks = 0;
+	num_pcr_configured = 0;
+
+	for (int i = 0; i < TPM2_PLATFORM_PCR; i++) {
+		pcr_digests_map[i] = -1;
+		digests_pcr_map[i] = -1;
+	}
+
+	for (int i = 0; i < HASH_ALGO__LAST; i++)
+		algo_pcr_bank_map[i] = -1;
+
+	if (ima_tpm_chip) {
+		num_tpm_banks = ima_tpm_chip->nr_allocated_banks;
+		/* Allocate memory for the ima start point PCR values */
+		ima_start_point_pcr_values = kcalloc(num_tpm_banks,
+						     sizeof(*ima_start_point_pcr_values),
+						     GFP_KERNEL);
+
+		if (!ima_start_point_pcr_values)
+			return -ENOMEM;
+
+		for (int i = 0; i < num_tpm_banks; i++) {
+			int algo_id = ima_tpm_chip->allocated_banks[i].crypto_id;
+
+			ima_start_point_pcr_values[i].alg_id = algo_id;
+			algo_pcr_bank_map[algo_id] = i;
+		}
+
+		ima_add_configured_pcr(CONFIG_IMA_MEASURE_PCR_IDX);
+	}
+#endif
+
 	/* It can be called before ima_init_digests(), it does not use TPM. */
 	ima_load_kexec_buffer();
 
diff --git a/security/integrity/ima/ima_log_trim.c b/security/integrity/ima/ima_log_trim.c
new file mode 100644
index 000000000000..85025d502db2
--- /dev/null
+++ b/security/integrity/ima/ima_log_trim.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Microsoft Corporation
+ *
+ * Authors:
+ * Steven Chen <chenste@linux.microsoft.com>
+ *
+ * File: ima_log_trim.c
+ *	Implements PCR value based IMA Event Log Trimming
+ */
+
+#include <linux/fcntl.h>
+#include <linux/kernel_read_file.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/vmalloc.h>
+#include <linux/ima.h>
+#include <crypto/hash_info.h>
+
+#include "ima.h"
+
+struct digest_value {
+	u8 digest[HASH_MAX_DIGESTSIZE];	/* digest value */
+};
+
+/* Average IMA event log data length */
+#define EXTEND_BUF_LEN 300
+
+static struct digest_value *ima_extended_pcr;
+static struct ima_pcr_value *target_pcr_values;
+static unsigned long pcr_matched;
+static int pcr_match_needed;
+
+/* Calculate the hash digest for the given data using the specified algorithm */
+static int ima_calculate_pcr(const void *data, size_t len, u8 *md, int algo)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	int ret = 0, size;
+
+	tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
+
+	if (IS_ERR(tfm)) {
+		ret = -ENOMEM;
+		goto out_nocleanup;
+	}
+
+	/* Allocate memory for the shash_desc structure and initialize it */
+	size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
+
+	desc = kzalloc(size, GFP_KERNEL);
+	if (!desc) {
+		crypto_free_shash(tfm);
+		ret = -ENOMEM;
+		goto out_nocleanup;
+	}
+
+	desc->tfm = tfm;
+
+	if (crypto_shash_init(desc)) {
+		ret = -EINVAL;
+		goto out_cleanup;
+	}
+
+	/* Update the hash with the input data */
+	if (crypto_shash_update(desc, data, len)) {
+		ret = -EINVAL;
+		goto out_cleanup;
+	}
+
+	/* Finalize the hash and retrieve the digest */
+	ret = crypto_shash_final(desc, md);
+	if (ret)
+		goto out_cleanup;
+
+	ret = crypto_shash_digestsize(tfm);
+
+out_cleanup:
+	kfree(desc);
+	crypto_free_shash(tfm);
+out_nocleanup:
+	return ret;
+}
+
+/* Add a new configured PCR */
+int ima_add_configured_pcr(int new_pcr_index)
+{
+	if (!ima_tpm_chip)
+		return -1;
+
+	if (new_pcr_index < 0 || new_pcr_index >= TPM2_PLATFORM_PCR)
+		return -1;
+
+	if (pcr_digests_map[new_pcr_index] != -1)
+		return 0;
+
+	/*
+	 * For each TPM bank, expand one more entry for the new PCR
+	 * Allocate new PCR digest buffers for the new PCR
+	 */
+	for (int i = 0; i < num_tpm_banks; ++i) {
+		int length = hash_digest_size[ima_start_point_pcr_values[i].alg_id];
+		u8 *new_pcr = kmalloc(sizeof(u8) * length, GFP_KERNEL);
+		u8 **new_buf;
+
+		if (!new_pcr)
+			return -ENOMEM;
+		memset(new_pcr, 0, sizeof(u8) * length);
+		new_buf = (u8 **)krealloc(ima_start_point_pcr_values[i].digests,
+					(num_pcr_configured + 1) * sizeof(u8 *), GFP_KERNEL);
+		if (!new_buf) {
+			kfree(new_pcr);
+			return -ENOMEM;
+		}
+		new_buf[num_pcr_configured] = new_pcr;
+		ima_start_point_pcr_values[i].digests = new_buf;
+	}
+
+	pcr_digests_map[new_pcr_index] = num_pcr_configured;
+	digests_pcr_map[num_pcr_configured] = new_pcr_index;
+
+	++num_pcr_configured;
+	return 0;
+}
+
+/* Delete the IMA event logs */
+static int ima_purge_event_log(int number_logs)
+{
+	struct ima_queue_entry *qe;
+	int cur = 0;
+
+	mutex_lock(&ima_extend_list_mutex);
+	rcu_read_lock();
+
+	/*
+	 * Remove this entry from both hash table and the measurement list
+	 * When removing from hash table, decrease the length counter
+	 * so that the hash table re-sizing logic works correctly
+	 */
+	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+		/* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
+		if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
+			hlist_del_rcu(&qe->hnext);
+
+		atomic_long_dec(&ima_htable.len);
+		list_del_rcu(&qe->later);
+		++cur;
+		if (cur >= number_logs)
+			break;
+	}
+
+	rcu_read_unlock();
+	mutex_unlock(&ima_extend_list_mutex);
+	return cur;
+}
+
+/* compare the target PCR values with IMA Start Point PCR Values */
+static int ima_compare_pcr_values(struct ima_pcr_value *target_pcr_val)
+{
+	int count_not_matched = 0;
+
+	for (int i = 0; i < num_pcr_configured; ++i) {
+		int algo_id_tmp;
+		int bank_id_tmp;
+		int hash_size;
+
+		algo_id_tmp = target_pcr_val[i].algo_id;
+		bank_id_tmp = algo_pcr_bank_map[algo_id_tmp];
+		hash_size = hash_digest_size[algo_id_tmp];
+
+		if (bank_id_tmp < 0 || bank_id_tmp >= num_tpm_banks)
+			return -EINVAL;
+
+		/* already matched this PCR, skip it */
+		if (memcmp(ima_start_point_pcr_values[bank_id_tmp].digests[i],
+			   target_pcr_val[i].digest, hash_size) == 0) {
+			set_bit(i, &pcr_matched);
+			continue;
+		}
+		count_not_matched++;
+	}
+	return count_not_matched;
+}
+
+static int ima_recalculate_check_pcrs(int pcr_map_index, char *org_pointer, char *digest_pointer,
+				      int total_length, bool digest_zero)
+{
+	int ret = 0;
+
+	/* Recalculate the PCR for each bank */
+	for (int i = 0; i < num_tpm_banks; i++) {
+		int digest_len, crypto_id, hash_size, idx, len;
+		u8 digest[HASH_MAX_DIGESTSIZE];
+
+		crypto_id = ima_start_point_pcr_values[i].alg_id;
+		hash_size = hash_digest_size[crypto_id];
+		digest_len = 0;
+		/* Prepare digest buffer */
+		if (digest_zero) {
+			/* all zero digest, use 0xFF to extend */
+			memset(digest, 0xFF, HASH_MAX_DIGESTSIZE);
+			digest_len = hash_size;
+		} else {
+			memcpy(digest_pointer, org_pointer, total_length);
+			digest_len = ima_calculate_pcr(digest_pointer, total_length, digest,
+						       crypto_id);
+			if (digest_len < 0) {
+				ret = digest_len;
+				break;
+			}
+		}
+
+		len = digest_len + hash_size;
+
+		idx = i * num_pcr_configured + pcr_map_index;
+		memcpy(digest_pointer, ima_extended_pcr[idx].digest, hash_size);
+		memcpy(digest_pointer + hash_size, digest, digest_len);
+
+		/* Recalculate the PCR starting with the IMA Start Point PCR value */
+		digest_len = ima_calculate_pcr(digest_pointer, len, ima_extended_pcr[idx].digest,
+					       crypto_id);
+
+		if (digest_len < 0) {
+			ret = digest_len;
+			break;
+		}
+
+		/*
+		 * Check if the extended PCR value matches the target PCR value
+		 * if matched, mark this PCR as matched
+		 * if all PCRs matched, set the entry_found flag
+		 */
+		if (crypto_id == target_pcr_values[pcr_map_index].algo_id) {
+			if (memcmp(ima_extended_pcr[idx].digest,
+				   target_pcr_values[pcr_map_index].digest, hash_size) == 0) {
+				set_bit(pcr_map_index, &pcr_matched);
+				--pcr_match_needed;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int ima_get_log_count(void)
+{
+	u8 algo_digest_buffer[EXTEND_BUF_LEN];
+	u8 digest_buffer[EXTEND_BUF_LEN];
+	struct ima_queue_entry *qe;
+	int count = 0;
+	int ret = 0;
+	unsigned int hash_digest_length;
+
+	/* Event log digests algorithm is SHA1 */
+	hash_digest_length = hash_digest_size[HASH_ALGO_SHA1];
+	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+		char *org_digest_pointer, *digest_pointer;
+		int pcr_idx, pcr_map_index, total_length;
+		struct ima_template_entry *e = qe->entry;
+		bool digest_zero;
+
+		if (!qe->entry) {
+			ret = -EINVAL;
+			break;
+		}
+		pcr_idx = e->pcr;
+		pcr_map_index = pcr_digests_map[pcr_idx];
+
+		if (test_bit(pcr_map_index, &pcr_matched) || pcr_digests_map[pcr_idx] == -1) {
+			/* already matched this PCR, something wrong */
+			ret = -EINVAL;
+			break;
+		}
+		/* The original digest buffer is used to save data for multiple banks/algorithms */
+		org_digest_pointer = digest_buffer;
+		digest_pointer = algo_digest_buffer;
+
+		total_length = e->template_data_len;
+
+		/* Allocate large memory for the original and digest buffers if needed */
+		if (total_length > EXTEND_BUF_LEN) {
+			org_digest_pointer = kzalloc(total_length, GFP_KERNEL);
+			if (!org_digest_pointer) {
+				ret = -ENOMEM;
+				break;
+			}
+			digest_pointer = kzalloc(total_length, GFP_KERNEL);
+			if (!digest_pointer) {
+				kfree(org_digest_pointer);
+				ret = -ENOMEM;
+				break;
+			}
+		}
+
+		digest_zero = true;
+		/*
+		 * Check if the original digest is all zeros or not
+		 * if not all zero, use template data to recalculate PCR
+		 */
+		if (memchr_inv(e->digests->digest, 0, hash_digest_length) != NULL) {
+			int offset = 0;
+
+			for (int i = 0; i < e->template_desc->num_fields; i++) {
+				memcpy(org_digest_pointer + offset, &e->template_data[i].len,
+				       sizeof(e->template_data[i].len));
+				offset += sizeof(e->template_data[i].len);
+				memcpy(org_digest_pointer + offset, e->template_data[i].data,
+				       e->template_data[i].len);
+				offset += e->template_data[i].len;
+			}
+			digest_zero = false;
+		}
+
+		count++;
+
+		/* Check if this log entry can match the target PCRs */
+		ret = ima_recalculate_check_pcrs(pcr_map_index, org_digest_pointer,
+						 digest_pointer, total_length, digest_zero);
+
+		if (total_length > EXTEND_BUF_LEN) {
+			kfree(org_digest_pointer);
+			kfree(digest_pointer);
+		}
+
+		/* If entry found or error occurred, break the loop */
+		if (ret < 0 || pcr_match_needed <= 0)
+			break;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	if (pcr_match_needed <= 0)
+		return count;
+	else
+		return 0;
+}
+
+/*
+ * Trim the IMA event log to match the given PCR values
+ * Return:
+ *  >0: number of log entries removed
+ *   0: no log entries removed
+ *  -1: error
+ *  -ENOENT: no matching log entry found
+ *  -EIO: I/O error when removing log entries
+ *  -EINVAL: invalid parameter
+ *  -ENOMEM: memory allocation failure
+ */
+int ima_trim_event_log(const void *bin)
+{
+	int count, ret = -1;
+
+	count = 0;
+	target_pcr_values = (struct ima_pcr_value *)bin;
+	pcr_matched = 0;
+
+	pcr_match_needed = ima_compare_pcr_values(target_pcr_values);
+
+	/* No need to trim */
+	if (pcr_match_needed <= 0) {
+		ret = 0;
+		goto out_nofree;
+	}
+
+	ima_extended_pcr = kcalloc(num_tpm_banks * num_pcr_configured,
+				   sizeof(*ima_extended_pcr), GFP_KERNEL);
+
+	if (!ima_extended_pcr) {
+		ret = -ENOMEM;
+		goto out_nofree;
+	}
+
+	/* Initialize ima_extended_pcr with ima_start_point_pcr_values */
+	for (int i = 0; i < num_tpm_banks; i++) {
+		int length = hash_digest_size[ima_start_point_pcr_values[i].alg_id];
+
+		for (int j = 0; j < num_pcr_configured; ++j) {
+			int record_index = i * num_pcr_configured + j;
+
+			memcpy(ima_extended_pcr[record_index].digest,
+			       ima_start_point_pcr_values[i].digests[j], length);
+		}
+	}
+
+	ret = ima_get_log_count();
+
+	if (ret <= 0)
+		goto out_notrim;
+
+	count = ret;
+
+	/* Remove logs from the IMA log list */
+	ret = ima_purge_event_log(count);
+
+	if (ret == count) {
+		/* Update the IMA Start Point PCR values */
+		for (int i = 0; i < num_tpm_banks; i++) {
+			int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+			int hash_size = hash_digest_size[algorithm_id];
+
+			for (int j = 0; j < num_pcr_configured; j++) {
+				int ext_idx = i * num_pcr_configured + j;
+
+				memcpy(ima_start_point_pcr_values[i].digests[j],
+				       ima_extended_pcr[ext_idx].digest, hash_size);
+			}
+		}
+	} else {
+		/* something wrong, should not happen */
+		ret = -EIO;
+	}
+
+out_notrim:
+	kfree(ima_extended_pcr);
+
+out_nofree:
+	return ret;
+}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 7468afaab686..fe537827ac1f 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1895,10 +1895,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 			ima_log_string(ab, "pcr", args[0].from);
 
 			result = kstrtoint(args[0].from, 10, &entry->pcr);
-			if (result || INVALID_PCR(entry->pcr))
+			if (result || INVALID_PCR(entry->pcr)) {
 				result = -EINVAL;
-			else
+			} else {
 				entry->flags |= IMA_PCR;
+				if (ima_add_configured_pcr(entry->pcr) < 0)
+					result = -EINVAL;
+			}
 
 			break;
 		case Opt_template:
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 590637e81ad1..7bbfdd2ce3b0 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -39,11 +39,12 @@ struct ima_h_table ima_htable = {
 	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
 };
 
-/* mutex protects atomicity of extending measurement list
+/*
+ * This mutex protects atomicity of extending measurement list
  * and extending the TPM PCR aggregate. Since tpm_extend can take
  * long (and the tpm driver uses a mutex), we can't use the spinlock.
  */
-static DEFINE_MUTEX(ima_extend_list_mutex);
+DEFINE_MUTEX(ima_extend_list_mutex);
 
 /*
  * Used internally by the kernel to suspend measurements.
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-19 21:33 [RFC v1 0/1] Implement IMA Event Log Trimming Anirudh Venkataramanan
  2025-11-19 21:33 ` [RFC v1 1/1] ima: Implement IMA event log trimming Anirudh Venkataramanan
@ 2025-11-20 11:02 ` Roberto Sassu
  2025-11-21 19:13   ` Anirudh Venkataramanan
  2025-11-20 22:58 ` Mimi Zohar
  2 siblings, 1 reply; 10+ messages in thread
From: Roberto Sassu @ 2025-11-20 11:02 UTC (permalink / raw)
  To: Anirudh Venkataramanan, linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
	Sush Shringarputale

On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
> ==========================================================================
> > A. Introduction                                                        |
> ==========================================================================
> 
> IMA events are kept in kernel memory and preserved across kexec soft
> reboots. This can lead to increased kernel memory usage over time,
> especially with aggressive IMA policies that measure everything. To reduce
> memory pressure, it becomes necessary to discard IMA events but given that
> IMA events are extended into PCRs in the TPM, just discarding events will
> break the PCR extension chain, making future verification of the IMA event
> log impossible.
> 
> This patch series proposes a method to discard IMA events while keeping the
> log verifiable. While reducing memory pressure is the primary objective,
> the second order benefit of trimming the IMA log is that IMA log verifiers
> (local userspace daemon or a remote cloud service) can process smaller IMA
> logs on a rolling basis, thus avoiding re-verification of previously
> verified events.

Hi Anirudh

I will rephrase this paragraph, to be sure that I understood it
correctly.

You are proposing a method to trim the measurement list and, at the
same time, to keep the measurement list verifiable after trimming. The
way you would like to achieve that is to keep the verification state in
the kernel in the form of PCR values.

Those values mean what verifiers have already verified. Thus, for the
next verification attempt, verifiers take the current PCR values as
starting values, replay the truncated IMA measurement list, and again
you match with the current PCR values and you trim until that point.

So the benefit of this proposal is that you keep the verification of
the IMA measurement list self-contained by using the last verification
state (PCR starting value) and the truncated IMA measurement list as
the inputs of your verification.

Let me reiterate on the trusted computing principles IMA relies on for
providing the evidence about a system's integrity.

Unless you are at the beginning of the measurement chain, where the
Root of Trust for Measurement (RTM) is trusted by assumption, the
measurements done by a component can be trusted because that component
was already measured by the previous component in the boot chain,
before it had any chance to corrupt the system.

In the context of IMA, IMA can be trusted to make new measurements
because it measures every file before those files could cause any harm
to the system. So, potentially IMA and the kernel can be corrupted by
any file.

What you are proposing would not work, because you are placing trust in
an input (the PCR starting value) that can be manipulated at any time
by a corrupted kernel, before you had the chance to detect such
corruption.

Let me describe a scenario where I could take advantage of such
weakness. After the first measurement list trim, I perform an attack on
the system such that it corrupts the kernel. IMA added a new entry in
the measurement list, which would reveal the attack.

But, since I have control of the kernel, I conveniently update the PCR
starting value to replay the new measurement entry, and remove the
measurement entry from the measurement list.

Now, from the perspective of the user space verifiers everything is
fine, the truncated IMA measurement list is clean, no attack, and the
current PCR values match by replaying the new PCR starting value with
the remaining of the IMA measurement list.

So, in my opinion the kernel should just offer the ability to trim the
measurement list, and a remote verifier should be responsible to verify
the measurement list, without relying on anything from the system being
evaluated.

Sure, the remote verifier can verify just the trimmed IMA measurement
list, but the remote verifier must solely rely on state information
maintained internally.

Roberto

> The method has other advantages too:
> 
>  1. It provides a userspace interface that can be used to precisely control
>     trim point, allowing for trim points to be optionally aligned with
>     userspace IMA event log validation.
> 
>  2. It ensures that all necessary information required for continued IMA
>     log validation is made available via the userspace interface at all
>     times.
> 
>  3. It provides a simple mechanism for userspace applications to determine
>     if the event log has been unexpectedly trimmed.
> 
>  4. The duration for which the IMA Measurement list mutex must be held (for
>     trimming) is minimal.
> 
> ==========================================================================
> > B. Solution                                                            |
> ==========================================================================
> 
> --------------------------------------------------------------------------
> > B.1 Overview                                                           |
> --------------------------------------------------------------------------
> 
> The kernel trims the IMA event log based on PCR values supplied by userspace.
> The core principles leveraged are as follows:
> 
>  - Given an IMA event log, PCR values for each IMA event can be obtained by
>    recalulating the PCR extension for each event. Thus processing N events
>    from the start will yield PCR values as of event N. This is referred to
>    as "IMA event log replay".
> 
>  - To get the PCR value for event N + 1, only the PCR value as of event N
>    is needed. If this can be known, events till and including N can be
>    safely purged.
> 
> Putting it all together, we get the following userspace + kernel flow:
> 
>  1. A userspace application replays the IMA event log to generate PCR
>     values and then triggers a trim by providing these values to the kernel
>     (by writing to a pseudo file). 
> 
>     Optionally, the userspace application may verify these PCR values
>     against the corresponding TPM quote, and trigger trimming only if
>     the calculated PCR values match up to the expectations in the quote's
>     PCR digest.
> 
>  2. The kernel uses the userspace supplied PCR values to trim the IMA
>     measurements list at a specific point, and so these are referred to as
>     "trim-to PCR values" in this context.
> 
>     Note that the kernel doesn't really understand what these userspace
>     provided PCR values mean or what IMA event they correspond to, and so
>     it does its own IMA event replay till either the replayed PCR values
>     match with the userspace provided ones, or it runs out of events.
> 
>     If a match is found, the kernel can proceed with trimming the IMA
>     measurements list. This is done in two steps, to keep locking context
>     minimal.
> 
>     step 1: Find and return the list entry (as a count from head) of exact
>             match. This does not lock the measurements list mutex, ensuring
>             new events can be appended to the log.
> 
>     step 2: Lock the measurements list mutex and trim the measurements list
>             at the previously identified list entry.
> 
>    If the trim is successful, the trim-to PCR values are saved as "starting
>    PCR values". The next time userspace wants to replay the IMA event log,
>    it will use the starting PCR values as the base for the IMA event log
>    replay.
> 
> --------------------------------------------------------------------------
> > B.2 Kernel Interfaces                                                  |
> --------------------------------------------------------------------------
> 
> A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
> following operations is exposed.
> 
>   read: returns starting PCR values stored in the kernel (within IMA
>         specifically).
> 
>  write: writes trim-to PCR values to trigger trimming. If trimming is
>         successful, trim-to PCR values are stored as starting PCR values.
>         requires root privileges.
> 
> --------------------------------------------------------------------------
> > B.3 Walk-through with a real example                                   |
> --------------------------------------------------------------------------
> 
> This is a real example from a test run.
> 
> Suppose this IMA policy is deployed:
> 
>   measure func=FILE_CHECK mask=MAY_READ pcr=10
>   measure func=FILE_CHECK mask=MAY_WRITE pcr=11
> 
> When the policy is deployed, a zero digest starting PCR value will be set
> for each PCR used. If the TPM supports multiple hashbanks, there will be
> one starting PCR value per PCR, per TPM hashbank. This can be seen in the
> following hexdump:
> 
> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
> 00000000  70 63 72 31 30 3a 73 68  61 31 3a 00 00 00 00 00  |pcr10:sha1:.....|
> 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 70  |...............p|
> 00000020  63 72 31 31 3a 73 68 61  31 3a 00 00 00 00 00 00  |cr11:sha1:......|
> 00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 70 63  |..............pc|
> 00000040  72 31 30 3a 73 68 61 32  35 36 3a 00 00 00 00 00  |r10:sha256:.....|
> 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 00000060  00 00 00 00 00 00 00 00  00 00 00 70 63 72 31 31  |...........pcr11|
> 00000070  3a 73 68 61 32 35 36 3a  00 00 00 00 00 00 00 00  |:sha256:........|
> 00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 00000090  00 00 00 00 00 00 00 00  70 63 72 31 30 3a 73 68  |........pcr10:sh|
> 000000a0  61 33 38 34 3a 00 00 00  00 00 00 00 00 00 00 00  |a384:...........|
> 000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 000000d0  00 00 00 00 00 70 63 72  31 31 3a 73 68 61 33 38  |.....pcr11:sha38|
> 000000e0  34 3a 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |4:..............|
> 000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> 00000110  00 00                                             |..|
> 00000112
> 
> Let's say that a userspace utility replays the IMA event log, and triggers
> trimming by writing the following PCR values (i.e. trim-to PCR values) to the
> pseudo file:
> 
> pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
> pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41
> 
> The trim is successful, 
> 
> 1. Some number of entries from the measurements log will disappear. This
>    can be verified by reading out the ASCII or binary IMA measurements
>    file.
> 
> 2. The trim-to PCR values are saved as starting PCR values. This can be
>    verified by reading out the pseudo file again as shown below. Note that
>    even through only sha256 PCR values were written, the kernel populated
>    sha1 and sha384 starting values as well.
> 
> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
> 
> 00000000  70 63 72 31 30 3a 73 68  61 31 3a c4 7f 9d 00 68  |pcr10:sha1:....h|
> 00000010  e4 86 71 bf bc ae f0 10  12 ff 68 e2 9e 74 e4 70  |..q.......h..t.p|
> 00000020  63 72 31 31 3a 73 68 61  31 3a 90 d7 17 ac 60 4d  |cr11:sha1:....`M|
> 00000030  c8 25 ce 77 7d 9d 94 cf  44 7b b2 2e 2e e2 70 63  |.%.w}...D{....pc|
> 00000040  72 31 30 3a 73 68 61 32  35 36 3a 82 68 78 29 06  |r10:sha256:.hx).|
> 00000050  55 5c f3 ae fc 17 9f 81  5c 87 85 27 dd 4e 67 ea  |U\......\..'.Ng.|
> 00000060  a8 36 57 2e ba ba b3 19  77 92 2c 70 63 72 31 31  |.6W.....w.,pcr11|
> 00000070  3a 73 68 61 32 35 36 3a  4c 7f 31 92 71 83 ea cb  |:sha256:L.1.q...|
> 00000080  53 d5 1d 95 b0 16 29 16  fd 3f ca 51 a8 d1 ef c6  |S.....)..?.Q....|
> 00000090  dd e3 80 5e b8 91 fe 41  70 63 72 31 30 3a 73 68  |...^...Apcr10:sh|
> 000000a0  61 33 38 34 3a 8e d6 12  18 b1 d6 cd 95 16 98 33  |a384:..........3|
> 000000b0  2b 7d a2 d6 d9 05 c7 e8  5b 15 b0 91 c5 fc 23 d1  |+}......[.....#.|
> 000000c0  f9 a8 8d 60 50 5c e9 64  5f d7 b3 b2 f1 9c 90 0a  |...`P\.d_.......|
> 000000d0  45 53 5d b2 57 70 63 72  31 31 3a 73 68 61 33 38  |ES].Wpcr11:sha38|
> 000000e0  34 3a 25 fc 21 28 31 5a  f7 c6 fb 0f 40 c9 06 e6  |4:%.!(1Z....@...|
> 000000f0  c5 da ed 20 61 a1 03 54  4f 67 18 88 82 0f 48 d1  |... a..TOg....H.|
> 00000100  2f e0 3d 36 46 5e 94 a4  88 51 f8 91 39 7e e5 97  |/.=6F^...Q..9~..|
> 00000110  2c c5                                             |,.|
> 00000112
> 
> --------------------------------------------------------------------------
> > C. Footnotes                                                           |
> --------------------------------------------------------------------------
> 
> 1. The 'pcrs' pseudo file is currently part of configfs. This was due to
>    some early internal feedback in a different context. This can as well be
>    in securityfs with the rest of the IMA pseudo files.
> 
> 2. PCR values are never read out of the TPM at any point. All PCR values
>    used are derived from IMA event log replay.
> 
> 3. Code is "RFC quality". Refinements can be made if the method is accepted.
> 
> 4. For functional validation, base kernel version was 6.17 stable, with the
>    most recent tested version being 6.17.8.
> 
> 5. Code has been validated to some degree using a python-based internal test
>    tool. This can be published if there is community interest. 
> 
> Steven Chen (1):
>   ima: Implement IMA event log trimming
> 
>  drivers/Kconfig                       |   2 +
>  drivers/Makefile                      |   1 +
>  drivers/ima/Kconfig                   |  13 +
>  drivers/ima/Makefile                  |   2 +
>  drivers/ima/ima_config_pcrs.c         | 291 ++++++++++++++++++
>  include/linux/ima.h                   |  27 ++
>  security/integrity/ima/Makefile       |   4 +
>  security/integrity/ima/ima.h          |   8 +
>  security/integrity/ima/ima_init.c     |  44 +++
>  security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
>  security/integrity/ima/ima_policy.c   |   7 +-
>  security/integrity/ima/ima_queue.c    |   5 +-
>  12 files changed, 821 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/ima/Kconfig
>  create mode 100644 drivers/ima/Makefile
>  create mode 100644 drivers/ima/ima_config_pcrs.c
>  create mode 100644 security/integrity/ima/ima_log_trim.c
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-19 21:33 [RFC v1 0/1] Implement IMA Event Log Trimming Anirudh Venkataramanan
  2025-11-19 21:33 ` [RFC v1 1/1] ima: Implement IMA event log trimming Anirudh Venkataramanan
  2025-11-20 11:02 ` [RFC v1 0/1] Implement IMA Event Log Trimming Roberto Sassu
@ 2025-11-20 22:58 ` Mimi Zohar
  2025-11-21 19:53   ` Anirudh Venkataramanan
  2 siblings, 1 reply; 10+ messages in thread
From: Mimi Zohar @ 2025-11-20 22:58 UTC (permalink / raw)
  To: Anirudh Venkataramanan, linux-integrity
  Cc: Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
	Sush Shringarputale

On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
> 
> 
>  2. The kernel uses the userspace supplied PCR values to trim the IMA
>     measurements list at a specific point, and so these are referred to as
>     "trim-to PCR values" in this context.
> 
>     Note that the kernel doesn't really understand what these userspace
>     provided PCR values mean or what IMA event they correspond to, and so
>     it does its own IMA event replay till either the replayed PCR values
>     match with the userspace provided ones, or it runs out of events.
> 
>     If a match is found, the kernel can proceed with trimming the IMA
>     measurements list. This is done in two steps, to keep locking context
>     minimal.
> 
>     step 1: Find and return the list entry (as a count from head) of exact
>             match. This does not lock the measurements list mutex, ensuring
>             new events can be appended to the log.
> 
>     step 2: Lock the measurements list mutex and trim the measurements list
>             at the previously identified list entry.
> 
>    If the trim is successful, the trim-to PCR values are saved as "starting
>    PCR values". The next time userspace wants to replay the IMA event log,
>    it will use the starting PCR values as the base for the IMA event log
>    replay.

Depending on the IMA policy, the IMA measurement list may contain integrity
violations, such as "ToM/ToU" (Time of Measurement/Time of Use) or "open-
writer". Either the userspace supplied PCR values will not match any measurement
record or the PCR values will be "fixed" to match the well known violation hash
value extended into the TPM.  Depending on how the userspace PCR values will
subsequently be used, saving the "fixed" PCR values could result in whitewashing
the integrity of the measurement list.

-- 
thanks,

Mimi

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-20 11:02 ` [RFC v1 0/1] Implement IMA Event Log Trimming Roberto Sassu
@ 2025-11-21 19:13   ` Anirudh Venkataramanan
  2025-11-24 10:01     ` Roberto Sassu
  0 siblings, 1 reply; 10+ messages in thread
From: Anirudh Venkataramanan @ 2025-11-21 19:13 UTC (permalink / raw)
  To: Roberto Sassu, linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
	Sush Shringarputale

On 11/20/2025 3:02 AM, Roberto Sassu wrote:
> On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
>> ==========================================================================
>>> A. Introduction                                                        |
>> ==========================================================================
>>
>> IMA events are kept in kernel memory and preserved across kexec soft
>> reboots. This can lead to increased kernel memory usage over time,
>> especially with aggressive IMA policies that measure everything. To reduce
>> memory pressure, it becomes necessary to discard IMA events but given that
>> IMA events are extended into PCRs in the TPM, just discarding events will
>> break the PCR extension chain, making future verification of the IMA event
>> log impossible.
>>
>> This patch series proposes a method to discard IMA events while keeping the
>> log verifiable. While reducing memory pressure is the primary objective,
>> the second order benefit of trimming the IMA log is that IMA log verifiers
>> (local userspace daemon or a remote cloud service) can process smaller IMA
>> logs on a rolling basis, thus avoiding re-verification of previously
>> verified events.
> 
> Hi Anirudh

Hi Roberto,

Thanks for the feedback! Few questions below.

> 
> I will rephrase this paragraph, to be sure that I understood it
> correctly.
> 
> You are proposing a method to trim the measurement list and, at the
> same time, to keep the measurement list verifiable after trimming. The
> way you would like to achieve that is to keep the verification state in
> the kernel in the form of PCR values.
> 
> Those values mean what verifiers have already verified. Thus, for the
> next verification attempt, verifiers take the current PCR values as
> starting values, replay the truncated IMA measurement list, and again
> you match with the current PCR values and you trim until that point.
> 
> So the benefit of this proposal is that you keep the verification of
> the IMA measurement list self-contained by using the last verification
> state (PCR starting value) and the truncated IMA measurement list as
> the inputs of your verification.

Your understanding as described above is correct.

> 
> Let me reiterate on the trusted computing principles IMA relies on for
> providing the evidence about a system's integrity.
> 
> Unless you are at the beginning of the measurement chain, where the
> Root of Trust for Measurement (RTM) is trusted by assumption, the
> measurements done by a component can be trusted because that component
> was already measured by the previous component in the boot chain,
> before it had any chance to corrupt the system.
> 
> In the context of IMA, IMA can be trusted to make new measurements
> because it measures every file before those files could cause any harm
> to the system. So, potentially IMA and the kernel can be corrupted by
> any file.
> 
> What you are proposing would not work, because you are placing trust in
> an input (the PCR starting value) that can be manipulated at any time
> by a corrupted kernel, before you had the chance to detect such
> corruption.

If starting PCR values can be corrupted, the IMA measurements list can 
also be corrupted, right?

More generally, what integrity guarantees can be provided (if any) if 
the kernel itself is corrupted?

> 
> Let me describe a scenario where I could take advantage of such
> weakness. After the first measurement list trim, I perform an attack on
> the system such that it corrupts the kernel. IMA added a new entry in
> the measurement list, which would reveal the attack.
> 
> But, since I have control of the kernel, I conveniently update the PCR
> starting value to replay the new measurement entry, and remove the
> measurement entry from the measurement list.
> 
> Now, from the perspective of the user space verifiers everything is
> fine, the truncated IMA measurement list is clean, no attack, and the
> current PCR values match by replaying the new PCR starting value with
> the remaining of the IMA measurement list.

Wouldn't the verifier detect the attack when it sees that its 
recalculated PCR values don't match up to the PCR digest in the TPM quote?

> 
> So, in my opinion the kernel should just offer the ability to trim the
> measurement list, and a remote verifier should be responsible to verify
> the measurement list, without relying on anything from the system being
> evaluated.
> 
> Sure, the remote verifier can verify just the trimmed IMA measurement
> list, but the remote verifier must solely rely on state information
> maintained internally.
> 
> Roberto
> 
>> The method has other advantages too:
>>
>>   1. It provides a userspace interface that can be used to precisely control
>>      trim point, allowing for trim points to be optionally aligned with
>>      userspace IMA event log validation.
>>
>>   2. It ensures that all necessary information required for continued IMA
>>      log validation is made available via the userspace interface at all
>>      times.
>>
>>   3. It provides a simple mechanism for userspace applications to determine
>>      if the event log has been unexpectedly trimmed.
>>
>>   4. The duration for which the IMA Measurement list mutex must be held (for
>>      trimming) is minimal.
>>
>> ==========================================================================
>>> B. Solution                                                            |
>> ==========================================================================
>>
>> --------------------------------------------------------------------------
>>> B.1 Overview                                                           |
>> --------------------------------------------------------------------------
>>
>> The kernel trims the IMA event log based on PCR values supplied by userspace.
>> The core principles leveraged are as follows:
>>
>>   - Given an IMA event log, PCR values for each IMA event can be obtained by
>>     recalulating the PCR extension for each event. Thus processing N events
>>     from the start will yield PCR values as of event N. This is referred to
>>     as "IMA event log replay".
>>
>>   - To get the PCR value for event N + 1, only the PCR value as of event N
>>     is needed. If this can be known, events till and including N can be
>>     safely purged.
>>
>> Putting it all together, we get the following userspace + kernel flow:
>>
>>   1. A userspace application replays the IMA event log to generate PCR
>>      values and then triggers a trim by providing these values to the kernel
>>      (by writing to a pseudo file).
>>
>>      Optionally, the userspace application may verify these PCR values
>>      against the corresponding TPM quote, and trigger trimming only if
>>      the calculated PCR values match up to the expectations in the quote's
>>      PCR digest.
>>
>>   2. The kernel uses the userspace supplied PCR values to trim the IMA
>>      measurements list at a specific point, and so these are referred to as
>>      "trim-to PCR values" in this context.
>>
>>      Note that the kernel doesn't really understand what these userspace
>>      provided PCR values mean or what IMA event they correspond to, and so
>>      it does its own IMA event replay till either the replayed PCR values
>>      match with the userspace provided ones, or it runs out of events.
>>
>>      If a match is found, the kernel can proceed with trimming the IMA
>>      measurements list. This is done in two steps, to keep locking context
>>      minimal.
>>
>>      step 1: Find and return the list entry (as a count from head) of exact
>>              match. This does not lock the measurements list mutex, ensuring
>>              new events can be appended to the log.
>>
>>      step 2: Lock the measurements list mutex and trim the measurements list
>>              at the previously identified list entry.
>>
>>     If the trim is successful, the trim-to PCR values are saved as "starting
>>     PCR values". The next time userspace wants to replay the IMA event log,
>>     it will use the starting PCR values as the base for the IMA event log
>>     replay.
>>
>> --------------------------------------------------------------------------
>>> B.2 Kernel Interfaces                                                  |
>> --------------------------------------------------------------------------
>>
>> A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
>> following operations is exposed.
>>
>>    read: returns starting PCR values stored in the kernel (within IMA
>>          specifically).
>>
>>   write: writes trim-to PCR values to trigger trimming. If trimming is
>>          successful, trim-to PCR values are stored as starting PCR values.
>>          requires root privileges.
>>
>> --------------------------------------------------------------------------
>>> B.3 Walk-through with a real example                                   |
>> --------------------------------------------------------------------------
>>
>> This is a real example from a test run.
>>
>> Suppose this IMA policy is deployed:
>>
>>    measure func=FILE_CHECK mask=MAY_READ pcr=10
>>    measure func=FILE_CHECK mask=MAY_WRITE pcr=11
>>
>> When the policy is deployed, a zero digest starting PCR value will be set
>> for each PCR used. If the TPM supports multiple hashbanks, there will be
>> one starting PCR value per PCR, per TPM hashbank. This can be seen in the
>> following hexdump:
>>
>> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
>> 00000000  70 63 72 31 30 3a 73 68  61 31 3a 00 00 00 00 00  |pcr10:sha1:.....|
>> 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 70  |...............p|
>> 00000020  63 72 31 31 3a 73 68 61  31 3a 00 00 00 00 00 00  |cr11:sha1:......|
>> 00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 70 63  |..............pc|
>> 00000040  72 31 30 3a 73 68 61 32  35 36 3a 00 00 00 00 00  |r10:sha256:.....|
>> 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 00000060  00 00 00 00 00 00 00 00  00 00 00 70 63 72 31 31  |...........pcr11|
>> 00000070  3a 73 68 61 32 35 36 3a  00 00 00 00 00 00 00 00  |:sha256:........|
>> 00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 00000090  00 00 00 00 00 00 00 00  70 63 72 31 30 3a 73 68  |........pcr10:sh|
>> 000000a0  61 33 38 34 3a 00 00 00  00 00 00 00 00 00 00 00  |a384:...........|
>> 000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 000000d0  00 00 00 00 00 70 63 72  31 31 3a 73 68 61 33 38  |.....pcr11:sha38|
>> 000000e0  34 3a 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |4:..............|
>> 000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
>> 00000110  00 00                                             |..|
>> 00000112
>>
>> Let's say that a userspace utility replays the IMA event log, and triggers
>> trimming by writing the following PCR values (i.e. trim-to PCR values) to the
>> pseudo file:
>>
>> pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
>> pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41
>>
>> The trim is successful,
>>
>> 1. Some number of entries from the measurements log will disappear. This
>>     can be verified by reading out the ASCII or binary IMA measurements
>>     file.
>>
>> 2. The trim-to PCR values are saved as starting PCR values. This can be
>>     verified by reading out the pseudo file again as shown below. Note that
>>     even through only sha256 PCR values were written, the kernel populated
>>     sha1 and sha384 starting values as well.
>>
>> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
>>
>> 00000000  70 63 72 31 30 3a 73 68  61 31 3a c4 7f 9d 00 68  |pcr10:sha1:....h|
>> 00000010  e4 86 71 bf bc ae f0 10  12 ff 68 e2 9e 74 e4 70  |..q.......h..t.p|
>> 00000020  63 72 31 31 3a 73 68 61  31 3a 90 d7 17 ac 60 4d  |cr11:sha1:....`M|
>> 00000030  c8 25 ce 77 7d 9d 94 cf  44 7b b2 2e 2e e2 70 63  |.%.w}...D{....pc|
>> 00000040  72 31 30 3a 73 68 61 32  35 36 3a 82 68 78 29 06  |r10:sha256:.hx).|
>> 00000050  55 5c f3 ae fc 17 9f 81  5c 87 85 27 dd 4e 67 ea  |U\......\..'.Ng.|
>> 00000060  a8 36 57 2e ba ba b3 19  77 92 2c 70 63 72 31 31  |.6W.....w.,pcr11|
>> 00000070  3a 73 68 61 32 35 36 3a  4c 7f 31 92 71 83 ea cb  |:sha256:L.1.q...|
>> 00000080  53 d5 1d 95 b0 16 29 16  fd 3f ca 51 a8 d1 ef c6  |S.....)..?.Q....|
>> 00000090  dd e3 80 5e b8 91 fe 41  70 63 72 31 30 3a 73 68  |...^...Apcr10:sh|
>> 000000a0  61 33 38 34 3a 8e d6 12  18 b1 d6 cd 95 16 98 33  |a384:..........3|
>> 000000b0  2b 7d a2 d6 d9 05 c7 e8  5b 15 b0 91 c5 fc 23 d1  |+}......[.....#.|
>> 000000c0  f9 a8 8d 60 50 5c e9 64  5f d7 b3 b2 f1 9c 90 0a  |...`P\.d_.......|
>> 000000d0  45 53 5d b2 57 70 63 72  31 31 3a 73 68 61 33 38  |ES].Wpcr11:sha38|
>> 000000e0  34 3a 25 fc 21 28 31 5a  f7 c6 fb 0f 40 c9 06 e6  |4:%.!(1Z....@...|
>> 000000f0  c5 da ed 20 61 a1 03 54  4f 67 18 88 82 0f 48 d1  |... a..TOg....H.|
>> 00000100  2f e0 3d 36 46 5e 94 a4  88 51 f8 91 39 7e e5 97  |/.=6F^...Q..9~..|
>> 00000110  2c c5                                             |,.|
>> 00000112
>>
>> --------------------------------------------------------------------------
>>> C. Footnotes                                                           |
>> --------------------------------------------------------------------------
>>
>> 1. The 'pcrs' pseudo file is currently part of configfs. This was due to
>>     some early internal feedback in a different context. This can as well be
>>     in securityfs with the rest of the IMA pseudo files.
>>
>> 2. PCR values are never read out of the TPM at any point. All PCR values
>>     used are derived from IMA event log replay.
>>
>> 3. Code is "RFC quality". Refinements can be made if the method is accepted.
>>
>> 4. For functional validation, base kernel version was 6.17 stable, with the
>>     most recent tested version being 6.17.8.
>>
>> 5. Code has been validated to some degree using a python-based internal test
>>     tool. This can be published if there is community interest.
>>
>> Steven Chen (1):
>>    ima: Implement IMA event log trimming
>>
>>   drivers/Kconfig                       |   2 +
>>   drivers/Makefile                      |   1 +
>>   drivers/ima/Kconfig                   |  13 +
>>   drivers/ima/Makefile                  |   2 +
>>   drivers/ima/ima_config_pcrs.c         | 291 ++++++++++++++++++
>>   include/linux/ima.h                   |  27 ++
>>   security/integrity/ima/Makefile       |   4 +
>>   security/integrity/ima/ima.h          |   8 +
>>   security/integrity/ima/ima_init.c     |  44 +++
>>   security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
>>   security/integrity/ima/ima_policy.c   |   7 +-
>>   security/integrity/ima/ima_queue.c    |   5 +-
>>   12 files changed, 821 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/ima/Kconfig
>>   create mode 100644 drivers/ima/Makefile
>>   create mode 100644 drivers/ima/ima_config_pcrs.c
>>   create mode 100644 security/integrity/ima/ima_log_trim.c
>>
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-20 22:58 ` Mimi Zohar
@ 2025-11-21 19:53   ` Anirudh Venkataramanan
  0 siblings, 0 replies; 10+ messages in thread
From: Anirudh Venkataramanan @ 2025-11-21 19:53 UTC (permalink / raw)
  To: Mimi Zohar, linux-integrity
  Cc: Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
	Sush Shringarputale

On 11/20/2025 2:58 PM, Mimi Zohar wrote:
> On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
>>
>>
>>   2. The kernel uses the userspace supplied PCR values to trim the IMA
>>      measurements list at a specific point, and so these are referred to as
>>      "trim-to PCR values" in this context.
>>
>>      Note that the kernel doesn't really understand what these userspace
>>      provided PCR values mean or what IMA event they correspond to, and so
>>      it does its own IMA event replay till either the replayed PCR values
>>      match with the userspace provided ones, or it runs out of events.
>>
>>      If a match is found, the kernel can proceed with trimming the IMA
>>      measurements list. This is done in two steps, to keep locking context
>>      minimal.
>>
>>      step 1: Find and return the list entry (as a count from head) of exact
>>              match. This does not lock the measurements list mutex, ensuring
>>              new events can be appended to the log.
>>
>>      step 2: Lock the measurements list mutex and trim the measurements list
>>              at the previously identified list entry.
>>
>>     If the trim is successful, the trim-to PCR values are saved as "starting
>>     PCR values". The next time userspace wants to replay the IMA event log,
>>     it will use the starting PCR values as the base for the IMA event log
>>     replay.
> 
> Depending on the IMA policy, the IMA measurement list may contain integrity
> violations, such as "ToM/ToU" (Time of Measurement/Time of Use) or "open-
> writer". Either the userspace supplied PCR values will not match any measurement
> record or the PCR values will be "fixed" to match the well known violation hash
> value extended into the TPM.  Depending on how the userspace PCR values will
> subsequently be used, saving the "fixed" PCR values could result in whitewashing
> the integrity of the measurement list.
> 

Yes, we account for this. IMA documentation [1] notes that:

"An all zeros hash indicates a measurement log violation. IMA is 
invalidating an entry. Trust in entries after that are up to the end 
user. If the Template Data Hash is all zeros, an all ones digest is 
extended."

A userspace verifier could be designed with a user option like 
"--ignore-violations". This would extend a digest of all ones in the 
event replay process and arrive at the same per-event PCR values that 
the TPM originally did. The trimming logic in the kernel would do the 
same thing.

By "whitewashing", you mean that violations will also get trimmed? Given 
that trust in entries post violation is up to the user, is this a 
problem? The IMA log itself can still be saved by userspace for further 
analysis, auditing, etc. The point of trimming is to get it out of the 
kernel memory.

Thanks!
Ani

[1] 
https://ima-doc.readthedocs.io/en/latest/event-log-format.html#template-data-hash

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-21 19:13   ` Anirudh Venkataramanan
@ 2025-11-24 10:01     ` Roberto Sassu
  2025-11-26 23:40       ` Gregory Lumen
  0 siblings, 1 reply; 10+ messages in thread
From: Roberto Sassu @ 2025-11-24 10:01 UTC (permalink / raw)
  To: Anirudh Venkataramanan, linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
	Sush Shringarputale

On Fri, 2025-11-21 at 11:13 -0800, Anirudh Venkataramanan wrote:
> On 11/20/2025 3:02 AM, Roberto Sassu wrote:
> > On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
> > > ==========================================================================
> > > > A. Introduction                                                        |
> > > ==========================================================================
> > > 
> > > IMA events are kept in kernel memory and preserved across kexec soft
> > > reboots. This can lead to increased kernel memory usage over time,
> > > especially with aggressive IMA policies that measure everything. To reduce
> > > memory pressure, it becomes necessary to discard IMA events but given that
> > > IMA events are extended into PCRs in the TPM, just discarding events will
> > > break the PCR extension chain, making future verification of the IMA event
> > > log impossible.
> > > 
> > > This patch series proposes a method to discard IMA events while keeping the
> > > log verifiable. While reducing memory pressure is the primary objective,
> > > the second order benefit of trimming the IMA log is that IMA log verifiers
> > > (local userspace daemon or a remote cloud service) can process smaller IMA
> > > logs on a rolling basis, thus avoiding re-verification of previously
> > > verified events.
> > 
> > Hi Anirudh
> 
> Hi Roberto,
> 
> Thanks for the feedback! Few questions below.
> 
> > 
> > I will rephrase this paragraph, to be sure that I understood it
> > correctly.
> > 
> > You are proposing a method to trim the measurement list and, at the
> > same time, to keep the measurement list verifiable after trimming. The
> > way you would like to achieve that is to keep the verification state in
> > the kernel in the form of PCR values.
> > 
> > Those values mean what verifiers have already verified. Thus, for the
> > next verification attempt, verifiers take the current PCR values as
> > starting values, replay the truncated IMA measurement list, and again
> > you match with the current PCR values and you trim until that point.
> > 
> > So the benefit of this proposal is that you keep the verification of
> > the IMA measurement list self-contained by using the last verification
> > state (PCR starting value) and the truncated IMA measurement list as
> > the inputs of your verification.
> 
> Your understanding as described above is correct.
> 
> > 
> > Let me reiterate on the trusted computing principles IMA relies on for
> > providing the evidence about a system's integrity.
> > 
> > Unless you are at the beginning of the measurement chain, where the
> > Root of Trust for Measurement (RTM) is trusted by assumption, the
> > measurements done by a component can be trusted because that component
> > was already measured by the previous component in the boot chain,
> > before it had any chance to corrupt the system.
> > 
> > In the context of IMA, IMA can be trusted to make new measurements
> > because it measures every file before those files could cause any harm
> > to the system. So, potentially IMA and the kernel can be corrupted by
> > any file.
> > 
> > What you are proposing would not work, because you are placing trust in
> > an input (the PCR starting value) that can be manipulated at any time
> > by a corrupted kernel, before you had the chance to detect such
> > corruption.
> 
> If starting PCR values can be corrupted, the IMA measurements list can 
> also be corrupted, right?

Yes, my point was that there could be a malicious update of the PCR
starting value, so that the IMA measurements list with omitted entries
looks not corrupted.

> More generally, what integrity guarantees can be provided (if any) if 
> the kernel itself is corrupted?

None. This is exactly the point: measuring the files before they get
accessed is the only way to provide reliable measurements. After that,
we assume that the operation can corrupt both the user space processes
and the kernel itself (the threat model takes into consideration only
regular files).

But if that happened, replaying the IMA measurements list will reveal
the attack at that point, and the remote verifier can conclude that any
measurement after that cannot be trusted.

Your solution is storing the verification state in the kernel, and make
remote verifiers rely on it for resuming their verification. But,
because the kernel can get potentially corrupted by every measurement,
the verification state can also get potentially corrupted by every
measurement, thus cannot be trusted.

The only way to make the verification of measurements list snapshots
work is that the verification state is stored outside the system to
evaluate (which can be assumed to be trusted), so that you are sure
that the system is not advancing the PCR starting value by itself.

> > Let me describe a scenario where I could take advantage of such
> > weakness. After the first measurement list trim, I perform an attack on
> > the system such that it corrupts the kernel. IMA added a new entry in
> > the measurement list, which would reveal the attack.
> > 
> > But, since I have control of the kernel, I conveniently update the PCR
> > starting value to replay the new measurement entry, and remove the
> > measurement entry from the measurement list.
> > 
> > Now, from the perspective of the user space verifiers everything is
> > fine, the truncated IMA measurement list is clean, no attack, and the
> > current PCR values match by replaying the new PCR starting value with
> > the remaining of the IMA measurement list.
> 
> Wouldn't the verifier detect the attack when it sees that its 
> recalculated PCR values don't match up to the PCR digest in the TPM quote?

I think not, because the system replayed the entries it wants to omit
by itself. If the remote verifier is trusting the PCR starting value
from the system, the remote verifier will replay the remaining
measurement entries and will obtain the PCR current values.

The same will happen if someone in the system just did a regular trim
without the remote attestation agent noticing it. Unless the agent
stored which was the last PCR starting value it took, it would not
notice.

But, again, if you rely on the remote attestation agent to maintain the
verification state locally in the system, you are assuming that the
agent will not be corrupted, which is a much stronger assumption than
just letting the agent pass data to the remote verifier (which instead
can be trusted to detect the PCR mismatch).

So, yes, the point of trimming is to just get the IMA measurements list
out of the kernel memory. I'm not opposing to trim N entries instead of
the entire IMA measurements list, as long as: (1) the PCR matching is
done in user space and is done only for convenience (can be totally
untrusted); (2) the verification state is stored in the remote verifier
(outside the system evaluated), and latter detects the PCR mismatch.

Roberto

> > So, in my opinion the kernel should just offer the ability to trim the
> > measurement list, and a remote verifier should be responsible to verify
> > the measurement list, without relying on anything from the system being
> > evaluated.
> > 
> > Sure, the remote verifier can verify just the trimmed IMA measurement
> > list, but the remote verifier must solely rely on state information
> > maintained internally.
> > 
> > Roberto
> > 
> > > The method has other advantages too:
> > > 
> > >   1. It provides a userspace interface that can be used to precisely control
> > >      trim point, allowing for trim points to be optionally aligned with
> > >      userspace IMA event log validation.
> > > 
> > >   2. It ensures that all necessary information required for continued IMA
> > >      log validation is made available via the userspace interface at all
> > >      times.
> > > 
> > >   3. It provides a simple mechanism for userspace applications to determine
> > >      if the event log has been unexpectedly trimmed.
> > > 
> > >   4. The duration for which the IMA Measurement list mutex must be held (for
> > >      trimming) is minimal.
> > > 
> > > ==========================================================================
> > > > B. Solution                                                            |
> > > ==========================================================================
> > > 
> > > --------------------------------------------------------------------------
> > > > B.1 Overview                                                           |
> > > --------------------------------------------------------------------------
> > > 
> > > The kernel trims the IMA event log based on PCR values supplied by userspace.
> > > The core principles leveraged are as follows:
> > > 
> > >   - Given an IMA event log, PCR values for each IMA event can be obtained by
> > >     recalulating the PCR extension for each event. Thus processing N events
> > >     from the start will yield PCR values as of event N. This is referred to
> > >     as "IMA event log replay".
> > > 
> > >   - To get the PCR value for event N + 1, only the PCR value as of event N
> > >     is needed. If this can be known, events till and including N can be
> > >     safely purged.
> > > 
> > > Putting it all together, we get the following userspace + kernel flow:
> > > 
> > >   1. A userspace application replays the IMA event log to generate PCR
> > >      values and then triggers a trim by providing these values to the kernel
> > >      (by writing to a pseudo file).
> > > 
> > >      Optionally, the userspace application may verify these PCR values
> > >      against the corresponding TPM quote, and trigger trimming only if
> > >      the calculated PCR values match up to the expectations in the quote's
> > >      PCR digest.
> > > 
> > >   2. The kernel uses the userspace supplied PCR values to trim the IMA
> > >      measurements list at a specific point, and so these are referred to as
> > >      "trim-to PCR values" in this context.
> > > 
> > >      Note that the kernel doesn't really understand what these userspace
> > >      provided PCR values mean or what IMA event they correspond to, and so
> > >      it does its own IMA event replay till either the replayed PCR values
> > >      match with the userspace provided ones, or it runs out of events.
> > > 
> > >      If a match is found, the kernel can proceed with trimming the IMA
> > >      measurements list. This is done in two steps, to keep locking context
> > >      minimal.
> > > 
> > >      step 1: Find and return the list entry (as a count from head) of exact
> > >              match. This does not lock the measurements list mutex, ensuring
> > >              new events can be appended to the log.
> > > 
> > >      step 2: Lock the measurements list mutex and trim the measurements list
> > >              at the previously identified list entry.
> > > 
> > >     If the trim is successful, the trim-to PCR values are saved as "starting
> > >     PCR values". The next time userspace wants to replay the IMA event log,
> > >     it will use the starting PCR values as the base for the IMA event log
> > >     replay.
> > > 
> > > --------------------------------------------------------------------------
> > > > B.2 Kernel Interfaces                                                  |
> > > --------------------------------------------------------------------------
> > > 
> > > A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
> > > following operations is exposed.
> > > 
> > >    read: returns starting PCR values stored in the kernel (within IMA
> > >          specifically).
> > > 
> > >   write: writes trim-to PCR values to trigger trimming. If trimming is
> > >          successful, trim-to PCR values are stored as starting PCR values.
> > >          requires root privileges.
> > > 
> > > --------------------------------------------------------------------------
> > > > B.3 Walk-through with a real example                                   |
> > > --------------------------------------------------------------------------
> > > 
> > > This is a real example from a test run.
> > > 
> > > Suppose this IMA policy is deployed:
> > > 
> > >    measure func=FILE_CHECK mask=MAY_READ pcr=10
> > >    measure func=FILE_CHECK mask=MAY_WRITE pcr=11
> > > 
> > > When the policy is deployed, a zero digest starting PCR value will be set
> > > for each PCR used. If the TPM supports multiple hashbanks, there will be
> > > one starting PCR value per PCR, per TPM hashbank. This can be seen in the
> > > following hexdump:
> > > 
> > > $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
> > > 00000000  70 63 72 31 30 3a 73 68  61 31 3a 00 00 00 00 00  |pcr10:sha1:.....|
> > > 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 70  |...............p|
> > > 00000020  63 72 31 31 3a 73 68 61  31 3a 00 00 00 00 00 00  |cr11:sha1:......|
> > > 00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 70 63  |..............pc|
> > > 00000040  72 31 30 3a 73 68 61 32  35 36 3a 00 00 00 00 00  |r10:sha256:.....|
> > > 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 00000060  00 00 00 00 00 00 00 00  00 00 00 70 63 72 31 31  |...........pcr11|
> > > 00000070  3a 73 68 61 32 35 36 3a  00 00 00 00 00 00 00 00  |:sha256:........|
> > > 00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 00000090  00 00 00 00 00 00 00 00  70 63 72 31 30 3a 73 68  |........pcr10:sh|
> > > 000000a0  61 33 38 34 3a 00 00 00  00 00 00 00 00 00 00 00  |a384:...........|
> > > 000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 000000d0  00 00 00 00 00 70 63 72  31 31 3a 73 68 61 33 38  |.....pcr11:sha38|
> > > 000000e0  34 3a 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |4:..............|
> > > 000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
> > > 00000110  00 00                                             |..|
> > > 00000112
> > > 
> > > Let's say that a userspace utility replays the IMA event log, and triggers
> > > trimming by writing the following PCR values (i.e. trim-to PCR values) to the
> > > pseudo file:
> > > 
> > > pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
> > > pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41
> > > 
> > > The trim is successful,
> > > 
> > > 1. Some number of entries from the measurements log will disappear. This
> > >     can be verified by reading out the ASCII or binary IMA measurements
> > >     file.
> > > 
> > > 2. The trim-to PCR values are saved as starting PCR values. This can be
> > >     verified by reading out the pseudo file again as shown below. Note that
> > >     even through only sha256 PCR values were written, the kernel populated
> > >     sha1 and sha384 starting values as well.
> > > 
> > > $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
> > > 
> > > 00000000  70 63 72 31 30 3a 73 68  61 31 3a c4 7f 9d 00 68  |pcr10:sha1:....h|
> > > 00000010  e4 86 71 bf bc ae f0 10  12 ff 68 e2 9e 74 e4 70  |..q.......h..t.p|
> > > 00000020  63 72 31 31 3a 73 68 61  31 3a 90 d7 17 ac 60 4d  |cr11:sha1:....`M|
> > > 00000030  c8 25 ce 77 7d 9d 94 cf  44 7b b2 2e 2e e2 70 63  |.%.w}...D{....pc|
> > > 00000040  72 31 30 3a 73 68 61 32  35 36 3a 82 68 78 29 06  |r10:sha256:.hx).|
> > > 00000050  55 5c f3 ae fc 17 9f 81  5c 87 85 27 dd 4e 67 ea  |U\......\..'.Ng.|
> > > 00000060  a8 36 57 2e ba ba b3 19  77 92 2c 70 63 72 31 31  |.6W.....w.,pcr11|
> > > 00000070  3a 73 68 61 32 35 36 3a  4c 7f 31 92 71 83 ea cb  |:sha256:L.1.q...|
> > > 00000080  53 d5 1d 95 b0 16 29 16  fd 3f ca 51 a8 d1 ef c6  |S.....)..?.Q....|
> > > 00000090  dd e3 80 5e b8 91 fe 41  70 63 72 31 30 3a 73 68  |...^...Apcr10:sh|
> > > 000000a0  61 33 38 34 3a 8e d6 12  18 b1 d6 cd 95 16 98 33  |a384:..........3|
> > > 000000b0  2b 7d a2 d6 d9 05 c7 e8  5b 15 b0 91 c5 fc 23 d1  |+}......[.....#.|
> > > 000000c0  f9 a8 8d 60 50 5c e9 64  5f d7 b3 b2 f1 9c 90 0a  |...`P\.d_.......|
> > > 000000d0  45 53 5d b2 57 70 63 72  31 31 3a 73 68 61 33 38  |ES].Wpcr11:sha38|
> > > 000000e0  34 3a 25 fc 21 28 31 5a  f7 c6 fb 0f 40 c9 06 e6  |4:%.!(1Z....@...|
> > > 000000f0  c5 da ed 20 61 a1 03 54  4f 67 18 88 82 0f 48 d1  |... a..TOg....H.|
> > > 00000100  2f e0 3d 36 46 5e 94 a4  88 51 f8 91 39 7e e5 97  |/.=6F^...Q..9~..|
> > > 00000110  2c c5                                             |,.|
> > > 00000112
> > > 
> > > --------------------------------------------------------------------------
> > > > C. Footnotes                                                           |
> > > --------------------------------------------------------------------------
> > > 
> > > 1. The 'pcrs' pseudo file is currently part of configfs. This was due to
> > >     some early internal feedback in a different context. This can as well be
> > >     in securityfs with the rest of the IMA pseudo files.
> > > 
> > > 2. PCR values are never read out of the TPM at any point. All PCR values
> > >     used are derived from IMA event log replay.
> > > 
> > > 3. Code is "RFC quality". Refinements can be made if the method is accepted.
> > > 
> > > 4. For functional validation, base kernel version was 6.17 stable, with the
> > >     most recent tested version being 6.17.8.
> > > 
> > > 5. Code has been validated to some degree using a python-based internal test
> > >     tool. This can be published if there is community interest.
> > > 
> > > Steven Chen (1):
> > >    ima: Implement IMA event log trimming
> > > 
> > >   drivers/Kconfig                       |   2 +
> > >   drivers/Makefile                      |   1 +
> > >   drivers/ima/Kconfig                   |  13 +
> > >   drivers/ima/Makefile                  |   2 +
> > >   drivers/ima/ima_config_pcrs.c         | 291 ++++++++++++++++++
> > >   include/linux/ima.h                   |  27 ++
> > >   security/integrity/ima/Makefile       |   4 +
> > >   security/integrity/ima/ima.h          |   8 +
> > >   security/integrity/ima/ima_init.c     |  44 +++
> > >   security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
> > >   security/integrity/ima/ima_policy.c   |   7 +-
> > >   security/integrity/ima/ima_queue.c    |   5 +-
> > >   12 files changed, 821 insertions(+), 4 deletions(-)
> > >   create mode 100644 drivers/ima/Kconfig
> > >   create mode 100644 drivers/ima/Makefile
> > >   create mode 100644 drivers/ima/ima_config_pcrs.c
> > >   create mode 100644 security/integrity/ima/ima_log_trim.c
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-24 10:01     ` Roberto Sassu
@ 2025-11-26 23:40       ` Gregory Lumen
  2025-11-27  9:45         ` Roberto Sassu
  0 siblings, 1 reply; 10+ messages in thread
From: Gregory Lumen @ 2025-11-26 23:40 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: Anirudh Venkataramanan, linux-integrity, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Lakshmi Ramasubramanian, Sush Shringarputale

Greetings Roberto,

If I may chime in a bit:

> The only way to make the verification of measurements list snapshots
> work is that the verification state is stored outside the system to
> evaluate (which can be assumed to be trusted), so that you are sure
> that the system is not advancing the PCR starting value by itself.

You are correct; to make the described approach work, an external source 
of trust is required in order to detect unexpected or unauthorized 
trimming of the event log (for example, by signing the trim-to PCR values 
from the previous verification/attestation cycle). This should be true 
regardless of the mechanism of trimming. More generally, I will go so far 
as to suggest that any attempt to attest the integrity of a system using 
IMA will likely fall into one of two general approaches: either the entire 
IMA event log is retained (either in kernel or user space) from boot and 
claims of system integrity are built by validating and examining the 
entire log for signs of tampering, or an external source of trust is 
introduced to allow incremental validation and examination of the log. 
Other more innovative approaches may exist, but we make no such claims.

I will also say that it should be possible to implement either approach to 
attestation (retaining the entire log, or relying on an external source of 
trust) with any sane implementation for IMA log trimming.

As for our proposed implementation, storing the starting PCR values in the 
kernel preserving the ability for any arbitrary user space entity to 
validate the retained portion of the IMA event log against the TPM PCRs at 
any time, without requiring awareness of other user space mechanisms 
implemented by other entities that may be initiating IMA trimming 
operations. My personal sense is that this capability is worth preserving, 
but it is entirely possible the general consensus is that the value 
offered does not balance against the additional technical complexity when 
compared to simpler alternatives (discussed in a moment). To stress the 
point, this capability would only enable validation of the integrity of 
the retained portion of the event log and its continuity with the PCRs, 
and could not be used to make any claims as to the overall integrity of 
the system since, as you observed, an attacker who has successfully 
compromised the system could simply trim the event log in order to discard 
evidence of the compromise.

If the ability to validate the retained portion of the IMA event log is 
not worth designing for, we could instead go with a simpler "Trim-to-N" 
approach, where the user space interface allows for the specification of 
an absolute index into the IMA log to be used as the trim position (as 
opposed to using calculated PCR values to indicate trim position in our 
current proposal). To protect against unexpected behavior in the event of 
concurrent trims, index counting would need to be fixed (hence absolute) 
such that index 0 would always refer to the very first entry written 
during boot, even if that entry has already been trimmed, with the number 
of trimmed entries (and thus starting index of the retained log) exposed 
to use space via a pseudo-file.

With such a trim approach, it should be possible to implement either 
general attestation approach: retaining the entire log (copy the log to 
user space, then trim the copied entries), or relying on an external 
source of trust (quote, determine the log index corresponding to the quote 
plus PCRs, trim, then securely store the trim position/starting PCRs for 
future cycles).

-Gregory Lumen


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-26 23:40       ` Gregory Lumen
@ 2025-11-27  9:45         ` Roberto Sassu
  2025-12-01 17:42           ` steven chen
  0 siblings, 1 reply; 10+ messages in thread
From: Roberto Sassu @ 2025-11-27  9:45 UTC (permalink / raw)
  To: Gregory Lumen
  Cc: Anirudh Venkataramanan, linux-integrity, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E . Hallyn, linux-security-module,
	Steven Chen, Lakshmi Ramasubramanian, Sush Shringarputale

On Wed, 2025-11-26 at 15:40 -0800, Gregory Lumen wrote:
> Greetings Roberto,
> 
> If I may chime in a bit:
> 
> > The only way to make the verification of measurements list snapshots
> > work is that the verification state is stored outside the system to
> > evaluate (which can be assumed to be trusted), so that you are sure
> > that the system is not advancing the PCR starting value by itself.
> 
> You are correct; to make the described approach work, an external source 
> of trust is required in order to detect unexpected or unauthorized 
> trimming of the event log (for example, by signing the trim-to PCR values 
> from the previous verification/attestation cycle). This should be true 
> regardless of the mechanism of trimming. More generally, I will go so far 
> as to suggest that any attempt to attest the integrity of a system using 
> IMA will likely fall into one of two general approaches: either the entire 
> IMA event log is retained (either in kernel or user space) from boot and 
> claims of system integrity are built by validating and examining the 
> entire log for signs of tampering, or an external source of trust is 
> introduced to allow incremental validation and examination of the log. 
> Other more innovative approaches may exist, but we make no such claims.
> 
> I will also say that it should be possible to implement either approach to 
> attestation (retaining the entire log, or relying on an external source of 
> trust) with any sane implementation for IMA log trimming.
> 
> As for our proposed implementation, storing the starting PCR values in the 
> kernel preserving the ability for any arbitrary user space entity to 
> validate the retained portion of the IMA event log against the TPM PCRs at 
> any time, without requiring awareness of other user space mechanisms 
> implemented by other entities that may be initiating IMA trimming 
> operations. My personal sense is that this capability is worth preserving, 
> but it is entirely possible the general consensus is that the value 
> offered does not balance against the additional technical complexity when 
> compared to simpler alternatives (discussed in a moment). To stress the 
> point, this capability would only enable validation of the integrity of 
> the retained portion of the event log and its continuity with the PCRs, 
> and could not be used to make any claims as to the overall integrity of 
> the system since, as you observed, an attacker who has successfully 
> compromised the system could simply trim the event log in order to discard 
> evidence of the compromise.

Hi Gregory

all you said can be implemented by maintaining the PCR starting value
outside the system, in a trusted entity. This would allow the
functionality you are hoping for to validate the retained portion of
the measurement list.

Keeping the PCR starting value in the kernel has the potential of
misleading users that this is an information they can rely on. I would
rather prefer to not run in such risk.

> If the ability to validate the retained portion of the IMA event log is 
> not worth designing for, we could instead go with a simpler "Trim-to-N" 
> approach, where the user space interface allows for the specification of 
> an absolute index into the IMA log to be used as the trim position (as 
> opposed to using calculated PCR values to indicate trim position in our 
> current proposal). To protect against unexpected behavior in the event of 

From implementation point of view, it looks much simpler to me to
specify N relative to the current measurement list.

> concurrent trims, index counting would need to be fixed (hence absolute) 
> such that index 0 would always refer to the very first entry written 
> during boot, even if that entry has already been trimmed, with the number 
> of trimmed entries (and thus starting index of the retained log) exposed 
> to use space via a pseudo-file.

In my draft patch [1] (still need to support trimming N entries instead
of the full measurement list), the risk of concurrent trims does not
exist because opening of the snapshot interface is exclusive (no one
else can request trimming concurrently).

If a more elaborated contention of remote attestation agent is
required, that could be done at user space level. I'm hoping to keep in
the kernel only the minimum code necessary for the remote attestation
to work.

Roberto

[1] https://github.com/robertosassu/linux/commit/b0bd002b6caa9d5d4f4d0db2a041b1fd91f33f8a

> With such a trim approach, it should be possible to implement either 
> general attestation approach: retaining the entire log (copy the log to 
> user space, then trim the copied entries), or relying on an external 
> source of trust (quote, determine the log index corresponding to the quote 
> plus PCRs, trim, then securely store the trim position/starting PCRs for 
> future cycles).
> 
> -Gregory Lumen


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
  2025-11-27  9:45         ` Roberto Sassu
@ 2025-12-01 17:42           ` steven chen
  0 siblings, 0 replies; 10+ messages in thread
From: steven chen @ 2025-12-01 17:42 UTC (permalink / raw)
  To: Roberto Sassu, Gregory Lumen
  Cc: Anirudh Venkataramanan, linux-integrity, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E . Hallyn, linux-security-module,
	Lakshmi Ramasubramanian, Sush Shringarputale

On 11/27/2025 1:45 AM, Roberto Sassu wrote:
> On Wed, 2025-11-26 at 15:40 -0800, Gregory Lumen wrote:
>> Greetings Roberto,
>>
>> If I may chime in a bit:
>>
>>> The only way to make the verification of measurements list snapshots
>>> work is that the verification state is stored outside the system to
>>> evaluate (which can be assumed to be trusted), so that you are sure
>>> that the system is not advancing the PCR starting value by itself.
>> You are correct; to make the described approach work, an external source
>> of trust is required in order to detect unexpected or unauthorized
>> trimming of the event log (for example, by signing the trim-to PCR values
>> from the previous verification/attestation cycle). This should be true
>> regardless of the mechanism of trimming. More generally, I will go so far
>> as to suggest that any attempt to attest the integrity of a system using
>> IMA will likely fall into one of two general approaches: either the entire
>> IMA event log is retained (either in kernel or user space) from boot and
>> claims of system integrity are built by validating and examining the
>> entire log for signs of tampering, or an external source of trust is
>> introduced to allow incremental validation and examination of the log.
>> Other more innovative approaches may exist, but we make no such claims.
>>
>> I will also say that it should be possible to implement either approach to
>> attestation (retaining the entire log, or relying on an external source of
>> trust) with any sane implementation for IMA log trimming.
>>
>> As for our proposed implementation, storing the starting PCR values in the
>> kernel preserving the ability for any arbitrary user space entity to
>> validate the retained portion of the IMA event log against the TPM PCRs at
>> any time, without requiring awareness of other user space mechanisms
>> implemented by other entities that may be initiating IMA trimming
>> operations. My personal sense is that this capability is worth preserving,
>> but it is entirely possible the general consensus is that the value
>> offered does not balance against the additional technical complexity when
>> compared to simpler alternatives (discussed in a moment). To stress the
>> point, this capability would only enable validation of the integrity of
>> the retained portion of the event log and its continuity with the PCRs,
>> and could not be used to make any claims as to the overall integrity of
>> the system since, as you observed, an attacker who has successfully
>> compromised the system could simply trim the event log in order to discard
>> evidence of the compromise.
> Hi Gregory
>
> all you said can be implemented by maintaining the PCR starting value
> outside the system, in a trusted entity. This would allow the
> functionality you are hoping for to validate the retained portion of
> the measurement list.
>
> Keeping the PCR starting value in the kernel has the potential of
> misleading users that this is an information they can rely on. I would
> rather prefer to not run in such risk.
>
>> If the ability to validate the retained portion of the IMA event log is
>> not worth designing for, we could instead go with a simpler "Trim-to-N"
>> approach, where the user space interface allows for the specification of
>> an absolute index into the IMA log to be used as the trim position (as
>> opposed to using calculated PCR values to indicate trim position in our
>> current proposal). To protect against unexpected behavior in the event of
> >From implementation point of view, it looks much simpler to me to
> specify N relative to the current measurement list.

Hi Roberto,

I will send "trim N entries" patch out this week.

Regards,

Steven

>> concurrent trims, index counting would need to be fixed (hence absolute)
>> such that index 0 would always refer to the very first entry written
>> during boot, even if that entry has already been trimmed, with the number
>> of trimmed entries (and thus starting index of the retained log) exposed
>> to use space via a pseudo-file.
> In my draft patch [1] (still need to support trimming N entries instead
> of the full measurement list), the risk of concurrent trims does not
> exist because opening of the snapshot interface is exclusive (no one
> else can request trimming concurrently).
>
> If a more elaborated contention of remote attestation agent is
> required, that could be done at user space level. I'm hoping to keep in
> the kernel only the minimum code necessary for the remote attestation
> to work.
>
> Roberto
>
> [1] https://github.com/robertosassu/linux/commit/b0bd002b6caa9d5d4f4d0db2a041b1fd91f33f8a
>
>> With such a trim approach, it should be possible to implement either
>> general attestation approach: retaining the entire log (copy the log to
>> user space, then trim the copied entries), or relying on an external
>> source of trust (quote, determine the log index corresponding to the quote
>> plus PCRs, trim, then securely store the trim position/starting PCRs for
>> future cycles).
>>
>> -Gregory Lumen



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-12-01 17:42 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-19 21:33 [RFC v1 0/1] Implement IMA Event Log Trimming Anirudh Venkataramanan
2025-11-19 21:33 ` [RFC v1 1/1] ima: Implement IMA event log trimming Anirudh Venkataramanan
2025-11-20 11:02 ` [RFC v1 0/1] Implement IMA Event Log Trimming Roberto Sassu
2025-11-21 19:13   ` Anirudh Venkataramanan
2025-11-24 10:01     ` Roberto Sassu
2025-11-26 23:40       ` Gregory Lumen
2025-11-27  9:45         ` Roberto Sassu
2025-12-01 17:42           ` steven chen
2025-11-20 22:58 ` Mimi Zohar
2025-11-21 19:53   ` Anirudh Venkataramanan

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).