You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
747 lines
20 KiB
747 lines
20 KiB
/*
|
|
* 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 <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 "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 {
|
|
VbPublicKey *k;
|
|
uint8_t *fv;
|
|
uint64_t fv_size;
|
|
uint32_t padding;
|
|
int strict;
|
|
int t_flag;
|
|
} option = {
|
|
.padding = 65536,
|
|
};
|
|
|
|
static void show_key(VbPublicKey *pubkey, const char *sp)
|
|
{
|
|
printf("%sAlgorithm: %" PRIu64 " %s\n", sp, pubkey->algorithm,
|
|
(pubkey->algorithm < kNumAlgorithms ?
|
|
algo_strings[pubkey->algorithm] : "(invalid)"));
|
|
printf("%sKey Version: %" PRIu64 "\n", sp, pubkey->key_version);
|
|
printf("%sKey sha1sum: ", sp);
|
|
PrintPubKeySha1Sum(pubkey);
|
|
printf("\n");
|
|
}
|
|
|
|
static void show_keyblock(VbKeyBlockHeader *key_block, const char *name,
|
|
int sign_key, int good_sig)
|
|
{
|
|
if (name)
|
|
printf("Key block: %s\n", name);
|
|
else
|
|
printf("Key block:\n");
|
|
printf(" Signature: %s\n",
|
|
sign_key ? (good_sig ? "valid" : "invalid") : "ignored");
|
|
printf(" Size: 0x%" PRIx64 "\n",
|
|
key_block->key_block_size);
|
|
printf(" Flags: %" PRIu64 " ",
|
|
key_block->key_block_flags);
|
|
if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
|
|
printf(" !DEV");
|
|
if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
|
|
printf(" DEV");
|
|
if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
|
|
printf(" !REC");
|
|
if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
|
|
printf(" REC");
|
|
printf("\n");
|
|
|
|
VbPublicKey *data_key = &key_block->data_key;
|
|
printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
|
|
(data_key->algorithm < kNumAlgorithms
|
|
? algo_strings[data_key->algorithm]
|
|
: "(invalid)"));
|
|
printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
|
|
printf(" Data key sha1sum: ");
|
|
PrintPubKeySha1Sum(data_key);
|
|
printf("\n");
|
|
}
|
|
|
|
int futil_cb_show_pubkey(struct futil_traverse_state_s *state)
|
|
{
|
|
VbPublicKey *pubkey = (VbPublicKey *)state->my_area->buf;
|
|
|
|
if (!PublicKeyLooksOkay(pubkey, state->my_area->len)) {
|
|
printf("%s looks bogus\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
printf("Public Key file: %s\n", state->in_filename);
|
|
show_key(pubkey, " ");
|
|
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
return 0;
|
|
}
|
|
|
|
int futil_cb_show_privkey(struct futil_traverse_state_s *state)
|
|
{
|
|
VbPrivateKey key;
|
|
int alg_okay;
|
|
|
|
key.algorithm = *(typeof(key.algorithm) *)state->my_area->buf;
|
|
|
|
printf("Private Key file: %s\n", state->in_filename);
|
|
alg_okay = key.algorithm < kNumAlgorithms;
|
|
printf(" Algorithm: %" PRIu64 " %s\n", key.algorithm,
|
|
alg_okay ? algo_strings[key.algorithm] : "(unknown)");
|
|
|
|
if (alg_okay)
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int futil_cb_show_gbb(struct futil_traverse_state_s *state)
|
|
{
|
|
uint8_t *buf = state->my_area->buf;
|
|
uint32_t len = state->my_area->len;
|
|
GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf;
|
|
VbPublicKey *pubkey;
|
|
BmpBlockHeader *bmp;
|
|
int retval = 0;
|
|
uint32_t maxlen = 0;
|
|
|
|
if (!len) {
|
|
printf("GBB header: %s <invalid>\n",
|
|
state->component == CB_GBB ?
|
|
state->in_filename : state->name);
|
|
return 1;
|
|
}
|
|
|
|
/* It looks like a GBB or we wouldn't be called. */
|
|
if (!futil_valid_gbb_header(gbb, len, &maxlen))
|
|
retval = 1;
|
|
|
|
printf("GBB header: %s\n",
|
|
state->component == CB_GBB ? state->in_filename : state->name);
|
|
printf(" Version: %d.%d\n",
|
|
gbb->major_version, gbb->minor_version);
|
|
printf(" Flags: 0x%08x\n", gbb->flags);
|
|
printf(" Regions: offset size\n");
|
|
printf(" hwid 0x%08x 0x%08x\n",
|
|
gbb->hwid_offset, gbb->hwid_size);
|
|
printf(" bmpvf 0x%08x 0x%08x\n",
|
|
gbb->bmpfv_offset, gbb->bmpfv_size);
|
|
printf(" rootkey 0x%08x 0x%08x\n",
|
|
gbb->rootkey_offset, gbb->rootkey_size);
|
|
printf(" recovery_key 0x%08x 0x%08x\n",
|
|
gbb->recovery_key_offset, gbb->recovery_key_size);
|
|
|
|
printf(" Size: 0x%08x / 0x%08x%s\n",
|
|
maxlen, len, maxlen > len ? " (not enough)" : "");
|
|
|
|
if (retval) {
|
|
printf("GBB header is invalid, ignoring content\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("GBB content:\n");
|
|
printf(" HWID: %s\n", buf + gbb->hwid_offset);
|
|
print_hwid_digest(gbb, " digest: ", "\n");
|
|
|
|
pubkey = (VbPublicKey *)(buf + gbb->rootkey_offset);
|
|
if (PublicKeyLooksOkay(pubkey, gbb->rootkey_size)) {
|
|
state->rootkey.offset = state->my_area->offset +
|
|
gbb->rootkey_offset;
|
|
state->rootkey.buf = buf + gbb->rootkey_offset;
|
|
state->rootkey.len = gbb->rootkey_size;
|
|
state->rootkey._flags |= AREA_IS_VALID;
|
|
printf(" Root Key:\n");
|
|
show_key(pubkey, " ");
|
|
} else {
|
|
retval = 1;
|
|
printf(" Root Key: <invalid>\n");
|
|
}
|
|
|
|
pubkey = (VbPublicKey *)(buf + gbb->recovery_key_offset);
|
|
if (PublicKeyLooksOkay(pubkey, gbb->recovery_key_size)) {
|
|
state->recovery_key.offset = state->my_area->offset +
|
|
gbb->recovery_key_offset;
|
|
state->recovery_key.buf = buf + gbb->recovery_key_offset;
|
|
state->recovery_key.len = gbb->recovery_key_size;
|
|
state->recovery_key._flags |= AREA_IS_VALID;
|
|
printf(" Recovery Key:\n");
|
|
show_key(pubkey, " ");
|
|
} else {
|
|
retval = 1;
|
|
printf(" Recovery Key: <invalid>\n");
|
|
}
|
|
|
|
bmp = (BmpBlockHeader *)(buf + gbb->bmpfv_offset);
|
|
if (0 != memcmp(bmp, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
|
|
printf(" BmpBlock: <invalid>\n");
|
|
/* We don't support older BmpBlock formats, so we can't
|
|
* be strict about this. */
|
|
} else {
|
|
printf(" BmpBlock:\n");
|
|
printf(" Version: %d.%d\n",
|
|
bmp->major_version, bmp->minor_version);
|
|
printf(" Localizations: %d\n",
|
|
bmp->number_of_localizations);
|
|
printf(" Screen layouts: %d\n",
|
|
bmp->number_of_screenlayouts);
|
|
printf(" Image infos: %d\n",
|
|
bmp->number_of_imageinfos);
|
|
}
|
|
|
|
if (!retval)
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
|
|
return retval;
|
|
}
|
|
|
|
int futil_cb_show_keyblock(struct futil_traverse_state_s *state)
|
|
{
|
|
VbKeyBlockHeader *block = (VbKeyBlockHeader *)state->my_area->buf;
|
|
VbPublicKey *sign_key = option.k;
|
|
int good_sig = 0;
|
|
int retval = 0;
|
|
|
|
/* Check the hash only first */
|
|
if (0 != KeyBlockVerify(block, state->my_area->len, NULL, 1)) {
|
|
printf("%s is invalid\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
/* Check the signature if we have one */
|
|
if (sign_key && VBOOT_SUCCESS ==
|
|
KeyBlockVerify(block, state->my_area->len, sign_key, 0))
|
|
good_sig = 1;
|
|
|
|
if (option.strict && (!sign_key || !good_sig))
|
|
retval = 1;
|
|
|
|
show_keyblock(block, state->in_filename, !!sign_key, good_sig);
|
|
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* 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_show_fw_main(struct futil_traverse_state_s *state)
|
|
{
|
|
if (!state->my_area->len) {
|
|
printf("Firmware body: %s <invalid>\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
printf("Firmware body: %s\n", state->name);
|
|
printf(" Offset: 0x%08x\n", state->my_area->offset);
|
|
printf(" Size: 0x%08x\n", state->my_area->len);
|
|
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state)
|
|
{
|
|
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf;
|
|
uint32_t len = state->my_area->len;
|
|
VbPublicKey *sign_key = option.k;
|
|
uint8_t *fv_data = option.fv;
|
|
uint64_t fv_size = option.fv_size;
|
|
struct cb_area_s *fw_body_area = 0;
|
|
int good_sig = 0;
|
|
int retval = 0;
|
|
|
|
/* Check the hash... */
|
|
if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
|
|
printf("%s keyblock component is invalid\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
switch (state->component) {
|
|
case CB_FMAP_VBLOCK_A:
|
|
if (!sign_key && (state->rootkey._flags & AREA_IS_VALID))
|
|
/* BIOS should have a rootkey in the GBB */
|
|
sign_key = (VbPublicKey *)state->rootkey.buf;
|
|
/* And we should have already seen the firmware body */
|
|
fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_A];
|
|
break;
|
|
case CB_FMAP_VBLOCK_B:
|
|
if (!sign_key && (state->rootkey._flags & AREA_IS_VALID))
|
|
/* BIOS should have a rootkey in the GBB */
|
|
sign_key = (VbPublicKey *)state->rootkey.buf;
|
|
/* And we should have already seen the firmware body */
|
|
fw_body_area = &state->cb_area[CB_FMAP_FW_MAIN_B];
|
|
break;
|
|
case CB_FW_PREAMBLE:
|
|
/* We have to provide a signature and body in the options. */
|
|
break;
|
|
default:
|
|
DIE;
|
|
}
|
|
|
|
/* If we have a key, check the signature too */
|
|
if (sign_key && VBOOT_SUCCESS ==
|
|
KeyBlockVerify(key_block, len, sign_key, 0))
|
|
good_sig = 1;
|
|
|
|
show_keyblock(key_block,
|
|
state->component == CB_FW_PREAMBLE
|
|
? state->in_filename : state->name,
|
|
!!sign_key, good_sig);
|
|
|
|
if (option.strict && (!sign_key || !good_sig))
|
|
retval = 1;
|
|
|
|
RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
|
|
if (!rsa) {
|
|
fprintf(stderr, "Error parsing data key in %s\n", state->name);
|
|
return 1;
|
|
}
|
|
uint32_t more = key_block->key_block_size;
|
|
VbFirmwarePreambleHeader *preamble =
|
|
(VbFirmwarePreambleHeader *)(state->my_area->buf + more);
|
|
|
|
if (VBOOT_SUCCESS != VerifyFirmwarePreamble(preamble,
|
|
len - more, rsa)) {
|
|
printf("%s is invalid\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
uint32_t flags = VbGetFirmwarePreambleFlags(preamble);
|
|
printf("Firmware Preamble:\n");
|
|
printf(" Size: %" PRIu64 "\n",
|
|
preamble->preamble_size);
|
|
printf(" Header version: %" PRIu32 ".%" PRIu32 "\n",
|
|
preamble->header_version_major, preamble->header_version_minor);
|
|
printf(" Firmware version: %" PRIu64 "\n",
|
|
preamble->firmware_version);
|
|
VbPublicKey *kernel_subkey = &preamble->kernel_subkey;
|
|
printf(" Kernel key algorithm: %" PRIu64 " %s\n",
|
|
kernel_subkey->algorithm,
|
|
(kernel_subkey->algorithm < kNumAlgorithms ?
|
|
algo_strings[kernel_subkey->algorithm] : "(invalid)"));
|
|
if (kernel_subkey->algorithm >= kNumAlgorithms)
|
|
retval = 1;
|
|
printf(" Kernel key version: %" PRIu64 "\n",
|
|
kernel_subkey->key_version);
|
|
printf(" Kernel key sha1sum: ");
|
|
PrintPubKeySha1Sum(kernel_subkey);
|
|
printf("\n");
|
|
printf(" Firmware body size: %" PRIu64 "\n",
|
|
preamble->body_signature.data_size);
|
|
printf(" Preamble flags: %" PRIu32 "\n", flags);
|
|
|
|
|
|
if (flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) {
|
|
printf("Preamble requests USE_RO_NORMAL;"
|
|
" skipping body verification.\n");
|
|
goto done;
|
|
}
|
|
|
|
/* We'll need to get the firmware body from somewhere... */
|
|
if (fw_body_area && (fw_body_area->_flags & AREA_IS_VALID)) {
|
|
fv_data = fw_body_area->buf;
|
|
fv_size = fw_body_area->len;
|
|
}
|
|
|
|
if (!fv_data) {
|
|
printf("No firmware body available to verify.\n");
|
|
if (option.strict)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (VBOOT_SUCCESS !=
|
|
VerifyData(fv_data, fv_size, &preamble->body_signature, rsa)) {
|
|
fprintf(stderr, "Error verifying firmware body.\n");
|
|
return 1;
|
|
}
|
|
|
|
done:
|
|
/* Can't trust the BIOS unless everything is signed,
|
|
* but standalone files are okay. */
|
|
if ((state->component == CB_FW_PREAMBLE) ||
|
|
(sign_key && good_sig)) {
|
|
if (!(flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL))
|
|
printf("Body verification succeeded.\n");
|
|
state->my_area->_flags |= AREA_IS_VALID;
|
|
} else {
|
|
printf("Seems legit, but the signature is unverified.\n");
|
|
if (option.strict)
|
|
retval = 1;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state)
|
|
{
|
|
|
|
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf;
|
|
uint32_t len = state->my_area->len;
|
|
VbPublicKey *sign_key = option.k;
|
|
uint8_t *kernel_blob = 0;
|
|
uint64_t kernel_size = 0;
|
|
int good_sig = 0;
|
|
int retval = 0;
|
|
uint64_t vmlinuz_header_size = 0;
|
|
uint64_t vmlinuz_header_address = 0;
|
|
uint32_t flags = 0;
|
|
|
|
/* Check the hash... */
|
|
if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) {
|
|
printf("%s keyblock component is invalid\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
/* If we have a key, check the signature too */
|
|
if (sign_key && VBOOT_SUCCESS ==
|
|
KeyBlockVerify(key_block, len, sign_key, 0))
|
|
good_sig = 1;
|
|
|
|
printf("Kernel partition: %s\n", state->in_filename);
|
|
show_keyblock(key_block, NULL, !!sign_key, good_sig);
|
|
|
|
if (option.strict && (!sign_key || !good_sig))
|
|
retval = 1;
|
|
|
|
RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key);
|
|
if (!rsa) {
|
|
fprintf(stderr, "Error parsing data key in %s\n", state->name);
|
|
return 1;
|
|
}
|
|
uint32_t more = key_block->key_block_size;
|
|
VbKernelPreambleHeader *preamble =
|
|
(VbKernelPreambleHeader *)(state->my_area->buf + more);
|
|
|
|
if (VBOOT_SUCCESS != VerifyKernelPreamble(preamble,
|
|
len - more, rsa)) {
|
|
printf("%s is invalid\n", state->name);
|
|
return 1;
|
|
}
|
|
|
|
printf("Kernel Preamble:\n");
|
|
printf(" Size: 0x%" PRIx64 "\n",
|
|
preamble->preamble_size);
|
|
printf(" Header version: %" PRIu32 ".%" PRIu32 "\n",
|
|
preamble->header_version_major,
|
|
preamble->header_version_minor);
|
|
printf(" Kernel version: %" PRIu64 "\n",
|
|
preamble->kernel_version);
|
|
printf(" Body load address: 0x%" PRIx64 "\n",
|
|
preamble->body_load_address);
|
|
printf(" Body size: 0x%" PRIx64 "\n",
|
|
preamble->body_signature.data_size);
|
|
printf(" Bootloader address: 0x%" PRIx64 "\n",
|
|
preamble->bootloader_address);
|
|
printf(" Bootloader size: 0x%" PRIx64 "\n",
|
|
preamble->bootloader_size);
|
|
|
|
if (VbGetKernelVmlinuzHeader(preamble,
|
|
&vmlinuz_header_address,
|
|
&vmlinuz_header_size)
|
|
!= VBOOT_SUCCESS) {
|
|
fprintf(stderr, "Unable to retrieve Vmlinuz Header!");
|
|
return 1;
|
|
}
|
|
if (vmlinuz_header_size) {
|
|
printf(" Vmlinuz_header address: 0x%" PRIx64 "\n",
|
|
vmlinuz_header_address);
|
|
printf(" Vmlinuz header size: 0x%" PRIx64 "\n",
|
|
vmlinuz_header_size);
|
|
}
|
|
|
|
if (VbKernelHasFlags(preamble) == VBOOT_SUCCESS)
|
|
flags = preamble->flags;
|
|
printf(" Flags: 0x%" PRIx32 "\n", flags);
|
|
|
|
/* Verify kernel body */
|
|
if (option.fv) {
|
|
/* It's in a separate file, which we've already read in */
|
|
kernel_blob = option.fv;
|
|
kernel_size = option.fv_size;
|
|
} else if (state->my_area->len > option.padding) {
|
|
/* It should be at an offset within the input file. */
|
|
kernel_blob = state->my_area->buf + option.padding;
|
|
kernel_size = state->my_area->len - option.padding;
|
|
}
|
|
|
|
if (!kernel_blob) {
|
|
/* TODO: Is this always a failure? The preamble is okay. */
|
|
fprintf(stderr, "No kernel blob available to verify.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (0 != VerifyData(kernel_blob, kernel_size,
|
|
&preamble->body_signature, rsa)) {
|
|
fprintf(stderr, "Error verifying kernel body.\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("Body verification succeeded.\n");
|
|
|
|
printf("Config:\n%s\n", kernel_blob + KernelCmdLineOffset(preamble));
|
|
|
|
return retval;
|
|
}
|
|
|
|
int futil_cb_show_begin(struct futil_traverse_state_s *state)
|
|
{
|
|
switch (state->in_type) {
|
|
case FILE_TYPE_UNKNOWN:
|
|
fprintf(stderr, "Unable to determine type of %s\n",
|
|
state->in_filename);
|
|
return 1;
|
|
|
|
case FILE_TYPE_BIOS_IMAGE:
|
|
case FILE_TYPE_OLD_BIOS_IMAGE:
|
|
printf("BIOS: %s\n", state->in_filename);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
enum no_short_opts {
|
|
OPT_PADDING = 1000,
|
|
};
|
|
|
|
static const char usage[] = "\n"
|
|
"Usage: " MYNAME " %s [OPTIONS] FILE [...]\n"
|
|
"\n"
|
|
"Where FILE could be a\n"
|
|
"\n"
|
|
"%s"
|
|
" keyblock (.keyblock)\n"
|
|
" firmware preamble signature (VBLOCK_A/B)\n"
|
|
" firmware image (bios.bin)\n"
|
|
" kernel partition (/dev/sda2, /dev/mmcblk0p2)\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -t Just show the type of each file\n"
|
|
" -k|--publickey FILE"
|
|
" Use this public key for validation\n"
|
|
" -f|--fv FILE Verify this payload (FW_MAIN_A/B)\n"
|
|
" --pad NUM Kernel vblock padding size\n"
|
|
"%s"
|
|
"\n";
|
|
|
|
static void print_help(const char *prog)
|
|
{
|
|
if (strcmp(prog, "verify"))
|
|
printf(usage, prog,
|
|
" public key (.vbpubk)\n",
|
|
" --strict "
|
|
"Fail unless all signatures are valid\n");
|
|
else
|
|
printf(usage, prog, "",
|
|
"\nIt will fail unless all signatures are valid\n");
|
|
}
|
|
|
|
static const struct option long_opts[] = {
|
|
/* name hasarg *flag val */
|
|
{"publickey", 1, 0, 'k'},
|
|
{"fv", 1, 0, 'f'},
|
|
{"pad", 1, NULL, OPT_PADDING},
|
|
{"verify", 0, &option.strict, 1},
|
|
{"debug", 0, &debugging_enabled, 1},
|
|
{NULL, 0, NULL, 0},
|
|
};
|
|
static char *short_opts = ":f:k:t";
|
|
|
|
|
|
static void show_type(char *filename)
|
|
{
|
|
enum futil_file_err err;
|
|
enum futil_file_type type;
|
|
err = futil_file_type(filename, &type);
|
|
switch (err) {
|
|
case FILE_ERR_NONE:
|
|
printf("%s:\t%s\n", filename, futil_file_type_str(type));
|
|
break;
|
|
case FILE_ERR_DIR:
|
|
printf("%s:\t%s\n", filename, "directory");
|
|
break;
|
|
case FILE_ERR_CHR:
|
|
printf("%s:\t%s\n", filename, "character special");
|
|
break;
|
|
case FILE_ERR_FIFO:
|
|
printf("%s:\t%s\n", filename, "FIFO");
|
|
break;
|
|
case FILE_ERR_SOCK:
|
|
printf("%s:\t%s\n", filename, "socket");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int do_show(int argc, char *argv[])
|
|
{
|
|
char *infile = 0;
|
|
int ifd, i;
|
|
int errorcnt = 0;
|
|
struct futil_traverse_state_s state;
|
|
uint8_t *buf;
|
|
uint32_t buf_len;
|
|
char *e = 0;
|
|
|
|
opterr = 0; /* quiet, you */
|
|
while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
|
|
switch (i) {
|
|
case 'f':
|
|
option.fv = ReadFile(optarg, &option.fv_size);
|
|
if (!option.fv) {
|
|
fprintf(stderr, "Error reading %s: %s\n",
|
|
optarg, strerror(errno));
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
case 'k':
|
|
option.k = PublicKeyRead(optarg);
|
|
if (!option.k) {
|
|
fprintf(stderr, "Error reading %s\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
case 't':
|
|
option.t_flag = 1;
|
|
break;
|
|
case OPT_PADDING:
|
|
option.padding = strtoul(optarg, &e, 0);
|
|
if (!*optarg || (e && *e)) {
|
|
fprintf(stderr,
|
|
"Invalid --padding \"%s\"\n", optarg);
|
|
errorcnt++;
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
if (optopt)
|
|
fprintf(stderr, "Unrecognized option: -%c\n",
|
|
optopt);
|
|
else
|
|
fprintf(stderr, "Unrecognized option\n");
|
|
errorcnt++;
|
|
break;
|
|
case ':':
|
|
fprintf(stderr, "Missing argument to -%c\n", optopt);
|
|
errorcnt++;
|
|
break;
|
|
case 0: /* handled option */
|
|
break;
|
|
default:
|
|
DIE;
|
|
}
|
|
}
|
|
|
|
if (errorcnt) {
|
|
print_help(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (argc - optind < 1) {
|
|
fprintf(stderr, "ERROR: missing input filename\n");
|
|
print_help(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (option.t_flag) {
|
|
for (i = optind; i < argc; i++)
|
|
show_type(argv[i]);
|
|
goto done;
|
|
}
|
|
|
|
for (i = optind; i < argc; i++) {
|
|
infile = argv[i];
|
|
ifd = open(infile, O_RDONLY);
|
|
if (ifd < 0) {
|
|
errorcnt++;
|
|
fprintf(stderr, "Can't open %s: %s\n",
|
|
infile, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
if (0 != futil_map_file(ifd, MAP_RO, &buf, &buf_len)) {
|
|
errorcnt++;
|
|
goto boo;
|
|
}
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
state.in_filename = infile ? infile : "<none>";
|
|
state.op = FUTIL_OP_SHOW;
|
|
|
|
errorcnt += futil_traverse(buf, buf_len, &state,
|
|
FILE_TYPE_UNKNOWN);
|
|
|
|
|
|
errorcnt += futil_unmap_file(ifd, MAP_RO, buf, buf_len);
|
|
|
|
boo:
|
|
if (close(ifd)) {
|
|
errorcnt++;
|
|
fprintf(stderr, "Error when closing %s: %s\n",
|
|
infile, strerror(errno));
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (option.k)
|
|
free(option.k);
|
|
if (option.fv)
|
|
free(option.fv);
|
|
|
|
return !!errorcnt;
|
|
}
|
|
|
|
DECLARE_FUTIL_COMMAND(show, do_show,
|
|
VBOOT_VERSION_ALL,
|
|
"Display the content of various binary components",
|
|
print_help);
|
|
|
|
static int do_verify(int argc, char *argv[])
|
|
{
|
|
option.strict = 1;
|
|
return do_show(argc, argv);
|
|
}
|
|
|
|
DECLARE_FUTIL_COMMAND(verify, do_verify,
|
|
VBOOT_VERSION_ALL,
|
|
"Verify the signatures of various binary components",
|
|
print_help);
|