child.c revision 279315
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: head/usr.sbin/uefisign/child.c 279315 2015-02-26 09:15:24Z trasz $"); 33279315Strasz 34279315Strasz#include <sys/param.h> 35279315Strasz#if __FreeBSD_version >= 1100000 36279315Strasz#include <sys/capsicum.h> 37279315Strasz#else 38279315Strasz#include <sys/capability.h> 39279315Strasz#endif 40279315Strasz#include <sys/types.h> 41279315Strasz#include <sys/stat.h> 42279315Strasz#include <assert.h> 43279315Strasz#include <err.h> 44279315Strasz#include <errno.h> 45279315Strasz#include <stdio.h> 46279315Strasz#include <stdlib.h> 47279315Strasz#include <string.h> 48279315Strasz#include <unistd.h> 49279315Strasz 50279315Strasz#include <openssl/evp.h> 51279315Strasz#include <openssl/err.h> 52279315Strasz#include <openssl/pem.h> 53279315Strasz 54279315Strasz#include "uefisign.h" 55279315Strasz 56279315Straszstatic void 57279315Straszload(struct executable *x) 58279315Strasz{ 59279315Strasz int error, fd; 60279315Strasz struct stat sb; 61279315Strasz char *buf; 62279315Strasz size_t nread, len; 63279315Strasz 64279315Strasz fd = fileno(x->x_fp); 65279315Strasz 66279315Strasz error = fstat(fd, &sb); 67279315Strasz if (error != 0) 68279315Strasz err(1, "%s: fstat", x->x_path); 69279315Strasz 70279315Strasz len = sb.st_size; 71279315Strasz if (len <= 0) 72279315Strasz errx(1, "%s: file is empty", x->x_path); 73279315Strasz 74279315Strasz buf = malloc(len); 75279315Strasz if (buf == NULL) 76279315Strasz err(1, "%s: cannot malloc %zd bytes", x->x_path, len); 77279315Strasz 78279315Strasz nread = fread(buf, len, 1, x->x_fp); 79279315Strasz if (nread != 1) 80279315Strasz err(1, "%s: fread", x->x_path); 81279315Strasz 82279315Strasz x->x_buf = buf; 83279315Strasz x->x_len = len; 84279315Strasz} 85279315Strasz 86279315Straszstatic void 87279315Straszdigest_range(struct executable *x, EVP_MD_CTX *mdctx, off_t off, size_t len) 88279315Strasz{ 89279315Strasz int ok; 90279315Strasz 91279315Strasz range_check(x, off, len, "chunk"); 92279315Strasz 93279315Strasz ok = EVP_DigestUpdate(mdctx, x->x_buf + off, len); 94279315Strasz if (ok == 0) { 95279315Strasz ERR_print_errors_fp(stderr); 96279315Strasz errx(1, "EVP_DigestUpdate(3) failed"); 97279315Strasz } 98279315Strasz} 99279315Strasz 100279315Straszstatic void 101279315Straszdigest(struct executable *x) 102279315Strasz{ 103279315Strasz EVP_MD_CTX *mdctx; 104279315Strasz const EVP_MD *md; 105279315Strasz size_t sum_of_bytes_hashed; 106279315Strasz int i, ok; 107279315Strasz 108279315Strasz /* 109279315Strasz * Windows Authenticode Portable Executable Signature Format 110279315Strasz * spec version 1.0 specifies MD5 and SHA1. However, pesign 111279315Strasz * and sbsign both use SHA256, so do the same. 112279315Strasz */ 113279315Strasz md = EVP_get_digestbyname(DIGEST); 114279315Strasz if (md == NULL) { 115279315Strasz ERR_print_errors_fp(stderr); 116279315Strasz errx(1, "EVP_get_digestbyname(\"%s\") failed", DIGEST); 117279315Strasz } 118279315Strasz 119279315Strasz mdctx = EVP_MD_CTX_create(); 120279315Strasz if (mdctx == NULL) { 121279315Strasz ERR_print_errors_fp(stderr); 122279315Strasz errx(1, "EVP_MD_CTX_create(3) failed"); 123279315Strasz } 124279315Strasz 125279315Strasz ok = EVP_DigestInit_ex(mdctx, md, NULL); 126279315Strasz if (ok == 0) { 127279315Strasz ERR_print_errors_fp(stderr); 128279315Strasz errx(1, "EVP_DigestInit_ex(3) failed"); 129279315Strasz } 130279315Strasz 131279315Strasz /* 132279315Strasz * According to the Authenticode spec, we need to compute 133279315Strasz * the digest in a rather... specific manner; see "Calculating 134279315Strasz * the PE Image Hash" part of the spec for details. 135279315Strasz * 136279315Strasz * First, everything from 0 to before the PE checksum. 137279315Strasz */ 138279315Strasz digest_range(x, mdctx, 0, x->x_checksum_off); 139279315Strasz 140279315Strasz /* 141279315Strasz * Second, from after the PE checksum to before the Certificate 142279315Strasz * entry in Data Directory. 143279315Strasz */ 144279315Strasz digest_range(x, mdctx, x->x_checksum_off + x->x_checksum_len, 145279315Strasz x->x_certificate_entry_off - 146279315Strasz (x->x_checksum_off + x->x_checksum_len)); 147279315Strasz 148279315Strasz /* 149279315Strasz * Then, from after the Certificate entry to the end of headers. 150279315Strasz */ 151279315Strasz digest_range(x, mdctx, 152279315Strasz x->x_certificate_entry_off + x->x_certificate_entry_len, 153279315Strasz x->x_headers_len - 154279315Strasz (x->x_certificate_entry_off + x->x_certificate_entry_len)); 155279315Strasz 156279315Strasz /* 157279315Strasz * Then, each section in turn, as specified in the PE Section Table. 158279315Strasz * 159279315Strasz * XXX: Sorting. 160279315Strasz */ 161279315Strasz sum_of_bytes_hashed = x->x_headers_len; 162279315Strasz for (i = 0; i < x->x_nsections; i++) { 163279315Strasz digest_range(x, mdctx, 164279315Strasz x->x_section_off[i], x->x_section_len[i]); 165279315Strasz sum_of_bytes_hashed += x->x_section_len[i]; 166279315Strasz } 167279315Strasz 168279315Strasz /* 169279315Strasz * I believe this can happen with overlapping sections. 170279315Strasz */ 171279315Strasz if (sum_of_bytes_hashed > x->x_len) 172279315Strasz errx(1, "number of bytes hashed is larger than file size"); 173279315Strasz 174279315Strasz /* 175279315Strasz * I can't really explain this one; just do what the spec says. 176279315Strasz */ 177279315Strasz if (sum_of_bytes_hashed < x->x_len) { 178279315Strasz digest_range(x, mdctx, sum_of_bytes_hashed, 179279315Strasz x->x_len - (signature_size(x) + sum_of_bytes_hashed)); 180279315Strasz } 181279315Strasz 182279315Strasz ok = EVP_DigestFinal_ex(mdctx, x->x_digest, &x->x_digest_len); 183279315Strasz if (ok == 0) { 184279315Strasz ERR_print_errors_fp(stderr); 185279315Strasz errx(1, "EVP_DigestFinal_ex(3) failed"); 186279315Strasz } 187279315Strasz 188279315Strasz EVP_MD_CTX_destroy(mdctx); 189279315Strasz} 190279315Strasz 191279315Straszstatic void 192279315Straszshow_digest(const struct executable *x) 193279315Strasz{ 194279315Strasz int i; 195279315Strasz 196279315Strasz printf("computed %s digest ", DIGEST); 197279315Strasz for (i = 0; i < (int)x->x_digest_len; i++) 198279315Strasz printf("%02x", (unsigned char)x->x_digest[i]); 199279315Strasz printf("; digest len %u\n", x->x_digest_len); 200279315Strasz} 201279315Strasz 202279315Straszstatic void 203279315Straszsend_digest(const struct executable *x, int pipefd) 204279315Strasz{ 205279315Strasz 206279315Strasz send_chunk(x->x_digest, x->x_digest_len, pipefd); 207279315Strasz} 208279315Strasz 209279315Straszstatic void 210279315Straszreceive_signature(struct executable *x, int pipefd) 211279315Strasz{ 212279315Strasz 213279315Strasz receive_chunk(&x->x_signature, &x->x_signature_len, pipefd); 214279315Strasz} 215279315Strasz 216279315Straszstatic void 217279315Straszsave(struct executable *x, FILE *fp, const char *path) 218279315Strasz{ 219279315Strasz size_t nwritten; 220279315Strasz 221279315Strasz assert(fp != NULL); 222279315Strasz assert(path != NULL); 223279315Strasz 224279315Strasz nwritten = fwrite(x->x_buf, x->x_len, 1, fp); 225279315Strasz if (nwritten != 1) 226279315Strasz err(1, "%s: fwrite", path); 227279315Strasz} 228279315Strasz 229279315Straszint 230279315Straszchild(const char *inpath, const char *outpath, int pipefd, 231279315Strasz bool Vflag, bool vflag) 232279315Strasz{ 233279315Strasz int error; 234279315Strasz FILE *outfp = NULL, *infp = NULL; 235279315Strasz struct executable *x; 236279315Strasz 237279315Strasz infp = checked_fopen(inpath, "r"); 238279315Strasz if (outpath != NULL) 239279315Strasz outfp = checked_fopen(outpath, "w"); 240279315Strasz 241279315Strasz error = cap_enter(); 242279315Strasz if (error != 0 && errno != ENOSYS) 243279315Strasz err(1, "cap_enter"); 244279315Strasz 245279315Strasz x = calloc(1, sizeof(*x)); 246279315Strasz if (x == NULL) 247279315Strasz err(1, "calloc"); 248279315Strasz x->x_path = inpath; 249279315Strasz x->x_fp = infp; 250279315Strasz 251279315Strasz load(x); 252279315Strasz parse(x); 253279315Strasz if (Vflag) { 254279315Strasz if (signature_size(x) == 0) 255279315Strasz errx(1, "file not signed"); 256279315Strasz 257279315Strasz printf("file contains signature\n"); 258279315Strasz if (vflag) { 259279315Strasz digest(x); 260279315Strasz show_digest(x); 261279315Strasz show_certificate(x); 262279315Strasz } 263279315Strasz } else { 264279315Strasz if (signature_size(x) != 0) 265279315Strasz errx(1, "file already signed"); 266279315Strasz 267279315Strasz digest(x); 268279315Strasz if (vflag) 269279315Strasz show_digest(x); 270279315Strasz send_digest(x, pipefd); 271279315Strasz receive_signature(x, pipefd); 272279315Strasz update(x); 273279315Strasz save(x, outfp, outpath); 274279315Strasz } 275279315Strasz 276279315Strasz return (0); 277279315Strasz} 278