Index: stable/11/usr.sbin/uefisign/child.c =================================================================== --- stable/11/usr.sbin/uefisign/child.c (revision 332614) +++ stable/11/usr.sbin/uefisign/child.c (revision 332615) @@ -1,277 +1,279 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #if __FreeBSD_version >= 1100000 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "uefisign.h" static void load(struct executable *x) { int error, fd; struct stat sb; char *buf; size_t nread, len; fd = fileno(x->x_fp); error = fstat(fd, &sb); if (error != 0) err(1, "%s: fstat", x->x_path); len = sb.st_size; if (len <= 0) errx(1, "%s: file is empty", x->x_path); buf = malloc(len); if (buf == NULL) err(1, "%s: cannot malloc %zd bytes", x->x_path, len); nread = fread(buf, len, 1, x->x_fp); if (nread != 1) err(1, "%s: fread", x->x_path); x->x_buf = buf; x->x_len = len; } static void digest_range(struct executable *x, EVP_MD_CTX *mdctx, off_t off, size_t len) { int ok; range_check(x, off, len, "chunk"); ok = EVP_DigestUpdate(mdctx, x->x_buf + off, len); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "EVP_DigestUpdate(3) failed"); } } static void digest(struct executable *x) { EVP_MD_CTX *mdctx; const EVP_MD *md; size_t sum_of_bytes_hashed; int i, ok; /* * Windows Authenticode Portable Executable Signature Format * spec version 1.0 specifies MD5 and SHA1. However, pesign * and sbsign both use SHA256, so do the same. */ md = EVP_get_digestbyname(DIGEST); if (md == NULL) { ERR_print_errors_fp(stderr); errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST); } mdctx = EVP_MD_CTX_create(); if (mdctx == NULL) { ERR_print_errors_fp(stderr); errx(1, "EVP_MD_CTX_create(3) failed"); } ok = EVP_DigestInit_ex(mdctx, md, NULL); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "EVP_DigestInit_ex(3) failed"); } /* * According to the Authenticode spec, we need to compute * the digest in a rather... specific manner; see "Calculating * the PE Image Hash" part of the spec for details. * * First, everything from 0 to before the PE checksum. */ digest_range(x, mdctx, 0, x->x_checksum_off); /* * Second, from after the PE checksum to before the Certificate * entry in Data Directory. */ digest_range(x, mdctx, x->x_checksum_off + x->x_checksum_len, x->x_certificate_entry_off - (x->x_checksum_off + x->x_checksum_len)); /* * Then, from after the Certificate entry to the end of headers. */ digest_range(x, mdctx, x->x_certificate_entry_off + x->x_certificate_entry_len, x->x_headers_len - (x->x_certificate_entry_off + x->x_certificate_entry_len)); /* * Then, each section in turn, as specified in the PE Section Table. * * XXX: Sorting. */ sum_of_bytes_hashed = x->x_headers_len; for (i = 0; i < x->x_nsections; i++) { digest_range(x, mdctx, x->x_section_off[i], x->x_section_len[i]); sum_of_bytes_hashed += x->x_section_len[i]; } /* * I believe this can happen with overlapping sections. */ if (sum_of_bytes_hashed > x->x_len) errx(1, "number of bytes hashed is larger than file size"); /* * I can't really explain this one; just do what the spec says. */ if (sum_of_bytes_hashed < x->x_len) { digest_range(x, mdctx, sum_of_bytes_hashed, x->x_len - (signature_size(x) + sum_of_bytes_hashed)); } ok = EVP_DigestFinal_ex(mdctx, x->x_digest, &x->x_digest_len); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "EVP_DigestFinal_ex(3) failed"); } EVP_MD_CTX_destroy(mdctx); } static void show_digest(const struct executable *x) { int i; printf("computed %s digest ", DIGEST); for (i = 0; i < (int)x->x_digest_len; i++) printf("%02x", (unsigned char)x->x_digest[i]); printf("; digest len %u\n", x->x_digest_len); } static void send_digest(const struct executable *x, int pipefd) { send_chunk(x->x_digest, x->x_digest_len, pipefd); } static void receive_signature(struct executable *x, int pipefd) { receive_chunk(&x->x_signature, &x->x_signature_len, pipefd); } static void save(struct executable *x, FILE *fp, const char *path) { size_t nwritten; assert(fp != NULL); assert(path != NULL); nwritten = fwrite(x->x_buf, x->x_len, 1, fp); if (nwritten != 1) err(1, "%s: fwrite", path); } int child(const char *inpath, const char *outpath, int pipefd, bool Vflag, bool vflag) { int error; FILE *outfp = NULL, *infp = NULL; struct executable *x; infp = checked_fopen(inpath, "r"); if (outpath != NULL) outfp = checked_fopen(outpath, "w"); error = cap_enter(); if (error != 0 && errno != ENOSYS) err(1, "cap_enter"); x = calloc(1, sizeof(*x)); if (x == NULL) err(1, "calloc"); x->x_path = inpath; x->x_fp = infp; load(x); parse(x); if (Vflag) { if (signature_size(x) == 0) errx(1, "file not signed"); printf("file contains signature\n"); if (vflag) { digest(x); show_digest(x); show_certificate(x); } } else { if (signature_size(x) != 0) errx(1, "file already signed"); digest(x); if (vflag) show_digest(x); send_digest(x, pipefd); receive_signature(x, pipefd); update(x); save(x, outfp, outpath); } return (0); } Index: stable/11/usr.sbin/uefisign/magic.h =================================================================== --- stable/11/usr.sbin/uefisign/magic.h (revision 332614) +++ stable/11/usr.sbin/uefisign/magic.h (revision 332615) @@ -1,66 +1,68 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * This file contains Authenticode-specific ASN.1 "configuration", used, * after being processed by asprintf(3), as an input to ASN1_generate_nconf(3). */ static const char *magic_fmt = "asn1 = SEQUENCE:SpcIndirectDataContent\n" "\n" "[SpcIndirectDataContent]\n" "a = SEQUENCE:SpcAttributeTypeAndOptionalValue\n" "b = SEQUENCE:DigestInfo\n" "\n" "[SpcAttributeTypeAndOptionalValue]\n" "# SPC_PE_IMAGE_DATAOBJ\n" "a = OID:1.3.6.1.4.1.311.2.1.15\n" "b = SEQUENCE:SpcPeImageData\n" "\n" "[SpcPeImageData]\n" "a = FORMAT:HEX,BITSTRING:00\n" /* * Well, there should be some other struct here, "SPCLink", but it doesn't * appear to be necessary for UEFI, and I have no idea how to synthesize it, * as it uses the CHOICE type. */ "\n" "[DigestInfo]\n" "a = SEQUENCE:AlgorithmIdentifier\n" /* * Here goes the digest computed from PE headers and sections. */ "b = FORMAT:HEX,OCTETSTRING:%s\n" "\n" "[AlgorithmIdentifier]\n" "a = OBJECT:sha256\n" "b = NULL\n"; Index: stable/11/usr.sbin/uefisign/pe.c =================================================================== --- stable/11/usr.sbin/uefisign/pe.c (revision 332614) +++ stable/11/usr.sbin/uefisign/pe.c (revision 332615) @@ -1,564 +1,566 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * PE format reference: * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uefisign.h" #ifndef CTASSERT #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] #endif struct mz_header { uint8_t mz_signature[2]; uint8_t mz_dont_care[58]; uint16_t mz_lfanew; } __attribute__((packed)); struct coff_header { uint8_t coff_dont_care[2]; uint16_t coff_number_of_sections; uint8_t coff_dont_care_either[16]; } __attribute__((packed)); #define PE_SIGNATURE 0x00004550 struct pe_header { uint32_t pe_signature; struct coff_header pe_coff; } __attribute__((packed)); #define PE_OPTIONAL_MAGIC_32 0x010B #define PE_OPTIONAL_MAGIC_32_PLUS 0x020B #define PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION 10 #define PE_OPTIONAL_SUBSYSTEM_EFI_BOOT 11 #define PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME 12 struct pe_optional_header_32 { uint16_t po_magic; uint8_t po_dont_care[58]; uint32_t po_size_of_headers; uint32_t po_checksum; uint16_t po_subsystem; uint8_t po_dont_care_either[22]; uint32_t po_number_of_rva_and_sizes; } __attribute__((packed)); CTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60); CTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64); CTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68); CTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92); struct pe_optional_header_32_plus { uint16_t po_magic; uint8_t po_dont_care[58]; uint32_t po_size_of_headers; uint32_t po_checksum; uint16_t po_subsystem; uint8_t po_dont_care_either[38]; uint32_t po_number_of_rva_and_sizes; } __attribute__((packed)); CTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60); CTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64); CTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68); CTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108); #define PE_DIRECTORY_ENTRY_CERTIFICATE 4 struct pe_directory_entry { uint32_t pde_rva; uint32_t pde_size; } __attribute__((packed)); struct pe_section_header { uint8_t psh_dont_care[16]; uint32_t psh_size_of_raw_data; uint32_t psh_pointer_to_raw_data; uint8_t psh_dont_care_either[16]; } __attribute__((packed)); CTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16); CTASSERT(offsetof(struct pe_section_header, psh_pointer_to_raw_data) == 20); #define PE_CERTIFICATE_REVISION 0x0200 #define PE_CERTIFICATE_TYPE 0x0002 struct pe_certificate { uint32_t pc_len; uint16_t pc_revision; uint16_t pc_type; char pc_signature[0]; } __attribute__((packed)); void range_check(const struct executable *x, off_t off, size_t len, const char *name) { if (off < 0) { errx(1, "%s starts at negative offset %jd", name, (intmax_t)off); } if (off >= (off_t)x->x_len) { errx(1, "%s starts at %jd, past the end of executable at %zd", name, (intmax_t)off, x->x_len); } if (len >= x->x_len) { errx(1, "%s size %zd is larger than the executable size %zd", name, len, x->x_len); } if (off + len > x->x_len) { errx(1, "%s extends to %jd, past the end of executable at %zd", name, (intmax_t)(off + len), x->x_len); } } size_t signature_size(const struct executable *x) { const struct pe_directory_entry *pde; range_check(x, x->x_certificate_entry_off, x->x_certificate_entry_len, "Certificate Directory"); pde = (struct pe_directory_entry *) (x->x_buf + x->x_certificate_entry_off); if (pde->pde_rva != 0 && pde->pde_size == 0) warnx("signature size is 0, but its RVA is %d", pde->pde_rva); if (pde->pde_rva == 0 && pde->pde_size != 0) warnx("signature RVA is 0, but its size is %d", pde->pde_size); return (pde->pde_size); } void show_certificate(const struct executable *x) { struct pe_certificate *pc; const struct pe_directory_entry *pde; range_check(x, x->x_certificate_entry_off, x->x_certificate_entry_len, "Certificate Directory"); pde = (struct pe_directory_entry *) (x->x_buf + x->x_certificate_entry_off); if (signature_size(x) == 0) { printf("file not signed\n"); return; } #if 0 printf("certificate chunk at offset %zd, size %zd\n", pde->pde_rva, pde->pde_size); #endif range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk"); pc = (struct pe_certificate *)(x->x_buf + pde->pde_rva); if (pc->pc_revision != PE_CERTIFICATE_REVISION) { errx(1, "wrong certificate chunk revision, is %d, should be %d", pc->pc_revision, PE_CERTIFICATE_REVISION); } if (pc->pc_type != PE_CERTIFICATE_TYPE) { errx(1, "wrong certificate chunk type, is %d, should be %d", pc->pc_type, PE_CERTIFICATE_TYPE); } printf("to dump PKCS7:\n " "dd if='%s' bs=1 skip=%zd | openssl pkcs7 -inform DER -print\n", x->x_path, pde->pde_rva + offsetof(struct pe_certificate, pc_signature)); printf("to dump raw ASN.1:\n " "openssl asn1parse -i -inform DER -offset %zd -in '%s'\n", pde->pde_rva + offsetof(struct pe_certificate, pc_signature), x->x_path); } static void parse_section_table(struct executable *x, off_t off, int number_of_sections) { const struct pe_section_header *psh; int i; range_check(x, off, sizeof(*psh) * number_of_sections, "section table"); if (x->x_headers_len <= off + sizeof(*psh) * number_of_sections) errx(1, "section table outside of headers"); psh = (const struct pe_section_header *)(x->x_buf + off); if (number_of_sections >= MAX_SECTIONS) { errx(1, "too many sections: got %d, should be %d", number_of_sections, MAX_SECTIONS); } x->x_nsections = number_of_sections; for (i = 0; i < number_of_sections; i++) { if (psh->psh_pointer_to_raw_data < x->x_headers_len) errx(1, "section points inside the headers"); range_check(x, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data, "section"); #if 0 printf("section %d: start %d, size %d\n", i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data); #endif x->x_section_off[i] = psh->psh_pointer_to_raw_data; x->x_section_len[i] = psh->psh_size_of_raw_data; psh++; } } static void parse_directory(struct executable *x, off_t off, int number_of_rva_and_sizes, int number_of_sections) { //int i; const struct pe_directory_entry *pde; //printf("Data Directory at offset %zd\n", off); if (number_of_rva_and_sizes <= PE_DIRECTORY_ENTRY_CERTIFICATE) { errx(1, "wrong NumberOfRvaAndSizes %d; should be at least %d", number_of_rva_and_sizes, PE_DIRECTORY_ENTRY_CERTIFICATE); } range_check(x, off, sizeof(*pde) * number_of_rva_and_sizes, "PE Data Directory"); if (x->x_headers_len <= off + sizeof(*pde) * number_of_rva_and_sizes) errx(1, "PE Data Directory outside of headers"); x->x_certificate_entry_off = off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE; x->x_certificate_entry_len = sizeof(*pde); #if 0 printf("certificate directory entry at offset %zd, len %zd\n", x->x_certificate_entry_off, x->x_certificate_entry_len); pde = (struct pe_directory_entry *)(x->x_buf + off); for (i = 0; i < number_of_rva_and_sizes; i++) { printf("rva %zd, size %zd\n", pde->pde_rva, pde->pde_size); pde++; } #endif return (parse_section_table(x, off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections)); } /* * The PE checksum algorithm is undocumented; this code is mostly based on * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html * * "Sum the entire image file, excluding the CheckSum field in the optional * header, as an array of USHORTs, allowing any carry above 16 bits to be added * back onto the low 16 bits. Then add the file size to get a 32-bit value." * * Note that most software does not care about the checksum at all; perhaps * we could just set it to 0 instead. * * XXX: Endianness? */ static uint32_t compute_checksum(const struct executable *x) { uint32_t cksum = 0; uint16_t tmp; int i; range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum"); assert(x->x_checksum_off % 2 == 0); for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) { /* * Don't checksum the checksum. The +2 is because the checksum * is 4 bytes, and here we're iterating over 2 byte chunks. */ if (i == x->x_checksum_off || i == x->x_checksum_off + 2) { tmp = 0; } else { assert(i + sizeof(tmp) <= x->x_len); memcpy(&tmp, x->x_buf + i, sizeof(tmp)); } cksum += tmp; cksum += cksum >> 16; cksum &= 0xffff; } cksum += cksum >> 16; cksum &= 0xffff; cksum += x->x_len; return (cksum); } static void parse_optional_32_plus(struct executable *x, off_t off, int number_of_sections) { #if 0 uint32_t computed_checksum; #endif const struct pe_optional_header_32_plus *po; range_check(x, off, sizeof(*po), "PE Optional Header"); po = (struct pe_optional_header_32_plus *)(x->x_buf + off); switch (po->po_subsystem) { case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION: case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT: case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME: break; default: errx(1, "wrong PE Optional Header subsystem 0x%x", po->po_subsystem); } #if 0 printf("subsystem %d, checksum 0x%x, %d data directories\n", po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes); #endif x->x_checksum_off = off + offsetof(struct pe_optional_header_32_plus, po_checksum); x->x_checksum_len = sizeof(po->po_checksum); #if 0 printf("checksum 0x%x at offset %zd, len %zd\n", po->po_checksum, x->x_checksum_off, x->x_checksum_len); computed_checksum = compute_checksum(x); if (computed_checksum != po->po_checksum) { warnx("invalid PE+ checksum; is 0x%x, should be 0x%x", po->po_checksum, computed_checksum); } #endif if (x->x_len < x->x_headers_len) errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers); x->x_headers_len = po->po_size_of_headers; //printf("Size of Headers: %d\n", po->po_size_of_headers); return (parse_directory(x, off + sizeof(*po), po->po_number_of_rva_and_sizes, number_of_sections)); } static void parse_optional_32(struct executable *x, off_t off, int number_of_sections) { #if 0 uint32_t computed_checksum; #endif const struct pe_optional_header_32 *po; range_check(x, off, sizeof(*po), "PE Optional Header"); po = (struct pe_optional_header_32 *)(x->x_buf + off); switch (po->po_subsystem) { case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION: case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT: case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME: break; default: errx(1, "wrong PE Optional Header subsystem 0x%x", po->po_subsystem); } #if 0 printf("subsystem %d, checksum 0x%x, %d data directories\n", po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes); #endif x->x_checksum_off = off + offsetof(struct pe_optional_header_32, po_checksum); x->x_checksum_len = sizeof(po->po_checksum); #if 0 printf("checksum at offset %zd, len %zd\n", x->x_checksum_off, x->x_checksum_len); computed_checksum = compute_checksum(x); if (computed_checksum != po->po_checksum) { warnx("invalid PE checksum; is 0x%x, should be 0x%x", po->po_checksum, computed_checksum); } #endif if (x->x_len < x->x_headers_len) errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers); x->x_headers_len = po->po_size_of_headers; //printf("Size of Headers: %d\n", po->po_size_of_headers); return (parse_directory(x, off + sizeof(*po), po->po_number_of_rva_and_sizes, number_of_sections)); } static void parse_optional(struct executable *x, off_t off, int number_of_sections) { const struct pe_optional_header_32 *po; //printf("Optional header offset %zd\n", off); range_check(x, off, sizeof(*po), "PE Optional Header"); po = (struct pe_optional_header_32 *)(x->x_buf + off); switch (po->po_magic) { case PE_OPTIONAL_MAGIC_32: return (parse_optional_32(x, off, number_of_sections)); case PE_OPTIONAL_MAGIC_32_PLUS: return (parse_optional_32_plus(x, off, number_of_sections)); default: errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic); } } static void parse_pe(struct executable *x, off_t off) { const struct pe_header *pe; //printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe)); range_check(x, off, sizeof(*pe), "PE header"); pe = (struct pe_header *)(x->x_buf + off); if (pe->pe_signature != PE_SIGNATURE) errx(1, "wrong PE signature 0x%x", pe->pe_signature); //printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections); parse_optional(x, off + sizeof(*pe), pe->pe_coff.coff_number_of_sections); } void parse(struct executable *x) { const struct mz_header *mz; range_check(x, 0, sizeof(*mz), "MZ header"); mz = (struct mz_header *)x->x_buf; if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z') errx(1, "MZ header not found"); return (parse_pe(x, mz->mz_lfanew)); } static off_t append(struct executable *x, void *ptr, size_t len) { off_t off; /* * XXX: Alignment. */ off = x->x_len; x->x_buf = realloc(x->x_buf, x->x_len + len); if (x->x_buf == NULL) err(1, "realloc"); memcpy(x->x_buf + x->x_len, ptr, len); x->x_len += len; return (off); } void update(struct executable *x) { uint32_t checksum; struct pe_certificate *pc; struct pe_directory_entry pde; size_t pc_len; off_t pc_off; pc_len = sizeof(*pc) + x->x_signature_len; pc = calloc(1, pc_len); if (pc == NULL) err(1, "calloc"); #if 0 /* * Note that pc_len is the length of pc_certificate, * not the whole structure. * * XXX: That's what the spec says - but it breaks at least * sbverify and "pesign -S", so the spec is probably wrong. */ pc->pc_len = x->x_signature_len; #else pc->pc_len = pc_len; #endif pc->pc_revision = PE_CERTIFICATE_REVISION; pc->pc_type = PE_CERTIFICATE_TYPE; memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len); pc_off = append(x, pc, pc_len); #if 0 printf("added signature chunk at offset %zd, len %zd\n", pc_off, pc_len); #endif free(pc); pde.pde_rva = pc_off; pde.pde_size = pc_len; memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde)); checksum = compute_checksum(x); assert(sizeof(checksum) == x->x_checksum_len); memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum)); #if 0 printf("new checksum 0x%x\n", checksum); #endif } Index: stable/11/usr.sbin/uefisign/uefisign.c =================================================================== --- stable/11/usr.sbin/uefisign/uefisign.c (revision 332614) +++ stable/11/usr.sbin/uefisign/uefisign.c (revision 332615) @@ -1,425 +1,427 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "uefisign.h" #include "magic.h" static void usage(void) { fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n" " uefisign -V [-c cert] [-v] file\n"); exit(1); } static char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) err(1, "strdup"); return (c); } FILE * checked_fopen(const char *path, const char *mode) { FILE *fp; assert(path != NULL); fp = fopen(path, mode); if (fp == NULL) err(1, "%s", path); return (fp); } void send_chunk(const void *buf, size_t len, int pipefd) { ssize_t ret; ret = write(pipefd, &len, sizeof(len)); if (ret != sizeof(len)) err(1, "write"); ret = write(pipefd, buf, len); if (ret != (ssize_t)len) err(1, "write"); } void receive_chunk(void **bufp, size_t *lenp, int pipefd) { ssize_t ret; size_t len; void *buf; ret = read(pipefd, &len, sizeof(len)); if (ret != sizeof(len)) err(1, "read"); buf = calloc(1, len); if (buf == NULL) err(1, "calloc"); ret = read(pipefd, buf, len); if (ret != (ssize_t)len) err(1, "read"); *bufp = buf; *lenp = len; } static char * bin2hex(const char *bin, size_t bin_len) { unsigned char *hex, *tmp, ch; size_t hex_len; size_t i; hex_len = bin_len * 2 + 1; /* +1 for '\0'. */ hex = malloc(hex_len); if (hex == NULL) err(1, "malloc"); tmp = hex; for (i = 0; i < bin_len; i++) { ch = bin[i]; tmp += sprintf(tmp, "%02x", ch); } return (hex); } /* * We need to replace a standard chunk of PKCS7 signature with one mandated * by Authenticode. Problem is, replacing it just like that and then calling * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal(). * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow * does not panic - and _then_ we replace it in the signature. This technique * was used in sbsigntool by Jeremy Kerr, and might have originated in * osslsigncode. */ static void magic(PKCS7 *pkcs7, const char *digest, size_t digest_len) { BIO *bio, *t_bio; ASN1_TYPE *t; ASN1_STRING *s; CONF *cnf; unsigned char *buf, *tmp; char *digest_hex, *magic_conf, *str; int len, nid, ok; digest_hex = bin2hex(digest, digest_len); /* * Construct the SpcIndirectDataContent chunk. */ nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL); asprintf(&magic_conf, magic_fmt, digest_hex); if (magic_conf == NULL) err(1, "asprintf"); bio = BIO_new_mem_buf((void *)magic_conf, -1); if (bio == NULL) { ERR_print_errors_fp(stderr); errx(1, "BIO_new_mem_buf(3) failed"); } cnf = NCONF_new(NULL); if (cnf == NULL) { ERR_print_errors_fp(stderr); errx(1, "NCONF_new(3) failed"); } ok = NCONF_load_bio(cnf, bio, NULL); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "NCONF_load_bio(3) failed"); } str = NCONF_get_string(cnf, "default", "asn1"); if (str == NULL) { ERR_print_errors_fp(stderr); errx(1, "NCONF_get_string(3) failed"); } t = ASN1_generate_nconf(str, cnf); if (t == NULL) { ERR_print_errors_fp(stderr); errx(1, "ASN1_generate_nconf(3) failed"); } /* * We now have our proprietary piece of ASN.1. Let's do * the actual signing. */ len = i2d_ASN1_TYPE(t, NULL); tmp = buf = calloc(1, len); if (tmp == NULL) err(1, "calloc"); i2d_ASN1_TYPE(t, &tmp); /* * We now have contents of 't' stuffed into memory buffer 'buf'. */ tmp = NULL; t = NULL; t_bio = PKCS7_dataInit(pkcs7, NULL); if (t_bio == NULL) { ERR_print_errors_fp(stderr); errx(1, "PKCS7_dataInit(3) failed"); } BIO_write(t_bio, buf + 2, len - 2); ok = PKCS7_dataFinal(pkcs7, t_bio); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "PKCS7_dataFinal(3) failed"); } t = ASN1_TYPE_new(); s = ASN1_STRING_new(); ASN1_STRING_set(s, buf, len); ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s); PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t); } static void sign(X509 *cert, EVP_PKEY *key, int pipefd) { PKCS7 *pkcs7; BIO *bio, *out; const EVP_MD *md; PKCS7_SIGNER_INFO *info; void *digest, *signature; size_t digest_len, signature_len; int ok; assert(cert != NULL); assert(key != NULL); receive_chunk(&digest, &digest_len, pipefd); bio = BIO_new_mem_buf(digest, digest_len); if (bio == NULL) { ERR_print_errors_fp(stderr); errx(1, "BIO_new_mem_buf(3) failed"); } pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL); if (pkcs7 == NULL) { ERR_print_errors_fp(stderr); errx(1, "PKCS7_sign(3) failed"); } md = EVP_get_digestbyname(DIGEST); if (md == NULL) { ERR_print_errors_fp(stderr); errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST); } info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0); if (info == NULL) { ERR_print_errors_fp(stderr); errx(1, "PKCS7_sign_add_signer(3) failed"); } /* * XXX: All the signed binaries seem to have this, but where is it * described in the spec? */ PKCS7_add_signed_attribute(info, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1)); magic(pkcs7, digest, digest_len); #if 0 out = BIO_new(BIO_s_file()); BIO_set_fp(out, stdout, BIO_NOCLOSE); PKCS7_print_ctx(out, pkcs7, 0, NULL); i2d_PKCS7_bio(out, pkcs7); #endif out = BIO_new(BIO_s_mem()); if (out == NULL) { ERR_print_errors_fp(stderr); errx(1, "BIO_new(3) failed"); } ok = i2d_PKCS7_bio(out, pkcs7); if (ok == 0) { ERR_print_errors_fp(stderr); errx(1, "i2d_PKCS7_bio(3) failed"); } signature_len = BIO_get_mem_data(out, &signature); if (signature_len <= 0) { ERR_print_errors_fp(stderr); errx(1, "BIO_get_mem_data(3) failed"); } (void)BIO_set_close(out, BIO_NOCLOSE); BIO_free(out); send_chunk(signature, signature_len, pipefd); } static int wait_for_child(pid_t pid) { int status; pid = waitpid(pid, &status, 0); if (pid == -1) err(1, "waitpid"); return (WEXITSTATUS(status)); } int main(int argc, char **argv) { int ch, error; bool Vflag = false, vflag = false; const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL; FILE *certfp = NULL, *keyfp = NULL; X509 *cert = NULL; EVP_PKEY *key = NULL; pid_t pid; int pipefds[2]; while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) { switch (ch) { case 'V': Vflag = true; break; case 'c': certpath = checked_strdup(optarg); break; case 'k': keypath = checked_strdup(optarg); break; case 'o': outpath = checked_strdup(optarg); break; case 'v': vflag = true; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); if (Vflag) { if (certpath != NULL) errx(1, "-V and -c are mutually exclusive"); if (keypath != NULL) errx(1, "-V and -k are mutually exclusive"); if (outpath != NULL) errx(1, "-V and -o are mutually exclusive"); } else { if (certpath == NULL) errx(1, "-c option is mandatory"); if (keypath == NULL) errx(1, "-k option is mandatory"); if (outpath == NULL) errx(1, "-o option is mandatory"); } inpath = argv[0]; OPENSSL_config(NULL); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); error = pipe(pipefds); if (error != 0) err(1, "pipe"); pid = fork(); if (pid < 0) err(1, "fork"); if (pid == 0) return (child(inpath, outpath, pipefds[1], Vflag, vflag)); if (!Vflag) { certfp = checked_fopen(certpath, "r"); cert = PEM_read_X509(certfp, NULL, NULL, NULL); if (cert == NULL) { ERR_print_errors_fp(stderr); errx(1, "failed to load certificate from %s", certpath); } keyfp = checked_fopen(keypath, "r"); key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL); if (key == NULL) { ERR_print_errors_fp(stderr); errx(1, "failed to load private key from %s", keypath); } sign(cert, key, pipefds[0]); } return (wait_for_child(pid)); } Index: stable/11/usr.sbin/uefisign/uefisign.h =================================================================== --- stable/11/usr.sbin/uefisign/uefisign.h (revision 332614) +++ stable/11/usr.sbin/uefisign/uefisign.h (revision 332615) @@ -1,91 +1,93 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef EFISIGN_H #define EFISIGN_H #include #include #define DIGEST "SHA256" #define MAX_SECTIONS 128 struct executable { const char *x_path; FILE *x_fp; char *x_buf; size_t x_len; /* * Set by pe_parse(), used by digest(). */ size_t x_headers_len; off_t x_checksum_off; size_t x_checksum_len; off_t x_certificate_entry_off; size_t x_certificate_entry_len; int x_nsections; off_t x_section_off[MAX_SECTIONS]; size_t x_section_len[MAX_SECTIONS]; /* * Computed by digest(). */ unsigned char x_digest[EVP_MAX_MD_SIZE]; unsigned int x_digest_len; /* * Received from the parent process, which computes it in sign(). */ void *x_signature; size_t x_signature_len; }; FILE *checked_fopen(const char *path, const char *mode); void send_chunk(const void *buf, size_t len, int pipefd); void receive_chunk(void **bufp, size_t *lenp, int pipefd); int child(const char *inpath, const char *outpath, int pipefd, bool Vflag, bool vflag); void parse(struct executable *x); void update(struct executable *x); size_t signature_size(const struct executable *x); void show_certificate(const struct executable *x); void range_check(const struct executable *x, off_t off, size_t len, const char *name); #endif /* !EFISIGN_H */ Index: stable/11 =================================================================== --- stable/11 (revision 332614) +++ stable/11 (revision 332615) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r328335