From patchwork Mon Sep 11 05:25:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Hendrik Farr X-Patchwork-Id: 13378786 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C153EEE7FF4 for ; Mon, 11 Sep 2023 05:26:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233913AbjIKF0d (ORCPT ); Mon, 11 Sep 2023 01:26:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44586 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229446AbjIKF0c (ORCPT ); Mon, 11 Sep 2023 01:26:32 -0400 Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2A85C1AE; Sun, 10 Sep 2023 22:26:27 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id 97A345C00CB; Mon, 11 Sep 2023 01:26:26 -0400 (EDT) Received: from mailfrontend2 ([10.202.2.163]) by compute5.internal (MEProxy); Mon, 11 Sep 2023 01:26:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jfarr.cc; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to; s=fm3; t=1694409986; x= 1694496386; bh=bnLTEENMoP7zGIZkcPERciYrqrfxHWq0c8MPE1NHsQY=; b=K kWMdAH5oztBO4eksVmbaAS5V/0rb5VxaWrSyPn1xBxL7VJe1yeRrQLVa37m9Rqt7 2Q9uHyv++9/xl0Bee0HxwuyO2oPEtjkPEOR+AMRS3DNLoHCyUSmd2VBIKtZichdp +iPvy7AbXNyq/syJMvRR0/V3pegk+ZsMgyXJmSZ0EdujemTynmUaR5DALRBYx+10 j8mVv5Ct//iI22+oyEDH//NhuI+w4BZtgRZ+VITiiIDx4nxW9NrHbHP3r1KZTXh1 ttpMEhJwI8y03fx9RTJrF9+Ndg8lC7Whe6LvWfGai6MUbQCwwkTGk33HQrLZ88Vx EN87rPeKt6LhbOe/JDPmg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1694409986; x= 1694496386; bh=bnLTEENMoP7zGIZkcPERciYrqrfxHWq0c8MPE1NHsQY=; b=J wS0Oy8+OsdBZ/kKIFx1+4MdqB8Bgv2lM0+jeTVYeVwRbk1PohvAr3cg0rU40DCUF 3VNSVQpVpubiS2jHUIbA8EugeTNoTbrF5sKkKj6TDYR/vGsY5DAaNAGswDMpgbGT 6on5qLdgbPpHq5KpJBiiCVb9fhjxA0sjGuupEDBlP8jMyy/qAI5hfwowwq7xYzCL r3o7mWJC5oWCvuPrQj08TmCBPCFf72SchzRBCVQEvwQAOwDhm95dwTDZCVDfXalC bQBezR9UYOgbbZGPXCpiUisdp6qLOtru/Peoj1kgDBt3sIl4/CHI0+ru7mNMMKAP RUZoUHTjhJKBX3OUeKh1g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedviedrudeifedgleefucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucgfrhhlucfvnfffucdludehmdenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpeflrghnucfjvghnughrihhk ucfhrghrrhcuoehkvghrnhgvlhesjhhfrghrrhdrtggtqeenucggtffrrghtthgvrhhnpe egfeeliefggeeiiedtudevveevvddvvefguddtvdeitdejleejuefgueelieehteenucev lhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehkvghrnhgvlh esjhhfrghrrhdrtggt X-ME-Proxy: Feedback-ID: i0fc947c4:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Mon, 11 Sep 2023 01:26:24 -0400 (EDT) From: Jan Hendrik Farr 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, bluca@debian.org, lennart@poettering.net, Jan Hendrik Farr Subject: [PATCH v2 1/2] move pefile_parse_binary to its own file Date: Mon, 11 Sep 2023 07:25:34 +0200 Message-Id: <20230911052535.335770-2-kernel@jfarr.cc> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230911052535.335770-1-kernel@jfarr.cc> References: <20230911052535.335770-1-kernel@jfarr.cc> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: keyrings@vger.kernel.org Signed-off-by: Jan Hendrik Farr --- crypto/asymmetric_keys/mscode_parser.c | 2 +- crypto/asymmetric_keys/verify_pefile.c | 99 +--------------------- crypto/asymmetric_keys/verify_pefile.h | 16 ---- include/linux/parse_pefile.h | 32 +++++++ lib/Makefile | 2 + lib/parse_pefile.c | 110 +++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 115 deletions(-) create mode 100644 include/linux/parse_pefile.h create mode 100644 lib/parse_pefile.c diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 839591ad21ac..996528b3f11c 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -11,7 +11,7 @@ #include #include #include -#include "verify_pefile.h" +#include #include "mscode.asn1.h" /* diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index f440767bd727..68592a328db7 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -14,106 +14,9 @@ #include #include #include +#include #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 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 #include -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__) diff --git a/include/linux/parse_pefile.h b/include/linux/parse_pefile.h new file mode 100644 index 000000000000..c29f8c98ee66 --- /dev/null +++ b/include/linux/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 + +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/lib/Makefile b/lib/Makefile index 2e08397f6210..01a6c13565b6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -365,6 +365,8 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o obj-$(CONFIG_PARMAN) += parman.o +obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += parse_pefile.o + obj-y += group_cpus.o # GCC library routines diff --git a/lib/parse_pefile.c b/lib/parse_pefile.c new file mode 100644 index 000000000000..9a8496b2588e --- /dev/null +++ b/lib/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. + * + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include + +#define pr_fmt(fmt) "parse_pefile: " fmt +#include +/* + * 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) { + 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; +} From patchwork Mon Sep 11 05:25:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Hendrik Farr X-Patchwork-Id: 13378787 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B1E8EE57DF for ; Mon, 11 Sep 2023 05:26:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234002AbjIKF0m (ORCPT ); Mon, 11 Sep 2023 01:26:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44666 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229446AbjIKF0g (ORCPT ); Mon, 11 Sep 2023 01:26:36 -0400 Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8003CCCA; Sun, 10 Sep 2023 22:26:31 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id E46AD5C00CE; Mon, 11 Sep 2023 01:26:30 -0400 (EDT) Received: from mailfrontend2 ([10.202.2.163]) by compute5.internal (MEProxy); Mon, 11 Sep 2023 01:26:30 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jfarr.cc; h=cc :cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to; s=fm3; t=1694409990; x= 1694496390; bh=BCXzQAxKcTxOEQhh9DLQ4hnHkrayZdUDOP+GxHQhMCc=; b=P O+W4r3BkSWadKS/4TCpzSkOOgcR+yJRn4ND1OnYvfIu/ihtCKbqOgkTQpCO0/SdN ZCjfaf9wiZz3DipGll9Ru55yaNi0hS+Xsp+WdkgSsbRS+LgN1hntDunDdxb8rMWr SKwY+DTFVMmKRVwg3HthjE9p06gu8APoBP2Dek6pth4NK/u/tTMh2jbhwfAiOtUK ttzSN+LRIabnzQzIzQ1G/Fk5NfjWm5trHmcur+OK650+nw7FuvCFVT+qRvRDpFJE dMV+B0k7qskjBvUq9wd7a7eymQqkm3zCCDfYGKpZibV/AIgnK52vH1ItNbgDjGgY KiQ3WGjC/3rM6Q4K5IOyA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1694409990; x= 1694496390; bh=BCXzQAxKcTxOEQhh9DLQ4hnHkrayZdUDOP+GxHQhMCc=; b=w jmd2kyLNblsdB/Q4uTcxZgXYO1U9Ni5XU4FmSGDse431WTNl7kWjB99NRRPpLx97 DJn2mQKr5viNZqdySQwGXPx7IVlTvVzRHV6vcMZpRLU0QBOyUZCbHmZUhaJ+6rqY /WIuTUoxpfFb46F3UsC4+2Xf9C58N7nAeqgXnJ6ht/sNzbD/ArNCbAzdn4XIKQGw o3veEiDJ3My7405/CH1u8RCeVa9nsCA0DVCN6Pl7XtUx+ww9mIsE49vkVqlJmqaj eiQR0wkMUDDlJe2uj97kVIkCQ4Ar8f3uhNSd2G572ojJ1Le1ShGNoQXUvJQrobCw XdXJBRHEM3fTDs+M0qnwA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedviedrudeifedgleefucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucgfrhhlucfvnfffucdlfedtmdenucfjughrpefhvf evufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpeflrghnucfjvghnughrihhk ucfhrghrrhcuoehkvghrnhgvlhesjhhfrghrrhdrtggtqeenucggtffrrghtthgvrhhnpe egfeeliefggeeiiedtudevveevvddvvefguddtvdeitdejleejuefgueelieehteenucev lhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehkvghrnhgvlh esjhhfrghrrhdrtggt X-ME-Proxy: Feedback-ID: i0fc947c4:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Mon, 11 Sep 2023 01:26:29 -0400 (EDT) From: Jan Hendrik Farr 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, bluca@debian.org, lennart@poettering.net, Jan Hendrik Farr Subject: [PATCH v2 2/2] x86/kexec: UKI support Date: Mon, 11 Sep 2023 07:25:35 +0200 Message-Id: <20230911052535.335770-3-kernel@jfarr.cc> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230911052535.335770-1-kernel@jfarr.cc> References: <20230911052535.335770-1-kernel@jfarr.cc> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: keyrings@vger.kernel.org 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 --- 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 + */ + +#define pr_fmt(fmt) "kexec-uki: " fmt + +#include +#include "linux/pe.h" +#include +#include + +#include +#include +#include + +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 #include #include +#include #include #include #include @@ -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))