[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1473900890-1476-6-git-send-email-bauerman@linux.vnet.ibm.com>
Date: Wed, 14 Sep 2016 21:54:50 -0300
From: Thiago Jung Bauermann <bauerman@...ux.vnet.ibm.com>
To: kexec@...ts.infradead.org
Cc: linux-ima-devel@...ts.sourceforge.net,
linuxppc-dev@...ts.ozlabs.org, x86@...nel.org,
linux-kernel@...r.kernel.org,
Eric Biederman <ebiederm@...ssion.com>,
Dave Young <dyoung@...hat.com>,
Vivek Goyal <vgoyal@...hat.com>, Baoquan He <bhe@...hat.com>,
Michael Ellerman <mpe@...erman.id.au>,
Stewart Smith <stewart@...ux.vnet.ibm.com>,
Mimi Zohar <zohar@...ux.vnet.ibm.com>,
Andrew Morton <akpm@...ux-foundation.org>,
Stephen Rothwell <sfr@...b.auug.org.au>,
Thomas Gleixner <tglx@...utronix.de>,
Ingo Molnar <mingo@...hat.com>,
"H. Peter Anvin" <hpa@...or.com>,
Thiago Jung Bauermann <bauerman@...ux.vnet.ibm.com>
Subject: [PATCH v5 5/5] IMA: Demonstration code for kexec buffer passing.
This shows how kernel code can use the kexec buffer passing mechanism
to pass information to the next kernel.
This patch is not intended to be committed.
[akpm@...ux-foundation.org: coding-style fixes]
Signed-off-by: Thiago Jung Bauermann <bauerman@...ux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@...ux-foundation.org>
Signed-off-by: Thiago Jung Bauermann <bauerman@...ux.vnet.ibm.com>
---
include/linux/ima.h | 11 +++++
kernel/kexec_file.c | 4 ++
security/integrity/ima/ima.h | 5 +++
security/integrity/ima/ima_init.c | 26 +++++++++++
security/integrity/ima/ima_template.c | 85 +++++++++++++++++++++++++++++++++++
5 files changed, 131 insertions(+)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 0eb7c2e7f0d6..96528d007139 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -11,6 +11,7 @@
#define _LINUX_IMA_H
#include <linux/fs.h>
+#include <linux/kexec.h>
struct linux_binprm;
#ifdef CONFIG_IMA
@@ -23,6 +24,10 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id id);
extern void ima_post_path_mknod(struct dentry *dentry);
+#ifdef CONFIG_KEXEC_FILE
+extern void ima_add_kexec_buffer(struct kimage *image);
+#endif
+
#else
static inline int ima_bprm_check(struct linux_binprm *bprm)
{
@@ -60,6 +65,12 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
return;
}
+#ifdef CONFIG_KEXEC_FILE
+static inline void ima_add_kexec_buffer(struct kimage *image)
+{
+}
+#endif
+
#endif /* CONFIG_IMA */
#ifdef CONFIG_IMA_APPRAISE
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index fbcec07bb3f5..0146619479a6 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -20,6 +20,7 @@
#include <linux/list.h>
#include <linux/highmem.h>
#include <linux/fs.h>
+#include <linux/ima.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/syscalls.h>
@@ -252,6 +253,9 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
}
}
+ /* IMA needs to pass the measurement list to the next kernel. */
+ ima_add_kexec_buffer(image);
+
/* Call arch image load handlers */
ldata = arch_kexec_kernel_image_load(image);
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index db25f54a04fe..0334001055d7 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -102,6 +102,11 @@ struct ima_queue_entry {
};
extern struct list_head ima_measurements; /* list of all measurements */
+#ifdef CONFIG_KEXEC_FILE
+extern void *kexec_buffer;
+extern size_t kexec_buffer_size;
+#endif
+
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 32912bd54ead..a1924d0f3b2b 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -21,6 +21,7 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/kexec.h>
#include "ima.h"
@@ -104,6 +105,29 @@ void __init ima_load_x509(void)
}
#endif
+#ifdef CONFIG_KEXEC_FILE
+static void ima_load_kexec_buffer(void)
+{
+ int rc;
+
+ /* Fetch the buffer from the previous kernel, if any. */
+ rc = kexec_get_handover_buffer(&kexec_buffer, &kexec_buffer_size);
+ if (rc == 0) {
+ /* Demonstrate that buffer handover works. */
+ pr_err("kexec buffer contents: %s\n", (char *) kexec_buffer);
+ pr_err("kexec buffer contents after update: %s\n",
+ (char *) kexec_buffer + 4 * PAGE_SIZE + 10);
+
+ kexec_free_handover_buffer();
+ } else if (rc == -ENOENT)
+ pr_debug("No kexec buffer from the previous kernel.\n");
+ else
+ pr_debug("Error restoring kexec buffer: %d\n", rc);
+}
+#else
+static void ima_load_kexec_buffer(void) { }
+#endif
+
int __init ima_init(void)
{
u8 pcr_i[TPM_DIGEST_SIZE];
@@ -134,5 +158,7 @@ int __init ima_init(void)
ima_init_policy();
+ ima_load_kexec_buffer();
+
return ima_fs_init();
}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index febd12ed9b55..92ea3afd9a1f 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -15,6 +15,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kexec.h>
+#include <linux/reboot.h>
#include "ima.h"
#include "ima_template_lib.h"
@@ -182,6 +184,89 @@ static int template_desc_init_fields(const char *template_fmt,
return 0;
}
+#ifdef CONFIG_KEXEC_FILE
+void *kexec_buffer;
+size_t kexec_buffer_size;
+
+/* Physical address of the measurement buffer in the next kernel. */
+unsigned long kexec_buffer_load_addr;
+
+/*
+ * Called during reboot. IMA can add here new events that were generated after
+ * the kexec image was loaded.
+ */
+static int ima_update_kexec_buffer(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ int ret;
+
+ if (!kexec_in_progress)
+ return NOTIFY_OK;
+
+ /*
+ * Add content deep in the buffer to show that we can update
+ * all of it.
+ */
+ strcpy(kexec_buffer + 4 * PAGE_SIZE + 10,
+ "Updated kexec buffer contents.");
+
+ ret = kexec_update_segment(kexec_buffer, kexec_buffer_size,
+ kexec_buffer_load_addr, kexec_buffer_size);
+ if (ret)
+ pr_err("Error updating kexec buffer: %d\n", ret);
+
+ return NOTIFY_OK;
+}
+
+struct notifier_block update_buffer_nb = {
+ .notifier_call = ima_update_kexec_buffer,
+};
+
+/*
+ * Called during kexec_file_load so that IMA can add a segment to the kexec
+ * image with the measurement event log for the next kernel.
+ */
+void ima_add_kexec_buffer(struct kimage *image)
+{
+ /* Ask not to checksum the segment, we may have to update it later. */
+ struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
+ .buf_min = 0, .buf_max = ULONG_MAX,
+ .top_down = true };
+ bool first_kexec_load = kexec_buffer_load_addr == 0;
+ int ret;
+
+ if (!kexec_can_hand_over_buffer())
+ return;
+
+ if (!first_kexec_load)
+ kfree(kexec_buffer);
+
+ /* Create a relatively big buffer, for testing. */
+ kexec_buffer_size = kbuf.bufsz = kbuf.memsz = 5 * PAGE_SIZE;
+ kexec_buffer = kbuf.buffer = kzalloc(kexec_buffer_size, GFP_KERNEL);
+ if (!kexec_buffer) {
+ pr_err("Not enough memory for the kexec measurement buffer.\n");
+ return;
+ }
+
+ /* Add some content for demonstration purposes. */
+ strcpy(kexec_buffer, "Buffer contents at kexec load time.");
+
+ ret = kexec_add_handover_buffer(&kbuf);
+ if (ret) {
+ pr_err("Error passing over kexec measurement buffer.\n");
+ return;
+ }
+ kexec_buffer_load_addr = kbuf.mem;
+
+ if (first_kexec_load)
+ register_reboot_notifier(&update_buffer_nb);
+
+ pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
+ kexec_buffer_load_addr);
+}
+#endif /* CONFIG_KEXEC_FILE */
+
struct ima_template_desc *ima_template_desc_current(void)
{
if (!ima_template)
--
1.9.1
Powered by blists - more mailing lists