Commit b69a2afd authored by Jonathan McDowell's avatar Jonathan McDowell Committed by Borislav Petkov
Browse files

x86/kexec: Carry forward IMA measurement log on kexec



On kexec file load, the Integrity Measurement Architecture (IMA)
subsystem may verify the IMA signature of the kernel and initramfs, and
measure it. The command line parameters passed to the kernel in the
kexec call may also be measured by IMA.

A remote attestation service can verify a TPM quote based on the TPM
event log, the IMA measurement list and the TPM PCR data. This can
be achieved only if the IMA measurement log is carried over from the
current kernel to the next kernel across the kexec call.

PowerPC and ARM64 both achieve this using device tree with a
"linux,ima-kexec-buffer" node. x86 platforms generally don't make use of
device tree, so use the setup_data mechanism to pass the IMA buffer to
the new kernel.

Signed-off-by: default avatarJonathan McDowell <noodles@fb.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> # IMA function definitions
Link: https://lore.kernel.org/r/YmKyvlF3my1yWTvK@noodles-fedora-PC23Y6EG
parent 03c765b0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2033,6 +2033,7 @@ config KEXEC_FILE
	bool "kexec file based system call"
	select KEXEC_CORE
	select BUILD_BIN2C
	select HAVE_IMA_KEXEC if IMA
	depends on X86_64
	depends on CRYPTO=y
	depends on CRYPTO_SHA256=y
+9 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#define SETUP_APPLE_PROPERTIES		5
#define SETUP_JAILHOUSE			6
#define SETUP_CC_BLOB			7
#define SETUP_IMA			8

#define SETUP_INDIRECT			(1<<31)

@@ -172,6 +173,14 @@ struct jailhouse_setup_data {
	} __attribute__((packed)) v2;
} __attribute__((packed));

/*
 * IMA buffer setup data information from the previous kernel during kexec
 */
struct ima_setup_data {
	__u64 addr;
	__u64 size;
} __attribute__((packed));

/* The so-called "zeropage" */
struct boot_params {
	struct screen_info screen_info;			/* 0x000 */
+3 −3
Original line number Diff line number Diff line
@@ -1017,10 +1017,10 @@ void __init e820__reserve_setup_data(void)
		e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);

		/*
		 * SETUP_EFI is supplied by kexec and does not need to be
		 * reserved.
		 * SETUP_EFI and SETUP_IMA are supplied by kexec and do not need
		 * to be reserved.
		 */
		if (data->type != SETUP_EFI)
		if (data->type != SETUP_EFI && data->type != SETUP_IMA)
			e820__range_update_kexec(pa_data,
						 sizeof(*data) + data->len,
						 E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
+40 −2
Original line number Diff line number Diff line
@@ -186,11 +186,38 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
}
#endif /* CONFIG_EFI */

static void
setup_ima_state(const struct kimage *image, struct boot_params *params,
		unsigned long params_load_addr,
		unsigned int ima_setup_data_offset)
{
#ifdef CONFIG_IMA_KEXEC
	struct setup_data *sd = (void *)params + ima_setup_data_offset;
	unsigned long setup_data_phys;
	struct ima_setup_data *ima;

	if (!image->ima_buffer_size)
		return;

	sd->type = SETUP_IMA;
	sd->len = sizeof(*ima);

	ima = (void *)sd + sizeof(struct setup_data);
	ima->addr = image->ima_buffer_addr;
	ima->size = image->ima_buffer_size;

	/* Add setup data */
	setup_data_phys = params_load_addr + ima_setup_data_offset;
	sd->next = params->hdr.setup_data;
	params->hdr.setup_data = setup_data_phys;
#endif /* CONFIG_IMA_KEXEC */
}

static int
setup_boot_parameters(struct kimage *image, struct boot_params *params,
		      unsigned long params_load_addr,
		      unsigned int efi_map_offset, unsigned int efi_map_sz,
		      unsigned int efi_setup_data_offset)
		      unsigned int setup_data_offset)
{
	unsigned int nr_e820_entries;
	unsigned long long mem_k, start, end;
@@ -245,8 +272,15 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
#ifdef CONFIG_EFI
	/* Setup EFI state */
	setup_efi_state(params, params_load_addr, efi_map_offset, efi_map_sz,
			efi_setup_data_offset);
			setup_data_offset);
	setup_data_offset += sizeof(struct setup_data) +
			sizeof(struct efi_setup_data);
#endif

	/* Setup IMA log buffer state */
	setup_ima_state(image, params, params_load_addr,
			setup_data_offset);

	/* Setup EDD info */
	memcpy(params->eddbuf, boot_params.eddbuf,
				EDDMAXNR * sizeof(struct edd_info));
@@ -403,6 +437,10 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
				sizeof(struct setup_data) +
				sizeof(struct efi_setup_data);

	if (IS_ENABLED(CONFIG_IMA_KEXEC))
		kbuf.bufsz += sizeof(struct setup_data) +
			      sizeof(struct ima_setup_data);

	params = kzalloc(kbuf.bufsz, GFP_KERNEL);
	if (!params)
		return ERR_PTR(-ENOMEM);
+63 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/dma-map-ops.h>
#include <linux/dmi.h>
#include <linux/efi.h>
#include <linux/ima.h>
#include <linux/init_ohci1394_dma.h>
#include <linux/initrd.h>
#include <linux/iscsi_ibft.h>
@@ -140,6 +141,11 @@ __visible unsigned long mmu_cr4_features __ro_after_init;
__visible unsigned long mmu_cr4_features __ro_after_init = X86_CR4_PAE;
#endif

#ifdef CONFIG_IMA
static phys_addr_t ima_kexec_buffer_phys;
static size_t ima_kexec_buffer_size;
#endif

/* Boot loader ID and version as integers, for the benefit of proc_dointvec */
int bootloader_type, bootloader_version;

@@ -330,6 +336,60 @@ static void __init reserve_initrd(void)
}
#endif /* CONFIG_BLK_DEV_INITRD */

static void __init add_early_ima_buffer(u64 phys_addr)
{
#ifdef CONFIG_IMA
	struct ima_setup_data *data;

	data = early_memremap(phys_addr + sizeof(struct setup_data), sizeof(*data));
	if (!data) {
		pr_warn("setup: failed to memremap ima_setup_data entry\n");
		return;
	}

	if (data->size) {
		memblock_reserve(data->addr, data->size);
		ima_kexec_buffer_phys = data->addr;
		ima_kexec_buffer_size = data->size;
	}

	early_memunmap(data, sizeof(*data));
#else
	pr_warn("Passed IMA kexec data, but CONFIG_IMA not set. Ignoring.\n");
#endif
}

#if defined(CONFIG_HAVE_IMA_KEXEC) && !defined(CONFIG_OF_FLATTREE)
int __init ima_free_kexec_buffer(void)
{
	int rc;

	if (!ima_kexec_buffer_size)
		return -ENOENT;

	rc = memblock_phys_free(ima_kexec_buffer_phys,
				ima_kexec_buffer_size);
	if (rc)
		return rc;

	ima_kexec_buffer_phys = 0;
	ima_kexec_buffer_size = 0;

	return 0;
}

int __init ima_get_kexec_buffer(void **addr, size_t *size)
{
	if (!ima_kexec_buffer_size)
		return -ENOENT;

	*addr = __va(ima_kexec_buffer_phys);
	*size = ima_kexec_buffer_size;

	return 0;
}
#endif

static void __init parse_setup_data(void)
{
	struct setup_data *data;
@@ -355,6 +415,9 @@ static void __init parse_setup_data(void)
		case SETUP_EFI:
			parse_efi_setup(pa_data, data_len);
			break;
		case SETUP_IMA:
			add_early_ima_buffer(pa_data);
			break;
		default:
			break;
		}
Loading