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