tcp: Cache align ACK queue header.
[dragonfly.git] / usr.sbin / uefisign / uefisign.c
blob390eab562bb2ccee90894855963404c4e1f4c3eb
1 /*-
2 * Copyright (c) 2014 The FreeBSD Foundation
3 * All rights reserved.
5 * This software was developed by Edward Tomasz Napierala under sponsorship
6 * from the FreeBSD Foundation.
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.
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.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: head/usr.sbin/uefisign/uefisign.c 279315 2015-02-26 09:15:24Z trasz $");
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>
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>
48 #include "uefisign.h"
49 #include "magic.h"
51 static void
52 usage(void)
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);
60 static char *
61 checked_strdup(const char *s)
63 char *c;
65 c = strdup(s);
66 if (c == NULL)
67 err(1, "strdup");
68 return (c);
71 FILE *
72 checked_fopen(const char *path, const char *mode)
74 FILE *fp;
76 assert(path != NULL);
78 fp = fopen(path, mode);
79 if (fp == NULL)
80 err(1, "%s", path);
81 return (fp);
84 void
85 send_chunk(const void *buf, size_t len, int pipefd)
87 ssize_t ret;
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");
97 void
98 receive_chunk(void **bufp, size_t *lenp, int pipefd)
100 ssize_t ret;
101 size_t len;
102 void *buf;
104 ret = read(pipefd, &len, sizeof(len));
105 if (ret != sizeof(len))
106 err(1, "read");
108 buf = calloc(1, len);
109 if (buf == NULL)
110 err(1, "calloc");
112 ret = read(pipefd, buf, len);
113 if (ret != (ssize_t)len)
114 err(1, "read");
116 *bufp = buf;
117 *lenp = len;
120 static char *
121 bin2hex(const char *bin, size_t bin_len)
123 unsigned char *hex, *tmp, ch;
124 size_t hex_len;
125 size_t i;
127 hex_len = bin_len * 2 + 1; /* +1 for '\0'. */
128 hex = malloc(hex_len);
129 if (hex == NULL)
130 err(1, "malloc");
132 tmp = hex;
133 for (i = 0; i < bin_len; i++) {
134 ch = bin[i];
135 tmp += sprintf(tmp, "%02x", ch);
138 return (hex);
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.
151 static void
152 magic(PKCS7 *pkcs7, const char *digest, size_t digest_len)
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;
162 digest_hex = bin2hex(digest, digest_len);
165 * Construct the SpcIndirectDataContent chunk.
167 nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL);
169 asprintf(&magic_conf, magic_fmt, digest_hex);
170 if (magic_conf == NULL)
171 err(1, "asprintf");
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");
179 cnf = NCONF_new(NULL);
180 if (cnf == NULL) {
181 ERR_print_errors_fp(stderr);
182 errx(1, "NCONF_new(3) failed");
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");
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");
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");
204 * We now have our proprietary piece of ASN.1. Let's do
205 * the actual signing.
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);
214 * We now have contents of 't' stuffed into memory buffer 'buf'.
216 tmp = NULL;
217 t = NULL;
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");
225 BIO_write(t_bio, buf + 2, len - 2);
227 ok = PKCS7_dataFinal(pkcs7, t_bio);
228 if (ok == 0) {
229 ERR_print_errors_fp(stderr);
230 errx(1, "PKCS7_dataFinal(3) failed");
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);
238 PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t);
241 static void
242 sign(X509 *cert, EVP_PKEY *key, int pipefd)
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;
252 assert(cert != NULL);
253 assert(key != NULL);
255 receive_chunk(&digest, &digest_len, pipefd);
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");
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");
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);
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");
282 * XXX: All the signed binaries seem to have this, but where is it
283 * described in the spec?
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));
288 magic(pkcs7, digest, digest_len);
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);
295 i2d_PKCS7_bio(out, pkcs7);
296 #endif
298 out = BIO_new(BIO_s_mem());
299 if (out == NULL) {
300 ERR_print_errors_fp(stderr);
301 errx(1, "BIO_new(3) failed");
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");
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");
316 (void)BIO_set_close(out, BIO_NOCLOSE);
317 BIO_free(out);
319 send_chunk(signature, signature_len, pipefd);
322 static int
323 wait_for_child(pid_t pid)
325 int status;
327 pid = waitpid(pid, &status, 0);
328 if (pid == -1)
329 err(1, "waitpid");
331 return (WEXITSTATUS(status));
335 main(int argc, char **argv)
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];
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();
368 argc -= optind;
369 argv += optind;
370 if (argc != 1)
371 usage();
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");
389 inpath = argv[0];
391 OPENSSL_config(NULL);
392 ERR_load_crypto_strings();
393 OpenSSL_add_all_algorithms();
395 error = pipe(pipefds);
396 if (error != 0)
397 err(1, "pipe");
399 pid = fork();
400 if (pid < 0)
401 err(1, "fork");
403 if (pid == 0)
404 return (child(inpath, outpath, pipefds[1], Vflag, vflag));
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);
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);
421 sign(cert, key, pipefds[0]);
424 return (wait_for_child(pid));