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
prev parent reply other threads:[~2022-07-28 14:11 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-08 10:10 [RFC PATCH 0/7] " 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 ` [RFC PATCH 2/7] lib/cpio: Improve error handling Jonathan McDowell
2022-07-08 10:12 ` [RFC PATCH 3/7] lib/cpio: use non __init filesystem related functions Jonathan McDowell
2022-07-11 8:38 ` [lib/cpio] 0e4846b4e7: Initramfs_unpacking_failed 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 ` [RFC PATCH 5/7] lib/cpio: Add a parse-only option that doesn't extract any files 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 ` [RFC PATCH 7/7] ima: Support measurement of kexec initramfs components Jonathan McDowell
2022-07-08 11:49 ` [RFC PATCH 0/7] " Mimi Zohar
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®