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