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