1279315Strasz/*- 2279315Strasz * Copyright (c) 2014 The FreeBSD Foundation 3279315Strasz * All rights reserved. 4279315Strasz * 5279315Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6279315Strasz * from the FreeBSD Foundation. 7279315Strasz * 8279315Strasz * Redistribution and use in source and binary forms, with or without 9279315Strasz * modification, are permitted provided that the following conditions 10279315Strasz * are met: 11279315Strasz * 1. Redistributions of source code must retain the above copyright 12279315Strasz * notice, this list of conditions and the following disclaimer. 13279315Strasz * 2. Redistributions in binary form must reproduce the above copyright 14279315Strasz * notice, this list of conditions and the following disclaimer in the 15279315Strasz * documentation and/or other materials provided with the distribution. 16279315Strasz * 17279315Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18279315Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19279315Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20279315Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21279315Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22279315Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23279315Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24279315Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25279315Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26279315Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27279315Strasz * SUCH DAMAGE. 28279315Strasz * 29279315Strasz */ 30279315Strasz 31279315Strasz#include <sys/cdefs.h> 32279315Strasz__FBSDID("$FreeBSD: releng/11.0/usr.sbin/uefisign/uefisign.c 279315 2015-02-26 09:15:24Z trasz $"); 33279315Strasz 34279315Strasz#include <sys/wait.h> 35279315Strasz#include <assert.h> 36279315Strasz#include <err.h> 37279315Strasz#include <errno.h> 38279315Strasz#include <stdio.h> 39279315Strasz#include <string.h> 40279315Strasz#include <unistd.h> 41279315Strasz 42279315Strasz#include <openssl/conf.h> 43279315Strasz#include <openssl/evp.h> 44279315Strasz#include <openssl/err.h> 45279315Strasz#include <openssl/pem.h> 46279315Strasz#include <openssl/pkcs7.h> 47279315Strasz 48279315Strasz#include "uefisign.h" 49279315Strasz#include "magic.h" 50279315Strasz 51279315Straszstatic void 52279315Straszusage(void) 53279315Strasz{ 54279315Strasz 55279315Strasz fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n" 56279315Strasz " uefisign -V [-c cert] [-v] file\n"); 57279315Strasz exit(1); 58279315Strasz} 59279315Strasz 60279315Straszstatic char * 61279315Straszchecked_strdup(const char *s) 62279315Strasz{ 63279315Strasz char *c; 64279315Strasz 65279315Strasz c = strdup(s); 66279315Strasz if (c == NULL) 67279315Strasz err(1, "strdup"); 68279315Strasz return (c); 69279315Strasz} 70279315Strasz 71279315StraszFILE * 72279315Straszchecked_fopen(const char *path, const char *mode) 73279315Strasz{ 74279315Strasz FILE *fp; 75279315Strasz 76279315Strasz assert(path != NULL); 77279315Strasz 78279315Strasz fp = fopen(path, mode); 79279315Strasz if (fp == NULL) 80279315Strasz err(1, "%s", path); 81279315Strasz return (fp); 82279315Strasz} 83279315Strasz 84279315Straszvoid 85279315Straszsend_chunk(const void *buf, size_t len, int pipefd) 86279315Strasz{ 87279315Strasz ssize_t ret; 88279315Strasz 89279315Strasz ret = write(pipefd, &len, sizeof(len)); 90279315Strasz if (ret != sizeof(len)) 91279315Strasz err(1, "write"); 92279315Strasz ret = write(pipefd, buf, len); 93279315Strasz if (ret != (ssize_t)len) 94279315Strasz err(1, "write"); 95279315Strasz} 96279315Strasz 97279315Straszvoid 98279315Straszreceive_chunk(void **bufp, size_t *lenp, int pipefd) 99279315Strasz{ 100279315Strasz ssize_t ret; 101279315Strasz size_t len; 102279315Strasz void *buf; 103279315Strasz 104279315Strasz ret = read(pipefd, &len, sizeof(len)); 105279315Strasz if (ret != sizeof(len)) 106279315Strasz err(1, "read"); 107279315Strasz 108279315Strasz buf = calloc(1, len); 109279315Strasz if (buf == NULL) 110279315Strasz err(1, "calloc"); 111279315Strasz 112279315Strasz ret = read(pipefd, buf, len); 113279315Strasz if (ret != (ssize_t)len) 114279315Strasz err(1, "read"); 115279315Strasz 116279315Strasz *bufp = buf; 117279315Strasz *lenp = len; 118279315Strasz} 119279315Strasz 120279315Straszstatic char * 121279315Straszbin2hex(const char *bin, size_t bin_len) 122279315Strasz{ 123279315Strasz unsigned char *hex, *tmp, ch; 124279315Strasz size_t hex_len; 125279315Strasz size_t i; 126279315Strasz 127279315Strasz hex_len = bin_len * 2 + 1; /* +1 for '\0'. */ 128279315Strasz hex = malloc(hex_len); 129279315Strasz if (hex == NULL) 130279315Strasz err(1, "malloc"); 131279315Strasz 132279315Strasz tmp = hex; 133279315Strasz for (i = 0; i < bin_len; i++) { 134279315Strasz ch = bin[i]; 135279315Strasz tmp += sprintf(tmp, "%02x", ch); 136279315Strasz } 137279315Strasz 138279315Strasz return (hex); 139279315Strasz} 140279315Strasz 141279315Strasz/* 142279315Strasz * We need to replace a standard chunk of PKCS7 signature with one mandated 143279315Strasz * by Authenticode. Problem is, replacing it just like that and then calling 144279315Strasz * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal(). 145279315Strasz * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific 146279315Strasz * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow 147279315Strasz * does not panic - and _then_ we replace it in the signature. This technique 148279315Strasz * was used in sbsigntool by Jeremy Kerr, and might have originated in 149279315Strasz * osslsigncode. 150279315Strasz */ 151279315Straszstatic void 152279315Straszmagic(PKCS7 *pkcs7, const char *digest, size_t digest_len) 153279315Strasz{ 154279315Strasz BIO *bio, *t_bio; 155279315Strasz ASN1_TYPE *t; 156279315Strasz ASN1_STRING *s; 157279315Strasz CONF *cnf; 158279315Strasz unsigned char *buf, *tmp; 159279315Strasz char *digest_hex, *magic_conf, *str; 160279315Strasz int len, nid, ok; 161279315Strasz 162279315Strasz digest_hex = bin2hex(digest, digest_len); 163279315Strasz 164279315Strasz /* 165279315Strasz * Construct the SpcIndirectDataContent chunk. 166279315Strasz */ 167279315Strasz nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL); 168279315Strasz 169279315Strasz asprintf(&magic_conf, magic_fmt, digest_hex); 170279315Strasz if (magic_conf == NULL) 171279315Strasz err(1, "asprintf"); 172279315Strasz 173279315Strasz bio = BIO_new_mem_buf((void *)magic_conf, -1); 174279315Strasz if (bio == NULL) { 175279315Strasz ERR_print_errors_fp(stderr); 176279315Strasz errx(1, "BIO_new_mem_buf(3) failed"); 177279315Strasz } 178279315Strasz 179279315Strasz cnf = NCONF_new(NULL); 180279315Strasz if (cnf == NULL) { 181279315Strasz ERR_print_errors_fp(stderr); 182279315Strasz errx(1, "NCONF_new(3) failed"); 183279315Strasz } 184279315Strasz 185279315Strasz ok = NCONF_load_bio(cnf, bio, NULL); 186279315Strasz if (ok == 0) { 187279315Strasz ERR_print_errors_fp(stderr); 188279315Strasz errx(1, "NCONF_load_bio(3) failed"); 189279315Strasz } 190279315Strasz 191279315Strasz str = NCONF_get_string(cnf, "default", "asn1"); 192279315Strasz if (str == NULL) { 193279315Strasz ERR_print_errors_fp(stderr); 194279315Strasz errx(1, "NCONF_get_string(3) failed"); 195279315Strasz } 196279315Strasz 197279315Strasz t = ASN1_generate_nconf(str, cnf); 198279315Strasz if (t == NULL) { 199279315Strasz ERR_print_errors_fp(stderr); 200279315Strasz errx(1, "ASN1_generate_nconf(3) failed"); 201279315Strasz } 202279315Strasz 203279315Strasz /* 204279315Strasz * We now have our proprietary piece of ASN.1. Let's do 205279315Strasz * the actual signing. 206279315Strasz */ 207279315Strasz len = i2d_ASN1_TYPE(t, NULL); 208279315Strasz tmp = buf = calloc(1, len); 209279315Strasz if (tmp == NULL) 210279315Strasz err(1, "calloc"); 211279315Strasz i2d_ASN1_TYPE(t, &tmp); 212279315Strasz 213279315Strasz /* 214279315Strasz * We now have contents of 't' stuffed into memory buffer 'buf'. 215279315Strasz */ 216279315Strasz tmp = NULL; 217279315Strasz t = NULL; 218279315Strasz 219279315Strasz t_bio = PKCS7_dataInit(pkcs7, NULL); 220279315Strasz if (t_bio == NULL) { 221279315Strasz ERR_print_errors_fp(stderr); 222279315Strasz errx(1, "PKCS7_dataInit(3) failed"); 223279315Strasz } 224279315Strasz 225279315Strasz BIO_write(t_bio, buf + 2, len - 2); 226279315Strasz 227279315Strasz ok = PKCS7_dataFinal(pkcs7, t_bio); 228279315Strasz if (ok == 0) { 229279315Strasz ERR_print_errors_fp(stderr); 230279315Strasz errx(1, "PKCS7_dataFinal(3) failed"); 231279315Strasz } 232279315Strasz 233279315Strasz t = ASN1_TYPE_new(); 234279315Strasz s = ASN1_STRING_new(); 235279315Strasz ASN1_STRING_set(s, buf, len); 236279315Strasz ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s); 237279315Strasz 238279315Strasz PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t); 239279315Strasz} 240279315Strasz 241279315Straszstatic void 242279315Straszsign(X509 *cert, EVP_PKEY *key, int pipefd) 243279315Strasz{ 244279315Strasz PKCS7 *pkcs7; 245279315Strasz BIO *bio, *out; 246279315Strasz const EVP_MD *md; 247279315Strasz PKCS7_SIGNER_INFO *info; 248279315Strasz void *digest, *signature; 249279315Strasz size_t digest_len, signature_len; 250279315Strasz int ok; 251279315Strasz 252279315Strasz assert(cert != NULL); 253279315Strasz assert(key != NULL); 254279315Strasz 255279315Strasz receive_chunk(&digest, &digest_len, pipefd); 256279315Strasz 257279315Strasz bio = BIO_new_mem_buf(digest, digest_len); 258279315Strasz if (bio == NULL) { 259279315Strasz ERR_print_errors_fp(stderr); 260279315Strasz errx(1, "BIO_new_mem_buf(3) failed"); 261279315Strasz } 262279315Strasz 263279315Strasz pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL); 264279315Strasz if (pkcs7 == NULL) { 265279315Strasz ERR_print_errors_fp(stderr); 266279315Strasz errx(1, "PKCS7_sign(3) failed"); 267279315Strasz } 268279315Strasz 269279315Strasz md = EVP_get_digestbyname(DIGEST); 270279315Strasz if (md == NULL) { 271279315Strasz ERR_print_errors_fp(stderr); 272279315Strasz errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST); 273279315Strasz } 274279315Strasz 275279315Strasz info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0); 276279315Strasz if (info == NULL) { 277279315Strasz ERR_print_errors_fp(stderr); 278279315Strasz errx(1, "PKCS7_sign_add_signer(3) failed"); 279279315Strasz } 280279315Strasz 281279315Strasz /* 282279315Strasz * XXX: All the signed binaries seem to have this, but where is it 283279315Strasz * described in the spec? 284279315Strasz */ 285279315Strasz PKCS7_add_signed_attribute(info, NID_pkcs9_contentType, 286279315Strasz V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1)); 287279315Strasz 288279315Strasz magic(pkcs7, digest, digest_len); 289279315Strasz 290279315Strasz#if 0 291279315Strasz out = BIO_new(BIO_s_file()); 292279315Strasz BIO_set_fp(out, stdout, BIO_NOCLOSE); 293279315Strasz PKCS7_print_ctx(out, pkcs7, 0, NULL); 294279315Strasz 295279315Strasz i2d_PKCS7_bio(out, pkcs7); 296279315Strasz#endif 297279315Strasz 298279315Strasz out = BIO_new(BIO_s_mem()); 299279315Strasz if (out == NULL) { 300279315Strasz ERR_print_errors_fp(stderr); 301279315Strasz errx(1, "BIO_new(3) failed"); 302279315Strasz } 303279315Strasz 304279315Strasz ok = i2d_PKCS7_bio(out, pkcs7); 305279315Strasz if (ok == 0) { 306279315Strasz ERR_print_errors_fp(stderr); 307279315Strasz errx(1, "i2d_PKCS7_bio(3) failed"); 308279315Strasz } 309279315Strasz 310279315Strasz signature_len = BIO_get_mem_data(out, &signature); 311279315Strasz if (signature_len <= 0) { 312279315Strasz ERR_print_errors_fp(stderr); 313279315Strasz errx(1, "BIO_get_mem_data(3) failed"); 314279315Strasz } 315279315Strasz 316279315Strasz (void)BIO_set_close(out, BIO_NOCLOSE); 317279315Strasz BIO_free(out); 318279315Strasz 319279315Strasz send_chunk(signature, signature_len, pipefd); 320279315Strasz} 321279315Strasz 322279315Straszstatic int 323279315Straszwait_for_child(pid_t pid) 324279315Strasz{ 325279315Strasz int status; 326279315Strasz 327279315Strasz pid = waitpid(pid, &status, 0); 328279315Strasz if (pid == -1) 329279315Strasz err(1, "waitpid"); 330279315Strasz 331279315Strasz return (WEXITSTATUS(status)); 332279315Strasz} 333279315Strasz 334279315Straszint 335279315Straszmain(int argc, char **argv) 336279315Strasz{ 337279315Strasz int ch, error; 338279315Strasz bool Vflag = false, vflag = false; 339279315Strasz const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL; 340279315Strasz FILE *certfp = NULL, *keyfp = NULL; 341279315Strasz X509 *cert = NULL; 342279315Strasz EVP_PKEY *key = NULL; 343279315Strasz pid_t pid; 344279315Strasz int pipefds[2]; 345279315Strasz 346279315Strasz while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) { 347279315Strasz switch (ch) { 348279315Strasz case 'V': 349279315Strasz Vflag = true; 350279315Strasz break; 351279315Strasz case 'c': 352279315Strasz certpath = checked_strdup(optarg); 353279315Strasz break; 354279315Strasz case 'k': 355279315Strasz keypath = checked_strdup(optarg); 356279315Strasz break; 357279315Strasz case 'o': 358279315Strasz outpath = checked_strdup(optarg); 359279315Strasz break; 360279315Strasz case 'v': 361279315Strasz vflag = true; 362279315Strasz break; 363279315Strasz default: 364279315Strasz usage(); 365279315Strasz } 366279315Strasz } 367279315Strasz 368279315Strasz argc -= optind; 369279315Strasz argv += optind; 370279315Strasz if (argc != 1) 371279315Strasz usage(); 372279315Strasz 373279315Strasz if (Vflag) { 374279315Strasz if (certpath != NULL) 375279315Strasz errx(1, "-V and -c are mutually exclusive"); 376279315Strasz if (keypath != NULL) 377279315Strasz errx(1, "-V and -k are mutually exclusive"); 378279315Strasz if (outpath != NULL) 379279315Strasz errx(1, "-V and -o are mutually exclusive"); 380279315Strasz } else { 381279315Strasz if (certpath == NULL) 382279315Strasz errx(1, "-c option is mandatory"); 383279315Strasz if (keypath == NULL) 384279315Strasz errx(1, "-k option is mandatory"); 385279315Strasz if (outpath == NULL) 386279315Strasz errx(1, "-o option is mandatory"); 387279315Strasz } 388279315Strasz 389279315Strasz inpath = argv[0]; 390279315Strasz 391279315Strasz OPENSSL_config(NULL); 392279315Strasz ERR_load_crypto_strings(); 393279315Strasz OpenSSL_add_all_algorithms(); 394279315Strasz 395279315Strasz error = pipe(pipefds); 396279315Strasz if (error != 0) 397279315Strasz err(1, "pipe"); 398279315Strasz 399279315Strasz pid = fork(); 400279315Strasz if (pid < 0) 401279315Strasz err(1, "fork"); 402279315Strasz 403279315Strasz if (pid == 0) 404279315Strasz return (child(inpath, outpath, pipefds[1], Vflag, vflag)); 405279315Strasz 406279315Strasz if (!Vflag) { 407279315Strasz certfp = checked_fopen(certpath, "r"); 408279315Strasz cert = PEM_read_X509(certfp, NULL, NULL, NULL); 409279315Strasz if (cert == NULL) { 410279315Strasz ERR_print_errors_fp(stderr); 411279315Strasz errx(1, "failed to load certificate from %s", certpath); 412279315Strasz } 413279315Strasz 414279315Strasz keyfp = checked_fopen(keypath, "r"); 415279315Strasz key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL); 416279315Strasz if (key == NULL) { 417279315Strasz ERR_print_errors_fp(stderr); 418279315Strasz errx(1, "failed to load private key from %s", keypath); 419279315Strasz } 420279315Strasz 421279315Strasz sign(cert, key, pipefds[0]); 422279315Strasz } 423279315Strasz 424279315Strasz return (wait_for_child(pid)); 425279315Strasz} 426