The functions ima_get_kexec_buffer() and ima_free_kexec_buffer(), that handle carrying forward the IMA measurement logs on kexec for powerpc do not have architecture specific code, but they are currently defined for powerpc only. Move ima_get_kexec_buffer() and ima_free_kexec_buffer() to IMA subsystem. A later patch in this series will use these functions for carrying forward the IMA measurement log for ARM64. With the above refactoring arch/powerpc/kexec/ima.c contains only functions used when CONFIG_IMA_KEXEC is enabled. Update Makefile in arch/powerpc/kexec to include arch/powerpc/kexec/ima.c only when CONFIG_IMA_KEXEC is enabled. Move ima_dump_measurement_list() and ima_add_kexec_buffer() to a new file namely ima_kexec_fdt.c in IMA. Update security/integrity/ima/Makefile to include ima_kexec_fdt.c only when CONFIG_IMA_KEXEC is enabled. Co-developed-by: Prakhar Srivastava <prsriva@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Prakhar Srivastava <prsriva@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Lakshmi Ramasubramanian <nramas@xxxxxxxxxxxxxxxxxxx> --- arch/powerpc/include/asm/ima.h | 3 - arch/powerpc/kexec/Makefile | 7 +- arch/powerpc/kexec/ima.c | 56 --------- security/integrity/ima/Makefile | 1 + security/integrity/ima/ima_kexec.c | 154 +++++++------------------ security/integrity/ima/ima_kexec_fdt.c | 136 ++++++++++++++++++++++ 6 files changed, 182 insertions(+), 175 deletions(-) create mode 100644 security/integrity/ima/ima_kexec_fdt.c diff --git a/arch/powerpc/include/asm/ima.h b/arch/powerpc/include/asm/ima.h index 6355a85a3289..8975f5e0cab8 100644 --- a/arch/powerpc/include/asm/ima.h +++ b/arch/powerpc/include/asm/ima.h @@ -6,9 +6,6 @@ struct kimage; -int ima_get_kexec_buffer(void **addr, size_t *size); -int ima_free_kexec_buffer(void); - #ifdef CONFIG_IMA_KEXEC int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr, size_t size); diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile index 4aff6846c772..f54a9dbff4c8 100644 --- a/arch/powerpc/kexec/Makefile +++ b/arch/powerpc/kexec/Makefile @@ -9,12 +9,7 @@ obj-$(CONFIG_PPC32) += relocate_32.o obj-$(CONFIG_KEXEC_FILE) += file_load.o ranges.o file_load_$(BITS).o elf_$(BITS).o -ifdef CONFIG_HAVE_IMA_KEXEC -ifdef CONFIG_IMA -obj-y += ima.o -endif -endif - +obj-$(CONFIG_IMA_KEXEC) += ima.o # Disable GCOV, KCOV & sanitizers in odd or sensitive code GCOV_PROFILE_core_$(BITS).o := n diff --git a/arch/powerpc/kexec/ima.c b/arch/powerpc/kexec/ima.c index 2b790230ea15..4fadd8d16891 100644 --- a/arch/powerpc/kexec/ima.c +++ b/arch/powerpc/kexec/ima.c @@ -30,61 +30,6 @@ static int get_addr_size_cells(int *addr_cells, int *size_cells) return 0; } -/** - * ima_get_kexec_buffer - get IMA buffer from the previous kernel - * @addr: On successful return, set to point to the buffer contents. - * @size: On successful return, set to the buffer size. - * - * Return: 0 on success, negative errno on error. - */ -int ima_get_kexec_buffer(void **addr, size_t *size) -{ - int ret, len; - unsigned long tmp_addr; - size_t tmp_size; - const void *prop; - - prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len); - if (!prop) - return -ENOENT; - - ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size); - if (ret) - return ret; - - *addr = __va(tmp_addr); - *size = tmp_size; - - return 0; -} - -/** - * ima_free_kexec_buffer - free memory used by the IMA buffer - */ -int ima_free_kexec_buffer(void) -{ - int ret; - unsigned long addr; - size_t size; - struct property *prop; - - prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL); - if (!prop) - return -ENOENT; - - ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size); - if (ret) - return ret; - - ret = of_remove_property(of_chosen, prop); - if (ret) - return ret; - - return memblock_free(addr, size); - -} - -#ifdef CONFIG_IMA_KEXEC /** * arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer * @@ -177,4 +122,3 @@ int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node) return 0; } -#endif /* CONFIG_IMA_KEXEC */ diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 67dabca670e2..0c440870a4e9 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -14,3 +14,4 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o +ima-$(CONFIG_IMA_KEXEC) += ima_kexec_fdt.o diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 121de3e04af2..987e464be889 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -7,131 +7,65 @@ * Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx> */ -#include <linux/seq_file.h> #include <linux/vmalloc.h> #include <linux/kexec.h> +#include <linux/of.h> +#include <linux/memblock.h> +#include <linux/libfdt.h> +#include <linux/ima.h> #include "ima.h" -#ifdef CONFIG_IMA_KEXEC -static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, - unsigned long segment_size) +/** + * ima_get_kexec_buffer - get IMA buffer from the previous kernel + * @addr: On successful return, set to point to the buffer contents. + * @size: On successful return, set to the buffer size. + * + * Return: 0 on success, negative errno on error. + */ +static int ima_get_kexec_buffer(void **addr, size_t *size) { - struct ima_queue_entry *qe; - struct seq_file file; - struct ima_kexec_hdr khdr; - 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 */ - - memset(&khdr, 0, sizeof(khdr)); - khdr.version = 1; - 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; - if (ima_canonical_fmt) { - khdr.version = cpu_to_le16(khdr.version); - khdr.count = cpu_to_le64(khdr.count); - khdr.buffer_size = cpu_to_le64(khdr.buffer_size); - } - 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; + int ret, len; + unsigned long tmp_addr; + size_t tmp_size; + const void *prop; + + prop = of_get_property(of_chosen, FDT_PROP_IMA_KEXEC_BUFFER, &len); + if (!prop) + return -ENOENT; + + ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size); + if (ret) + return ret; + + *addr = __va(tmp_addr); + *size = tmp_size; + return 0; } -/* - * 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. +/** + * ima_free_kexec_buffer - free memory used by the IMA buffer */ -void ima_add_kexec_buffer(struct kimage *image) +static int ima_free_kexec_buffer(void) { - 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; + unsigned long addr; + size_t size; + struct property *prop; - /* - * 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; - } + prop = of_find_property(of_chosen, FDT_PROP_IMA_KEXEC_BUFFER, NULL); + if (!prop) + return -ENOENT; - 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; - } + ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size); + if (ret) + return ret; - 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; - } + ret = of_remove_property(of_chosen, prop); + if (ret) + return ret; - pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n", - kbuf.mem); + return memblock_free(addr, size); } -#endif /* IMA_KEXEC */ /* * Restore the measurement list from the previous kernel. diff --git a/security/integrity/ima/ima_kexec_fdt.c b/security/integrity/ima/ima_kexec_fdt.c new file mode 100644 index 000000000000..186051467e5b --- /dev/null +++ b/security/integrity/ima/ima_kexec_fdt.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 IBM Corporation + * + * Authors: + * Thiago Jung Bauermann <bauerman@xxxxxxxxxxxxxxxxxx> + * Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx> + */ + +#include <linux/seq_file.h> +#include <linux/vmalloc.h> +#include <linux/kexec.h> +#include <linux/of.h> +#include <linux/memblock.h> +#include <linux/libfdt.h> +#include <linux/ima.h> +#include "ima.h" + +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; + 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 */ + + memset(&khdr, 0, sizeof(khdr)); + khdr.version = 1; + 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; + if (ima_canonical_fmt) { + khdr.version = cpu_to_le16(khdr.version); + khdr.count = cpu_to_le64(khdr.count); + khdr.buffer_size = cpu_to_le64(khdr.buffer_size); + } + 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); +} -- 2.28.0