xref: /freebsd-src/usr.sbin/uefisign/child.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
1e595e65bSEdward Tomasz Napierala /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31aa6f9aeSEdward Tomasz Napierala  *
4e595e65bSEdward Tomasz Napierala  * Copyright (c) 2014 The FreeBSD Foundation
5e595e65bSEdward Tomasz Napierala  *
6e595e65bSEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
7e595e65bSEdward Tomasz Napierala  * from the FreeBSD Foundation.
8e595e65bSEdward Tomasz Napierala  *
9e595e65bSEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
10e595e65bSEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
11e595e65bSEdward Tomasz Napierala  * are met:
12e595e65bSEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
13e595e65bSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
14e595e65bSEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
15e595e65bSEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
16e595e65bSEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
17e595e65bSEdward Tomasz Napierala  *
18e595e65bSEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19e595e65bSEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20e595e65bSEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21e595e65bSEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22e595e65bSEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23e595e65bSEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24e595e65bSEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25e595e65bSEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26e595e65bSEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27e595e65bSEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28e595e65bSEdward Tomasz Napierala  * SUCH DAMAGE.
29e595e65bSEdward Tomasz Napierala  *
30e595e65bSEdward Tomasz Napierala  */
31e595e65bSEdward Tomasz Napierala 
32e595e65bSEdward Tomasz Napierala #include <sys/param.h>
33e595e65bSEdward Tomasz Napierala #include <sys/capsicum.h>
34e595e65bSEdward Tomasz Napierala #include <sys/types.h>
35e595e65bSEdward Tomasz Napierala #include <sys/stat.h>
36e595e65bSEdward Tomasz Napierala #include <assert.h>
371afab1feSMariusz Zaborski #include <capsicum_helpers.h>
38e595e65bSEdward Tomasz Napierala #include <err.h>
39e595e65bSEdward Tomasz Napierala #include <errno.h>
40e595e65bSEdward Tomasz Napierala #include <stdio.h>
41e595e65bSEdward Tomasz Napierala #include <stdlib.h>
42e595e65bSEdward Tomasz Napierala #include <string.h>
43e595e65bSEdward Tomasz Napierala #include <unistd.h>
44e595e65bSEdward Tomasz Napierala 
45e595e65bSEdward Tomasz Napierala #include <openssl/evp.h>
46e595e65bSEdward Tomasz Napierala #include <openssl/err.h>
47e595e65bSEdward Tomasz Napierala #include <openssl/pem.h>
48e595e65bSEdward Tomasz Napierala 
49e595e65bSEdward Tomasz Napierala #include "uefisign.h"
50e595e65bSEdward Tomasz Napierala 
51e595e65bSEdward Tomasz Napierala static void
load(struct executable * x)52e595e65bSEdward Tomasz Napierala load(struct executable *x)
53e595e65bSEdward Tomasz Napierala {
54e595e65bSEdward Tomasz Napierala 	int error, fd;
55e595e65bSEdward Tomasz Napierala 	struct stat sb;
56e595e65bSEdward Tomasz Napierala 	char *buf;
57e595e65bSEdward Tomasz Napierala 	size_t nread, len;
58e595e65bSEdward Tomasz Napierala 
59e595e65bSEdward Tomasz Napierala 	fd = fileno(x->x_fp);
60e595e65bSEdward Tomasz Napierala 
61e595e65bSEdward Tomasz Napierala 	error = fstat(fd, &sb);
62e595e65bSEdward Tomasz Napierala 	if (error != 0)
63e595e65bSEdward Tomasz Napierala 		err(1, "%s: fstat", x->x_path);
64e595e65bSEdward Tomasz Napierala 
65e595e65bSEdward Tomasz Napierala 	len = sb.st_size;
66e595e65bSEdward Tomasz Napierala 	if (len <= 0)
67e595e65bSEdward Tomasz Napierala 		errx(1, "%s: file is empty", x->x_path);
68e595e65bSEdward Tomasz Napierala 
69e595e65bSEdward Tomasz Napierala 	buf = malloc(len);
70e595e65bSEdward Tomasz Napierala 	if (buf == NULL)
71e595e65bSEdward Tomasz Napierala 		err(1, "%s: cannot malloc %zd bytes", x->x_path, len);
72e595e65bSEdward Tomasz Napierala 
73e595e65bSEdward Tomasz Napierala 	nread = fread(buf, len, 1, x->x_fp);
74e595e65bSEdward Tomasz Napierala 	if (nread != 1)
75e595e65bSEdward Tomasz Napierala 		err(1, "%s: fread", x->x_path);
76e595e65bSEdward Tomasz Napierala 
77e595e65bSEdward Tomasz Napierala 	x->x_buf = buf;
78e595e65bSEdward Tomasz Napierala 	x->x_len = len;
79e595e65bSEdward Tomasz Napierala }
80e595e65bSEdward Tomasz Napierala 
81e595e65bSEdward Tomasz Napierala static void
digest_range(struct executable * x,EVP_MD_CTX * mdctx,off_t off,size_t len)82e595e65bSEdward Tomasz Napierala digest_range(struct executable *x, EVP_MD_CTX *mdctx, off_t off, size_t len)
83e595e65bSEdward Tomasz Napierala {
84e595e65bSEdward Tomasz Napierala 	int ok;
85e595e65bSEdward Tomasz Napierala 
86e595e65bSEdward Tomasz Napierala 	range_check(x, off, len, "chunk");
87e595e65bSEdward Tomasz Napierala 
88e595e65bSEdward Tomasz Napierala 	ok = EVP_DigestUpdate(mdctx, x->x_buf + off, len);
89e595e65bSEdward Tomasz Napierala 	if (ok == 0) {
90e595e65bSEdward Tomasz Napierala 		ERR_print_errors_fp(stderr);
91e595e65bSEdward Tomasz Napierala 		errx(1, "EVP_DigestUpdate(3) failed");
92e595e65bSEdward Tomasz Napierala 	}
93e595e65bSEdward Tomasz Napierala }
94e595e65bSEdward Tomasz Napierala 
95e595e65bSEdward Tomasz Napierala static void
digest(struct executable * x)96e595e65bSEdward Tomasz Napierala digest(struct executable *x)
97e595e65bSEdward Tomasz Napierala {
98e595e65bSEdward Tomasz Napierala 	EVP_MD_CTX *mdctx;
99e595e65bSEdward Tomasz Napierala 	const EVP_MD *md;
100e595e65bSEdward Tomasz Napierala 	size_t sum_of_bytes_hashed;
101e595e65bSEdward Tomasz Napierala 	int i, ok;
102e595e65bSEdward Tomasz Napierala 
103e595e65bSEdward Tomasz Napierala 	/*
104e595e65bSEdward Tomasz Napierala 	 * Windows Authenticode Portable Executable Signature Format
105e595e65bSEdward Tomasz Napierala 	 * spec version 1.0 specifies MD5 and SHA1.  However, pesign
106e595e65bSEdward Tomasz Napierala 	 * and sbsign both use SHA256, so do the same.
107e595e65bSEdward Tomasz Napierala 	 */
108e595e65bSEdward Tomasz Napierala 	md = EVP_get_digestbyname(DIGEST);
109e595e65bSEdward Tomasz Napierala 	if (md == NULL) {
110e595e65bSEdward Tomasz Napierala 		ERR_print_errors_fp(stderr);
111e595e65bSEdward Tomasz Napierala 		errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
112e595e65bSEdward Tomasz Napierala 	}
113e595e65bSEdward Tomasz Napierala 
114e595e65bSEdward Tomasz Napierala 	mdctx = EVP_MD_CTX_create();
115e595e65bSEdward Tomasz Napierala 	if (mdctx == NULL) {
116e595e65bSEdward Tomasz Napierala 		ERR_print_errors_fp(stderr);
117e595e65bSEdward Tomasz Napierala 		errx(1, "EVP_MD_CTX_create(3) failed");
118e595e65bSEdward Tomasz Napierala 	}
119e595e65bSEdward Tomasz Napierala 
120e595e65bSEdward Tomasz Napierala 	ok = EVP_DigestInit_ex(mdctx, md, NULL);
121e595e65bSEdward Tomasz Napierala 	if (ok == 0) {
122e595e65bSEdward Tomasz Napierala 		ERR_print_errors_fp(stderr);
123e595e65bSEdward Tomasz Napierala 		errx(1, "EVP_DigestInit_ex(3) failed");
124e595e65bSEdward Tomasz Napierala 	}
125e595e65bSEdward Tomasz Napierala 
126e595e65bSEdward Tomasz Napierala 	/*
127e595e65bSEdward Tomasz Napierala 	 * According to the Authenticode spec, we need to compute
128e595e65bSEdward Tomasz Napierala 	 * the digest in a rather... specific manner; see "Calculating
129e595e65bSEdward Tomasz Napierala 	 * the PE Image Hash" part of the spec for details.
130e595e65bSEdward Tomasz Napierala 	 *
131e595e65bSEdward Tomasz Napierala 	 * First, everything from 0 to before the PE checksum.
132e595e65bSEdward Tomasz Napierala 	 */
133e595e65bSEdward Tomasz Napierala 	digest_range(x, mdctx, 0, x->x_checksum_off);
134e595e65bSEdward Tomasz Napierala 
135e595e65bSEdward Tomasz Napierala 	/*
136e595e65bSEdward Tomasz Napierala 	 * Second, from after the PE checksum to before the Certificate
137e595e65bSEdward Tomasz Napierala 	 * entry in Data Directory.
138e595e65bSEdward Tomasz Napierala 	 */
139e595e65bSEdward Tomasz Napierala 	digest_range(x, mdctx, x->x_checksum_off + x->x_checksum_len,
140e595e65bSEdward Tomasz Napierala 	    x->x_certificate_entry_off -
141e595e65bSEdward Tomasz Napierala 	    (x->x_checksum_off + x->x_checksum_len));
142e595e65bSEdward Tomasz Napierala 
143e595e65bSEdward Tomasz Napierala 	/*
144e595e65bSEdward Tomasz Napierala 	 * Then, from after the Certificate entry to the end of headers.
145e595e65bSEdward Tomasz Napierala 	 */
146e595e65bSEdward Tomasz Napierala 	digest_range(x, mdctx,
147e595e65bSEdward Tomasz Napierala 	    x->x_certificate_entry_off + x->x_certificate_entry_len,
148e595e65bSEdward Tomasz Napierala 	    x->x_headers_len -
149e595e65bSEdward Tomasz Napierala 	    (x->x_certificate_entry_off + x->x_certificate_entry_len));
150e595e65bSEdward Tomasz Napierala 
151e595e65bSEdward Tomasz Napierala 	/*
152e595e65bSEdward Tomasz Napierala 	 * Then, each section in turn, as specified in the PE Section Table.
153e595e65bSEdward Tomasz Napierala 	 *
154e595e65bSEdward Tomasz Napierala 	 * XXX: Sorting.
155e595e65bSEdward Tomasz Napierala 	 */
156e595e65bSEdward Tomasz Napierala 	sum_of_bytes_hashed = x->x_headers_len;
157e595e65bSEdward Tomasz Napierala 	for (i = 0; i < x->x_nsections; i++) {
158e595e65bSEdward Tomasz Napierala 		digest_range(x, mdctx,
159e595e65bSEdward Tomasz Napierala 		    x->x_section_off[i], x->x_section_len[i]);
160e595e65bSEdward Tomasz Napierala 		sum_of_bytes_hashed += x->x_section_len[i];
161e595e65bSEdward Tomasz Napierala 	}
162e595e65bSEdward Tomasz Napierala 
163e595e65bSEdward Tomasz Napierala 	/*
164e595e65bSEdward Tomasz Napierala 	 * I believe this can happen with overlapping sections.
165e595e65bSEdward Tomasz Napierala 	 */
166e595e65bSEdward Tomasz Napierala 	if (sum_of_bytes_hashed > x->x_len)
167e595e65bSEdward Tomasz Napierala 		errx(1, "number of bytes hashed is larger than file size");
168e595e65bSEdward Tomasz Napierala 
169e595e65bSEdward Tomasz Napierala 	/*
170e595e65bSEdward Tomasz Napierala 	 * I can't really explain this one; just do what the spec says.
171e595e65bSEdward Tomasz Napierala 	 */
172e595e65bSEdward Tomasz Napierala 	if (sum_of_bytes_hashed < x->x_len) {
173e595e65bSEdward Tomasz Napierala 		digest_range(x, mdctx, sum_of_bytes_hashed,
174e595e65bSEdward Tomasz Napierala 		    x->x_len - (signature_size(x) + sum_of_bytes_hashed));
175e595e65bSEdward Tomasz Napierala 	}
176e595e65bSEdward Tomasz Napierala 
177e595e65bSEdward Tomasz Napierala 	ok = EVP_DigestFinal_ex(mdctx, x->x_digest, &x->x_digest_len);
178e595e65bSEdward Tomasz Napierala 	if (ok == 0) {
179e595e65bSEdward Tomasz Napierala 		ERR_print_errors_fp(stderr);
180e595e65bSEdward Tomasz Napierala 		errx(1, "EVP_DigestFinal_ex(3) failed");
181e595e65bSEdward Tomasz Napierala 	}
182e595e65bSEdward Tomasz Napierala 
183e595e65bSEdward Tomasz Napierala 	EVP_MD_CTX_destroy(mdctx);
184e595e65bSEdward Tomasz Napierala }
185e595e65bSEdward Tomasz Napierala 
186e595e65bSEdward Tomasz Napierala static void
show_digest(const struct executable * x)187e595e65bSEdward Tomasz Napierala show_digest(const struct executable *x)
188e595e65bSEdward Tomasz Napierala {
189e595e65bSEdward Tomasz Napierala 	int i;
190e595e65bSEdward Tomasz Napierala 
191e595e65bSEdward Tomasz Napierala 	printf("computed %s digest ", DIGEST);
192e595e65bSEdward Tomasz Napierala 	for (i = 0; i < (int)x->x_digest_len; i++)
193e595e65bSEdward Tomasz Napierala 		printf("%02x", (unsigned char)x->x_digest[i]);
194e595e65bSEdward Tomasz Napierala 	printf("; digest len %u\n", x->x_digest_len);
195e595e65bSEdward Tomasz Napierala }
196e595e65bSEdward Tomasz Napierala 
197e595e65bSEdward Tomasz Napierala static void
send_digest(const struct executable * x,int pipefd)198e595e65bSEdward Tomasz Napierala send_digest(const struct executable *x, int pipefd)
199e595e65bSEdward Tomasz Napierala {
200e595e65bSEdward Tomasz Napierala 
201e595e65bSEdward Tomasz Napierala 	send_chunk(x->x_digest, x->x_digest_len, pipefd);
202e595e65bSEdward Tomasz Napierala }
203e595e65bSEdward Tomasz Napierala 
204e595e65bSEdward Tomasz Napierala static void
receive_signature(struct executable * x,int pipefd)205e595e65bSEdward Tomasz Napierala receive_signature(struct executable *x, int pipefd)
206e595e65bSEdward Tomasz Napierala {
207e595e65bSEdward Tomasz Napierala 
208e595e65bSEdward Tomasz Napierala 	receive_chunk(&x->x_signature, &x->x_signature_len, pipefd);
209e595e65bSEdward Tomasz Napierala }
210e595e65bSEdward Tomasz Napierala 
211e595e65bSEdward Tomasz Napierala static void
save(struct executable * x,FILE * fp,const char * path)212e595e65bSEdward Tomasz Napierala save(struct executable *x, FILE *fp, const char *path)
213e595e65bSEdward Tomasz Napierala {
214e595e65bSEdward Tomasz Napierala 	size_t nwritten;
215e595e65bSEdward Tomasz Napierala 
216e595e65bSEdward Tomasz Napierala 	assert(fp != NULL);
217e595e65bSEdward Tomasz Napierala 	assert(path != NULL);
218e595e65bSEdward Tomasz Napierala 
219e595e65bSEdward Tomasz Napierala 	nwritten = fwrite(x->x_buf, x->x_len, 1, fp);
220e595e65bSEdward Tomasz Napierala 	if (nwritten != 1)
221e595e65bSEdward Tomasz Napierala 		err(1, "%s: fwrite", path);
222e595e65bSEdward Tomasz Napierala }
223e595e65bSEdward Tomasz Napierala 
224e595e65bSEdward Tomasz Napierala int
child(const char * inpath,const char * outpath,int pipefd,bool Vflag,bool vflag)225e595e65bSEdward Tomasz Napierala child(const char *inpath, const char *outpath, int pipefd,
226e595e65bSEdward Tomasz Napierala     bool Vflag, bool vflag)
227e595e65bSEdward Tomasz Napierala {
228e595e65bSEdward Tomasz Napierala 	FILE *outfp = NULL, *infp = NULL;
229e595e65bSEdward Tomasz Napierala 	struct executable *x;
230e595e65bSEdward Tomasz Napierala 
231e595e65bSEdward Tomasz Napierala 	infp = checked_fopen(inpath, "r");
232e595e65bSEdward Tomasz Napierala 	if (outpath != NULL)
233e595e65bSEdward Tomasz Napierala 		outfp = checked_fopen(outpath, "w");
234e595e65bSEdward Tomasz Napierala 
2351afab1feSMariusz Zaborski 	if (caph_enter() < 0)
236e595e65bSEdward Tomasz Napierala 		err(1, "cap_enter");
237e595e65bSEdward Tomasz Napierala 
238e595e65bSEdward Tomasz Napierala 	x = calloc(1, sizeof(*x));
239e595e65bSEdward Tomasz Napierala 	if (x == NULL)
240e595e65bSEdward Tomasz Napierala 		err(1, "calloc");
241e595e65bSEdward Tomasz Napierala 	x->x_path = inpath;
242e595e65bSEdward Tomasz Napierala 	x->x_fp = infp;
243e595e65bSEdward Tomasz Napierala 
244e595e65bSEdward Tomasz Napierala 	load(x);
245e595e65bSEdward Tomasz Napierala 	parse(x);
246e595e65bSEdward Tomasz Napierala 	if (Vflag) {
247e595e65bSEdward Tomasz Napierala 		if (signature_size(x) == 0)
248e595e65bSEdward Tomasz Napierala 			errx(1, "file not signed");
249e595e65bSEdward Tomasz Napierala 
250e595e65bSEdward Tomasz Napierala 		printf("file contains signature\n");
251e595e65bSEdward Tomasz Napierala 		if (vflag) {
252e595e65bSEdward Tomasz Napierala 			digest(x);
253e595e65bSEdward Tomasz Napierala 			show_digest(x);
254e595e65bSEdward Tomasz Napierala 			show_certificate(x);
255e595e65bSEdward Tomasz Napierala 		}
256e595e65bSEdward Tomasz Napierala 	} else {
257e595e65bSEdward Tomasz Napierala 		if (signature_size(x) != 0)
258e595e65bSEdward Tomasz Napierala 			errx(1, "file already signed");
259e595e65bSEdward Tomasz Napierala 
260e595e65bSEdward Tomasz Napierala 		digest(x);
261e595e65bSEdward Tomasz Napierala 		if (vflag)
262e595e65bSEdward Tomasz Napierala 			show_digest(x);
263e595e65bSEdward Tomasz Napierala 		send_digest(x, pipefd);
264e595e65bSEdward Tomasz Napierala 		receive_signature(x, pipefd);
265e595e65bSEdward Tomasz Napierala 		update(x);
266e595e65bSEdward Tomasz Napierala 		save(x, outfp, outpath);
267e595e65bSEdward Tomasz Napierala 	}
268e595e65bSEdward Tomasz Napierala 
269e595e65bSEdward Tomasz Napierala 	return (0);
270e595e65bSEdward Tomasz Napierala }
271