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