]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/uefisign/uefisign.c
sysctl(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / uefisign / uefisign.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 The FreeBSD Foundation
5  *
6  * This software was developed by Edward Tomasz Napierala under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/wait.h>
36 #include <assert.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <openssl/conf.h>
44 #include <openssl/evp.h>
45 #include <openssl/err.h>
46 #include <openssl/pem.h>
47 #include <openssl/pkcs7.h>
48
49 #include "uefisign.h"
50 #include "magic.h"
51
52 static void
53 usage(void)
54 {
55
56         fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n"
57                         "       uefisign -V [-c cert] [-v] file\n");
58         exit(1);
59 }
60
61 static char *
62 checked_strdup(const char *s)
63 {
64         char *c;
65
66         c = strdup(s);
67         if (c == NULL)
68                 err(1, "strdup");
69         return (c);
70 }
71
72 FILE *
73 checked_fopen(const char *path, const char *mode)
74 {
75         FILE *fp;
76
77         assert(path != NULL);
78
79         fp = fopen(path, mode);
80         if (fp == NULL)
81                 err(1, "%s", path);
82         return (fp);
83 }
84
85 void
86 send_chunk(const void *buf, size_t len, int pipefd)
87 {
88         ssize_t ret;
89
90         ret = write(pipefd, &len, sizeof(len));
91         if (ret != sizeof(len))
92                 err(1, "write");
93         ret = write(pipefd, buf, len);
94         if (ret != (ssize_t)len)
95                 err(1, "write");
96 }
97
98 void
99 receive_chunk(void **bufp, size_t *lenp, int pipefd)
100 {
101         ssize_t ret;
102         size_t len;
103         void *buf;
104
105         ret = read(pipefd, &len, sizeof(len));
106         if (ret != sizeof(len))
107                 err(1, "read");
108
109         buf = calloc(1, len);
110         if (buf == NULL)
111                 err(1, "calloc");
112
113         ret = read(pipefd, buf, len);
114         if (ret != (ssize_t)len)
115                 err(1, "read");
116
117         *bufp = buf;
118         *lenp = len;
119 }
120
121 static char *
122 bin2hex(const char *bin, size_t bin_len)
123 {
124         unsigned char *hex, *tmp, ch;
125         size_t hex_len;
126         size_t i;
127
128         hex_len = bin_len * 2 + 1; /* +1 for '\0'. */
129         hex = malloc(hex_len);
130         if (hex == NULL)
131                 err(1, "malloc");
132
133         tmp = hex;
134         for (i = 0; i < bin_len; i++) {
135                 ch = bin[i];
136                 tmp += sprintf(tmp, "%02x", ch);
137         }
138
139         return (hex);
140 }
141
142 /*
143  * We need to replace a standard chunk of PKCS7 signature with one mandated
144  * by Authenticode.  Problem is, replacing it just like that and then calling
145  * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal().
146  * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific
147  * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow
148  * does not panic - and _then_ we replace it in the signature.  This technique
149  * was used in sbsigntool by Jeremy Kerr, and might have originated in
150  * osslsigncode.
151  */
152 static void
153 magic(PKCS7 *pkcs7, const char *digest, size_t digest_len)
154 {
155         BIO *bio, *t_bio;
156         ASN1_TYPE *t;
157         ASN1_STRING *s;
158         CONF *cnf;
159         unsigned char *buf, *tmp;
160         char *digest_hex, *magic_conf, *str;
161         int len, nid, ok;
162
163         digest_hex = bin2hex(digest, digest_len);
164
165         /*
166          * Construct the SpcIndirectDataContent chunk.
167          */
168         nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL);
169
170         asprintf(&magic_conf, magic_fmt, digest_hex);
171         if (magic_conf == NULL)
172                 err(1, "asprintf");
173
174         bio = BIO_new_mem_buf((void *)magic_conf, -1);
175         if (bio == NULL) {
176                 ERR_print_errors_fp(stderr);
177                 errx(1, "BIO_new_mem_buf(3) failed");
178         }
179
180         cnf = NCONF_new(NULL);
181         if (cnf == NULL) {
182                 ERR_print_errors_fp(stderr);
183                 errx(1, "NCONF_new(3) failed");
184         }
185
186         ok = NCONF_load_bio(cnf, bio, NULL);
187         if (ok == 0) {
188                 ERR_print_errors_fp(stderr);
189                 errx(1, "NCONF_load_bio(3) failed");
190         }
191
192         str = NCONF_get_string(cnf, "default", "asn1");
193         if (str == NULL) {
194                 ERR_print_errors_fp(stderr);
195                 errx(1, "NCONF_get_string(3) failed");
196         }
197
198         t = ASN1_generate_nconf(str, cnf);
199         if (t == NULL) {
200                 ERR_print_errors_fp(stderr);
201                 errx(1, "ASN1_generate_nconf(3) failed");
202         }
203
204         /*
205          * We now have our proprietary piece of ASN.1.  Let's do
206          * the actual signing.
207          */
208         len = i2d_ASN1_TYPE(t, NULL);
209         tmp = buf = calloc(1, len);
210         if (tmp == NULL)
211                 err(1, "calloc");
212         i2d_ASN1_TYPE(t, &tmp);
213
214         /*
215          * We now have contents of 't' stuffed into memory buffer 'buf'.
216          */
217         tmp = NULL;
218         t = NULL;
219
220         t_bio = PKCS7_dataInit(pkcs7, NULL);
221         if (t_bio == NULL) {
222                 ERR_print_errors_fp(stderr);
223                 errx(1, "PKCS7_dataInit(3) failed");
224         }
225
226         BIO_write(t_bio, buf + 2, len - 2);
227
228         ok = PKCS7_dataFinal(pkcs7, t_bio);
229         if (ok == 0) {
230                 ERR_print_errors_fp(stderr);
231                 errx(1, "PKCS7_dataFinal(3) failed");
232         }
233
234         t = ASN1_TYPE_new();
235         s = ASN1_STRING_new();
236         ASN1_STRING_set(s, buf, len);
237         ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s);
238
239         PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t);
240 }
241
242 static void
243 sign(X509 *cert, EVP_PKEY *key, int pipefd)
244 {
245         PKCS7 *pkcs7;
246         BIO *bio, *out;
247         const EVP_MD *md;
248         PKCS7_SIGNER_INFO *info;
249         void *digest, *signature;
250         size_t digest_len, signature_len;
251         int ok;
252
253         assert(cert != NULL);
254         assert(key != NULL);
255
256         receive_chunk(&digest, &digest_len, pipefd);
257
258         bio = BIO_new_mem_buf(digest, digest_len);
259         if (bio == NULL) {
260                 ERR_print_errors_fp(stderr);
261                 errx(1, "BIO_new_mem_buf(3) failed");
262         }
263
264         pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL);
265         if (pkcs7 == NULL) {
266                 ERR_print_errors_fp(stderr);
267                 errx(1, "PKCS7_sign(3) failed");
268         }
269
270         md = EVP_get_digestbyname(DIGEST);
271         if (md == NULL) {
272                 ERR_print_errors_fp(stderr);
273                 errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST);
274         }
275
276         info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0);
277         if (info == NULL) {
278                 ERR_print_errors_fp(stderr);
279                 errx(1, "PKCS7_sign_add_signer(3) failed");
280         }
281
282         /*
283          * XXX: All the signed binaries seem to have this, but where is it
284          *      described in the spec?
285          */
286         PKCS7_add_signed_attribute(info, NID_pkcs9_contentType,
287             V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1));
288
289         magic(pkcs7, digest, digest_len);
290
291 #if 0
292         out = BIO_new(BIO_s_file());
293         BIO_set_fp(out, stdout, BIO_NOCLOSE);
294         PKCS7_print_ctx(out, pkcs7, 0, NULL);
295
296         i2d_PKCS7_bio(out, pkcs7);
297 #endif
298
299         out = BIO_new(BIO_s_mem());
300         if (out == NULL) {
301                 ERR_print_errors_fp(stderr);
302                 errx(1, "BIO_new(3) failed");
303         }
304
305         ok = i2d_PKCS7_bio(out, pkcs7);
306         if (ok == 0) {
307                 ERR_print_errors_fp(stderr);
308                 errx(1, "i2d_PKCS7_bio(3) failed");
309         }
310
311         signature_len = BIO_get_mem_data(out, &signature);
312         if (signature_len <= 0) {
313                 ERR_print_errors_fp(stderr);
314                 errx(1, "BIO_get_mem_data(3) failed");
315         }
316
317         (void)BIO_set_close(out, BIO_NOCLOSE);
318         BIO_free(out);
319
320         send_chunk(signature, signature_len, pipefd);
321 }
322
323 static int
324 wait_for_child(pid_t pid)
325 {
326         int status;
327
328         pid = waitpid(pid, &status, 0);
329         if (pid == -1)
330                 err(1, "waitpid");
331
332         return (WEXITSTATUS(status));
333 }
334
335 int
336 main(int argc, char **argv)
337 {
338         int ch, error;
339         bool Vflag = false, vflag = false;
340         const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL;
341         FILE *certfp = NULL, *keyfp = NULL;
342         X509 *cert = NULL;
343         EVP_PKEY *key = NULL;
344         pid_t pid;
345         int pipefds[2];
346
347         while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) {
348                 switch (ch) {
349                 case 'V':
350                         Vflag = true;
351                         break;
352                 case 'c':
353                         certpath = checked_strdup(optarg);
354                         break;
355                 case 'k':
356                         keypath = checked_strdup(optarg);
357                         break;
358                 case 'o':
359                         outpath = checked_strdup(optarg);
360                         break;
361                 case 'v':
362                         vflag = true;
363                         break;
364                 default:
365                         usage();
366                 }
367         }
368
369         argc -= optind;
370         argv += optind;
371         if (argc != 1)
372                 usage();
373
374         if (Vflag) {
375                 if (certpath != NULL)
376                         errx(1, "-V and -c are mutually exclusive");
377                 if (keypath != NULL)
378                         errx(1, "-V and -k are mutually exclusive");
379                 if (outpath != NULL)
380                         errx(1, "-V and -o are mutually exclusive");
381         } else {
382                 if (certpath == NULL)
383                         errx(1, "-c option is mandatory");
384                 if (keypath == NULL)
385                         errx(1, "-k option is mandatory");
386                 if (outpath == NULL)
387                         errx(1, "-o option is mandatory");
388         }
389
390         inpath = argv[0];
391
392         OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG |
393             OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
394             OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
395
396         error = pipe(pipefds);
397         if (error != 0)
398                 err(1, "pipe");
399
400         pid = fork();
401         if (pid < 0)
402                 err(1, "fork");
403
404         if (pid == 0)
405                 return (child(inpath, outpath, pipefds[1], Vflag, vflag));
406
407         if (!Vflag) {
408                 certfp = checked_fopen(certpath, "r");
409                 cert = PEM_read_X509(certfp, NULL, NULL, NULL);
410                 if (cert == NULL) {
411                         ERR_print_errors_fp(stderr);
412                         errx(1, "failed to load certificate from %s", certpath);
413                 }
414
415                 keyfp = checked_fopen(keypath, "r");
416                 key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL);
417                 if (key == NULL) {
418                         ERR_print_errors_fp(stderr);
419                         errx(1, "failed to load private key from %s", keypath);
420                 }
421
422                 sign(cert, key, pipefds[0]);
423         }
424
425         return (wait_for_child(pid));
426 }