/*
* Copyright 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "bmpblk_header.h"
#include "file_type.h"
#include "fmap.h"
#include "futility.h"
#include "gbb_header.h"
#include "host_common.h"
#include "kernel_blob.h"
#include "traversal.h"
#include "util_misc.h"
#include "vb1_helper.h"
#include "vboot_common.h"
/* Local values for cb_area_s._flags */
enum callback_flags {
AREA_IS_VALID = 0x00000001,
};
/* Local structure for args, etc. */
static struct local_data_s {
VbPrivateKey *signprivate;
VbKeyBlockHeader *keyblock;
VbPublicKey *kernel_subkey;
VbPrivateKey *devsignprivate;
VbKeyBlockHeader *devkeyblock;
uint32_t version;
int version_specified;
uint32_t flags;
int flags_specified;
char *loemdir;
char *loemid;
uint8_t *bootloader_data;
uint64_t bootloader_size;
uint8_t *config_data;
uint64_t config_size;
enum arch_t arch;
int fv_specified;
uint32_t kloadaddr;
uint32_t padding;
int vblockonly;
char *outfile;
int create_new_outfile;
char *pem_signpriv;
int pem_algo_specified;
uint32_t pem_algo;
char *pem_external;
} option = {
.version = 1,
.arch = ARCH_UNSPECIFIED,
.kloadaddr = CROS_32BIT_ENTRY_ADDR,
.padding = 65536,
};
/* Helper to complain about invalid args. Returns num errors discovered */
static int no_opt_if(int expr, const char *optname)
{
if (expr) {
fprintf(stderr, "Missing --%s option\n", optname);
return 1;
}
return 0;
}
/* This wraps/signs a public key, producing a keyblock. */
int futil_cb_sign_pubkey(struct futil_traverse_state_s *state)
{
VbPublicKey *data_key = (VbPublicKey *)state->my_area->buf;
VbKeyBlockHeader *vblock;
if (option.pem_signpriv) {
if (option.pem_external) {
/* External signing uses the PEM file directly. */
vblock = KeyBlockCreate_external(
data_key,
option.pem_signpriv,
option.pem_algo, option.flags,
option.pem_external);
} else {
option.signprivate = PrivateKeyReadPem(
option.pem_signpriv, option.pem_algo);
if (!option.signprivate) {
fprintf(stderr,
"Unable to read PEM signing key: %s\n",
strerror(errno));
return 1;
}
vblock = KeyBlockCreate(data_key, option.signprivate,
option.flags);
}
} else {
/* Not PEM. Should already have a signing key. */
vblock = KeyBlockCreate(data_key, option.signprivate,
option.flags);
}
/* Write it out */
return WriteSomeParts(option.outfile,
vblock, vblock->key_block_size,
NULL, 0);
}
/*
* This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
* The data in state->my_area is just the RW firmware blob, so there's nothing
* useful to show about it. We'll just mark it as present so when we encounter
* corresponding VBLOCK area, we'll have this to verify.
*/
int futil_cb_sign_fw_main(struct futil_traverse_state_s *state)
{
state->my_area->_flags |= AREA_IS_VALID;
return 0;
}
/*
* This handles VBLOCK_A and VBLOCK_B while processing a BIOS image.
* We don't do any signing here. We just check to see if the VBLOCK
* area contains a firmware preamble.
*/
int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state)
{
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf;
uint32_t len = state->my_area->len;
/*
* If we have a valid keyblock and fw_preamble, then we can use them to
* determine the size of the firmware body. Otherwise, we'll have to
* just sign the whole region.
*/
if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
fprintf(stderr, "Warning: %s keyblock is invalid. "
"Signing the entire FW FMAP region...\n",
state->name);
goto whatever;
}
RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
if (!rsa) {
fprintf(stderr, "Warning: %s public key is invalid. "
"Signing the entire FW FMAP region...\n",
state->name);
goto whatever;
}
uint32_t more = key_block->key_block_size;
VbFirmwarePreambleHeader *preamble =
(VbFirmwarePreambleHeader *)(state->my_area->buf + more);
uint32_t fw_size = preamble->body_signature.data_size;
struct cb_area_s *fw_body_area = 0;
switch (state->component) {
case CB_FMAP_VBLOCK_A:
fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_A];
/* Preserve the flags if they're not specified */
if (!option.flags_specified)
option.flags = preamble->flags;
break;
case CB_FMAP_VBLOCK_B:
fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_B];
break;
default:
DIE;
}
if (fw_size > fw_body_area->len) {
fprintf(stderr,
"%s says the firmware is larger than we have\n",
state->name);
return 1;
}
/* Update the firmware size */
fw_body_area->len = fw_size;
whatever:
state->my_area->_flags |= AREA_IS_VALID;
return 0;
}
int futil_cb_create_kernel_part(struct futil_traverse_state_s *state)
{
uint8_t *vmlinuz_data, *kblob_data, *vblock_data;
uint64_t vmlinuz_size, kblob_size, vblock_size;
int rv;
vmlinuz_data = state->my_area->buf;
vmlinuz_size = state->my_area->len;
kblob_data = CreateKernelBlob(
vmlinuz_data, vmlinuz_size,
option.arch, option.kloadaddr,
option.config_data, option.config_size,
option.bootloader_data, option.bootloader_size,
&kblob_size);
if (!kblob_data) {
fprintf(stderr, "Unable to create kernel blob\n");
return 1;
}
Debug("kblob_size = 0x%" PRIx64 "\n", kblob_size);
vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding,
option.version, option.kloadaddr,
option.keyblock, option.signprivate,
option.flags, &vblock_size);
if (!vblock_data) {
fprintf(stderr, "Unable to sign kernel blob\n");
free(kblob_data);
return 1;
}
Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size);
/* We should be creating a completely new output file.
* If not, something's wrong. */
if (!option.create_new_outfile)
DIE;
if (option.vblockonly)
rv = WriteSomeParts(option.outfile,
vblock_data, vblock_size,
NULL, 0);
else
rv = WriteSomeParts(option.outfile,
vblock_data, vblock_size,
kblob_data, kblob_size);
free(vblock_data);
free(kblob_data);
return rv;
}
int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state)
{
uint8_t *kpart_data, *kblob_data, *vblock_data;
uint64_t kpart_size, kblob_size, vblock_size;
VbKeyBlockHeader *keyblock = NULL;
VbKernelPreambleHeader *preamble = NULL;
int rv = 0;
kpart_data = state->my_area->buf;
kpart_size = state->my_area->len;
/* Note: This just sets some static pointers. It doesn't malloc. */
kblob_data = UnpackKPart(kpart_data, kpart_size, option.padding,
&keyblock, &preamble, &kblob_size);
if (!kblob_data) {
fprintf(stderr, "Unable to unpack kernel partition\n");
return 1;
}
/*
* We don't let --kloadaddr change when resigning, because the original
* vbutil_kernel program didn't do it right. Since obviously no one
* ever noticed, we'll maintain bug-compatibility by just not allowing
* it here either. To enable it, we'd need to update the zeropage
* table's cmd_line_ptr as well as the preamble.
*/
option.kloadaddr = preamble->body_load_address;
/* Replace the config if asked */
if (option.config_data &&
0 != UpdateKernelBlobConfig(kblob_data, kblob_size,
option.config_data,
option.config_size)) {
fprintf(stderr, "Unable to update config\n");
return 1;
}
/* Preserve the version unless a new one is given */
if (!option.version_specified)
option.version = preamble->kernel_version;
/* Preserve the flags if not specified */
if (VbKernelHasFlags(preamble) == VBOOT_SUCCESS) {
if (option.flags_specified == 0)
option.flags = preamble->flags;
}
/* Replace the keyblock if asked */
if (option.keyblock)
keyblock = option.keyblock;
/* Compute the new signature */
vblock_data = SignKernelBlob(kblob_data, kblob_size, option.padding,
option.version, option.kloadaddr,
keyblock, option.signprivate,
option.flags, &vblock_size);
if (!vblock_data) {
fprintf(stderr, "Unable to sign kernel blob\n");
return 1;
}
Debug("vblock_size = 0x%" PRIx64 "\n", vblock_size);
if (option.create_new_outfile) {
/* Write out what we've been asked for */
if (option.vblockonly)
rv = WriteSomeParts(option.outfile,
vblock_data, vblock_size,
NULL, 0);
else
rv = WriteSomeParts(option.outfile,
vblock_data, vblock_size,
kblob_data, kblob_size);
} else {
/* If we're modifying an existing file, it's mmap'ed so that
* all our modifications to the buffer will get flushed to
* disk when we close it. */
Memcpy(kpart_data, vblock_data, vblock_size);
}
free(vblock_data);
return rv;
}
int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state)
{
VbSignature *body_sig;
VbFirmwarePreambleHeader *preamble;
int rv;
body_sig = CalculateSignature(state->my_area->buf, state->my_area->len,
option.signprivate);
if (!body_sig) {
fprintf(stderr, "Error calculating body signature\n");
return 1;
}
preamble = CreateFirmwarePreamble(option.version,
option.kernel_subkey,
body_sig,
option.signprivate,
option.flags);
if (!preamble) {
fprintf(stderr, "Error creating firmware preamble.\n");
free(body_sig);
return 1;
}
rv = WriteSomeParts(option.outfile,
option.keyblock, option.keyblock->key_block_size,
preamble, preamble->preamble_size);
free(preamble);
free(body_sig);
return rv;
}
int futil_cb_sign_begin(struct futil_traverse_state_s *state)
{
if (state->in_type == FILE_TYPE_UNKNOWN) {
fprintf(stderr, "Unable to determine type of %s\n",
state->in_filename);
return 1;
}
return 0;
}
static int write_new_preamble(struct cb_area_s *vblock,
struct cb_area_s *fw_body,
VbPrivateKey *signkey,
VbKeyBlockHeader *keyblock)
{
VbSignature *body_sig;
VbFirmwarePreambleHeader *preamble;
body_sig = CalculateSignature(fw_body->buf, fw_body->len, signkey);
if (!body_sig) {
fprintf(stderr, "Error calculating body signature\n");
return 1;
}
preamble = CreateFirmwarePreamble(option.version,
option.kernel_subkey,
body_sig,
signkey,
option.flags);
if (!preamble) {
fprintf(stderr, "Error creating firmware preamble.\n");
free(body_sig);
return 1;
}
/* Write the new keyblock */
uint32_t more = keyblock->key_block_size;
memcpy(vblock->buf, keyblock, more);
/* and the new preamble */
memcpy(vblock->buf + more, preamble, preamble->preamble_size);
free(preamble);
free(body_sig);
return 0;
}
static int write_loem(const char *ab, struct cb_area_s *vblock)
{
char filename[PATH_MAX];
int n;
n = snprintf(filename, sizeof(filename), "%s/vblock_%s.%s",
option.loemdir ? option.loemdir : ".",
ab, option.loemid);
if (n >= sizeof(filename)) {
fprintf(stderr, "LOEM args produce bogus filename\n");
return 1;
}
FILE *fp = fopen(filename, "w");
if (!fp) {
fprintf(stderr, "Can't open %s for writing: %s\n",
filename, strerror(errno));
return 1;
}
if (1 != fwrite(vblock->buf, vblock->len, 1, fp)) {
fprintf(stderr, "Can't write to %s: %s\n",
filename, strerror(errno));
fclose(fp);
return 1;
}
if (fclose(fp)) {
fprintf(stderr, "Failed closing loem output: %s\n",
strerror(errno));
return 1;
}
return 0;
}
/* This signs a full BIOS image after it's been traversed. */
static int sign_bios_at_end(struct futil_traverse_state_s *state)
{
struct cb_area_s *vblock_a = &state->cb_area[CB_FMAP_VBLOCK_A];
struct cb_area_s *vblock_b = &state->cb_area[CB_FMAP_VBLOCK_B];
struct cb_area_s *fw_a = &state->cb_area[CB_FMAP_FW_MAIN_A];
struct cb_area_s *fw_b = &state->cb_area[CB_FMAP_FW_MAIN_B];
int retval = 0;
if (state->errors ||
!(vblock_a->_flags & AREA_IS_VALID) ||
!(vblock_b->_flags & AREA_IS_VALID) ||
!(fw_a->_flags & AREA_IS_VALID) ||
!(fw_b->_flags & AREA_IS_VALID)) {
fprintf(stderr, "Something's wrong. Not changing anything\n");
return 1;
}
/* Do A & B differ ? */
if (fw_a->len != fw_b->len ||
memcmp(fw_a->buf, fw_b->buf, fw_a->len)) {
/* Yes, must use DEV keys for A */
if (!option.devsignprivate || !option.devkeyblock) {
fprintf(stderr,
"FW A & B differ. DEV keys are required.\n");
return 1;
}
retval |= write_new_preamble(vblock_a, fw_a,
option.devsignprivate,
option.devkeyblock);
} else {
retval |= write_new_preamble(vblock_a, fw_a,
option.signprivate,
option.keyblock);
}
/* FW B is always normal keys */
retval |= write_new_preamble(vblock_b, fw_b,
option.signprivate,
option.keyblock);
if (option.loemid) {
retval |= write_loem("A", vblock_a);
retval |= write_loem("B", vblock_b);
}
return retval;
}
int futil_cb_sign_end(struct futil_traverse_state_s *state)
{
switch (state->in_type) {
case FILE_TYPE_BIOS_IMAGE:
case FILE_TYPE_OLD_BIOS_IMAGE:
return sign_bios_at_end(state);
default:
/* Any other cleanup needed? */
break;
}
return state->errors;
}
static const char usage[] = "\n"
"Usage: " MYNAME " %s [PARAMS] INFILE [OUTFILE]\n"
"\n"
"Where INFILE is a\n"
"\n"
" public key (.vbpubk); OUTFILE is a keyblock\n"
" raw firmware blob (FW_MAIN_A/B); OUTFILE is a VBLOCK_A/B\n"
" complete firmware image (bios.bin)\n"
" raw linux kernel; OUTFILE is a kernel partition image\n"
" kernel partition image (/dev/sda2, /dev/mmcblk0p2)\n";
static const char usage_pubkey[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a public key / create a new keyblock:\n"
"\n"
"Required PARAMS:\n"
" [--datapubkey] INFILE The public key to wrap\n"
" [--outfile] OUTFILE The resulting keyblock\n"
"\n"
"Optional PARAMS:\n"
" A private signing key, specified as either\n"
" -s|--signprivate FILE.vbprivk Signing key in .vbprivk format\n"
" Or\n"
" --pem_signpriv FILE.pem Signing key in PEM format...\n"
" --pem_algo NUM AND the algorithm to use (0 - %d)\n"
"\n"
" If a signing key is not given, the keyblock will not be signed (duh)."
"\n\n"
"And these, too:\n\n"
" -f|--flags NUM Flags specifying use conditions\n"
" --pem_external PROGRAM"
" External program to compute the signature\n"
" (requires a PEM signing key)\n";
static const char usage_fw_main[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a raw firmware blob (FW_MAIN_A/B):\n"
"\n"
"Required PARAMS:\n"
" -s|--signprivate FILE.vbprivk The private firmware data key\n"
" -b|--keyblock FILE.keyblock The keyblock containing the\n"
" public firmware data key\n"
" -k|--kernelkey FILE.vbpubk The public kernel subkey\n"
" -v|--version NUM The firmware version number\n"
" [--fv] INFILE"
" The raw firmware blob (FW_MAIN_A/B)\n"
" [--outfile] OUTFILE Output VBLOCK_A/B\n"
"\n"
"Optional PARAMS:\n"
" -f|--flags NUM The preamble flags value"
" (default is 0)\n";
static const char usage_bios[] = "\n"
"-----------------------------------------------------------------\n"
"To sign a complete firmware image (bios.bin):\n"
"\n"
"Required PARAMS:\n"
" -s|--signprivate FILE.vbprivk The private firmware data key\n"
" -b|--keyblock FILE.keyblock The keyblock containing the\n"
" public firmware data key\n"
" -k|--kernelkey FILE.vbpubk The public kernel subkey\n"
" [--infile] INFILE Input firmware image (modified\n"
" in place if no OUTFILE given)\n"
"\n"
"These are required if the A and B firmware differ:\n"
" -S|--devsign FILE.vbprivk The DEV private firmware data key\n"
" -B|--devkeyblock FILE.keyblock The keyblock containing the\n"
" DEV public firmware data key\n"
"\n"
"Optional PARAMS:\n"
" -v|--version NUM The firmware version number"
" (default %d)\n"
" -f|--flags NUM The preamble flags value"
" (default is\n"
" unchanged, or 0 if unknown)\n"
" -d|--loemdir DIR Local OEM output vblock directory\n"
" -l|--loemid STRING Local OEM vblock suffix\n"
" [--outfile] OUTFILE Output firmware image\n";
static const char usage_new_kpart[] = "\n"
"-----------------------------------------------------------------\n"
"To create a new kernel parition image (/dev/sda2, /dev/mmcblk0p2):\n"
"\n"
"Required PARAMS:\n"
" -s|--signprivate FILE.vbprivk"
" The private key to sign the kernel blob\n"
" -b|--keyblock FILE.keyblock The keyblock containing the public\n"
" key to verify the kernel blob\n"
" -v|--version NUM The kernel version number\n"
" --bootloader FILE Bootloader stub\n"
" --config FILE The kernel commandline file\n"
" --arch ARCH The CPU architecture (one of\n"
" x86|amd64, arm|aarch64, mips)\n"
" [--vmlinuz] INFILE Linux kernel bzImage file\n"
" [--outfile] OUTFILE Output kernel partition or vblock\n"
"\n"
"Optional PARAMS:\n"
" --kloadaddr NUM"
" RAM address to load the kernel body\n"
" (default 0x%x)\n"
" --pad NUM The vblock padding size in bytes\n"
" (default 0x%x)\n"
" --vblockonly Emit just the vblock (requires a\n"
" distinct outfile)\n"
" -f|--flags NUM The preamble flags value\n";
static const char usage_old_kpart[] = "\n"
"-----------------------------------------------------------------\n"
"To resign an existing kernel parition (/dev/sda2, /dev/mmcblk0p2):\n"
"\n"
"Required PARAMS:\n"
" -s|--signprivate FILE.vbprivk"
" The private key to sign the kernel blob\n"
" [--infile] INFILE Input kernel partition (modified\n"
" in place if no OUTFILE given)\n"
"\n"
"Optional PARAMS:\n"
" -b|--keyblock FILE.keyblock The keyblock containing the public\n"
" key to verify the kernel blob\n"
" -v|--version NUM The kernel version number\n"
" --config FILE The kernel commandline file\n"
" --pad NUM The vblock padding size in bytes\n"
" (default 0x%x)\n"
" [--outfile] OUTFILE Output kernel partition or vblock\n"
" --vblockonly Emit just the vblock (requires a\n"
" distinct OUTFILE)\n"
" -f|--flags NUM The preamble flags value\n"
"\n";
static void print_help(const char *prog)
{
printf(usage, prog);
printf(usage_pubkey, kNumAlgorithms - 1);
puts(usage_fw_main);
printf(usage_bios, option.version);
printf(usage_new_kpart, option.kloadaddr, option.padding);
printf(usage_old_kpart, option.padding);
}
enum no_short_opts {
OPT_FV = 1000,
OPT_INFILE, /* aka "--vmlinuz" */
OPT_OUTFILE,
OPT_BOOTLOADER,
OPT_CONFIG,
OPT_ARCH,
OPT_KLOADADDR,
OPT_PADDING,
OPT_PEM_SIGNPRIV,
OPT_PEM_ALGO,
OPT_PEM_EXTERNAL,
};
static const struct option long_opts[] = {
/* name hasarg *flag val */
{"signprivate", 1, NULL, 's'},
{"keyblock", 1, NULL, 'b'},
{"kernelkey", 1, NULL, 'k'},
{"devsign", 1, NULL, 'S'},
{"devkeyblock", 1, NULL, 'B'},
{"version", 1, NULL, 'v'},
{"flags", 1, NULL, 'f'},
{"loemdir", 1, NULL, 'd'},
{"loemid", 1, NULL, 'l'},
{"fv", 1, NULL, OPT_FV},
{"infile", 1, NULL, OPT_INFILE},
{"datapubkey", 1, NULL, OPT_INFILE}, /* alias */
{"vmlinuz", 1, NULL, OPT_INFILE}, /* alias */
{"outfile", 1, NULL, OPT_OUTFILE},
{"bootloader", 1, NULL, OPT_BOOTLOADER},
{"config", 1, NULL, OPT_CONFIG},
{"arch", 1, NULL, OPT_ARCH},
{"kloadaddr", 1, NULL, OPT_KLOADADDR},
{"pad", 1, NULL, OPT_PADDING},
{"pem_signpriv", 1, NULL, OPT_PEM_SIGNPRIV},
{"pem_algo", 1, NULL, OPT_PEM_ALGO},
{"pem_external", 1, NULL, OPT_PEM_EXTERNAL},
{"vblockonly", 0, &option.vblockonly, 1},
{"debug", 0, &debugging_enabled, 1},
{NULL, 0, NULL, 0},
};
static char *short_opts = ":s:b:k:S:B:v:f:d:l:";
static int do_sign(int argc, char *argv[])
{
char *infile = 0;
int i;
int ifd = -1;
int errorcnt = 0;
struct futil_traverse_state_s state;
uint8_t *buf;
uint32_t buf_len;
char *e = 0;
enum futil_file_type type;
int inout_file_count = 0;
int mapping;
opterr = 0; /* quiet, you */
while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
switch (i) {
case 's':
option.signprivate = PrivateKeyRead(optarg);
if (!option.signprivate) {
fprintf(stderr, "Error reading %s\n", optarg);
errorcnt++;
}
break;
case 'b':
option.keyblock = KeyBlockRead(optarg);
if (!option.keyblock) {
fprintf(stderr, "Error reading %s\n", optarg);
errorcnt++;
}
break;
case 'k':
option.kernel_subkey = PublicKeyRead(optarg);
if (!option.kernel_subkey) {
fprintf(stderr, "Error reading %s\n", optarg);
errorcnt++;
}
break;
case 'S':
option.devsignprivate = PrivateKeyRead(optarg);
if (!option.devsignprivate) {
fprintf(stderr, "Error reading %s\n", optarg);
errorcnt++;
}
break;
case 'B':
option.devkeyblock = KeyBlockRead(optarg);
if (!option.devkeyblock) {
fprintf(stderr, "Error reading %s\n", optarg);
errorcnt++;
}
break;
case 'v':
option.version_specified = 1;
option.version = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e)) {
fprintf(stderr,
"Invalid --version \"%s\"\n", optarg);
errorcnt++;
}
break;
case 'f':
option.flags_specified = 1;
option.flags = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e)) {
fprintf(stderr,
"Invalid --flags \"%s\"\n", optarg);
errorcnt++;
}
break;
case 'd':
option.loemdir = optarg;
break;
case 'l':
option.loemid = optarg;
break;
case OPT_FV:
option.fv_specified = 1;
/* fallthrough */
case OPT_INFILE: /* aka "--vmlinuz" */
inout_file_count++;
infile = optarg;
break;
case OPT_OUTFILE:
inout_file_count++;
option.outfile = optarg;
break;
case OPT_BOOTLOADER:
option.bootloader_data = ReadFile(
optarg, &option.bootloader_size);
if (!option.bootloader_data) {
fprintf(stderr,
"Error reading bootloader file: %s\n",
strerror(errno));
errorcnt++;
}
Debug("bootloader file size=0x%" PRIx64 "\n",
option.bootloader_size);
break;
case OPT_CONFIG:
option.config_data = ReadConfigFile(
optarg, &option.config_size);
if (!option.config_data) {
fprintf(stderr,
"Error reading config file: %s\n",
strerror(errno));
errorcnt++;
}
break;
case OPT_ARCH:
/* check the first 3 characters to also match x86_64 */
if ((!strncasecmp(optarg, "x86", 3)) ||
(!strcasecmp(optarg, "amd64")))
option.arch = ARCH_X86;
else if ((!strcasecmp(optarg, "arm")) ||
(!strcasecmp(optarg, "aarch64")))
option.arch = ARCH_ARM;
else if (!strcasecmp(optarg, "mips"))
option.arch = ARCH_MIPS;
else {
fprintf(stderr,
"Unknown architecture: \"%s\"\n",
optarg);
errorcnt++;
}
break;
case OPT_KLOADADDR:
option.kloadaddr = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e)) {
fprintf(stderr,
"Invalid --kloadaddr \"%s\"\n", optarg);
errorcnt++;
}
break;
case OPT_PADDING:
option.padding = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e)) {
fprintf(stderr,
"Invalid --padding \"%s\"\n", optarg);
errorcnt++;
}
break;
case OPT_PEM_SIGNPRIV:
option.pem_signpriv = optarg;
break;
case OPT_PEM_ALGO:
option.pem_algo_specified = 1;
option.pem_algo = strtoul(optarg, &e, 0);
if (!*optarg || (e && *e) ||
(option.pem_algo >= kNumAlgorithms)) {
fprintf(stderr,
"Invalid --pem_algo \"%s\"\n", optarg);
errorcnt++;
}
break;
case OPT_PEM_EXTERNAL:
option.pem_external = optarg;
break;
case '?':
if (optopt)
fprintf(stderr, "Unrecognized option: -%c\n",
optopt);
else
fprintf(stderr, "Unrecognized option: %s\n",
argv[optind - 1]);
errorcnt++;
break;
case ':':
fprintf(stderr, "Missing argument to -%c\n", optopt);
errorcnt++;
break;
case 0: /* handled option */
break;
default:
Debug("i=%d\n", i);
DIE;
}
}
/* If we don't have an input file already, we need one */
if (!infile) {
if (argc - optind <= 0) {
errorcnt++;
fprintf(stderr, "ERROR: missing input filename\n");
goto done;
} else {
inout_file_count++;
infile = argv[optind++];
}
}
/* Look for an output file if we don't have one, just in case. */
if (!option.outfile && argc - optind > 0) {
inout_file_count++;
option.outfile = argv[optind++];
}
/* What are we looking at? */
if (futil_file_type(infile, &type)) {
errorcnt++;
goto done;
}
/* We may be able to infer the type based on the other args */
if (type == FILE_TYPE_UNKNOWN) {
if (option.bootloader_data || option.config_data
|| option.arch != ARCH_UNSPECIFIED)
type = FILE_TYPE_RAW_KERNEL;
else if (option.kernel_subkey || option.fv_specified)
type = FILE_TYPE_RAW_FIRMWARE;
}
Debug("type=%s\n", futil_file_type_str(type));
/* Check the arguments for the type of thing we want to sign */
switch (type) {
case FILE_TYPE_UNKNOWN:
fprintf(stderr,
"Unable to determine the type of the input file\n");
errorcnt++;
goto done;
case FILE_TYPE_PUBKEY:
option.create_new_outfile = 1;
if (option.signprivate && option.pem_signpriv) {
fprintf(stderr,
"Only one of --signprivate and --pem_signpriv"
" can be specified\n");
errorcnt++;
}
if ((option.signprivate && option.pem_algo_specified) ||
(option.pem_signpriv && !option.pem_algo_specified)) {
fprintf(stderr, "--pem_algo must be used with"
" --pem_signpriv\n");
errorcnt++;
}
if (option.pem_external && !option.pem_signpriv) {
fprintf(stderr, "--pem_external must be used with"
" --pem_signpriv\n");
errorcnt++;
}
/* We'll wait to read the PEM file, since the external signer
* may want to read it instead. */
break;
case FILE_TYPE_KEYBLOCK:
fprintf(stderr, "Resigning a keyblock is kind of pointless.\n");
fprintf(stderr, "Just create a new one.\n");
errorcnt++;
break;
case FILE_TYPE_FW_PREAMBLE:
fprintf(stderr,
"%s IS a signature. Sign the firmware instead\n",
infile);
break;
case FILE_TYPE_GBB:
fprintf(stderr, "There's no way to sign a GBB\n");
errorcnt++;
break;
case FILE_TYPE_BIOS_IMAGE:
case FILE_TYPE_OLD_BIOS_IMAGE:
errorcnt += no_opt_if(!option.signprivate, "signprivate");
errorcnt += no_opt_if(!option.keyblock, "keyblock");
errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey");
break;
case FILE_TYPE_KERN_PREAMBLE:
errorcnt += no_opt_if(!option.signprivate, "signprivate");
if (option.vblockonly || inout_file_count > 1)
option.create_new_outfile = 1;
break;
case FILE_TYPE_RAW_FIRMWARE:
option.create_new_outfile = 1;
errorcnt += no_opt_if(!option.signprivate, "signprivate");
errorcnt += no_opt_if(!option.keyblock, "keyblock");
errorcnt += no_opt_if(!option.kernel_subkey, "kernelkey");
errorcnt += no_opt_if(!option.version_specified, "version");
break;
case FILE_TYPE_RAW_KERNEL:
option.create_new_outfile = 1;
errorcnt += no_opt_if(!option.signprivate, "signprivate");
errorcnt += no_opt_if(!option.keyblock, "keyblock");
errorcnt += no_opt_if(!option.version_specified, "version");
errorcnt += no_opt_if(!option.bootloader_data, "bootloader");
errorcnt += no_opt_if(!option.config_data, "config");
errorcnt += no_opt_if(option.arch == ARCH_UNSPECIFIED, "arch");
break;
case FILE_TYPE_CHROMIUMOS_DISK:
fprintf(stderr, "Signing a %s is not yet supported\n",
futil_file_type_str(type));
errorcnt++;
break;
default:
DIE;
}
Debug("infile=%s\n", infile);
Debug("inout_file_count=%d\n", inout_file_count);
Debug("option.create_new_outfile=%d\n", option.create_new_outfile);
/* Make sure we have an output file if one is needed */
if (!option.outfile) {
if (option.create_new_outfile) {
errorcnt++;
fprintf(stderr, "Missing output filename\n");
goto done;
} else {
option.outfile = infile;
}
}
Debug("option.outfile=%s\n", option.outfile);
if (argc - optind > 0) {
errorcnt++;
fprintf(stderr, "ERROR: too many arguments left over\n");
}
if (errorcnt)
goto done;
memset(&state, 0, sizeof(state));
state.op = FUTIL_OP_SIGN;
if (option.create_new_outfile) {
/* The input is read-only, the output is write-only. */
mapping = MAP_RO;
state.in_filename = infile;
Debug("open RO %s\n", infile);
ifd = open(infile, O_RDONLY);
if (ifd < 0) {
errorcnt++;
fprintf(stderr, "Can't open %s for reading: %s\n",
infile, strerror(errno));
goto done;
}
} else {
/* We'll read-modify-write the output file */
mapping = MAP_RW;
state.in_filename = option.outfile;
if (inout_file_count > 1)
futil_copy_file_or_die(infile, option.outfile);
Debug("open RW %s\n", option.outfile);
ifd = open(option.outfile, O_RDWR);
if (ifd < 0) {
errorcnt++;
fprintf(stderr, "Can't open %s for writing: %s\n",
option.outfile, strerror(errno));
goto done;
}
}
if (0 != futil_map_file(ifd, mapping, &buf, &buf_len)) {
errorcnt++;
goto done;
}
errorcnt += futil_traverse(buf, buf_len, &state, type);
errorcnt += futil_unmap_file(ifd, MAP_RW, buf, buf_len);
done:
if (ifd >= 0 && close(ifd)) {
errorcnt++;
fprintf(stderr, "Error when closing ifd: %s\n",
strerror(errno));
}
if (option.signprivate)
free(option.signprivate);
if (option.keyblock)
free(option.keyblock);
if (option.kernel_subkey)
free(option.kernel_subkey);
if (errorcnt)
fprintf(stderr, "Use --help for usage instructions\n");
return !!errorcnt;
}
DECLARE_FUTIL_COMMAND(sign, do_sign,
VBOOT_VERSION_ALL,
"Sign / resign various binary components",
print_help);