Skip to content

Commit

Permalink
IMA: demonstration code for kexec buffer passing
Browse files Browse the repository at this point in the history
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.

Link: http://lkml.kernel.org/r/1472149111-30598-6-git-send-email-bauerman@linux.vnet.ibm.com
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Dave Young <dyoung@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Cc: Mimi Zohar <zohar@linux.vnet.ibm.com>
Cc: Eric Richter <erichte@linux.vnet.ibm.com>
Cc: Balbir Singh <bsingharora@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
bauermann authored and hnaz committed Aug 26, 2016
1 parent 2a409d5 commit 79986ab
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 0 deletions.
11 changes: 11 additions & 0 deletions include/linux/ima.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define _LINUX_IMA_H

#include <linux/fs.h>
#include <linux/kexec.h>
struct linux_binprm;

#ifdef CONFIG_IMA
Expand All @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions kernel/kexec_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/ima.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/syscalls.h>
Expand Down Expand Up @@ -248,6 +249,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);

Expand Down
5 changes: 5 additions & 0 deletions security/integrity/ima/ima.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions security/integrity/ima/ima_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/kexec.h>

#include "ima.h"

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -134,5 +158,7 @@ int __init ima_init(void)

ima_init_policy();

ima_load_kexec_buffer();

return ima_fs_init();
}
85 changes: 85 additions & 0 deletions security/integrity/ima/ima_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -182,6 +184,89 @@ static int template_desc_init_fields(const char *template_fmt,
return 0;
}

#ifdef CONFIG_KEXEC_FILE
void *kexec_buffer = NULL;
size_t kexec_buffer_size = 0;

/* Physical address of the measurement buffer in the next kernel. */
unsigned long kexec_buffer_load_addr = 0;

/*
* 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, .skip_checksum = 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)
Expand Down

0 comments on commit 79986ab

Please sign in to comment.