2 * Copyright (c) 2014 The FreeBSD Foundation
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
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
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: head/usr.sbin/uefisign/uefisign.c 279315 2015-02-26 09:15:24Z trasz $");
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>
55 fprintf(stderr
, "usage: uefisign -c cert -k key -o outfile [-v] file\n"
56 " uefisign -V [-c cert] [-v] file\n");
61 checked_strdup(const char *s
)
72 checked_fopen(const char *path
, const char *mode
)
78 fp
= fopen(path
, mode
);
85 send_chunk(const void *buf
, size_t len
, int pipefd
)
89 ret
= write(pipefd
, &len
, sizeof(len
));
90 if (ret
!= sizeof(len
))
92 ret
= write(pipefd
, buf
, len
);
93 if (ret
!= (ssize_t
)len
)
98 receive_chunk(void **bufp
, size_t *lenp
, int pipefd
)
104 ret
= read(pipefd
, &len
, sizeof(len
));
105 if (ret
!= sizeof(len
))
108 buf
= calloc(1, len
);
112 ret
= read(pipefd
, buf
, len
);
113 if (ret
!= (ssize_t
)len
)
121 bin2hex(const char *bin
, size_t bin_len
)
123 unsigned char *hex
, *tmp
, ch
;
127 hex_len
= bin_len
* 2 + 1; /* +1 for '\0'. */
128 hex
= malloc(hex_len
);
133 for (i
= 0; i
< bin_len
; i
++) {
135 tmp
+= sprintf(tmp
, "%02x", ch
);
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
152 magic(PKCS7
*pkcs7
, const char *digest
, size_t digest_len
)
158 unsigned char *buf
, *tmp
;
159 char *digest_hex
, *magic_conf
, *str
;
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
)
173 bio
= BIO_new_mem_buf((void *)magic_conf
, -1);
175 ERR_print_errors_fp(stderr
);
176 errx(1, "BIO_new_mem_buf(3) failed");
179 cnf
= NCONF_new(NULL
);
181 ERR_print_errors_fp(stderr
);
182 errx(1, "NCONF_new(3) failed");
185 ok
= NCONF_load_bio(cnf
, bio
, NULL
);
187 ERR_print_errors_fp(stderr
);
188 errx(1, "NCONF_load_bio(3) failed");
191 str
= NCONF_get_string(cnf
, "default", "asn1");
193 ERR_print_errors_fp(stderr
);
194 errx(1, "NCONF_get_string(3) failed");
197 t
= ASN1_generate_nconf(str
, cnf
);
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
);
211 i2d_ASN1_TYPE(t
, &tmp
);
214 * We now have contents of 't' stuffed into memory buffer 'buf'.
219 t_bio
= PKCS7_dataInit(pkcs7
, 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
);
229 ERR_print_errors_fp(stderr
);
230 errx(1, "PKCS7_dataFinal(3) failed");
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
);
242 sign(X509
*cert
, EVP_PKEY
*key
, int pipefd
)
247 PKCS7_SIGNER_INFO
*info
;
248 void *digest
, *signature
;
249 size_t digest_len
, signature_len
;
252 assert(cert
!= NULL
);
255 receive_chunk(&digest
, &digest_len
, pipefd
);
257 bio
= BIO_new_mem_buf(digest
, digest_len
);
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
);
265 ERR_print_errors_fp(stderr
);
266 errx(1, "PKCS7_sign(3) failed");
269 md
= EVP_get_digestbyname(DIGEST
);
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);
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
);
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
);
298 out
= BIO_new(BIO_s_mem());
300 ERR_print_errors_fp(stderr
);
301 errx(1, "BIO_new(3) failed");
304 ok
= i2d_PKCS7_bio(out
, pkcs7
);
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
);
319 send_chunk(signature
, signature_len
, pipefd
);
323 wait_for_child(pid_t pid
)
327 pid
= waitpid(pid
, &status
, 0);
331 return (WEXITSTATUS(status
));
335 main(int argc
, char **argv
)
338 bool Vflag
= false, vflag
= false;
339 const char *certpath
= NULL
, *keypath
= NULL
, *outpath
= NULL
, *inpath
= NULL
;
340 FILE *certfp
= NULL
, *keyfp
= NULL
;
342 EVP_PKEY
*key
= NULL
;
346 while ((ch
= getopt(argc
, argv
, "Vc:k:o:v")) != -1) {
352 certpath
= checked_strdup(optarg
);
355 keypath
= checked_strdup(optarg
);
358 outpath
= checked_strdup(optarg
);
374 if (certpath
!= NULL
)
375 errx(1, "-V and -c are mutually exclusive");
377 errx(1, "-V and -k are mutually exclusive");
379 errx(1, "-V and -o are mutually exclusive");
381 if (certpath
== NULL
)
382 errx(1, "-c option is mandatory");
384 errx(1, "-k option is mandatory");
386 errx(1, "-o option is mandatory");
391 OPENSSL_config(NULL
);
392 ERR_load_crypto_strings();
393 OpenSSL_add_all_algorithms();
395 error
= pipe(pipefds
);
404 return (child(inpath
, outpath
, pipefds
[1], Vflag
, vflag
));
407 certfp
= checked_fopen(certpath
, "r");
408 cert
= PEM_read_X509(certfp
, NULL
, NULL
, 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
);
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
));