All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jan Hendrik Farr <kernel@jfarr.cc>
To: linux-kernel@vger.kernel.org
Cc: kexec@lists.infradead.org, x86@kernel.org, tglx@linutronix.de,
	dhowells@redhat.com, vgoyal@redhat.com, keyrings@vger.kernel.org,
	akpm@linux-foundation.org, bhe@redhat.com, bhelgaas@google.com,
	lennart@poettering.net, bluca@debian.org,
	systemd-devel@lists.freedesktop.org, kernel@jfarr.cc
Subject: [PATCH 1/1] x86/kexec: UKI support
Date: Sat,  9 Sep 2023 18:18:51 +0200	[thread overview]
Message-ID: <20230909161851.223627-2-kernel@jfarr.cc> (raw)
In-Reply-To: <20230909161851.223627-1-kernel@jfarr.cc>

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@jfarr.cc>
---
 arch/x86/include/asm/kexec-uki.h       |   7 ++
 arch/x86/include/asm/parse_pefile.h    |  32 +++++++
 arch/x86/kernel/Makefile               |   2 +
 arch/x86/kernel/kexec-uki.c            | 113 +++++++++++++++++++++++++
 arch/x86/kernel/machine_kexec_64.c     |   2 +
 arch/x86/kernel/parse_pefile.c         | 110 ++++++++++++++++++++++++
 crypto/asymmetric_keys/mscode_parser.c |   2 +-
 crypto/asymmetric_keys/verify_pefile.c | 110 +++---------------------
 crypto/asymmetric_keys/verify_pefile.h |  16 ----
 9 files changed, 278 insertions(+), 116 deletions(-)
 create mode 100644 arch/x86/include/asm/kexec-uki.h
 create mode 100644 arch/x86/include/asm/parse_pefile.h
 create mode 100644 arch/x86/kernel/kexec-uki.c
 create mode 100644 arch/x86/kernel/parse_pefile.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/include/asm/parse_pefile.h b/arch/x86/include/asm/parse_pefile.h
new file mode 100644
index 000000000000..c29f8c98ee66
--- /dev/null
+++ b/arch/x86/include/asm/parse_pefile.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _ASM_PARSE_PEFILE_H
+#define _ASM_PARSE_PEFILE_H
+
+#include <linux/pe.h>
+
+struct pefile_context {
+	unsigned	header_size;
+	unsigned	image_checksum_offset;
+	unsigned	cert_dirent_offset;
+	unsigned	n_data_dirents;
+	unsigned	n_sections;
+	unsigned	certs_size;
+	unsigned	sig_offset;
+	unsigned	sig_len;
+	const struct section_header *secs;
+
+	/* PKCS#7 MS Individual Code Signing content */
+	const void	*digest;		/* Digest */
+	unsigned	digest_len;		/* Digest length */
+	const char	*digest_algo;		/* Digest algorithm */
+};
+int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+			       struct pefile_context *ctx);
+
+#endif // _ASM_PARSE_PEFILE_H
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 3269a0e23d3a..8a37a977bf72 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -101,6 +101,8 @@ 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_KEXEC_FILE)	+= parse_pefile.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..9275196a6b71
--- /dev/null
+++ b/arch/x86/kernel/kexec-uki.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Kexec UKI loader
+ *
+ * Copyright (C) 2023 Jan Hendrik Farr
+ *
+ * Authors:
+ *      Jan Hendrik Farr <kernel@jfarr.cc>
+ */
+
+#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 <asm/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;
+	struct pefile_context pe_ctx;
+
+	int r = pefile_parse_binary(buf, len, &pe_ctx);
+
+	if (r) {
+		pr_info("Not a UKI. Not a valid PE file\n");
+		return ret;
+	}
+
+	const struct section_header *_;
+
+	if (find_section(&pe_ctx, ".linux", &_) ||
+	    find_section(&pe_ctx, ".initrd", &_) ||
+	    find_section(&pe_ctx, ".cmdline", &_)) {
+		pr_info("Not a UKI. Missing .linux, .initrd, or .cmdline\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;
+	int r = pefile_parse_binary(kernel, kernel_len, &pe_ctx);
+
+	if (r)
+		return ERR_PTR(r);
+
+	pr_debug("pefile_parse_binary return %d, number of sections: %d\n",
+		 r, pe_ctx.n_sections);
+
+	const struct section_header *sec_linux, *sec_initrd, *sec_cmdline;
+	int r_linux = find_section(&pe_ctx, ".linux", &sec_linux);
+	int r_initrd = find_section(&pe_ctx, ".initrd", &sec_initrd);
+	int r_cmdline = find_section(&pe_ctx, ".cmdline", &sec_cmdline);
+
+	if (r_linux || r_initrd || r_cmdline)
+		return ERR_PTR(-EINVAL);
+
+	void *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,
+		kernel + sec_cmdline->data_addr,
+		sec_cmdline->raw_data_size
+	);
+
+	if (IS_ERR(ret))
+		pr_warn("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/arch/x86/kernel/parse_pefile.c b/arch/x86/kernel/parse_pefile.c
new file mode 100644
index 000000000000..7737c94a1848
--- /dev/null
+++ b/arch/x86/kernel/parse_pefile.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Parse a PE binary
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2023 Jan Hendrik Farr
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <asm/parse_pefile.h>
+#include <linux/err.h>
+
+#define pr_fmt(fmt)	"parse_pefile: " fmt
+#include <linux/module.h>
+/*
+ * Parse a PE binary.
+ */
+int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+			       struct pefile_context *ctx)
+{
+	const struct mz_hdr *mz = pebuf;
+	const struct pe_hdr *pe;
+	const struct pe32_opt_hdr *pe32;
+	const struct pe32plus_opt_hdr *pe64;
+	const struct data_directory *ddir;
+	const struct data_dirent *dde;
+	const struct section_header *secs, *sec;
+	size_t cursor, datalen = pelen;
+
+
+#define chkaddr(base, x, s)						\
+	do {								\
+		if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+			return -ELIBBAD;				\
+	} while (0)
+
+	chkaddr(0, 0, sizeof(*mz));
+	if (mz->magic != MZ_MAGIC)
+		return -ELIBBAD;
+	cursor = sizeof(*mz);
+
+	chkaddr(cursor, mz->peaddr, sizeof(*pe));
+	pe = pebuf + mz->peaddr;
+	if (pe->magic != PE_MAGIC)
+		return -ELIBBAD;
+	cursor = mz->peaddr + sizeof(*pe);
+
+	chkaddr(0, cursor, sizeof(pe32->magic));
+	pe32 = pebuf + cursor;
+	pe64 = pebuf + cursor;
+
+	switch (pe32->magic) {
+	case PE_OPT_MAGIC_PE32:
+		chkaddr(0, cursor, sizeof(*pe32));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe32->csum - (unsigned long)pebuf;
+		ctx->header_size = pe32->header_size;
+		cursor += sizeof(*pe32);
+		ctx->n_data_dirents = pe32->data_dirs;
+		break;
+
+	case PE_OPT_MAGIC_PE32PLUS:
+		chkaddr(0, cursor, sizeof(*pe64));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe64->csum - (unsigned long)pebuf;
+		ctx->header_size = pe64->header_size;
+		cursor += sizeof(*pe64);
+		ctx->n_data_dirents = pe64->data_dirs;
+		break;
+
+	default:
+		pr_warn("Unknown PEOPT magic = %04hx\n", pe32->magic);
+		return -ELIBBAD;
+	}
+
+	pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
+	pr_debug("header size = %x\n", ctx->header_size);
+
+	if (cursor >= ctx->header_size || ctx->header_size >= datalen)
+		return -ELIBBAD;
+
+	if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
+		return -ELIBBAD;
+
+	ddir = pebuf + cursor;
+	cursor += sizeof(*dde) * ctx->n_data_dirents;
+
+	ctx->cert_dirent_offset =
+		(unsigned long)&ddir->certs - (unsigned long)pebuf;
+	ctx->certs_size = ddir->certs.size;
+
+	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);
+
+	}
+
+	ctx->n_sections = pe->sections;
+	if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
+		return -ELIBBAD;
+	ctx->secs = secs = pebuf + cursor;
+
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c
index 839591ad21ac..063b348bb4c3 100644
--- a/crypto/asymmetric_keys/mscode_parser.c
+++ b/crypto/asymmetric_keys/mscode_parser.c
@@ -11,7 +11,7 @@
 #include <linux/err.h>
 #include <linux/oid_registry.h>
 #include <crypto/pkcs7.h>
-#include "verify_pefile.h"
+#include <asm/parse_pefile.h>
 #include "mscode.asn1.h"
 
 /*
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index f440767bd727..fbb319094c0a 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -14,106 +14,9 @@
 #include <linux/asn1.h>
 #include <linux/verification.h>
 #include <crypto/hash.h>
+#include <asm/parse_pefile.h>
 #include "verify_pefile.h"
 
-/*
- * Parse a PE binary.
- */
-static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
-			       struct pefile_context *ctx)
-{
-	const struct mz_hdr *mz = pebuf;
-	const struct pe_hdr *pe;
-	const struct pe32_opt_hdr *pe32;
-	const struct pe32plus_opt_hdr *pe64;
-	const struct data_directory *ddir;
-	const struct data_dirent *dde;
-	const struct section_header *secs, *sec;
-	size_t cursor, datalen = pelen;
-
-	kenter("");
-
-#define chkaddr(base, x, s)						\
-	do {								\
-		if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
-			return -ELIBBAD;				\
-	} while (0)
-
-	chkaddr(0, 0, sizeof(*mz));
-	if (mz->magic != MZ_MAGIC)
-		return -ELIBBAD;
-	cursor = sizeof(*mz);
-
-	chkaddr(cursor, mz->peaddr, sizeof(*pe));
-	pe = pebuf + mz->peaddr;
-	if (pe->magic != PE_MAGIC)
-		return -ELIBBAD;
-	cursor = mz->peaddr + sizeof(*pe);
-
-	chkaddr(0, cursor, sizeof(pe32->magic));
-	pe32 = pebuf + cursor;
-	pe64 = pebuf + cursor;
-
-	switch (pe32->magic) {
-	case PE_OPT_MAGIC_PE32:
-		chkaddr(0, cursor, sizeof(*pe32));
-		ctx->image_checksum_offset =
-			(unsigned long)&pe32->csum - (unsigned long)pebuf;
-		ctx->header_size = pe32->header_size;
-		cursor += sizeof(*pe32);
-		ctx->n_data_dirents = pe32->data_dirs;
-		break;
-
-	case PE_OPT_MAGIC_PE32PLUS:
-		chkaddr(0, cursor, sizeof(*pe64));
-		ctx->image_checksum_offset =
-			(unsigned long)&pe64->csum - (unsigned long)pebuf;
-		ctx->header_size = pe64->header_size;
-		cursor += sizeof(*pe64);
-		ctx->n_data_dirents = pe64->data_dirs;
-		break;
-
-	default:
-		pr_warn("Unknown PEOPT magic = %04hx\n", pe32->magic);
-		return -ELIBBAD;
-	}
-
-	pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
-	pr_debug("header size = %x\n", ctx->header_size);
-
-	if (cursor >= ctx->header_size || ctx->header_size >= datalen)
-		return -ELIBBAD;
-
-	if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
-		return -ELIBBAD;
-
-	ddir = pebuf + cursor;
-	cursor += sizeof(*dde) * ctx->n_data_dirents;
-
-	ctx->cert_dirent_offset =
-		(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;
-	}
-
-	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))
-		return -ELIBBAD;
-	ctx->secs = secs = pebuf + cursor;
-
-	return 0;
-}
 
 /*
  * Check and strip the PE wrapper from around the signature and check that the
@@ -431,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;
@@ -439,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/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h
index e1628e100cde..5ab2f9a5b2ef 100644
--- a/crypto/asymmetric_keys/verify_pefile.h
+++ b/crypto/asymmetric_keys/verify_pefile.h
@@ -8,22 +8,6 @@
 #include <crypto/pkcs7.h>
 #include <crypto/hash_info.h>
 
-struct pefile_context {
-	unsigned	header_size;
-	unsigned	image_checksum_offset;
-	unsigned	cert_dirent_offset;
-	unsigned	n_data_dirents;
-	unsigned	n_sections;
-	unsigned	certs_size;
-	unsigned	sig_offset;
-	unsigned	sig_len;
-	const struct section_header *secs;
-
-	/* PKCS#7 MS Individual Code Signing content */
-	const void	*digest;		/* Digest */
-	unsigned	digest_len;		/* Digest length */
-	const char	*digest_algo;		/* Digest algorithm */
-};
 
 #define kenter(FMT, ...)					\
 	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
-- 
2.40.1


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

WARNING: multiple messages have this Message-ID (diff)
From: Jan Hendrik Farr <kernel@jfarr.cc>
To: linux-kernel@vger.kernel.org
Cc: kexec@lists.infradead.org, x86@kernel.org, tglx@linutronix.de,
	dhowells@redhat.com, vgoyal@redhat.com, keyrings@vger.kernel.org,
	akpm@linux-foundation.org, bhe@redhat.com, bhelgaas@google.com,
	lennart@poettering.net, bluca@debian.org,
	systemd-devel@lists.freedesktop.org, kernel@jfarr.cc
Subject: [PATCH 1/1] x86/kexec: UKI support
Date: Sat,  9 Sep 2023 18:18:51 +0200	[thread overview]
Message-ID: <20230909161851.223627-2-kernel@jfarr.cc> (raw)
In-Reply-To: <20230909161851.223627-1-kernel@jfarr.cc>

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@jfarr.cc>
---
 arch/x86/include/asm/kexec-uki.h       |   7 ++
 arch/x86/include/asm/parse_pefile.h    |  32 +++++++
 arch/x86/kernel/Makefile               |   2 +
 arch/x86/kernel/kexec-uki.c            | 113 +++++++++++++++++++++++++
 arch/x86/kernel/machine_kexec_64.c     |   2 +
 arch/x86/kernel/parse_pefile.c         | 110 ++++++++++++++++++++++++
 crypto/asymmetric_keys/mscode_parser.c |   2 +-
 crypto/asymmetric_keys/verify_pefile.c | 110 +++---------------------
 crypto/asymmetric_keys/verify_pefile.h |  16 ----
 9 files changed, 278 insertions(+), 116 deletions(-)
 create mode 100644 arch/x86/include/asm/kexec-uki.h
 create mode 100644 arch/x86/include/asm/parse_pefile.h
 create mode 100644 arch/x86/kernel/kexec-uki.c
 create mode 100644 arch/x86/kernel/parse_pefile.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/include/asm/parse_pefile.h b/arch/x86/include/asm/parse_pefile.h
new file mode 100644
index 000000000000..c29f8c98ee66
--- /dev/null
+++ b/arch/x86/include/asm/parse_pefile.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _ASM_PARSE_PEFILE_H
+#define _ASM_PARSE_PEFILE_H
+
+#include <linux/pe.h>
+
+struct pefile_context {
+	unsigned	header_size;
+	unsigned	image_checksum_offset;
+	unsigned	cert_dirent_offset;
+	unsigned	n_data_dirents;
+	unsigned	n_sections;
+	unsigned	certs_size;
+	unsigned	sig_offset;
+	unsigned	sig_len;
+	const struct section_header *secs;
+
+	/* PKCS#7 MS Individual Code Signing content */
+	const void	*digest;		/* Digest */
+	unsigned	digest_len;		/* Digest length */
+	const char	*digest_algo;		/* Digest algorithm */
+};
+int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+			       struct pefile_context *ctx);
+
+#endif // _ASM_PARSE_PEFILE_H
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 3269a0e23d3a..8a37a977bf72 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -101,6 +101,8 @@ 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_KEXEC_FILE)	+= parse_pefile.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..9275196a6b71
--- /dev/null
+++ b/arch/x86/kernel/kexec-uki.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Kexec UKI loader
+ *
+ * Copyright (C) 2023 Jan Hendrik Farr
+ *
+ * Authors:
+ *      Jan Hendrik Farr <kernel@jfarr.cc>
+ */
+
+#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 <asm/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;
+	struct pefile_context pe_ctx;
+
+	int r = pefile_parse_binary(buf, len, &pe_ctx);
+
+	if (r) {
+		pr_info("Not a UKI. Not a valid PE file\n");
+		return ret;
+	}
+
+	const struct section_header *_;
+
+	if (find_section(&pe_ctx, ".linux", &_) ||
+	    find_section(&pe_ctx, ".initrd", &_) ||
+	    find_section(&pe_ctx, ".cmdline", &_)) {
+		pr_info("Not a UKI. Missing .linux, .initrd, or .cmdline\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;
+	int r = pefile_parse_binary(kernel, kernel_len, &pe_ctx);
+
+	if (r)
+		return ERR_PTR(r);
+
+	pr_debug("pefile_parse_binary return %d, number of sections: %d\n",
+		 r, pe_ctx.n_sections);
+
+	const struct section_header *sec_linux, *sec_initrd, *sec_cmdline;
+	int r_linux = find_section(&pe_ctx, ".linux", &sec_linux);
+	int r_initrd = find_section(&pe_ctx, ".initrd", &sec_initrd);
+	int r_cmdline = find_section(&pe_ctx, ".cmdline", &sec_cmdline);
+
+	if (r_linux || r_initrd || r_cmdline)
+		return ERR_PTR(-EINVAL);
+
+	void *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,
+		kernel + sec_cmdline->data_addr,
+		sec_cmdline->raw_data_size
+	);
+
+	if (IS_ERR(ret))
+		pr_warn("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/arch/x86/kernel/parse_pefile.c b/arch/x86/kernel/parse_pefile.c
new file mode 100644
index 000000000000..7737c94a1848
--- /dev/null
+++ b/arch/x86/kernel/parse_pefile.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Parse a PE binary
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2023 Jan Hendrik Farr
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <asm/parse_pefile.h>
+#include <linux/err.h>
+
+#define pr_fmt(fmt)	"parse_pefile: " fmt
+#include <linux/module.h>
+/*
+ * Parse a PE binary.
+ */
+int pefile_parse_binary(const void *pebuf, unsigned int pelen,
+			       struct pefile_context *ctx)
+{
+	const struct mz_hdr *mz = pebuf;
+	const struct pe_hdr *pe;
+	const struct pe32_opt_hdr *pe32;
+	const struct pe32plus_opt_hdr *pe64;
+	const struct data_directory *ddir;
+	const struct data_dirent *dde;
+	const struct section_header *secs, *sec;
+	size_t cursor, datalen = pelen;
+
+
+#define chkaddr(base, x, s)						\
+	do {								\
+		if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+			return -ELIBBAD;				\
+	} while (0)
+
+	chkaddr(0, 0, sizeof(*mz));
+	if (mz->magic != MZ_MAGIC)
+		return -ELIBBAD;
+	cursor = sizeof(*mz);
+
+	chkaddr(cursor, mz->peaddr, sizeof(*pe));
+	pe = pebuf + mz->peaddr;
+	if (pe->magic != PE_MAGIC)
+		return -ELIBBAD;
+	cursor = mz->peaddr + sizeof(*pe);
+
+	chkaddr(0, cursor, sizeof(pe32->magic));
+	pe32 = pebuf + cursor;
+	pe64 = pebuf + cursor;
+
+	switch (pe32->magic) {
+	case PE_OPT_MAGIC_PE32:
+		chkaddr(0, cursor, sizeof(*pe32));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe32->csum - (unsigned long)pebuf;
+		ctx->header_size = pe32->header_size;
+		cursor += sizeof(*pe32);
+		ctx->n_data_dirents = pe32->data_dirs;
+		break;
+
+	case PE_OPT_MAGIC_PE32PLUS:
+		chkaddr(0, cursor, sizeof(*pe64));
+		ctx->image_checksum_offset =
+			(unsigned long)&pe64->csum - (unsigned long)pebuf;
+		ctx->header_size = pe64->header_size;
+		cursor += sizeof(*pe64);
+		ctx->n_data_dirents = pe64->data_dirs;
+		break;
+
+	default:
+		pr_warn("Unknown PEOPT magic = %04hx\n", pe32->magic);
+		return -ELIBBAD;
+	}
+
+	pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
+	pr_debug("header size = %x\n", ctx->header_size);
+
+	if (cursor >= ctx->header_size || ctx->header_size >= datalen)
+		return -ELIBBAD;
+
+	if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
+		return -ELIBBAD;
+
+	ddir = pebuf + cursor;
+	cursor += sizeof(*dde) * ctx->n_data_dirents;
+
+	ctx->cert_dirent_offset =
+		(unsigned long)&ddir->certs - (unsigned long)pebuf;
+	ctx->certs_size = ddir->certs.size;
+
+	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);
+
+	}
+
+	ctx->n_sections = pe->sections;
+	if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
+		return -ELIBBAD;
+	ctx->secs = secs = pebuf + cursor;
+
+	return 0;
+}
diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c
index 839591ad21ac..063b348bb4c3 100644
--- a/crypto/asymmetric_keys/mscode_parser.c
+++ b/crypto/asymmetric_keys/mscode_parser.c
@@ -11,7 +11,7 @@
 #include <linux/err.h>
 #include <linux/oid_registry.h>
 #include <crypto/pkcs7.h>
-#include "verify_pefile.h"
+#include <asm/parse_pefile.h>
 #include "mscode.asn1.h"
 
 /*
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index f440767bd727..fbb319094c0a 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -14,106 +14,9 @@
 #include <linux/asn1.h>
 #include <linux/verification.h>
 #include <crypto/hash.h>
+#include <asm/parse_pefile.h>
 #include "verify_pefile.h"
 
-/*
- * Parse a PE binary.
- */
-static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
-			       struct pefile_context *ctx)
-{
-	const struct mz_hdr *mz = pebuf;
-	const struct pe_hdr *pe;
-	const struct pe32_opt_hdr *pe32;
-	const struct pe32plus_opt_hdr *pe64;
-	const struct data_directory *ddir;
-	const struct data_dirent *dde;
-	const struct section_header *secs, *sec;
-	size_t cursor, datalen = pelen;
-
-	kenter("");
-
-#define chkaddr(base, x, s)						\
-	do {								\
-		if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
-			return -ELIBBAD;				\
-	} while (0)
-
-	chkaddr(0, 0, sizeof(*mz));
-	if (mz->magic != MZ_MAGIC)
-		return -ELIBBAD;
-	cursor = sizeof(*mz);
-
-	chkaddr(cursor, mz->peaddr, sizeof(*pe));
-	pe = pebuf + mz->peaddr;
-	if (pe->magic != PE_MAGIC)
-		return -ELIBBAD;
-	cursor = mz->peaddr + sizeof(*pe);
-
-	chkaddr(0, cursor, sizeof(pe32->magic));
-	pe32 = pebuf + cursor;
-	pe64 = pebuf + cursor;
-
-	switch (pe32->magic) {
-	case PE_OPT_MAGIC_PE32:
-		chkaddr(0, cursor, sizeof(*pe32));
-		ctx->image_checksum_offset =
-			(unsigned long)&pe32->csum - (unsigned long)pebuf;
-		ctx->header_size = pe32->header_size;
-		cursor += sizeof(*pe32);
-		ctx->n_data_dirents = pe32->data_dirs;
-		break;
-
-	case PE_OPT_MAGIC_PE32PLUS:
-		chkaddr(0, cursor, sizeof(*pe64));
-		ctx->image_checksum_offset =
-			(unsigned long)&pe64->csum - (unsigned long)pebuf;
-		ctx->header_size = pe64->header_size;
-		cursor += sizeof(*pe64);
-		ctx->n_data_dirents = pe64->data_dirs;
-		break;
-
-	default:
-		pr_warn("Unknown PEOPT magic = %04hx\n", pe32->magic);
-		return -ELIBBAD;
-	}
-
-	pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
-	pr_debug("header size = %x\n", ctx->header_size);
-
-	if (cursor >= ctx->header_size || ctx->header_size >= datalen)
-		return -ELIBBAD;
-
-	if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
-		return -ELIBBAD;
-
-	ddir = pebuf + cursor;
-	cursor += sizeof(*dde) * ctx->n_data_dirents;
-
-	ctx->cert_dirent_offset =
-		(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;
-	}
-
-	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))
-		return -ELIBBAD;
-	ctx->secs = secs = pebuf + cursor;
-
-	return 0;
-}
 
 /*
  * Check and strip the PE wrapper from around the signature and check that the
@@ -431,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;
@@ -439,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/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h
index e1628e100cde..5ab2f9a5b2ef 100644
--- a/crypto/asymmetric_keys/verify_pefile.h
+++ b/crypto/asymmetric_keys/verify_pefile.h
@@ -8,22 +8,6 @@
 #include <crypto/pkcs7.h>
 #include <crypto/hash_info.h>
 
-struct pefile_context {
-	unsigned	header_size;
-	unsigned	image_checksum_offset;
-	unsigned	cert_dirent_offset;
-	unsigned	n_data_dirents;
-	unsigned	n_sections;
-	unsigned	certs_size;
-	unsigned	sig_offset;
-	unsigned	sig_len;
-	const struct section_header *secs;
-
-	/* PKCS#7 MS Individual Code Signing content */
-	const void	*digest;		/* Digest */
-	unsigned	digest_len;		/* Digest length */
-	const char	*digest_algo;		/* Digest algorithm */
-};
 
 #define kenter(FMT, ...)					\
 	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
-- 
2.40.1


  reply	other threads:[~2023-09-09 16:19 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-09 16:18 [PATCH 0/1] x86/kexec: UKI support Jan Hendrik Farr
2023-09-09 16:18 ` Jan Hendrik Farr
2023-09-09 16:18 ` Jan Hendrik Farr [this message]
2023-09-09 16:18   ` [PATCH 1/1] " Jan Hendrik Farr
2023-09-09 17:15 ` [PATCH 0/1] " Luca Boccassi
2023-09-09 17:15   ` Luca Boccassi
2023-09-09 17:57   ` Jan Hendrik Farr
2023-09-09 17:57     ` Jan Hendrik Farr
2023-09-09 18:10     ` Luca Boccassi
2023-09-09 18:10       ` Luca Boccassi
2023-09-11  3:23       ` Jan Hendrik Farr
2023-09-11  3:23         ` Jan Hendrik Farr
2023-09-11 22:02 ` Jarkko Sakkinen
2023-09-11 22:02   ` Jarkko Sakkinen
2023-09-11 22:54   ` Jan Hendrik Farr
2023-09-11 22:54     ` Jan Hendrik Farr
2023-09-12 10:33     ` Jarkko Sakkinen
2023-09-12 10:33       ` Jarkko Sakkinen
2023-09-12 15:32       ` Jan Hendrik Farr
2023-09-12 15:32         ` Jan Hendrik Farr
2023-09-12 17:41         ` Jarkko Sakkinen
2023-09-12 17:41           ` Jarkko Sakkinen
2023-09-12 18:56           ` Jan Hendrik Farr
2023-09-12 18:56             ` Jan Hendrik Farr
2023-09-12 19:24             ` Jarkko Sakkinen
2023-09-12 19:24               ` Jarkko Sakkinen
2023-09-12 19:38               ` Jan Hendrik Farr
2023-09-12 19:38                 ` Jan Hendrik Farr
2023-09-12 20:49               ` Jan Hendrik Farr
2023-09-12 20:49                 ` Jan Hendrik Farr
2023-09-13 14:45                 ` Jarkko Sakkinen
2023-09-13 14:45                   ` Jarkko Sakkinen
2023-09-13 15:07                   ` Jan Hendrik Farr
2023-09-13 15:07                     ` Jan Hendrik Farr
2023-09-13 15:58                     ` Jarkko Sakkinen
2023-09-13 15:58                       ` Jarkko Sakkinen
2023-09-14  9:11                   ` Lennart Poettering
2023-09-14  9:11                     ` Lennart Poettering
2023-09-14 12:12                     ` Jarkko Sakkinen
2023-09-14 12:12                       ` Jarkko Sakkinen
2023-09-14  8:48         ` Lennart Poettering
2023-09-14  8:48           ` Lennart Poettering
2023-09-14 11:52           ` Jarkko Sakkinen
2023-09-14 11:52             ` Jarkko Sakkinen
2023-09-11 23:20   ` [systemd-devel] " Neal Gompa
2023-09-11 23:20     ` Neal Gompa
2023-09-12 10:37     ` Jarkko Sakkinen
2023-09-12 10:37       ` Jarkko Sakkinen
2023-09-18 15:41       ` Dimitri John Ledkov
2023-09-18 15:41         ` Dimitri John Ledkov
2023-09-25 16:43         ` Jarkko Sakkinen
2023-09-25 16:43           ` Jarkko Sakkinen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230909161851.223627-2-kernel@jfarr.cc \
    --to=kernel@jfarr.cc \
    --cc=akpm@linux-foundation.org \
    --cc=bhe@redhat.com \
    --cc=bhelgaas@google.com \
    --cc=bluca@debian.org \
    --cc=dhowells@redhat.com \
    --cc=kexec@lists.infradead.org \
    --cc=keyrings@vger.kernel.org \
    --cc=lennart@poettering.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=systemd-devel@lists.freedesktop.org \
    --cc=tglx@linutronix.de \
    --cc=vgoyal@redhat.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.