mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
BUG=none BRANCH=ToT TEST=make runtests This also modifies the tests to compare the futility sign command results against the vbutil_kernel results. Signed-off-by: Bill Richardson <wfrichar@chromium.org> Change-Id: Ibc659f134cc83982e3f0c0bcc108cc0eddbe228e Reviewed-on: https://chromium-review.googlesource.com/219730 Reviewed-by: Randall Spangler <rspangler@chromium.org>
361 lines
9.7 KiB
C
361 lines
9.7 KiB
C
/*
|
|
* 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "fmap.h"
|
|
#include "futility.h"
|
|
#include "gbb_header.h"
|
|
#include "host_common.h"
|
|
#include "host_key.h"
|
|
#include "traversal.h"
|
|
|
|
/* What functions do we invoke for a particular operation and component? */
|
|
|
|
/* FUTIL_OP_SHOW */
|
|
static int (* const cb_show_funcs[])(struct futil_traverse_state_s *state) = {
|
|
futil_cb_show_begin, /* CB_BEGIN_TRAVERSAL */
|
|
NULL, /* CB_END_TRAVERSAL */
|
|
futil_cb_show_gbb, /* CB_FMAP_GBB */
|
|
futil_cb_show_fw_preamble, /* CB_FMAP_VBLOCK_A */
|
|
futil_cb_show_fw_preamble, /* CB_FMAP_VBLOCK_B */
|
|
futil_cb_show_fw_main, /* CB_FMAP_FW_MAIN_A */
|
|
futil_cb_show_fw_main, /* CB_FMAP_FW_MAIN_B */
|
|
futil_cb_show_key, /* CB_PUBKEY */
|
|
futil_cb_show_keyblock, /* CB_KEYBLOCK */
|
|
futil_cb_show_gbb, /* CB_GBB */
|
|
futil_cb_show_fw_preamble, /* CB_FW_PREAMBLE */
|
|
futil_cb_show_kernel_preamble, /* CB_KERN_PREAMBLE */
|
|
NULL, /* CB_RAW_FIRMWARE */
|
|
NULL, /* CB_RAW_KERNEL */
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(cb_show_funcs) == NUM_CB_COMPONENTS);
|
|
|
|
/* FUTIL_OP_SIGN */
|
|
static int (* const cb_sign_funcs[])(struct futil_traverse_state_s *state) = {
|
|
futil_cb_sign_begin, /* CB_BEGIN_TRAVERSAL */
|
|
futil_cb_sign_end, /* CB_END_TRAVERSAL */
|
|
NULL, /* CB_FMAP_GBB */
|
|
futil_cb_sign_fw_vblock, /* CB_FMAP_VBLOCK_A */
|
|
futil_cb_sign_fw_vblock, /* CB_FMAP_VBLOCK_B */
|
|
futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_A */
|
|
futil_cb_sign_fw_main, /* CB_FMAP_FW_MAIN_B */
|
|
futil_cb_sign_pubkey, /* CB_PUBKEY */
|
|
NULL, /* CB_KEYBLOCK */
|
|
NULL, /* CB_GBB */
|
|
NULL, /* CB_FW_PREAMBLE */
|
|
futil_cb_resign_kernel_part, /* CB_KERN_PREAMBLE */
|
|
futil_cb_sign_raw_firmware, /* CB_RAW_FIRMWARE */
|
|
futil_cb_create_kernel_part, /* CB_RAW_KERNEL */
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(cb_sign_funcs) == NUM_CB_COMPONENTS);
|
|
|
|
static int (* const * const cb_func[])(struct futil_traverse_state_s *state) = {
|
|
cb_show_funcs,
|
|
cb_sign_funcs,
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(cb_func) == NUM_FUTIL_OPS);
|
|
|
|
/*
|
|
* File types that don't need iterating can use a lookup table to determine the
|
|
* callback component and name. The index is the file type.
|
|
*/
|
|
static const struct {
|
|
enum futil_cb_component component;
|
|
const char * const name;
|
|
} direct_callback[] = {
|
|
{0, NULL}, /* FILE_TYPE_UNKNOWN */
|
|
{CB_PUBKEY, "VbPublicKey"}, /* FILE_TYPE_PUBKEY */
|
|
{CB_KEYBLOCK, "VbKeyBlock"}, /* FILE_TYPE_KEYBLOCK */
|
|
{CB_FW_PREAMBLE, "FW Preamble"}, /* FILE_TYPE_FW_PREAMBLE */
|
|
{CB_GBB, "GBB"}, /* FILE_TYPE_GBB */
|
|
{0, NULL}, /* FILE_TYPE_BIOS_IMAGE */
|
|
{0, NULL}, /* FILE_TYPE_OLD_BIOS_IMAGE */
|
|
{CB_KERN_PREAMBLE, "Kernel Preamble"}, /* FILE_TYPE_KERN_PREAMBLE */
|
|
{CB_RAW_FIRMWARE, "raw firmware"}, /* FILE_TYPE_RAW_FIRMWARE */
|
|
{CB_RAW_KERNEL, "raw kernel"}, /* FILE_TYPE_RAW_KERNEL */
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(direct_callback) == NUM_FILE_TYPES);
|
|
|
|
/*
|
|
* The Chrome OS BIOS must contain specific FMAP areas, and we generally want
|
|
* to look at each one in a certain order.
|
|
*/
|
|
struct bios_area_s {
|
|
const char * const name;
|
|
enum futil_cb_component component;
|
|
};
|
|
|
|
/* This are the expected areas, in order of traversal. */
|
|
static const struct bios_area_s bios_area[] = {
|
|
{"GBB", CB_FMAP_GBB},
|
|
{"FW_MAIN_A", CB_FMAP_FW_MAIN_A},
|
|
{"FW_MAIN_B", CB_FMAP_FW_MAIN_B},
|
|
{"VBLOCK_A", CB_FMAP_VBLOCK_A},
|
|
{"VBLOCK_B", CB_FMAP_VBLOCK_B},
|
|
{0, 0}
|
|
};
|
|
|
|
/* Really old BIOS images had different names, but worked the same. */
|
|
static const struct bios_area_s old_bios_area[] = {
|
|
{"GBB Area", CB_FMAP_GBB},
|
|
{"Firmware A Data", CB_FMAP_FW_MAIN_A},
|
|
{"Firmware B Data", CB_FMAP_FW_MAIN_B},
|
|
{"Firmware A Key", CB_FMAP_VBLOCK_A},
|
|
{"Firmware B Key", CB_FMAP_VBLOCK_B},
|
|
{0, 0}
|
|
};
|
|
|
|
|
|
static int has_all_areas(uint8_t *buf, uint32_t len, FmapHeader *fmap,
|
|
const struct bios_area_s *area)
|
|
{
|
|
/* We must have all the expected areas */
|
|
for (; area->name; area++)
|
|
if (!fmap_find_by_name(buf, len, fmap, area->name, 0))
|
|
return 0;
|
|
|
|
/* Found 'em all */
|
|
return 1;
|
|
}
|
|
|
|
const char * const futil_file_type_str[] = {
|
|
"FILE_TYPE_UNKNOWN",
|
|
"FILE_TYPE_PUBKEY",
|
|
"FILE_TYPE_KEYBLOCK",
|
|
"FILE_TYPE_FW_PREAMBLE",
|
|
"FILE_TYPE_GBB",
|
|
"FILE_TYPE_BIOS_IMAGE",
|
|
"FILE_TYPE_OLD_BIOS_IMAGE",
|
|
"FILE_TYPE_KERN_PREAMBLE",
|
|
"FILE_TYPE_RAW_FIRMWARE",
|
|
"FILE_TYPE_RAW_KERNEL",
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(futil_file_type_str) == NUM_FILE_TYPES);
|
|
|
|
const char * const futil_cb_component_str[] = {
|
|
"CB_BEGIN_TRAVERSAL",
|
|
"CB_END_TRAVERSAL",
|
|
"CB_FMAP_GBB",
|
|
"CB_FMAP_VBLOCK_A",
|
|
"CB_FMAP_VBLOCK_B",
|
|
"CB_FMAP_FW_MAIN_A",
|
|
"CB_FMAP_FW_MAIN_B",
|
|
"CB_PUBKEY",
|
|
"CB_KEYBLOCK",
|
|
"CB_GBB",
|
|
"CB_FW_PREAMBLE",
|
|
"CB_KERN_PREAMBLE",
|
|
"CB_RAW_FIRMWARE",
|
|
"CB_RAW_KERNEL",
|
|
};
|
|
BUILD_ASSERT(ARRAY_SIZE(futil_cb_component_str) == NUM_CB_COMPONENTS);
|
|
|
|
|
|
static int invoke_callback(struct futil_traverse_state_s *state,
|
|
enum futil_cb_component c, const char *name,
|
|
uint32_t offset, uint8_t *buf, uint32_t len)
|
|
{
|
|
Debug("%s: name \"%s\" op %d component %s"
|
|
" offset=0x%08x len=0x%08x, buf=%p\n",
|
|
__func__, name, state->op, futil_cb_component_str[c],
|
|
offset, len, buf);
|
|
|
|
if (c < 0 || c >= NUM_CB_COMPONENTS) {
|
|
fprintf(stderr, "Invalid component %d\n", c);
|
|
return 1;
|
|
}
|
|
|
|
state->component = c;
|
|
state->name = name;
|
|
state->cb_area[c].offset = offset;
|
|
state->cb_area[c].buf = buf;
|
|
state->cb_area[c].len = len;
|
|
state->my_area = &state->cb_area[c];
|
|
|
|
if (cb_func[state->op][c])
|
|
return cb_func[state->op][c](state);
|
|
else
|
|
Debug("<no callback registered>\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum futil_file_type futil_what_file_type_buf(uint8_t *buf, uint32_t len)
|
|
{
|
|
VbPublicKey *pubkey = (VbPublicKey *)buf;
|
|
VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf;
|
|
GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf;
|
|
VbFirmwarePreambleHeader *fw_preamble;
|
|
VbKernelPreambleHeader *kern_preamble;
|
|
RSAPublicKey *rsa;
|
|
FmapHeader *fmap;
|
|
|
|
/*
|
|
* Complex structs may begin with simpler structs first, so try them
|
|
* in reverse order.
|
|
*/
|
|
|
|
fmap = fmap_find(buf, len);
|
|
if (fmap) {
|
|
if (has_all_areas(buf, len, fmap, bios_area))
|
|
return FILE_TYPE_BIOS_IMAGE;
|
|
if (has_all_areas(buf, len, fmap, old_bios_area))
|
|
return FILE_TYPE_OLD_BIOS_IMAGE;
|
|
}
|
|
|
|
if (futil_looks_like_gbb(gbb, len))
|
|
return FILE_TYPE_GBB;
|
|
|
|
if (VBOOT_SUCCESS == KeyBlockVerify(key_block, len, NULL, 1)) {
|
|
rsa = PublicKeyToRSA(&key_block->data_key);
|
|
uint32_t more = key_block->key_block_size;
|
|
|
|
/* and firmware preamble too? */
|
|
fw_preamble = (VbFirmwarePreambleHeader *)(buf + more);
|
|
if (VBOOT_SUCCESS ==
|
|
VerifyFirmwarePreamble(fw_preamble, len - more, rsa))
|
|
return FILE_TYPE_FW_PREAMBLE;
|
|
|
|
/* or maybe kernel preamble? */
|
|
kern_preamble = (VbKernelPreambleHeader *)(buf + more);
|
|
if (VBOOT_SUCCESS ==
|
|
VerifyKernelPreamble(kern_preamble, len - more, rsa))
|
|
return FILE_TYPE_KERN_PREAMBLE;
|
|
|
|
/* no, just keyblock */
|
|
return FILE_TYPE_KEYBLOCK;
|
|
}
|
|
|
|
if (PublicKeyLooksOkay(pubkey, len))
|
|
return FILE_TYPE_PUBKEY;
|
|
|
|
return FILE_TYPE_UNKNOWN;
|
|
}
|
|
|
|
enum futil_file_type futil_what_file_type(const char *filename)
|
|
{
|
|
enum futil_file_type type;
|
|
int ifd;
|
|
uint8_t *buf;
|
|
uint32_t buf_len;
|
|
|
|
ifd = open(filename, O_RDONLY);
|
|
if (ifd < 0) {
|
|
fprintf(stderr, "Can't open %s: %s\n",
|
|
filename, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
if (0 != futil_map_file(ifd, MAP_RO, &buf, &buf_len)) {
|
|
close(ifd);
|
|
exit(1);
|
|
}
|
|
|
|
type = futil_what_file_type_buf(buf, buf_len);
|
|
|
|
if (0 != futil_unmap_file(ifd, MAP_RO, buf, buf_len)) {
|
|
close(ifd);
|
|
exit(1);
|
|
}
|
|
|
|
if (close(ifd)) {
|
|
fprintf(stderr, "Error when closing %s: %s\n",
|
|
filename, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
int futil_traverse(uint8_t *buf, uint32_t len,
|
|
struct futil_traverse_state_s *state,
|
|
enum futil_file_type type)
|
|
{
|
|
FmapHeader *fmap;
|
|
FmapAreaHeader *ah = 0;
|
|
const struct bios_area_s *area;
|
|
int retval = 0;
|
|
|
|
if (state->op < 0 || state->op >= NUM_FUTIL_OPS) {
|
|
fprintf(stderr, "Invalid op %d\n", state->op);
|
|
return 1;
|
|
}
|
|
|
|
if (type == FILE_TYPE_UNKNOWN)
|
|
type = futil_what_file_type_buf(buf, len);
|
|
state->in_type = type;
|
|
|
|
state->errors = retval;
|
|
retval |= invoke_callback(state, CB_BEGIN_TRAVERSAL, "<begin>",
|
|
0, buf, len);
|
|
state->errors = retval;
|
|
|
|
switch (type) {
|
|
case FILE_TYPE_PUBKEY:
|
|
case FILE_TYPE_KEYBLOCK:
|
|
case FILE_TYPE_FW_PREAMBLE:
|
|
case FILE_TYPE_GBB:
|
|
case FILE_TYPE_KERN_PREAMBLE:
|
|
case FILE_TYPE_RAW_FIRMWARE:
|
|
case FILE_TYPE_RAW_KERNEL:
|
|
retval |= invoke_callback(state,
|
|
direct_callback[type].component,
|
|
direct_callback[type].name,
|
|
0, buf, len);
|
|
state->errors = retval;
|
|
break;
|
|
|
|
case FILE_TYPE_BIOS_IMAGE:
|
|
/* We've already checked, so we know this will work. */
|
|
fmap = fmap_find(buf, len);
|
|
for (area = bios_area; area->name; area++) {
|
|
/* We know this will work, too */
|
|
fmap_find_by_name(buf, len, fmap, area->name, &ah);
|
|
retval |= invoke_callback(state,
|
|
area->component,
|
|
area->name,
|
|
ah->area_offset,
|
|
buf + ah->area_offset,
|
|
ah->area_size);
|
|
state->errors = retval;
|
|
}
|
|
break;
|
|
|
|
case FILE_TYPE_OLD_BIOS_IMAGE:
|
|
/* We've already checked, so we know this will work. */
|
|
fmap = fmap_find(buf, len);
|
|
for (area = old_bios_area; area->name; area++) {
|
|
/* We know this will work, too */
|
|
fmap_find_by_name(buf, len, fmap, area->name, &ah);
|
|
retval |= invoke_callback(state,
|
|
area->component,
|
|
area->name,
|
|
ah->area_offset,
|
|
buf + ah->area_offset,
|
|
ah->area_size);
|
|
state->errors = retval;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Debug("%s:%d unhandled type %s\n", __FILE__, __LINE__,
|
|
futil_file_type_str[type]);
|
|
retval = 1;
|
|
}
|
|
|
|
retval |= invoke_callback(state, CB_END_TRAVERSAL, "<end>",
|
|
0, buf, len);
|
|
return retval;
|
|
}
|