Signed-off-by: Jan Hendrik Farr <kernel@xxxxxxxx> --- 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 <linux/err.h> #include <linux/oid_registry.h> #include <crypto/pkcs7.h> -#include "verify_pefile.h" +#include <linux/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..68592a328db7 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 <linux/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 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__) 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@xxxxxxxxxx) + */ + +#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/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@xxxxxxxxxx) + */ + +#include <linux/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) { + 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; +} -- 2.40.1 _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec