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