lkml.org 
[lkml]   [2022]   [Jul]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[RFC PATCH 7/7] ima: Support measurement of kexec initramfs components
Date
An initramfs can be made up of multiple components that are concatenated
together e.g. an early uncompressed cpio archive containing early
firmware followed by a gziped cpio archive containing the actual
userspace initramfs. Add a Kconfig option to allow the IMA subsystem to
measure these components separately rather than as a single blob,
allowing for easier reasoning about system state when checking TPM PCR
values or the IMA integrity log.

Signed-off-by: Jonathan McDowell <noodles@fb.com>
---
security/integrity/ima/Kconfig | 16 +++
security/integrity/ima/ima_main.c | 191 ++++++++++++++++++++++++++++--
2 files changed, 199 insertions(+), 8 deletions(-)

diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 7249f16257c7..b75da44a32f2 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -41,6 +41,22 @@ config IMA_KEXEC
Depending on the IMA policy, the measurement list can grow to
be very large.

+config IMA_MEASURE_INITRAMFS_COMPONENTS
+ bool "Enable measurement of individual kexec initramfs components"
+ depends on IMA
+ select CPIO
+ default n
+ help
+ initramfs images can be made up of multiple separate components,
+ e.g. an early uncompressed cpio archive containing early firmware
+ followed by a gziped cpio archive containing the actual userspace
+ initramfs. More complex systems might involve a firmware archive,
+ a userspace archive and then a kernel module archive, allowing for
+ only the piece that needs changed to vary between boots.
+
+ This option tells IMA to measure each individual component of the
+ initramfs separately, rather than as a single blob.
+
config IMA_MEASURE_PCR_IDX
int
depends on IMA
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 040b03ddc1c7..be7f446df4f2 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -26,6 +26,8 @@
#include <linux/ima.h>
#include <linux/iversion.h>
#include <linux/fs.h>
+#include <linux/cpio.h>
+#include <linux/decompress/generic.h>

#include "ima.h"

@@ -198,6 +200,169 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}

+#ifdef CONFIG_IMA_MEASURE_INITRAMFS_COMPONENTS
+static void initrd_error(char *x)
+{
+ pr_err("measure initrd: error from decompressor: %s\n", x);
+}
+
+static long initrd_flush(void *buf, unsigned long size)
+{
+ return size;
+}
+
+static int process_initrd_measurement(struct integrity_iint_cache *iint,
+ struct file *file, char *buf,
+ loff_t size, const char *pathname,
+ struct modsig *modsig, int pcr,
+ struct evm_ima_xattr_data *xattr_value,
+ int xattr_len,
+ struct ima_template_desc *template_desc)
+{
+ struct cpio_context cpio_ctx;
+ const char *compress_name;
+ enum hash_algo hash_algo;
+ decompress_fn decompress;
+ long consumed, written;
+ char *start, *cur;
+ char *component;
+ int buf_len;
+ bool in_cpio;
+ int rc = 0;
+ int part;
+
+ /*
+ * We collect this once, over the whole buffer.
+ */
+ if (modsig)
+ ima_collect_modsig(modsig, buf, size);
+
+ hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
+
+ /*
+ * Pathname, compression name, 2 : separators, single digit part
+ * and a trailing NUL.
+ */
+ buf_len = strlen(pathname) + 5 + 2 + 2;
+ component = kmalloc(buf_len, GFP_KERNEL);
+ if (!component)
+ return -ENOMEM;
+
+ memset(&cpio_ctx, 0, sizeof(cpio_ctx));
+ cpio_ctx.parse_only = true;
+ rc = cpio_start(&cpio_ctx);
+ if (rc)
+ goto out;
+ in_cpio = false;
+ start = buf;
+ cur = buf;
+ part = 0;
+
+ while (rc == 0 && size) {
+ loff_t saved_offset = cpio_ctx.this_header;
+
+ /* It's a CPIO archive, process it */
+ if (*buf == '0' && !(cpio_ctx.this_header & 3)) {
+ in_cpio = true;
+ cpio_ctx.state = CPIO_START;
+ written = cpio_write_buffer(&cpio_ctx, buf, size);
+
+ if (written < 0) {
+ pr_err("Failed to process archive: %ld\n",
+ written);
+ break;
+ }
+
+ buf += written;
+ size -= written;
+ continue;
+ }
+ if (!*buf) {
+ buf++;
+ size--;
+ cpio_ctx.this_header++;
+ continue;
+ }
+
+ if (in_cpio) {
+ iint->flags &= ~(IMA_COLLECTED);
+ iint->measured_pcrs &= ~(0x1 << pcr);
+ rc = ima_collect_measurement(iint, file, cur,
+ buf - cur, hash_algo,
+ NULL);
+ if (rc == -ENOMEM)
+ return rc;
+
+ snprintf(component, buf_len, "%s:%s:%d",
+ pathname, "cpio", part);
+
+ ima_store_measurement(iint, file, component,
+ xattr_value, xattr_len, NULL, pcr,
+ template_desc);
+ part++;
+
+ in_cpio = false;
+ }
+
+ decompress = decompress_method(buf, size, &compress_name);
+ if (decompress) {
+ rc = decompress(buf, size, NULL, initrd_flush, NULL,
+ &consumed, initrd_error);
+ if (rc) {
+ pr_err("Failed to decompress archive\n");
+ break;
+ }
+ } else if (compress_name) {
+ pr_info("Compression method %s not configured.\n", compress_name);
+ break;
+ }
+
+ iint->flags &= ~(IMA_COLLECTED);
+ iint->measured_pcrs &= ~(0x1 << pcr);
+ rc = ima_collect_measurement(iint, file, buf,
+ consumed, hash_algo, NULL);
+ if (rc == -ENOMEM)
+ goto out;
+
+ snprintf(component, buf_len, "%s:%s:%d", pathname,
+ compress_name, part);
+
+ ima_store_measurement(iint, file, component,
+ xattr_value, xattr_len, NULL, pcr,
+ template_desc);
+ part++;
+
+ cpio_ctx.this_header = saved_offset + consumed;
+ buf += consumed;
+ size -= consumed;
+ cur = buf;
+ }
+ cpio_finish(&cpio_ctx);
+
+ /* Measure anything that remains */
+ if (size != 0) {
+ iint->flags &= ~(IMA_COLLECTED);
+ iint->measured_pcrs &= ~(0x1 << pcr);
+ rc = ima_collect_measurement(iint, file, buf, size, hash_algo,
+ NULL);
+ if (rc == -ENOMEM)
+ goto out;
+
+ snprintf(component, buf_len, "%s:left:%d",
+ pathname,
+ part);
+
+ ima_store_measurement(iint, file, component,
+ xattr_value, xattr_len, NULL, pcr,
+ template_desc);
+ }
+
+out:
+ kfree(component);
+ return rc;
+}
+#endif
+
static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
enum ima_hooks func)
@@ -334,17 +499,27 @@ static int process_measurement(struct file *file, const struct cred *cred,

hash_algo = ima_get_hash_algo(xattr_value, xattr_len);

- rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig);
- if (rc == -ENOMEM)
- goto out_locked;
-
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);

- if (action & IMA_MEASURE)
- ima_store_measurement(iint, file, pathname,
- xattr_value, xattr_len, modsig, pcr,
- template_desc);
+ if (IS_ENABLED(CONFIG_IMA_MEASURE_INITRAMFS_COMPONENTS) &&
+ (action & IMA_MEASURE) && func == KEXEC_INITRAMFS_CHECK) {
+ rc = process_initrd_measurement(iint, file, buf, size,
+ pathname, modsig, pcr,
+ xattr_value, xattr_len,
+ template_desc);
+ } else {
+ rc = ima_collect_measurement(iint, file, buf, size, hash_algo,
+ modsig);
+ if (rc == -ENOMEM)
+ goto out_locked;
+
+ if (action & IMA_MEASURE)
+ ima_store_measurement(iint, file, pathname,
+ xattr_value, xattr_len, modsig,
+ pcr, template_desc);
+ }
+
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
rc = ima_check_blacklist(iint, modsig, pcr);
if (rc != -EPERM) {
--
2.36.1
\
 
 \ /
  Last update: 2022-07-08 12:14    [W:0.209 / U:0.048 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site