1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2014 The FreeBSD Foundation 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/wait.h> 36 #include <assert.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <openssl/conf.h> 44 #include <openssl/evp.h> 45 #include <openssl/err.h> 46 #include <openssl/pem.h> 47 #include <openssl/pkcs7.h> 48 49 #include "uefisign.h" 50 #include "magic.h" 51 52 static void 53 usage(void) 54 { 55 56 fprintf(stderr, "usage: uefisign -c cert -k key -o outfile [-v] file\n" 57 " uefisign -V [-c cert] [-v] file\n"); 58 exit(1); 59 } 60 61 static char * 62 checked_strdup(const char *s) 63 { 64 char *c; 65 66 c = strdup(s); 67 if (c == NULL) 68 err(1, "strdup"); 69 return (c); 70 } 71 72 FILE * 73 checked_fopen(const char *path, const char *mode) 74 { 75 FILE *fp; 76 77 assert(path != NULL); 78 79 fp = fopen(path, mode); 80 if (fp == NULL) 81 err(1, "%s", path); 82 return (fp); 83 } 84 85 void 86 send_chunk(const void *buf, size_t len, int pipefd) 87 { 88 ssize_t ret; 89 90 ret = write(pipefd, &len, sizeof(len)); 91 if (ret != sizeof(len)) 92 err(1, "write"); 93 ret = write(pipefd, buf, len); 94 if (ret != (ssize_t)len) 95 err(1, "write"); 96 } 97 98 void 99 receive_chunk(void **bufp, size_t *lenp, int pipefd) 100 { 101 ssize_t ret; 102 size_t len; 103 void *buf; 104 105 ret = read(pipefd, &len, sizeof(len)); 106 if (ret != sizeof(len)) 107 err(1, "read"); 108 109 buf = calloc(1, len); 110 if (buf == NULL) 111 err(1, "calloc"); 112 113 ret = read(pipefd, buf, len); 114 if (ret != (ssize_t)len) 115 err(1, "read"); 116 117 *bufp = buf; 118 *lenp = len; 119 } 120 121 static char * 122 bin2hex(const char *bin, size_t bin_len) 123 { 124 unsigned char *hex, *tmp, ch; 125 size_t hex_len; 126 size_t i; 127 128 hex_len = bin_len * 2 + 1; /* +1 for '\0'. */ 129 hex = malloc(hex_len); 130 if (hex == NULL) 131 err(1, "malloc"); 132 133 tmp = hex; 134 for (i = 0; i < bin_len; i++) { 135 ch = bin[i]; 136 tmp += sprintf(tmp, "%02x", ch); 137 } 138 139 return (hex); 140 } 141 142 /* 143 * We need to replace a standard chunk of PKCS7 signature with one mandated 144 * by Authenticode. Problem is, replacing it just like that and then calling 145 * PKCS7_final() would make OpenSSL segfault somewhere in PKCS7_dataFinal(). 146 * So, instead, we call PKCS7_dataInit(), then put our Authenticode-specific 147 * data into BIO it returned, then call PKCS7_dataFinal() - which now somehow 148 * does not panic - and _then_ we replace it in the signature. This technique 149 * was used in sbsigntool by Jeremy Kerr, and might have originated in 150 * osslsigncode. 151 */ 152 static void 153 magic(PKCS7 *pkcs7, const char *digest, size_t digest_len) 154 { 155 BIO *bio, *t_bio; 156 ASN1_TYPE *t; 157 ASN1_STRING *s; 158 CONF *cnf; 159 unsigned char *buf, *tmp; 160 char *digest_hex, *magic_conf, *str; 161 int len, nid, ok; 162 163 digest_hex = bin2hex(digest, digest_len); 164 165 /* 166 * Construct the SpcIndirectDataContent chunk. 167 */ 168 nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", NULL, NULL); 169 170 asprintf(&magic_conf, magic_fmt, digest_hex); 171 if (magic_conf == NULL) 172 err(1, "asprintf"); 173 174 bio = BIO_new_mem_buf((void *)magic_conf, -1); 175 if (bio == NULL) { 176 ERR_print_errors_fp(stderr); 177 errx(1, "BIO_new_mem_buf(3) failed"); 178 } 179 180 cnf = NCONF_new(NULL); 181 if (cnf == NULL) { 182 ERR_print_errors_fp(stderr); 183 errx(1, "NCONF_new(3) failed"); 184 } 185 186 ok = NCONF_load_bio(cnf, bio, NULL); 187 if (ok == 0) { 188 ERR_print_errors_fp(stderr); 189 errx(1, "NCONF_load_bio(3) failed"); 190 } 191 192 str = NCONF_get_string(cnf, "default", "asn1"); 193 if (str == NULL) { 194 ERR_print_errors_fp(stderr); 195 errx(1, "NCONF_get_string(3) failed"); 196 } 197 198 t = ASN1_generate_nconf(str, cnf); 199 if (t == NULL) { 200 ERR_print_errors_fp(stderr); 201 errx(1, "ASN1_generate_nconf(3) failed"); 202 } 203 204 /* 205 * We now have our proprietary piece of ASN.1. Let's do 206 * the actual signing. 207 */ 208 len = i2d_ASN1_TYPE(t, NULL); 209 tmp = buf = calloc(1, len); 210 if (tmp == NULL) 211 err(1, "calloc"); 212 i2d_ASN1_TYPE(t, &tmp); 213 214 /* 215 * We now have contents of 't' stuffed into memory buffer 'buf'. 216 */ 217 tmp = NULL; 218 t = NULL; 219 220 t_bio = PKCS7_dataInit(pkcs7, NULL); 221 if (t_bio == NULL) { 222 ERR_print_errors_fp(stderr); 223 errx(1, "PKCS7_dataInit(3) failed"); 224 } 225 226 BIO_write(t_bio, buf + 2, len - 2); 227 228 ok = PKCS7_dataFinal(pkcs7, t_bio); 229 if (ok == 0) { 230 ERR_print_errors_fp(stderr); 231 errx(1, "PKCS7_dataFinal(3) failed"); 232 } 233 234 t = ASN1_TYPE_new(); 235 s = ASN1_STRING_new(); 236 ASN1_STRING_set(s, buf, len); 237 ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s); 238 239 PKCS7_set0_type_other(pkcs7->d.sign->contents, nid, t); 240 } 241 242 static void 243 sign(X509 *cert, EVP_PKEY *key, int pipefd) 244 { 245 PKCS7 *pkcs7; 246 BIO *bio, *out; 247 const EVP_MD *md; 248 PKCS7_SIGNER_INFO *info; 249 void *digest, *signature; 250 size_t digest_len, signature_len; 251 int ok; 252 253 assert(cert != NULL); 254 assert(key != NULL); 255 256 receive_chunk(&digest, &digest_len, pipefd); 257 258 bio = BIO_new_mem_buf(digest, digest_len); 259 if (bio == NULL) { 260 ERR_print_errors_fp(stderr); 261 errx(1, "BIO_new_mem_buf(3) failed"); 262 } 263 264 pkcs7 = PKCS7_sign(NULL, NULL, NULL, bio, PKCS7_BINARY | PKCS7_PARTIAL); 265 if (pkcs7 == NULL) { 266 ERR_print_errors_fp(stderr); 267 errx(1, "PKCS7_sign(3) failed"); 268 } 269 270 md = EVP_get_digestbyname(DIGEST); 271 if (md == NULL) { 272 ERR_print_errors_fp(stderr); 273 errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST); 274 } 275 276 info = PKCS7_sign_add_signer(pkcs7, cert, key, md, 0); 277 if (info == NULL) { 278 ERR_print_errors_fp(stderr); 279 errx(1, "PKCS7_sign_add_signer(3) failed"); 280 } 281 282 /* 283 * XXX: All the signed binaries seem to have this, but where is it 284 * described in the spec? 285 */ 286 PKCS7_add_signed_attribute(info, NID_pkcs9_contentType, 287 V_ASN1_OBJECT, OBJ_txt2obj("1.3.6.1.4.1.311.2.1.4", 1)); 288 289 magic(pkcs7, digest, digest_len); 290 291 #if 0 292 out = BIO_new(BIO_s_file()); 293 BIO_set_fp(out, stdout, BIO_NOCLOSE); 294 PKCS7_print_ctx(out, pkcs7, 0, NULL); 295 296 i2d_PKCS7_bio(out, pkcs7); 297 #endif 298 299 out = BIO_new(BIO_s_mem()); 300 if (out == NULL) { 301 ERR_print_errors_fp(stderr); 302 errx(1, "BIO_new(3) failed"); 303 } 304 305 ok = i2d_PKCS7_bio(out, pkcs7); 306 if (ok == 0) { 307 ERR_print_errors_fp(stderr); 308 errx(1, "i2d_PKCS7_bio(3) failed"); 309 } 310 311 signature_len = BIO_get_mem_data(out, &signature); 312 if (signature_len <= 0) { 313 ERR_print_errors_fp(stderr); 314 errx(1, "BIO_get_mem_data(3) failed"); 315 } 316 317 (void)BIO_set_close(out, BIO_NOCLOSE); 318 BIO_free(out); 319 320 send_chunk(signature, signature_len, pipefd); 321 } 322 323 static int 324 wait_for_child(pid_t pid) 325 { 326 int status; 327 328 pid = waitpid(pid, &status, 0); 329 if (pid == -1) 330 err(1, "waitpid"); 331 332 return (WEXITSTATUS(status)); 333 } 334 335 int 336 main(int argc, char **argv) 337 { 338 int ch, error; 339 bool Vflag = false, vflag = false; 340 const char *certpath = NULL, *keypath = NULL, *outpath = NULL, *inpath = NULL; 341 FILE *certfp = NULL, *keyfp = NULL; 342 X509 *cert = NULL; 343 EVP_PKEY *key = NULL; 344 pid_t pid; 345 int pipefds[2]; 346 347 while ((ch = getopt(argc, argv, "Vc:k:o:v")) != -1) { 348 switch (ch) { 349 case 'V': 350 Vflag = true; 351 break; 352 case 'c': 353 if (certpath == NULL) 354 certpath = checked_strdup(optarg); 355 else 356 err(1, "-c can only be specified once"); 357 break; 358 case 'k': 359 if (keypath == NULL) 360 keypath = checked_strdup(optarg); 361 else 362 err(1, "-k can only be specified once"); 363 break; 364 case 'o': 365 if (outpath == NULL) 366 outpath = checked_strdup(optarg); 367 else 368 err(1, "-o can only be specified once"); 369 break; 370 case 'v': 371 vflag = true; 372 break; 373 default: 374 usage(); 375 } 376 } 377 378 argc -= optind; 379 argv += optind; 380 if (argc != 1) 381 usage(); 382 383 if (Vflag) { 384 if (certpath != NULL) 385 errx(1, "-V and -c are mutually exclusive"); 386 if (keypath != NULL) 387 errx(1, "-V and -k are mutually exclusive"); 388 if (outpath != NULL) 389 errx(1, "-V and -o are mutually exclusive"); 390 } else { 391 if (certpath == NULL) 392 errx(1, "-c option is mandatory"); 393 if (keypath == NULL) 394 errx(1, "-k option is mandatory"); 395 if (outpath == NULL) 396 errx(1, "-o option is mandatory"); 397 } 398 399 inpath = argv[0]; 400 401 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG | 402 OPENSSL_INIT_LOAD_CRYPTO_STRINGS | 403 OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); 404 405 error = pipe(pipefds); 406 if (error != 0) 407 err(1, "pipe"); 408 409 pid = fork(); 410 if (pid < 0) 411 err(1, "fork"); 412 413 if (pid == 0) 414 exit(child(inpath, outpath, pipefds[1], Vflag, vflag)); 415 416 if (!Vflag) { 417 certfp = checked_fopen(certpath, "r"); 418 cert = PEM_read_X509(certfp, NULL, NULL, NULL); 419 if (cert == NULL) { 420 ERR_print_errors_fp(stderr); 421 errx(1, "failed to load certificate from %s", certpath); 422 } 423 424 keyfp = checked_fopen(keypath, "r"); 425 key = PEM_read_PrivateKey(keyfp, NULL, NULL, NULL); 426 if (key == NULL) { 427 ERR_print_errors_fp(stderr); 428 errx(1, "failed to load private key from %s", keypath); 429 } 430 431 sign(cert, key, pipefds[0]); 432 } 433 434 exit(wait_for_child(pid)); 435 } 436