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