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