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_config(NULL); 394 ERR_load_crypto_strings(); 395 OpenSSL_add_all_algorithms(); 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 } 428