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