svn commit: r279315 - in head/usr.sbin: . uefisign
Edward Tomasz Napierala
trasz at FreeBSD.org
Thu Feb 26 09:15:27 UTC 2015
Author: trasz
Date: Thu Feb 26 09:15:24 2015
New Revision: 279315
URL: https://svnweb.freebsd.org/changeset/base/279315
Log:
Add uefisign(8), UEFI Secure Boot signing utility.
MFC after: 1 month
Sponsored by: The FreeBSD Foundation
Added:
head/usr.sbin/uefisign/
head/usr.sbin/uefisign/Makefile (contents, props changed)
head/usr.sbin/uefisign/child.c (contents, props changed)
head/usr.sbin/uefisign/magic.h (contents, props changed)
head/usr.sbin/uefisign/pe.c (contents, props changed)
head/usr.sbin/uefisign/uefisign.8 (contents, props changed)
head/usr.sbin/uefisign/uefisign.c (contents, props changed)
head/usr.sbin/uefisign/uefisign.h (contents, props changed)
Modified:
head/usr.sbin/Makefile
Modified: head/usr.sbin/Makefile
==============================================================================
--- head/usr.sbin/Makefile Thu Feb 26 09:08:48 2015 (r279314)
+++ head/usr.sbin/Makefile Thu Feb 26 09:15:24 2015 (r279315)
@@ -86,6 +86,7 @@ SUBDIR= adduser \
traceroute \
trpt \
tzsetup \
+ uefisign \
ugidfw \
vigr \
vipw \
Added: head/usr.sbin/uefisign/Makefile
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/usr.sbin/uefisign/Makefile Thu Feb 26 09:15:24 2015 (r279315)
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= uefisign
+SRCS= uefisign.c child.c pe.c
+MAN= uefisign.8
+
+LDFLAGS= -lcrypto
+
+WARNS= 6
+
+.include <bsd.prog.mk>
Added: head/usr.sbin/uefisign/child.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/usr.sbin/uefisign/child.c Thu Feb 26 09:15:24 2015 (r279315)
@@ -0,0 +1,277 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#if __FreeBSD_version >= 1100000
+#include <sys/capsicum.h>
+#else
+#include <sys/capability.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#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);
+}
Added: head/usr.sbin/uefisign/magic.h
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/usr.sbin/uefisign/magic.h Thu Feb 26 09:15:24 2015 (r279315)
@@ -0,0 +1,66 @@
+/*-
+ * 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 neccessary 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";
Added: head/usr.sbin/uefisign/pe.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/usr.sbin/uefisign/pe.c Thu Feb 26 09:15:24 2015 (r279315)
@@ -0,0 +1,560 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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: Endianess?
+ */
+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)
+{
+ uint32_t computed_checksum;
+ 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);
+#endif
+
+ 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);
+ }
+
+ 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)
+{
+ uint32_t computed_checksum;
+ 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);
+#endif
+
+ 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);
+ }
+
+ 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
+}
Added: head/usr.sbin/uefisign/uefisign.8
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/usr.sbin/uefisign/uefisign.8 Thu Feb 26 09:15:24 2015 (r279315)
@@ -0,0 +1,93 @@
+.\" 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 AUTHORS 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 AUTHORS 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$
+.\"
+.Dd December 10, 2014
+.Dt UEFISIGN 8
+.Os
+.Sh NAME
+.Nm uefisign
+.Nd UEFI Secure Boot signing utility
+.Sh SYNOPSIS
+.Nm
+.Fl k Ar key
+.Fl c Ar certificate
+.Fl o Ar output
+.Op Fl v
+.Ar file
+.Nm
+.Fl V
+.Op Fl v
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility signs PE binary files using Authenticode scheme, as required by
+UEFI Secure Boot specification.
+Alternatively, it can be used to view and verify existing signatures.
+These options are available:
+.Bl -tag -width ".Fl l"
+.It Fl V
+Determine whether the file is signed.
+Note that this does not verify the correctness of the signature;
+only that the file contains a signature.
+.It Fl k
+Name of file containing the private key used to sign the binary.
+.It Fl c
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-all
mailing list