[Midnightbsd-cvs] src [10320] trunk/usr.sbin/uefisign: add uefisign(8)

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun Jun 3 15:11:08 EDT 2018


Revision: 10320
          http://svnweb.midnightbsd.org/src/?rev=10320
Author:   laffer1
Date:     2018-06-03 15:11:07 -0400 (Sun, 03 Jun 2018)
Log Message:
-----------
add uefisign(8)

Added Paths:
-----------
    trunk/usr.sbin/uefisign/
    trunk/usr.sbin/uefisign/Makefile
    trunk/usr.sbin/uefisign/child.c
    trunk/usr.sbin/uefisign/magic.h
    trunk/usr.sbin/uefisign/pe.c
    trunk/usr.sbin/uefisign/uefisign.8
    trunk/usr.sbin/uefisign/uefisign.c
    trunk/usr.sbin/uefisign/uefisign.h

Added: trunk/usr.sbin/uefisign/Makefile
===================================================================
--- trunk/usr.sbin/uefisign/Makefile	                        (rev 0)
+++ trunk/usr.sbin/uefisign/Makefile	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,12 @@
+# $MidnightBSD$
+# $FreeBSD: stable/10/usr.sbin/uefisign/Makefile 279315 2015-02-26 09:15:24Z trasz $
+
+PROG=	uefisign
+SRCS=	uefisign.c child.c pe.c
+MAN=	uefisign.8
+
+LDFLAGS=	-lcrypto
+
+WARNS=	6
+
+.include <bsd.prog.mk>


Property changes on: trunk/usr.sbin/uefisign/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/child.c
===================================================================
--- trunk/usr.sbin/uefisign/child.c	                        (rev 0)
+++ trunk/usr.sbin/uefisign/child.c	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,278 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/uefisign/child.c 279315 2015-02-26 09:15:24Z trasz $");
+
+#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);
+}


Property changes on: trunk/usr.sbin/uefisign/child.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/magic.h
===================================================================
--- trunk/usr.sbin/uefisign/magic.h	                        (rev 0)
+++ trunk/usr.sbin/uefisign/magic.h	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,67 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/uefisign/magic.h 293290 2016-01-07 00:40:51Z bdrewery $
+ *
+ */
+
+/*
+ * 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";


Property changes on: trunk/usr.sbin/uefisign/magic.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/pe.c
===================================================================
--- trunk/usr.sbin/uefisign/pe.c	                        (rev 0)
+++ trunk/usr.sbin/uefisign/pe.c	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,565 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/uefisign/pe.c 293290 2016-01-07 00:40:51Z bdrewery $");
+
+#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: 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
+}


Property changes on: trunk/usr.sbin/uefisign/pe.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/uefisign.8
===================================================================
--- trunk/usr.sbin/uefisign/uefisign.8	                        (rev 0)
+++ trunk/usr.sbin/uefisign/uefisign.8	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,94 @@
+.\" $MidnightBSD$
+.\" 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: stable/10/usr.sbin/uefisign/uefisign.8 286187 2015-08-02 09:28:51Z trasz $
+.\"
+.Dd July 11, 2015
+.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
+Name of file containing the certificate used to sign the binary.
+.It Fl o
+Name of file to write the signed binary to.
+.It Fl v
+Be verbose.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+Generate self-signed certificate and use it to sign a binary:
+.Dl /usr/share/examples/uefisign/uefikeys testcert
+.Dl uefisign -c testcert.pem -k testcert.key -o signed-binary binary
+.Pp
+View signature:
+.Dl uefisign -Vv binary
+.Sh SEE ALSO
+.Xr openssl 1 ,
+.Xr loader 8 ,
+.Xr uefi 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 10.2 .
+.Sh AUTHORS
+The
+.Nm
+utility was developed by
+.An Edward Tomasz Napierala Aq Mt trasz at FreeBSD.org
+under sponsorship from the FreeBSD Foundation.


Property changes on: trunk/usr.sbin/uefisign/uefisign.8
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/uefisign.c
===================================================================
--- trunk/usr.sbin/uefisign/uefisign.c	                        (rev 0)
+++ trunk/usr.sbin/uefisign/uefisign.c	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,426 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/uefisign/uefisign.c 279315 2015-02-26 09:15:24Z trasz $");
+
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+
+#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));
+}


Property changes on: trunk/usr.sbin/uefisign/uefisign.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/usr.sbin/uefisign/uefisign.h
===================================================================
--- trunk/usr.sbin/uefisign/uefisign.h	                        (rev 0)
+++ trunk/usr.sbin/uefisign/uefisign.h	2018-06-03 19:11:07 UTC (rev 10320)
@@ -0,0 +1,92 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/usr.sbin/uefisign/uefisign.h 279315 2015-02-26 09:15:24Z trasz $
+ */
+
+#ifndef EFISIGN_H
+#define	EFISIGN_H
+
+#include <stdbool.h>
+#include <openssl/evp.h>
+
+#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 */


Property changes on: trunk/usr.sbin/uefisign/uefisign.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list