Add uefisign(8), UEFI Secure Boot signing utility.

MFC after:	1 month
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Edward Tomasz Napierala 2015-02-26 09:15:24 +00:00
parent f584f51d96
commit e595e65b8a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=279315
8 changed files with 1524 additions and 0 deletions

View File

@ -86,6 +86,7 @@ SUBDIR= adduser \
traceroute \
trpt \
tzsetup \
uefisign \
ugidfw \
vigr \
vipw \

View File

@ -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>

277
usr.sbin/uefisign/child.c Normal file
View File

@ -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);
}

66
usr.sbin/uefisign/magic.h Normal file
View File

@ -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";

560
usr.sbin/uefisign/pe.c Normal file
View File

@ -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
}

View File

@ -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
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 11.0 .
.Sh AUTHORS
The
.Nm
utility was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

View File

@ -0,0 +1,425 @@
/*-
* 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/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));
}

View File

@ -0,0 +1,91 @@
/*-
* 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 <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 */