| /* |
| * 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); |