Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b8589cc authored by Mimi Zohar's avatar Mimi Zohar Committed by Linus Torvalds
Browse files

ima: on soft reboot, save the measurement list

The TPM PCRs are only reset on a hard reboot.  In order to validate a
TPM's quote after a soft reboot (eg.  kexec -e), the IMA measurement
list of the running kernel must be saved and restored on boot.

This patch uses the kexec buffer passing mechanism to pass the
serialized IMA binary_runtime_measurements to the next kernel.

Link: http://lkml.kernel.org/r/1480554346-29071-7-git-send-email-zohar@linux.vnet.ibm.com


Signed-off-by: default avatarThiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: default avatarMimi Zohar <zohar@linux.vnet.ibm.com>
Acked-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: default avatarDmitry Kasatkin <dmitry.kasatkin@gmail.com>
Cc: Andreas Steffen <andreas.steffen@strongswan.org>
Cc: Josh Sklar <sklar@linux.vnet.ibm.com>
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: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ab6b1d1f
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image);
#endif

#else
static inline int ima_bprm_check(struct linux_binprm *bprm)
{
@@ -62,6 +67,13 @@ static inline void ima_post_path_mknod(struct dentry *dentry)

#endif /* CONFIG_IMA */

#ifndef CONFIG_IMA_KEXEC
struct kimage;

static inline void ima_add_kexec_buffer(struct kimage *image)
{}
#endif

#ifdef CONFIG_IMA_APPRAISE
extern void ima_inode_post_setattr(struct dentry *dentry);
extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+4 −0
Original line number Diff line number Diff line
@@ -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>
@@ -132,6 +133,9 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
		return ret;
	image->kernel_buf_len = size;

	/* IMA needs to pass the measurement list to the next kernel. */
	ima_add_kexec_buffer(image);

	/* Call arch image probe handlers */
	ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
					    image->kernel_buf_len);
+1 −0
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
struct ima_template_desc *ima_template_desc_current(void);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);

+1 −1
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ void ima_putc(struct seq_file *m, void *data, int datalen)
 *       [eventdata length]
 *       eventdata[n]=template specific data
 */
static int ima_measurements_show(struct seq_file *m, void *v)
int ima_measurements_show(struct seq_file *m, void *v)
{
	/* the list never shrinks, so we don't need a lock here */
	struct ima_queue_entry *qe = v;
+117 −0
Original line number Diff line number Diff line
@@ -10,8 +10,125 @@
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
#include <linux/kexec.h>
#include "ima.h"

#ifdef CONFIG_IMA_KEXEC
static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
				     unsigned long segment_size)
{
	struct ima_queue_entry *qe;
	struct seq_file file;
	struct ima_kexec_hdr khdr = {
		.version = 1, .buffer_size = 0, .count = 0};
	int ret = 0;

	/* segment size can't change between kexec load and execute */
	file.buf = vmalloc(segment_size);
	if (!file.buf) {
		ret = -ENOMEM;
		goto out;
	}

	file.size = segment_size;
	file.read_pos = 0;
	file.count = sizeof(khdr);	/* reserved space */

	list_for_each_entry_rcu(qe, &ima_measurements, later) {
		if (file.count < file.size) {
			khdr.count++;
			ima_measurements_show(&file, qe);
		} else {
			ret = -EINVAL;
			break;
		}
	}

	if (ret < 0)
		goto out;

	/*
	 * fill in reserved space with some buffer details
	 * (eg. version, buffer size, number of measurements)
	 */
	khdr.buffer_size = file.count;
	memcpy(file.buf, &khdr, sizeof(khdr));
	print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
			16, 1, file.buf,
			file.count < 100 ? file.count : 100, true);

	*buffer_size = file.count;
	*buffer = file.buf;
out:
	if (ret == -EINVAL)
		vfree(file.buf);
	return ret;
}

/*
 * Called during kexec_file_load so that IMA can add a segment to the kexec
 * image for the measurement list for the next kernel.
 *
 * This function assumes that kexec_mutex is held.
 */
void ima_add_kexec_buffer(struct kimage *image)
{
	struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
				  .buf_min = 0, .buf_max = ULONG_MAX,
				  .top_down = true };
	unsigned long binary_runtime_size;

	/* use more understandable variable names than defined in kbuf */
	void *kexec_buffer = NULL;
	size_t kexec_buffer_size;
	size_t kexec_segment_size;
	int ret;

	/*
	 * Reserve an extra half page of memory for additional measurements
	 * added during the kexec load.
	 */
	binary_runtime_size = ima_get_binary_runtime_size();
	if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
		kexec_segment_size = ULONG_MAX;
	else
		kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
					   PAGE_SIZE / 2, PAGE_SIZE);
	if ((kexec_segment_size == ULONG_MAX) ||
	    ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages / 2)) {
		pr_err("Binary measurement list too large.\n");
		return;
	}

	ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
				  kexec_segment_size);
	if (!kexec_buffer) {
		pr_err("Not enough memory for the kexec measurement buffer.\n");
		return;
	}

	kbuf.buffer = kexec_buffer;
	kbuf.bufsz = kexec_buffer_size;
	kbuf.memsz = kexec_segment_size;
	ret = kexec_add_buffer(&kbuf);
	if (ret) {
		pr_err("Error passing over kexec measurement buffer.\n");
		return;
	}

	ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size);
	if (ret) {
		pr_err("Error passing over kexec measurement buffer.\n");
		return;
	}

	pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
		 kbuf.mem);
}
#endif /* IMA_KEXEC */

/*
 * Restore the measurement list from the previous kernel.
 */