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