make the kernel accept UKIs (Unified Kernel Images) for kexec_file_load. UKIs contain the kernel bzImage, initrd, and cmdline all packaged up as one EFI application. The main advantage of this is that the whole combination is signed together as a package for secure boot. This implementation parses the UKI and passes the bzImage, initrd, and cmdline to the normal bzImage loader. Signed-off-by: Jan Hendrik Farr <kernel@xxxxxxxx> --- arch/x86/include/asm/kexec-uki.h | 7 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/kexec-uki.c | 126 +++++++++++++++++++++++++ arch/x86/kernel/machine_kexec_64.c | 2 + crypto/asymmetric_keys/verify_pefile.c | 11 ++- lib/Makefile | 1 + lib/parse_pefile.c | 21 ++--- 7 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 arch/x86/include/asm/kexec-uki.h create mode 100644 arch/x86/kernel/kexec-uki.c diff --git a/arch/x86/include/asm/kexec-uki.h b/arch/x86/include/asm/kexec-uki.h new file mode 100644 index 000000000000..87fd2c6fb091 --- /dev/null +++ b/arch/x86/include/asm/kexec-uki.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_KEXEC_UKI_H +#define _ASM_KEXEC_UKI_H + +extern const struct kexec_file_ops kexec_uki_ops; + +#endif /* _ASM_KEXEC_UKI_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 3269a0e23d3a..a73d61f86d29 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core_$(BITS).o obj-$(CONFIG_KEXEC_CORE) += machine_kexec_$(BITS).o obj-$(CONFIG_KEXEC_CORE) += relocate_kernel_$(BITS).o crash.o obj-$(CONFIG_KEXEC_FILE) += kexec-bzimage64.o +obj-$(CONFIG_KEXEC_FILE) += kexec-uki.o obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o obj-y += kprobes/ obj-$(CONFIG_MODULES) += module.o diff --git a/arch/x86/kernel/kexec-uki.c b/arch/x86/kernel/kexec-uki.c new file mode 100644 index 000000000000..5a381e4f3728 --- /dev/null +++ b/arch/x86/kernel/kexec-uki.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Kexec UKI loader + * + * Copyright (C) 2023 Jan Hendrik Farr + * + * Authors: + * Jan Hendrik Farr <kernel@xxxxxxxx> + */ + +#define pr_fmt(fmt) "kexec-uki: " fmt + +#include <linux/kernel.h> +#include "linux/pe.h" +#include <linux/kexec.h> +#include <linux/err.h> + +#include <asm/kexec-uki.h> +#include <asm/kexec-bzimage64.h> +#include <linux/parse_pefile.h> + +static int find_section(struct pefile_context *ctx, const char *name, + const struct section_header **sec) +{ + for (int i = 0; i < ctx->n_sections; i++) { + const struct section_header *cur_sec = &ctx->secs[i]; + + if (!strncmp(cur_sec->name, name, ARRAY_SIZE(cur_sec->name))) { + *sec = cur_sec; + return 0; + } + } + + return -EINVAL; +} + +static int uki_probe(const char *buf, unsigned long len) +{ + int ret = -ENOEXEC; + int r = 0; + struct pefile_context pe_ctx; + const struct section_header *s; + + memset(&pe_ctx, 0, sizeof(pe_ctx)); + r = pefile_parse_binary(buf, len, &pe_ctx); + + if (r) { + pr_info("Not a UKI. Not a valid PE file\n"); + return ret; + } + + if (find_section(&pe_ctx, ".linux", &s) || + find_section(&pe_ctx, ".initrd", &s)) { + pr_info("Not a UKI. Missing .linux, or .initrd\n"); + return ret; + } + + pr_info("It's a UKI\n"); + return 0; +} + +static void *uki_load(struct kimage *image, char *kernel, + unsigned long kernel_len, char *initrd, + unsigned long initrd_len, char *cmdline, + unsigned long cmdline_len) +{ + struct pefile_context pe_ctx; + const struct section_header *sec_linux, *sec_initrd, *sec_cmdline; + int r_linux, r_initrd, r_cmdline, r = 0; + void *ret; + + if (initrd_len || strcmp(cmdline, "") || cmdline_len != 1) { + pr_err("No manual cmdline or initrd allowed for UKIs"); + return ERR_PTR(-EPERM); + } + + memset(&pe_ctx, 0, sizeof(pe_ctx)); + r = pefile_parse_binary(kernel, kernel_len, &pe_ctx); + + if (r) + return ERR_PTR(r); + + r_linux = find_section(&pe_ctx, ".linux", &sec_linux); + r_initrd = find_section(&pe_ctx, ".initrd", &sec_initrd); + r_cmdline = find_section(&pe_ctx, ".cmdline", &sec_cmdline); + + if (r_linux || r_initrd) + return ERR_PTR(-EINVAL); + + if (r_cmdline) { + cmdline = ""; + cmdline_len = 1; + } else { + cmdline = kernel + sec_cmdline->data_addr; + cmdline_len = sec_cmdline->raw_data_size; + } + + ret = kexec_bzImage64_ops.load( + image, + kernel + sec_linux->data_addr, + sec_linux->raw_data_size, + kernel + sec_initrd->data_addr, + sec_initrd->raw_data_size, + cmdline, + cmdline_len + ); + + if (IS_ERR(ret)) + pr_err("bzImage64_load error\n"); + + return ret; +} + +static int uki_cleanup(void *loader_data) +{ + return kexec_bzImage64_ops.cleanup(loader_data); +} + +const struct kexec_file_ops kexec_uki_ops = { + .probe = uki_probe, + .load = uki_load, + .cleanup = uki_cleanup, +#ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG + .verify_sig = kexec_kernel_verify_pe_sig, +#endif +}; diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index 1a3e2c05a8a5..072f5aac52b9 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -25,6 +25,7 @@ #include <asm/io_apic.h> #include <asm/debugreg.h> #include <asm/kexec-bzimage64.h> +#include <asm/kexec-uki.h> #include <asm/setup.h> #include <asm/set_memory.h> #include <asm/cpu.h> @@ -81,6 +82,7 @@ static int map_acpi_tables(struct x86_mapping_info *info, pgd_t *level4p) { retu #ifdef CONFIG_KEXEC_FILE const struct kexec_file_ops * const kexec_file_loaders[] = { &kexec_bzImage64_ops, + &kexec_uki_ops, NULL }; #endif diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 68592a328db7..ea183feca231 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -334,6 +334,13 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; + const struct data_dirent *certs = pebuf + ctx.cert_dirent_offset; + + if (!certs->virtual_address || !certs->size) { + pr_warn("Unsigned PE binary\n"); + return -ENODATA; + } + ret = pefile_strip_sig_wrapper(pebuf, &ctx); if (ret < 0) return ret; @@ -342,8 +349,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, pebuf + ctx.sig_offset, ctx.sig_len, trusted_keys, usage, mscode_parse, &ctx); - if (ret < 0) + if (ret < 0) { + pr_warn("invalid PE file signature\n"); goto error; + } pr_debug("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest); diff --git a/lib/Makefile b/lib/Makefile index 01a6c13565b6..ad6af1bab3ef 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -366,6 +366,7 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o obj-$(CONFIG_PARMAN) += parman.o obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += parse_pefile.o +obj-$(CONFIG_KEXEC_FILE) += parse_pefile.o obj-y += group_cpus.o diff --git a/lib/parse_pefile.c b/lib/parse_pefile.c index 9a8496b2588e..672a044d380c 100644 --- a/lib/parse_pefile.c +++ b/lib/parse_pefile.c @@ -88,18 +88,17 @@ int pefile_parse_binary(const void *pebuf, unsigned int pelen, (unsigned long)&ddir->certs - (unsigned long)pebuf; ctx->certs_size = ddir->certs.size; - if (!ddir->certs.virtual_address || !ddir->certs.size) { - pr_warn("Unsigned PE binary\n"); - return -ENODATA; - } + if (ddir->certs.virtual_address && ddir->certs.size) { + + chkaddr(ctx->header_size, ddir->certs.virtual_address, + ddir->certs.size); + ctx->sig_offset = ddir->certs.virtual_address; + ctx->sig_len = ddir->certs.size; + pr_debug("cert = %x @%x [%*ph]\n", + ctx->sig_len, ctx->sig_offset, + ctx->sig_len, pebuf + ctx->sig_offset); - chkaddr(ctx->header_size, ddir->certs.virtual_address, - ddir->certs.size); - ctx->sig_offset = ddir->certs.virtual_address; - ctx->sig_len = ddir->certs.size; - pr_debug("cert = %x @%x [%*ph]\n", - ctx->sig_len, ctx->sig_offset, - ctx->sig_len, pebuf + ctx->sig_offset); + } ctx->n_sections = pe->sections; if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) -- 2.40.1 _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec