All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jonathan McDowell <noodles@fb.com>
To: "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"linux-fsdevel@vger.kernel.org" <linux-fsdevel@vger.kernel.org>,
	"linux-integrity@vger.kernel.org"
	<linux-integrity@vger.kernel.org>,
	"linux-security-module@vger.kernel.org" 
	<linux-security-module@vger.kernel.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
	Mimi Zohar <zohar@linux.ibm.com>,
	Dmitry Kasatkin <dmitry.kasatkin@gmail.com>,
	James Morris <jmorris@namei.org>,
	"Serge E. Hallyn" <serge@hallyn.com>,
	Matthew Garrett <mjg59@srcf.ucam.org>,
	Dmitrii Potoskuev <dpotoskuev@fb.com>
Subject: [RFC PATCH v2 7/7] ima: Support measurement of kexec initramfs components
Date: Thu, 28 Jul 2022 14:09:56 +0000	[thread overview]
Message-ID: <034c6e491e871e5902ca4d0af884adb07b37a39d.1659003817.git.noodles@fb.com> (raw)
In-Reply-To: <cover.1659003817.git.noodles@fb.com>

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

      parent reply	other threads:[~2022-07-28 14:11 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-08 10:10 [RFC PATCH 0/7] ima: Support measurement of kexec initramfs components Jonathan McDowell
2022-07-08 10:10 ` Jonathan McDowell
2022-07-08 10:11 ` [RFC PATCH 1/7] initramfs: Move cpio handling routines into lib/ Jonathan McDowell
2022-07-08 10:11   ` Jonathan McDowell
2022-07-08 10:11 ` [RFC PATCH 2/7] lib/cpio: Improve error handling Jonathan McDowell
2022-07-08 10:11   ` Jonathan McDowell
2022-07-08 10:12 ` [RFC PATCH 3/7] lib/cpio: use non __init filesystem related functions Jonathan McDowell
2022-07-08 10:12   ` Jonathan McDowell
2022-07-11  8:38   ` [lib/cpio] 0e4846b4e7: Initramfs_unpacking_failed kernel test robot
2022-07-11  8:38     ` kernel test robot
2022-07-11  8:38     ` kernel test robot
2022-07-08 10:12 ` [RFC PATCH 4/7] lib/cpio: Allow use outside of initramfs creation Jonathan McDowell
2022-07-08 10:12   ` Jonathan McDowell
2022-07-08 10:12 ` [RFC PATCH 5/7] lib/cpio: Add a parse-only option that doesn't extract any files Jonathan McDowell
2022-07-08 10:12   ` Jonathan McDowell
2022-07-08 10:12 ` [RFC PATCH 6/7] HACK: Allow the use of generic decompress with gzip outside __init Jonathan McDowell
2022-07-08 10:12   ` Jonathan McDowell
2022-07-08 10:12 ` [RFC PATCH 7/7] ima: Support measurement of kexec initramfs components Jonathan McDowell
2022-07-08 10:12   ` Jonathan McDowell
2022-07-08 11:49 ` [RFC PATCH 0/7] " Mimi Zohar
2022-07-08 11:49   ` Mimi Zohar
2022-07-08 15:34   ` Jonathan McDowell
2022-07-08 15:34     ` Jonathan McDowell
2022-07-28 14:08 ` [RFC PATCH v2 " Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 1/7] initramfs: Move cpio handling routines into lib/ Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 2/7] lib/cpio: Improve error handling Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 3/7] lib/cpio: use non __init filesystem related functions Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 4/7] lib/cpio: Allow use outside of initramfs creation Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 5/7] lib/cpio: Add a parse-only option that doesn't extract any files Jonathan McDowell
2022-07-28 14:09   ` [RFC PATCH v2 6/7] HACK: Allow the use of generic decompress with gzip outside __init Jonathan McDowell
2022-07-28 14:09   ` Jonathan McDowell [this message]

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=034c6e491e871e5902ca4d0af884adb07b37a39d.1659003817.git.noodles@fb.com \
    --to=noodles@fb.com \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=dpotoskuev@fb.com \
    --cc=jmorris@namei.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=serge@hallyn.com \
    --cc=viro@zeniv.linux.org.uk \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.