1279315Strasz/*-
2332615Strasz * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3332615Strasz *
4279315Strasz * Copyright (c) 2014 The FreeBSD Foundation
5279315Strasz * All rights reserved.
6279315Strasz *
7279315Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
8279315Strasz * from the FreeBSD Foundation.
9279315Strasz *
10279315Strasz * Redistribution and use in source and binary forms, with or without
11279315Strasz * modification, are permitted provided that the following conditions
12279315Strasz * are met:
13279315Strasz * 1. Redistributions of source code must retain the above copyright
14279315Strasz *    notice, this list of conditions and the following disclaimer.
15279315Strasz * 2. Redistributions in binary form must reproduce the above copyright
16279315Strasz *    notice, this list of conditions and the following disclaimer in the
17279315Strasz *    documentation and/or other materials provided with the distribution.
18279315Strasz *
19279315Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20279315Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21279315Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22279315Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23279315Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24279315Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25279315Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26279315Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27279315Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28279315Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29279315Strasz * SUCH DAMAGE.
30279315Strasz *
31279315Strasz */
32279315Strasz
33279315Strasz#include <sys/cdefs.h>
34279315Strasz__FBSDID("$FreeBSD: stable/11/usr.sbin/uefisign/child.c 332615 2018-04-16 17:13:54Z trasz $");
35279315Strasz
36279315Strasz#include <sys/param.h>
37279315Strasz#if __FreeBSD_version >= 1100000
38279315Strasz#include <sys/capsicum.h>
39279315Strasz#else
40279315Strasz#include <sys/capability.h>
41279315Strasz#endif
42279315Strasz#include <sys/types.h>
43279315Strasz#include <sys/stat.h>
44279315Strasz#include <assert.h>
45279315Strasz#include <err.h>
46279315Strasz#include <errno.h>
47279315Strasz#include <stdio.h>
48279315Strasz#include <stdlib.h>
49279315Strasz#include <string.h>
50279315Strasz#include <unistd.h>
51279315Strasz
52279315Strasz#include <openssl/evp.h>
53279315Strasz#include <openssl/err.h>
54279315Strasz#include <openssl/pem.h>
55279315Strasz
56279315Strasz#include "uefisign.h"
57279315Strasz
58279315Straszstatic void
59279315Straszload(struct executable *x)
60279315Strasz{
61279315Strasz	int error, fd;
62279315Strasz	struct stat sb;
63279315Strasz	char *buf;
64279315Strasz	size_t nread, len;
65279315Strasz
66279315Strasz	fd = fileno(x->x_fp);
67279315Strasz
68279315Strasz	error = fstat(fd, &sb);
69279315Strasz	if (error != 0)
70279315Strasz		err(1, "%s: fstat", x->x_path);
71279315Strasz
72279315Strasz	len = sb.st_size;
73279315Strasz	if (len <= 0)
74279315Strasz		errx(1, "%s: file is empty", x->x_path);
75279315Strasz
76279315Strasz	buf = malloc(len);
77279315Strasz	if (buf == NULL)
78279315Strasz		err(1, "%s: cannot malloc %zd bytes", x->x_path, len);
79279315Strasz
80279315Strasz	nread = fread(buf, len, 1, x->x_fp);
81279315Strasz	if (nread != 1)
82279315Strasz		err(1, "%s: fread", x->x_path);
83279315Strasz
84279315Strasz	x->x_buf = buf;
85279315Strasz	x->x_len = len;
86279315Strasz}
87279315Strasz
88279315Straszstatic void
89279315Straszdigest_range(struct executable *x, EVP_MD_CTX *mdctx, off_t off, size_t len)
90279315Strasz{
91279315Strasz	int ok;
92279315Strasz
93279315Strasz	range_check(x, off, len, "chunk");
94279315Strasz
95279315Strasz	ok = EVP_DigestUpdate(mdctx, x->x_buf + off, len);
96279315Strasz	if (ok == 0) {
97279315Strasz		ERR_print_errors_fp(stderr);
98279315Strasz		errx(1, "EVP_DigestUpdate(3) failed");
99279315Strasz	}
100279315Strasz}
101279315Strasz
102279315Straszstatic void
103279315Straszdigest(struct executable *x)
104279315Strasz{
105279315Strasz	EVP_MD_CTX *mdctx;
106279315Strasz	const EVP_MD *md;
107279315Strasz	size_t sum_of_bytes_hashed;
108279315Strasz	int i, ok;
109279315Strasz
110279315Strasz	/*
111279315Strasz	 * Windows Authenticode Portable Executable Signature Format
112279315Strasz	 * spec version 1.0 specifies MD5 and SHA1.  However, pesign
113279315Strasz	 * and sbsign both use SHA256, so do the same.
114279315Strasz	 */
115279315Strasz	md = EVP_get_digestbyname(DIGEST);
116279315Strasz	if (md == NULL) {
117279315Strasz		ERR_print_errors_fp(stderr);
118279315Strasz		errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
119279315Strasz	}
120279315Strasz
121279315Strasz	mdctx = EVP_MD_CTX_create();
122279315Strasz	if (mdctx == NULL) {
123279315Strasz		ERR_print_errors_fp(stderr);
124279315Strasz		errx(1, "EVP_MD_CTX_create(3) failed");
125279315Strasz	}
126279315Strasz
127279315Strasz	ok = EVP_DigestInit_ex(mdctx, md, NULL);
128279315Strasz	if (ok == 0) {
129279315Strasz		ERR_print_errors_fp(stderr);
130279315Strasz		errx(1, "EVP_DigestInit_ex(3) failed");
131279315Strasz	}
132279315Strasz
133279315Strasz	/*
134279315Strasz	 * According to the Authenticode spec, we need to compute
135279315Strasz	 * the digest in a rather... specific manner; see "Calculating
136279315Strasz	 * the PE Image Hash" part of the spec for details.
137279315Strasz	 *
138279315Strasz	 * First, everything from 0 to before the PE checksum.
139279315Strasz	 */
140279315Strasz	digest_range(x, mdctx, 0, x->x_checksum_off);
141279315Strasz
142279315Strasz	/*
143279315Strasz	 * Second, from after the PE checksum to before the Certificate
144279315Strasz	 * entry in Data Directory.
145279315Strasz	 */
146279315Strasz	digest_range(x, mdctx, x->x_checksum_off + x->x_checksum_len,
147279315Strasz	    x->x_certificate_entry_off -
148279315Strasz	    (x->x_checksum_off + x->x_checksum_len));
149279315Strasz
150279315Strasz	/*
151279315Strasz	 * Then, from after the Certificate entry to the end of headers.
152279315Strasz	 */
153279315Strasz	digest_range(x, mdctx,
154279315Strasz	    x->x_certificate_entry_off + x->x_certificate_entry_len,
155279315Strasz	    x->x_headers_len -
156279315Strasz	    (x->x_certificate_entry_off + x->x_certificate_entry_len));
157279315Strasz
158279315Strasz	/*
159279315Strasz	 * Then, each section in turn, as specified in the PE Section Table.
160279315Strasz	 *
161279315Strasz	 * XXX: Sorting.
162279315Strasz	 */
163279315Strasz	sum_of_bytes_hashed = x->x_headers_len;
164279315Strasz	for (i = 0; i < x->x_nsections; i++) {
165279315Strasz		digest_range(x, mdctx,
166279315Strasz		    x->x_section_off[i], x->x_section_len[i]);
167279315Strasz		sum_of_bytes_hashed += x->x_section_len[i];
168279315Strasz	}
169279315Strasz
170279315Strasz	/*
171279315Strasz	 * I believe this can happen with overlapping sections.
172279315Strasz	 */
173279315Strasz	if (sum_of_bytes_hashed > x->x_len)
174279315Strasz		errx(1, "number of bytes hashed is larger than file size");
175279315Strasz
176279315Strasz	/*
177279315Strasz	 * I can't really explain this one; just do what the spec says.
178279315Strasz	 */
179279315Strasz	if (sum_of_bytes_hashed < x->x_len) {
180279315Strasz		digest_range(x, mdctx, sum_of_bytes_hashed,
181279315Strasz		    x->x_len - (signature_size(x) + sum_of_bytes_hashed));
182279315Strasz	}
183279315Strasz
184279315Strasz	ok = EVP_DigestFinal_ex(mdctx, x->x_digest, &x->x_digest_len);
185279315Strasz	if (ok == 0) {
186279315Strasz		ERR_print_errors_fp(stderr);
187279315Strasz		errx(1, "EVP_DigestFinal_ex(3) failed");
188279315Strasz	}
189279315Strasz
190279315Strasz	EVP_MD_CTX_destroy(mdctx);
191279315Strasz}
192279315Strasz
193279315Straszstatic void
194279315Straszshow_digest(const struct executable *x)
195279315Strasz{
196279315Strasz	int i;
197279315Strasz
198279315Strasz	printf("computed %s digest ", DIGEST);
199279315Strasz	for (i = 0; i < (int)x->x_digest_len; i++)
200279315Strasz		printf("%02x", (unsigned char)x->x_digest[i]);
201279315Strasz	printf("; digest len %u\n", x->x_digest_len);
202279315Strasz}
203279315Strasz
204279315Straszstatic void
205279315Straszsend_digest(const struct executable *x, int pipefd)
206279315Strasz{
207279315Strasz
208279315Strasz	send_chunk(x->x_digest, x->x_digest_len, pipefd);
209279315Strasz}
210279315Strasz
211279315Straszstatic void
212279315Straszreceive_signature(struct executable *x, int pipefd)
213279315Strasz{
214279315Strasz
215279315Strasz	receive_chunk(&x->x_signature, &x->x_signature_len, pipefd);
216279315Strasz}
217279315Strasz
218279315Straszstatic void
219279315Straszsave(struct executable *x, FILE *fp, const char *path)
220279315Strasz{
221279315Strasz	size_t nwritten;
222279315Strasz
223279315Strasz	assert(fp != NULL);
224279315Strasz	assert(path != NULL);
225279315Strasz
226279315Strasz	nwritten = fwrite(x->x_buf, x->x_len, 1, fp);
227279315Strasz	if (nwritten != 1)
228279315Strasz		err(1, "%s: fwrite", path);
229279315Strasz}
230279315Strasz
231279315Straszint
232279315Straszchild(const char *inpath, const char *outpath, int pipefd,
233279315Strasz    bool Vflag, bool vflag)
234279315Strasz{
235279315Strasz	int error;
236279315Strasz	FILE *outfp = NULL, *infp = NULL;
237279315Strasz	struct executable *x;
238279315Strasz
239279315Strasz	infp = checked_fopen(inpath, "r");
240279315Strasz	if (outpath != NULL)
241279315Strasz		outfp = checked_fopen(outpath, "w");
242279315Strasz
243279315Strasz	error = cap_enter();
244279315Strasz	if (error != 0 && errno != ENOSYS)
245279315Strasz		err(1, "cap_enter");
246279315Strasz
247279315Strasz	x = calloc(1, sizeof(*x));
248279315Strasz	if (x == NULL)
249279315Strasz		err(1, "calloc");
250279315Strasz	x->x_path = inpath;
251279315Strasz	x->x_fp = infp;
252279315Strasz
253279315Strasz	load(x);
254279315Strasz	parse(x);
255279315Strasz	if (Vflag) {
256279315Strasz		if (signature_size(x) == 0)
257279315Strasz			errx(1, "file not signed");
258279315Strasz
259279315Strasz		printf("file contains signature\n");
260279315Strasz		if (vflag) {
261279315Strasz			digest(x);
262279315Strasz			show_digest(x);
263279315Strasz			show_certificate(x);
264279315Strasz		}
265279315Strasz	} else {
266279315Strasz		if (signature_size(x) != 0)
267279315Strasz			errx(1, "file already signed");
268279315Strasz
269279315Strasz		digest(x);
270279315Strasz		if (vflag)
271279315Strasz			show_digest(x);
272279315Strasz		send_digest(x, pipefd);
273279315Strasz		receive_signature(x, pipefd);
274279315Strasz		update(x);
275279315Strasz		save(x, outfp, outpath);
276279315Strasz	}
277279315Strasz
278279315Strasz	return (0);
279279315Strasz}
280