diff options
author | Bill Richardson <wfrichar@chromium.org> | 2015-03-26 15:31:54 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-03-31 06:28:09 +0000 |
commit | 6ea2f72d0356a73915b736a5ef3498ac06a50770 (patch) | |
tree | 32934d70b776950a78511e965d1b672f8f7b1c7b | |
parent | 35c69cc159af325261a1365cce28f6ecf106307f (diff) | |
download | vboot-6ea2f72d0356a73915b736a5ef3498ac06a50770.tar.gz |
futility: refactor to handle each file type individually
Instead of calling futil_traverse() to iterate through a preset
list of functions, this establishes a separate show() and sign()
function for each type of file.
The only significant change is that walking through the FMAP areas
within BIOS images is done in BIOS-specific functions instead of
that being the "normal" case with every other type of file
skipping that traversal.
This is a refactoring only. There is no externally visible change.
BUG=chromium:231574
BRANCH=none
TEST=make runtests
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Change-Id: I7080afb43e35308c8bb618793c8382c2efb3d6a1
Reviewed-on: https://chromium-review.googlesource.com/262894
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | futility/cmd_show.c | 325 | ||||
-rw-r--r-- | futility/cmd_sign.c | 244 | ||||
-rw-r--r-- | futility/file_type.c | 25 | ||||
-rw-r--r-- | futility/file_type.h | 18 | ||||
-rw-r--r-- | futility/file_type.inc | 70 | ||||
-rw-r--r-- | futility/traversal.c | 296 | ||||
-rw-r--r-- | futility/traversal.h | 111 | ||||
-rw-r--r-- | futility/vb2_helper.c | 21 |
8 files changed, 466 insertions, 644 deletions
diff --git a/futility/cmd_show.c b/futility/cmd_show.c index 4853ad43..661aef2b 100644 --- a/futility/cmd_show.c +++ b/futility/cmd_show.c @@ -31,11 +31,6 @@ #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; @@ -48,6 +43,41 @@ static struct local_data_s { .padding = 65536, }; +/* Stuff for BIOS images. */ + +/* Forward declarations */ +static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len, + void *data); + +/* These are the functions we'll call for each FMAP area. */ +static int (*fmap_func[])(const char *name, uint8_t *buf, uint32_t len, + void *data) = { + ft_show_gbb, + fmap_fw_main, + fmap_fw_main, + ft_show_fw_preamble, + ft_show_fw_preamble, +}; +BUILD_ASSERT(ARRAY_SIZE(fmap_func) == NUM_BIOS_COMPONENTS); + +/* Where is the component we're looking at? */ +struct bios_area_s { + uint32_t offset; /* to avoid pointer math */ + uint8_t *buf; + uint32_t len; + uint32_t is_valid; +}; + +/* When looking at the FMAP areas, we need to gather some state for later. */ +struct show_state_s { + /* Current component */ + enum bios_component c; + /* Other activites, possibly before or after the current one */ + struct bios_area_s area[NUM_BIOS_COMPONENTS]; + struct bios_area_s recovery_key; + struct bios_area_s rootkey; +}; + static void show_key(VbPublicKey *pubkey, const char *sp) { printf("%sVboot API: 1.0\n", sp); @@ -94,34 +124,37 @@ static void show_keyblock(VbKeyBlockHeader *key_block, const char *name, printf("\n"); } -int futil_cb_show_pubkey(struct futil_traverse_state_s *state) +int ft_show_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data) { - VbPublicKey *pubkey = (VbPublicKey *)state->my_area->buf; + VbPublicKey *pubkey = (VbPublicKey *)buf; - if (!PublicKeyLooksOkay(pubkey, state->my_area->len)) { - printf("%s looks bogus\n", state->name); + if (!PublicKeyLooksOkay(pubkey, len)) { + printf("%s looks bogus\n", name); return 1; } - printf("Public Key file: %s\n", state->in_filename); + printf("Public Key file: %s\n", name); show_key(pubkey, " "); - state->my_area->_flags |= AREA_IS_VALID; return 0; } -int futil_cb_show_privkey(struct futil_traverse_state_s *state) +int ft_show_privkey(const char *name, uint8_t *buf, uint32_t len, void *data) { VbPrivateKey key; const unsigned char *start; - int len, alg_okay; + int alg_okay; - key.algorithm = *(typeof(key.algorithm) *)state->my_area->buf; - start = state->my_area->buf + sizeof(key.algorithm); - len = state->my_area->len - sizeof(key.algorithm); + key.algorithm = *(typeof(key.algorithm) *)buf; + start = buf + sizeof(key.algorithm); + if (len <= sizeof(key.algorithm)) { + printf("%s looks bogus\n", name); + return 1; + } + len -= sizeof(key.algorithm); key.rsa_private_key = d2i_RSAPrivateKey(NULL, &start, len); - printf("Private Key file: %s\n", state->in_filename); + printf("Private Key file: %s\n", name); printf(" Vboot API: 1.0\n"); alg_okay = key.algorithm < kNumAlgorithms; printf(" Algorithm: %" PRIu64 " %s\n", key.algorithm, @@ -135,26 +168,20 @@ int futil_cb_show_privkey(struct futil_traverse_state_s *state) } printf("\n"); - if (alg_okay) - state->my_area->_flags |= AREA_IS_VALID; - return 0; } -int futil_cb_show_gbb(struct futil_traverse_state_s *state) +int ft_show_gbb(const char *name, uint8_t *buf, uint32_t len, void *data) { - uint8_t *buf = state->my_area->buf; - uint32_t len = state->my_area->len; GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)buf; + struct show_state_s *state = (struct show_state_s *)data; 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); + printf("GBB header: %s <invalid>\n", name); return 1; } @@ -162,8 +189,7 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state) 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("GBB header: %s\n", name); printf(" Version: %d.%d\n", gbb->major_version, gbb->minor_version); printf(" Flags: 0x%08x\n", gbb->flags); @@ -191,11 +217,14 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state) 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; + if (state) { + state->rootkey.offset = + state->area[BIOS_FMAP_GBB].offset + + gbb->rootkey_offset; + state->rootkey.buf = buf + gbb->rootkey_offset; + state->rootkey.len = gbb->rootkey_size; + state->rootkey.is_valid = 1; + } printf(" Root Key:\n"); show_key(pubkey, " "); } else { @@ -205,11 +234,15 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state) 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; + if (state) { + state->recovery_key.offset = + state->area[BIOS_FMAP_GBB].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.is_valid = 1; + } printf(" Recovery Key:\n"); show_key(pubkey, " "); } else { @@ -234,36 +267,34 @@ int futil_cb_show_gbb(struct futil_traverse_state_s *state) bmp->number_of_imageinfos); } - if (!retval) - state->my_area->_flags |= AREA_IS_VALID; + if (!retval && state) + state->area[BIOS_FMAP_GBB].is_valid = 1; return retval; } -int futil_cb_show_keyblock(struct futil_traverse_state_s *state) +int ft_show_keyblock(const char *name, uint8_t *buf, uint32_t len, void *data) { - VbKeyBlockHeader *block = (VbKeyBlockHeader *)state->my_area->buf; + VbKeyBlockHeader *block = (VbKeyBlockHeader *)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); + if (0 != KeyBlockVerify(block, len, NULL, 1)) { + printf("%s is invalid\n", name); return 1; } /* Check the signature if we have one */ if (sign_key && VBOOT_SUCCESS == - KeyBlockVerify(block, state->my_area->len, sign_key, 0)) + KeyBlockVerify(block, 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; + show_keyblock(block, name, !!sign_key, good_sig); return retval; } @@ -271,63 +302,64 @@ int futil_cb_show_keyblock(struct futil_traverse_state_s *state) /* * 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. + * The data 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) +static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len, + void *data) { - if (!state->my_area->len) { - printf("Firmware body: %s <invalid>\n", state->name); + struct show_state_s *state = (struct show_state_s *)data; + + if (!len) { + printf("Firmware body: %s <invalid>\n", 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); + printf("Firmware body: %s\n", name); + printf(" Offset: 0x%08x\n", + state->area[state->c].offset); + printf(" Size: 0x%08x\n", len); - state->my_area->_flags |= AREA_IS_VALID; + state->area[state->c].is_valid = 1; return 0; } -int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) +int ft_show_fw_preamble(const char *name, uint8_t *buf, uint32_t len, + void *data) { - VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; - uint32_t len = state->my_area->len; + VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf; + struct show_state_s *state = (struct show_state_s *)data; 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; + struct bios_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); + printf("%s keyblock component is invalid\n", 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)) + /* + * If we're being invoked while poking through a BIOS, we should + * be given the keys and data to verify as part of the state. If we + * have no state, then we're just looking at a standalone fw_preamble, + * so we'll have to get any keys or data from options. + */ + if (state) { + + if (!sign_key && state->rootkey.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; + /* Identify the firmware body for this VBLOCK */ + enum bios_component body_c = state->c == BIOS_FMAP_VBLOCK_A + ? BIOS_FMAP_FW_MAIN_A + : BIOS_FMAP_FW_MAIN_B; + fw_body_area = &state->area[body_c]; } /* If we have a key, check the signature too */ @@ -335,26 +367,23 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) 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); + show_keyblock(key_block, 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); + fprintf(stderr, "Error parsing data key in %s\n", name); return 1; } uint32_t more = key_block->key_block_size; VbFirmwarePreambleHeader *preamble = - (VbFirmwarePreambleHeader *)(state->my_area->buf + more); + (VbFirmwarePreambleHeader *)(buf + more); if (VBOOT_SUCCESS != VerifyFirmwarePreamble(preamble, len - more, rsa)) { - printf("%s is invalid\n", state->name); + printf("%s is invalid\n", name); return 1; } @@ -390,7 +419,7 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) } /* We'll need to get the firmware body from somewhere... */ - if (fw_body_area && (fw_body_area->_flags & AREA_IS_VALID)) { + if (fw_body_area && fw_body_area->is_valid) { fv_data = fw_body_area->buf; fv_size = fw_body_area->len; } @@ -409,13 +438,13 @@ int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state) } 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)) { + /* Can't trust the BIOS unless everything is signed (in which case + * we've already returned), but standalone files are okay. */ + if (state || (sign_key && good_sig)) { if (!(flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL)) printf("Body verification succeeded.\n"); - state->my_area->_flags |= AREA_IS_VALID; + if (state) + state->area[state->c].is_valid = 1; } else { printf("Seems legit, but the signature is unverified.\n"); if (option.strict) @@ -425,11 +454,59 @@ done: return retval; } -int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) +int ft_show_bios(const char *name, uint8_t *buf, uint32_t len, void *data) { + FmapHeader *fmap; + FmapAreaHeader *ah = 0; + char ah_name[FMAP_NAMELEN + 1]; + int i; + int retval = 0; + struct show_state_s state; + + memset(&state, 0, sizeof(state)); + + printf("BIOS: %s\n", name); + + /* We've already checked, so we know this will work. */ + fmap = fmap_find(buf, len); + for (i = 0; i < NUM_BIOS_COMPONENTS; i++) { + /* We know one of these will work, too */ + if (fmap_find_by_name(buf, len, fmap, + bios_area[i].name, &ah) || + fmap_find_by_name(buf, len, fmap, + bios_area[i].oldname, &ah)) { + /* But the file might be truncated */ + fmap_limit_area(ah, len); + /* The name is not necessarily null-terminated */ + snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name); + + /* Update the state we're passing around */ + state.c = i; + state.area[i].offset = ah->area_offset; + state.area[i].buf = buf + ah->area_offset; + state.area[i].len = ah->area_size; + + Debug("%s() showing FMAP area %d (%s)," + " offset=0x%08x len=0x%08x\n", + __func__, i, ah_name, + ah->area_offset, ah->area_size); + + /* Go look at it. */ + if (fmap_func[i]) + retval += fmap_func[i](ah_name, + state.area[i].buf, + state.area[i].len, + &state); + } + } + + return retval; +} - VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; - uint32_t len = state->my_area->len; +int ft_show_kernel_preamble(const char *name, uint8_t *buf, uint32_t len, + void *data) +{ + VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf; VbPublicKey *sign_key = option.k; uint8_t *kernel_blob = 0; uint64_t kernel_size = 0; @@ -441,7 +518,7 @@ int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) /* Check the hash... */ if (VBOOT_SUCCESS != KeyBlockVerify(key_block, len, NULL, 1)) { - printf("%s keyblock component is invalid\n", state->name); + printf("%s keyblock component is invalid\n", name); return 1; } @@ -450,7 +527,7 @@ int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) KeyBlockVerify(key_block, len, sign_key, 0)) good_sig = 1; - printf("Kernel partition: %s\n", state->in_filename); + printf("Kernel partition: %s\n", name); show_keyblock(key_block, NULL, !!sign_key, good_sig); if (option.strict && (!sign_key || !good_sig)) @@ -458,16 +535,16 @@ int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) RSAPublicKey *rsa = PublicKeyToRSA(&key_block->data_key); if (!rsa) { - fprintf(stderr, "Error parsing data key in %s\n", state->name); + fprintf(stderr, "Error parsing data key in %s\n", name); return 1; } uint32_t more = key_block->key_block_size; VbKernelPreambleHeader *preamble = - (VbKernelPreambleHeader *)(state->my_area->buf + more); + (VbKernelPreambleHeader *)(buf + more); if (VBOOT_SUCCESS != VerifyKernelPreamble(preamble, len - more, rsa)) { - printf("%s is invalid\n", state->name); + printf("%s is invalid\n", name); return 1; } @@ -511,10 +588,10 @@ int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) /* 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) { + } else if (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; + kernel_blob = buf + option.padding; + kernel_size = len - option.padding; } if (!kernel_blob) { @@ -536,25 +613,6 @@ int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state) 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, OPT_HELP, @@ -597,7 +655,7 @@ static const struct option long_opts[] = { {"publickey", 1, 0, 'k'}, {"fv", 1, 0, 'f'}, {"pad", 1, NULL, OPT_PADDING}, - {"verify", 0, &option.strict, 1}, + {"strict", 0, &option.strict, 1}, {"help", 0, NULL, OPT_HELP}, {NULL, 0, NULL, 0}, }; @@ -635,9 +693,9 @@ static int do_show(int argc, char *argv[]) char *infile = 0; int ifd, i; int errorcnt = 0; - struct futil_traverse_state_s state; + enum futil_file_type type; uint8_t *buf; - uint32_t buf_len; + uint32_t len; char *e = 0; opterr = 0; /* quiet, you */ @@ -719,21 +777,16 @@ static int do_show(int argc, char *argv[]) continue; } - if (0 != futil_map_file(ifd, MAP_RO, &buf, &buf_len)) { + if (0 != futil_map_file(ifd, MAP_RO, &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); - + type = futil_file_type_buf(buf, len); - errorcnt += futil_unmap_file(ifd, MAP_RO, buf, buf_len); + errorcnt += futil_file_type_show(type, infile, buf, len); + errorcnt += futil_unmap_file(ifd, MAP_RO, buf, len); boo: if (close(ifd)) { errorcnt++; diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c index 2db53136..cae08b2e 100644 --- a/futility/cmd_sign.c +++ b/futility/cmd_sign.c @@ -29,11 +29,6 @@ #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; @@ -80,10 +75,45 @@ static int no_opt_if(int expr, const char *optname) return 0; } +/* Stuff for BIOS images */ + +/* Forward declarations */ +static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len, + void *data); +static int fmap_fw_preamble(const char *name, uint8_t *buf, uint32_t len, + void *data); + +/* These are the functions we'll call for each FMAP area. */ +static int (*fmap_func[])(const char *name, uint8_t *buf, uint32_t len, + void *data) = { + 0, + fmap_fw_main, + fmap_fw_main, + fmap_fw_preamble, + fmap_fw_preamble, +}; +BUILD_ASSERT(ARRAY_SIZE(fmap_func) == NUM_BIOS_COMPONENTS); + +/* Where is the component we're looking at? */ +struct bios_area_s { + uint8_t *buf; + uint32_t len; + uint32_t is_valid; +}; + +/* When looking at the FMAP areas, we need to gather some state for later. */ +struct sign_state_s { + /* Current component */ + enum bios_component c; + /* Other activites, possibly before or after the current one */ + struct bios_area_s area[NUM_BIOS_COMPONENTS]; +}; + + /* This wraps/signs a public key, producing a keyblock. */ -int futil_cb_sign_pubkey(struct futil_traverse_state_s *state) +int ft_sign_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data) { - VbPublicKey *data_key = (VbPublicKey *)state->my_area->buf; + VbPublicKey *data_key = (VbPublicKey *)buf; VbKeyBlockHeader *vblock; if (option.pem_signpriv) { @@ -119,26 +149,30 @@ int futil_cb_sign_pubkey(struct futil_traverse_state_s *state) } /* - * 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. + * This handles FW_MAIN_A and FW_MAIN_B while signing a BIOS image. The data is + * just the RW firmware blob so there's nothing useful to do with it, but we'll + * mark it as valid so that we'll know that this FMAP area exists and can + * be signed. */ -int futil_cb_sign_fw_main(struct futil_traverse_state_s *state) +static int fmap_fw_main(const char *name, uint8_t *buf, uint32_t len, + void *data) { - state->my_area->_flags |= AREA_IS_VALID; + struct sign_state_s *state = (struct sign_state_s *)data; + state->area[state->c].is_valid = 1; 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. + * 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 existing FMAP area contains + * a firmware preamble so we can preserve its contents. We do the signing once + * we've looked over all the components. */ -int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state) +static int fmap_fw_preamble(const char *name, uint8_t *buf, uint32_t len, + void *data) { - VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)state->my_area->buf; - uint32_t len = state->my_area->len; + VbKeyBlockHeader *key_block = (VbKeyBlockHeader *)buf; + struct sign_state_s *state = (struct sign_state_s *)data; /* * If we have a valid keyblock and fw_preamble, then we can use them to @@ -147,33 +181,31 @@ int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state) */ 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); + "Signing the entire FW FMAP region...\n", 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); + "Signing the entire FW FMAP region...\n", name); goto whatever; } uint32_t more = key_block->key_block_size; VbFirmwarePreambleHeader *preamble = - (VbFirmwarePreambleHeader *)(state->my_area->buf + more); + (VbFirmwarePreambleHeader *)(buf + more); uint32_t fw_size = preamble->body_signature.data_size; - struct cb_area_s *fw_body_area = 0; + struct bios_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]; + switch (state->c) { + case BIOS_FMAP_VBLOCK_A: + fw_body_area = &state->area[BIOS_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]; + case BIOS_FMAP_VBLOCK_B: + fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B]; break; default: DIE; @@ -182,7 +214,7 @@ int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state) if (fw_size > fw_body_area->len) { fprintf(stderr, "%s says the firmware is larger than we have\n", - state->name); + name); return 1; } @@ -190,19 +222,20 @@ int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state) fw_body_area->len = fw_size; whatever: - state->my_area->_flags |= AREA_IS_VALID; + state->area[state->c].is_valid = 1; return 0; } -int futil_cb_create_kernel_part(struct futil_traverse_state_s *state) +int ft_sign_raw_kernel(const char *name, uint8_t *buf, uint32_t len, + void *data) { 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; + vmlinuz_data = buf; + vmlinuz_size = len; kblob_data = CreateKernelBlob( vmlinuz_data, vmlinuz_size, @@ -246,7 +279,8 @@ int futil_cb_create_kernel_part(struct futil_traverse_state_s *state) return rv; } -int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state) +int ft_sign_kern_preamble(const char *name, uint8_t *buf, uint32_t len, + void *data) { uint8_t *kpart_data, *kblob_data, *vblock_data; uint64_t kpart_size, kblob_size, vblock_size; @@ -254,8 +288,8 @@ int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state) VbKernelPreambleHeader *preamble = NULL; int rv = 0; - kpart_data = state->my_area->buf; - kpart_size = state->my_area->len; + kpart_data = buf; + kpart_size = len; /* Note: This just sets some static pointers. It doesn't malloc. */ kblob_data = UnpackKPart(kpart_data, kpart_size, option.padding, @@ -331,14 +365,14 @@ int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state) } -int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state) +int ft_sign_raw_firmware(const char *name, uint8_t *buf, uint32_t len, + void *data) { VbSignature *body_sig; VbFirmwarePreambleHeader *preamble; int rv; - body_sig = CalculateSignature(state->my_area->buf, state->my_area->len, - option.signprivate); + body_sig = CalculateSignature(buf, len, option.signprivate); if (!body_sig) { fprintf(stderr, "Error calculating body signature\n"); return 1; @@ -366,19 +400,8 @@ int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state) } -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, +static int write_new_preamble(struct bios_area_s *vblock, + struct bios_area_s *fw_body, VbPrivateKey *signkey, VbKeyBlockHeader *keyblock) { @@ -414,7 +437,7 @@ static int write_new_preamble(struct cb_area_s *vblock, return 0; } -static int write_loem(const char *ab, struct cb_area_s *vblock) +static int write_loem(const char *ab, struct bios_area_s *vblock) { char filename[PATH_MAX]; int n; @@ -449,19 +472,16 @@ static int write_loem(const char *ab, struct cb_area_s *vblock) } /* This signs a full BIOS image after it's been traversed. */ -static int sign_bios_at_end(struct futil_traverse_state_s *state) +static int sign_bios_at_end(struct sign_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]; + struct bios_area_s *vblock_a = &state->area[BIOS_FMAP_VBLOCK_A]; + struct bios_area_s *vblock_b = &state->area[BIOS_FMAP_VBLOCK_B]; + struct bios_area_s *fw_a = &state->area[BIOS_FMAP_FW_MAIN_A]; + struct bios_area_s *fw_b = &state->area[BIOS_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)) { + if (!vblock_a->is_valid || !vblock_b->is_valid || + !fw_a->is_valid || !fw_b->is_valid) { fprintf(stderr, "Something's wrong. Not changing anything\n"); return 1; } @@ -500,19 +520,53 @@ static int sign_bios_at_end(struct futil_traverse_state_s *state) return retval; } -int futil_cb_sign_end(struct futil_traverse_state_s *state) + +int ft_sign_bios(const char *name, uint8_t *buf, uint32_t len, void *data) { - switch (state->in_type) { - case FILE_TYPE_BIOS_IMAGE: - case FILE_TYPE_OLD_BIOS_IMAGE: - return sign_bios_at_end(state); + FmapHeader *fmap; + FmapAreaHeader *ah = 0; + char ah_name[FMAP_NAMELEN + 1]; + int i; + int retval = 0; + struct sign_state_s state; - default: - /* Any other cleanup needed? */ - break; + memset(&state, 0, sizeof(state)); + + /* We've already checked, so we know this will work. */ + fmap = fmap_find(buf, len); + for (i = 0; i < NUM_BIOS_COMPONENTS; i++) { + /* We know one of these will work, too */ + if (fmap_find_by_name(buf, len, fmap, + bios_area[i].name, &ah) || + fmap_find_by_name(buf, len, fmap, + bios_area[i].oldname, &ah)) { + /* But the file might be truncated */ + fmap_limit_area(ah, len); + /* The name is not necessarily null-terminated */ + snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name); + + /* Update the state we're passing around */ + state.c = i; + state.area[i].buf = buf + ah->area_offset; + state.area[i].len = ah->area_size; + + Debug("%s() examining FMAP area %d (%s)," + " offset=0x%08x len=0x%08x\n", + __func__, i, ah_name, + ah->area_offset, ah->area_size); + + /* Go look at it, but abort on error */ + if (fmap_func[i]) + retval += fmap_func[i](ah_name, + state.area[i].buf, + state.area[i].len, + &state); + } } - return state->errors; + retval += sign_bios_at_end(&state); + + return retval; } static const char usage_pubkey[] = "\n" @@ -684,7 +738,7 @@ static void print_help(int argc, char *argv[]) enum no_short_opts { OPT_FV = 1000, - OPT_INFILE, /* aka "--vmlinuz" */ + OPT_INFILE, OPT_OUTFILE, OPT_BOOTLOADER, OPT_CONFIG, @@ -733,7 +787,6 @@ static int do_sign(int argc, char *argv[]) int i; int ifd = -1; int errorcnt = 0; - struct futil_traverse_state_s state; uint8_t *buf; uint32_t buf_len; char *e = 0; @@ -808,7 +861,7 @@ static int do_sign(int argc, char *argv[]) case OPT_FV: option.fv_specified = 1; /* fallthrough */ - case OPT_INFILE: /* aka "--vmlinuz" */ + case OPT_INFILE: inout_file_count++; infile = optarg; break; @@ -959,11 +1012,6 @@ static int do_sign(int argc, char *argv[]) /* 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) { @@ -986,20 +1034,6 @@ static int do_sign(int argc, char *argv[]) /* 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"); @@ -1027,13 +1061,10 @@ static int do_sign(int argc, char *argv[]) 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_desc(type)); - errorcnt++; - break; default: - DIE; + fprintf(stderr, "Unable to sign type %s\n", + futil_file_type_name(type)); + errorcnt++; } Debug("infile=%s\n", infile); @@ -1061,13 +1092,9 @@ static int do_sign(int argc, char *argv[]) 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) { @@ -1079,10 +1106,10 @@ static int do_sign(int argc, char *argv[]) } 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); + infile = option.outfile; ifd = open(option.outfile, O_RDWR); if (ifd < 0) { errorcnt++; @@ -1097,7 +1124,7 @@ static int do_sign(int argc, char *argv[]) goto done; } - errorcnt += futil_traverse(buf, buf_len, &state, type); + errorcnt += futil_file_type_sign(type, infile, buf, buf_len); errorcnt += futil_unmap_file(ifd, MAP_RW, buf, buf_len); @@ -1123,4 +1150,3 @@ done: DECLARE_FUTIL_COMMAND(sign, do_sign, VBOOT_VERSION_ALL, "Sign / resign various binary components"); - diff --git a/futility/file_type.c b/futility/file_type.c index aa017138..08fd00e0 100644 --- a/futility/file_type.c +++ b/futility/file_type.c @@ -154,3 +154,28 @@ enum futil_file_err futil_file_type(const char *filename, return err; } + +int futil_file_type_show(enum futil_file_type type, + const char *filename, + uint8_t *buf, uint32_t len) +{ + if (futil_file_types[type].show) + return futil_file_types[type].show(filename, buf, len, 0); + + fprintf(stderr, "Don't know how to show %s (type %s)\n", + filename, futil_file_type_name(type)); + return 1; +} + +int futil_file_type_sign(enum futil_file_type type, + const char *filename, + uint8_t *buf, uint32_t len) +{ + if (futil_file_types[type].sign) + return futil_file_types[type].sign(filename, buf, len, 0); + + fprintf(stderr, "Don't know how to sign %s (type %s)\n", + filename, futil_file_type_name(type)); + return 1; +} + diff --git a/futility/file_type.h b/futility/file_type.h index 58e14383..a1be2172 100644 --- a/futility/file_type.h +++ b/futility/file_type.h @@ -39,6 +39,24 @@ enum futil_file_type futil_file_type_buf(uint8_t *buf, uint32_t len); enum futil_file_err futil_file_type(const char *filename, enum futil_file_type *type); +/* + * Call the show() method on a buffer containing a specific file type. + * Returns zero on success. It's up to the caller to ensure that only valid + * file types are invoked. + */ +int futil_file_type_show(enum futil_file_type type, + const char *filename, + uint8_t *buf, uint32_t len); + +/* + * Call the sign() method on a buffer containing a specific file type. + * Returns zero on success. It's up to the caller to ensure that only valid + * file types are invoked. + */ +int futil_file_type_sign(enum futil_file_type type, + const char *filename, + uint8_t *buf, uint32_t len); + /* Declare the file_type functions. */ #define R_(FOO) \ enum futil_file_type FOO(uint8_t *buf, uint32_t len); diff --git a/futility/file_type.inc b/futility/file_type.inc index 076c9408..7d40d45c 100644 --- a/futility/file_type.inc +++ b/futility/file_type.inc @@ -15,59 +15,59 @@ * show function * sign function */ -FILE_TYPE(PUBKEY, "pubkey", "VbPublicKey (.vbpubk)", - R_(ft_recognize_vb1_key), - NONE, - NONE) -FILE_TYPE(KEYBLOCK, "keyblock", "VbKeyBlock", - R_(ft_recognize_vblock1), - NONE, - NONE) -FILE_TYPE(FW_PREAMBLE, "fw_pre", "VbFirmwarePreamble (VBLOCK_A/B)", - R_(ft_recognize_vblock1), - NONE, - NONE) -FILE_TYPE(GBB, "gbb", "GBB", - R_(ft_recognize_gbb), - NONE, - NONE) FILE_TYPE(BIOS_IMAGE, "bios", "Chrome OS BIOS image", R_(ft_recognize_bios_image), - NONE, - NONE) + S_(ft_show_bios), + S_(ft_sign_bios)) FILE_TYPE(OLD_BIOS_IMAGE, "oldbios", "Cr-48 Chrome OS BIOS image", R_(ft_recognize_bios_image), - NONE, + S_(ft_show_bios), + S_(ft_sign_bios)) +FILE_TYPE(GBB, "gbb", "GBB", + R_(ft_recognize_gbb), + S_(ft_show_gbb), NONE) -FILE_TYPE(KERN_PREAMBLE, "kernel", "kernel preamble/partition", +FILE_TYPE(FW_PREAMBLE, "fw_pre", "VbFirmwarePreamble (VBLOCK_A/B)", R_(ft_recognize_vblock1), - NONE, + S_(ft_show_fw_preamble), NONE) -FILE_TYPE(RAW_FIRMWARE, "fwblob", "raw firmware blob (FW_MAIN_A/B)", - NONE, - NONE, - NONE) -FILE_TYPE(RAW_KERNEL, "vmlinuz", "raw linux kernel", - NONE, - NONE, - NONE) -FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image", - NONE, - NONE, +FILE_TYPE(KERN_PREAMBLE, "kernel", "kernel preamble/partition", + R_(ft_recognize_vblock1), + S_(ft_show_kernel_preamble), + S_(ft_sign_kern_preamble)) +FILE_TYPE(KEYBLOCK, "keyblock", "VbKeyBlock", + R_(ft_recognize_vblock1), + S_(ft_show_keyblock), NONE) +FILE_TYPE(PUBKEY, "pubkey", "VbPublicKey (.vbpubk)", + R_(ft_recognize_vb1_key), + S_(ft_show_pubkey), + S_(ft_sign_pubkey)) FILE_TYPE(PRIVKEY, "prikey", "VbPrivateKey (.vbprivk)", R_(ft_recognize_vb1_key), - NONE, + S_(ft_show_privkey), NONE) FILE_TYPE(VB2_PUBKEY, "pubkey21", "vb21 public key (.vbpubk2)", R_(ft_recognize_vb2_key), - NONE, + S_(ft_show_vb2_pubkey), NONE) FILE_TYPE(VB2_PRIVKEY, "prikey21", "vb21 private key (.vbprik2)", R_(ft_recognize_vb2_key), - NONE, + S_(ft_show_vb2_privkey), NONE) FILE_TYPE(PEM, "pem", "RSA private key (.pem)", R_(ft_recognize_pem), + S_(ft_show_pem), + NONE) +FILE_TYPE(RAW_FIRMWARE, "fwblob", "raw firmware blob (FW_MAIN_A/B)", + NONE, + NONE, + S_(ft_sign_raw_firmware)) +FILE_TYPE(RAW_KERNEL, "vmlinuz", "raw linux kernel", + NONE, + NONE, + S_(ft_sign_raw_kernel)) +FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image", + NONE, NONE, NONE) diff --git a/futility/traversal.c b/futility/traversal.c index 413ec83c..99cc477c 100644 --- a/futility/traversal.c +++ b/futility/traversal.c @@ -1,200 +1,20 @@ -/* - * 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 <stdint.h> -#include <stdio.h> - -#include "file_type.h" #include "fmap.h" +#include "file_type.h" #include "futility.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_pubkey, /* 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 */ - futil_cb_show_privkey, /* CB_PRIVKEY */ - futil_cb_show_vb2_pubkey, /* CB_VB2_PUBKEY */ - futil_cb_show_vb2_privkey, /* CB_VB2_PRIVKEY */ - futil_cb_show_pem, /* CB_PEM */ -}; -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 */ - NULL, /* CB_PRIVKEY */ - NULL, /* CB_VB2_PUBKEY */ - NULL, /* CB_VB2_PRIVKEY */ - NULL, /* CB_PEM */ -}; -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 */ - {0, "chromiumos disk"}, /* FILE_TYPE_CHROMIUMOS_DISK */ - {CB_PRIVKEY, "VbPrivateKey"}, /* FILE_TYPE_PRIVKEY */ - {CB_VB2_PUBKEY, "vb21 public key"}, /* FILE_TYPE_VB2_PUBKEY */ - {CB_VB2_PRIVKEY, "vb21 private key"}, /* FILE_TYPE_VB2_PRIVKEY */ - {CB_PEM, "RSA private key"}, /* FILE_TYPE_PEM */ -}; -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} +/* These are the expected areas, in order of traversal */ +struct bios_fmap_s bios_area[] = { + {BIOS_FMAP_GBB, "GBB", "GBB Area"}, + {BIOS_FMAP_FW_MAIN_A, "FW_MAIN_A", "Firmware A Data"}, + {BIOS_FMAP_FW_MAIN_B, "FW_MAIN_B", "Firmware B Data"}, + {BIOS_FMAP_VBLOCK_A, "VBLOCK_A", "Firmware A Key"}, + {BIOS_FMAP_VBLOCK_B, "VBLOCK_B", "Firmware B Key"}, }; +BUILD_ASSERT(ARRAY_SIZE(bios_area) == NUM_BIOS_COMPONENTS); -/* 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; -} -enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len) -{ - FmapHeader *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; - } - return FILE_TYPE_UNKNOWN; -} - -static 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", - "CB_PRIVKEY", - "CB_VB2_PUBKEY", - "CB_VB2_PRIVKEY", - "CB_PEM", -}; -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 ((int) 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); - - return 0; -} - -static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len) +void fmap_limit_area(FmapAreaHeader *ah, uint32_t len) { uint32_t sum = ah->area_offset + ah->area_size; if (sum < ah->area_size || sum > len) { @@ -206,82 +26,28 @@ static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len) } } -int futil_traverse(uint8_t *buf, uint32_t len, - struct futil_traverse_state_s *state, - enum futil_file_type type) +enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len) { FmapHeader *fmap; - FmapAreaHeader *ah = 0; - const struct bios_area_s *area; - int retval = 0; - - if ((int) 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_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_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); - /* But the file might be truncated */ - fmap_limit_area(ah, len); - retval |= invoke_callback(state, - area->component, - area->name, - ah->area_offset, - buf + ah->area_offset, - ah->area_size); - state->errors = retval; - } - break; + int i; + + fmap = fmap_find(buf, len); + if (!fmap) + return FILE_TYPE_UNKNOWN; + + for (i = 0; i < NUM_BIOS_COMPONENTS; i++) + if (!fmap_find_by_name(buf, len, fmap, + bios_area[i].name, 0)) + break; + if (i == NUM_BIOS_COMPONENTS) + return FILE_TYPE_BIOS_IMAGE; + + for (i = 0; i < NUM_BIOS_COMPONENTS; i++) + if (!fmap_find_by_name(buf, len, fmap, + bios_area[i].oldname, 0)) + break; + if (i == NUM_BIOS_COMPONENTS) + return FILE_TYPE_OLD_BIOS_IMAGE; - 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); - /* But the file might be truncated */ - fmap_limit_area(ah, len); - 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_UNKNOWN: - case FILE_TYPE_CHROMIUMOS_DISK: - /* Nothing to do for these file types (yet) */ - break; - - default: - /* All other file types have their own callbacks */ - retval |= invoke_callback(state, - direct_callback[type].component, - direct_callback[type].name, - 0, buf, len); - state->errors = retval; - break; - } - - retval |= invoke_callback(state, CB_END_TRAVERSAL, "<end>", - 0, buf, len); - return retval; + return FILE_TYPE_UNKNOWN; } diff --git a/futility/traversal.h b/futility/traversal.h index e975469a..cb2052d0 100644 --- a/futility/traversal.h +++ b/futility/traversal.h @@ -6,98 +6,33 @@ #ifndef VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_ #define VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_ #include <stdint.h> - - -/* What are we trying to accomplish? */ -enum futil_op_type { - FUTIL_OP_SHOW, - FUTIL_OP_SIGN, - - NUM_FUTIL_OPS -}; - -/* What component are we currently handling in the callback routine? */ -enum futil_cb_component { - /* entire input buffer */ - CB_BEGIN_TRAVERSAL, - CB_END_TRAVERSAL, - /* fmap areas within a bios image */ - CB_FMAP_GBB, - CB_FMAP_VBLOCK_A, - CB_FMAP_VBLOCK_B, - CB_FMAP_FW_MAIN_A, - CB_FMAP_FW_MAIN_B, - /* individual files (extracted from a bios, for example) */ - CB_PUBKEY, - CB_KEYBLOCK, - CB_GBB, - CB_FW_PREAMBLE, - CB_KERN_PREAMBLE, - CB_RAW_FIRMWARE, - CB_RAW_KERNEL, - CB_PRIVKEY, - CB_VB2_PUBKEY, - CB_VB2_PRIVKEY, - CB_PEM, - - NUM_CB_COMPONENTS -}; - -/* Where is the component we're poking at? */ -struct cb_area_s { - uint32_t offset; /* to avoid pointer math */ - uint8_t *buf; - uint32_t len; - uint32_t _flags; /* for callback use */ -}; - -/* What do we know at this point in time? */ -struct futil_traverse_state_s { - /* These two should be initialized by the caller as needed */ - const char *in_filename; - enum futil_op_type op; - /* Current activity during traversal */ - enum futil_cb_component component; - struct cb_area_s *my_area; - const char *name; - /* Other activites, possibly before or after the current one */ - struct cb_area_s cb_area[NUM_CB_COMPONENTS]; - struct cb_area_s recovery_key; - struct cb_area_s rootkey; - enum futil_file_type in_type; - int errors; -}; - +#include "fmap.h" /* - * Traverse the buffer using the provided state, which should be initialized - * before calling. Returns nonzero (but no details) if there were any errors. + * The Chrome OS BIOS must contain specific FMAP areas, and we generally want + * to look at each one in a certain order. */ -int futil_traverse(uint8_t *buf, uint32_t len, - struct futil_traverse_state_s *state, - enum futil_file_type type_hint); +enum bios_component { + BIOS_FMAP_GBB, + BIOS_FMAP_FW_MAIN_A, + BIOS_FMAP_FW_MAIN_B, + BIOS_FMAP_VBLOCK_A, + BIOS_FMAP_VBLOCK_B, + + NUM_BIOS_COMPONENTS +}; + +/* These are the expected areas, in order of traversal */ +extern struct bios_fmap_s { + enum bios_component component; + const char * const name; + /* The Cr-48 BIOS images have different FMAP names but work the same, + * so we allow those too. */ + const char * const oldname; +} bios_area[]; -/* These are invoked by the traversal. They also return nonzero on error. */ -int futil_cb_show_begin(struct futil_traverse_state_s *state); -int futil_cb_show_pubkey(struct futil_traverse_state_s *state); -int futil_cb_show_gbb(struct futil_traverse_state_s *state); -int futil_cb_show_keyblock(struct futil_traverse_state_s *state); -int futil_cb_show_fw_main(struct futil_traverse_state_s *state); -int futil_cb_show_fw_preamble(struct futil_traverse_state_s *state); -int futil_cb_show_kernel_preamble(struct futil_traverse_state_s *state); -int futil_cb_show_privkey(struct futil_traverse_state_s *state); -int futil_cb_show_vb2_pubkey(struct futil_traverse_state_s *state); -int futil_cb_show_vb2_privkey(struct futil_traverse_state_s *state); -int futil_cb_show_pem(struct futil_traverse_state_s *state); -int futil_cb_sign_pubkey(struct futil_traverse_state_s *state); -int futil_cb_sign_fw_main(struct futil_traverse_state_s *state); -int futil_cb_sign_fw_vblock(struct futil_traverse_state_s *state); -int futil_cb_sign_raw_firmware(struct futil_traverse_state_s *state); -int futil_cb_resign_kernel_part(struct futil_traverse_state_s *state); -int futil_cb_create_kernel_part(struct futil_traverse_state_s *state); -int futil_cb_sign_begin(struct futil_traverse_state_s *state); -int futil_cb_sign_end(struct futil_traverse_state_s *state); +void fmap_limit_area(FmapAreaHeader *ah, uint32_t len); -#endif /* VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_ */ +#endif /* VBOOT_REFERENCE_FUTILITY_TRAVERSAL_H_ */ diff --git a/futility/vb2_helper.c b/futility/vb2_helper.c index 35ca4780..c6ffbb75 100644 --- a/futility/vb2_helper.c +++ b/futility/vb2_helper.c @@ -64,7 +64,7 @@ static uint8_t *vb2_public_key_sha1sum(struct vb2_public_key *key) return digest; } -int futil_cb_show_vb2_pubkey(struct futil_traverse_state_s *state) +int ft_show_vb2_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data) { struct vb2_public_key key; const struct vb2_text_vs_enum *entry; @@ -72,13 +72,12 @@ int futil_cb_show_vb2_pubkey(struct futil_traverse_state_s *state) /* The key's members will point into the state buffer after this. Don't * free anything. */ - if (VB2_SUCCESS != vb2_unpack_key(&key, state->my_area->buf, - state->my_area->len)) + if (VB2_SUCCESS != vb2_unpack_key(&key, buf, len)) return 1; sha1sum = vb2_public_key_sha1sum(&key); - printf("Public Key file: %s\n", state->in_filename); + printf("Public Key file: %s\n", name); printf(" Vboot API: 2.1\n"); printf(" Desc: \"%s\"\n", key.desc); entry = vb2_lookup_by_num(vb2_text_vs_sig, key.sig_alg); @@ -114,19 +113,19 @@ static uint8_t *vb2_private_key_sha1sum(struct vb2_private_key *key) return digest; } -int futil_cb_show_vb2_privkey(struct futil_traverse_state_s *state) +int ft_show_vb2_privkey(const char *name, uint8_t *buf, uint32_t len, + void *data) { struct vb2_private_key *key = 0; const struct vb2_text_vs_enum *entry; uint8_t *sha1sum; - if (VB2_SUCCESS != vb2_private_key_unpack(&key, state->my_area->buf, - state->my_area->len)) + if (VB2_SUCCESS != vb2_private_key_unpack(&key, buf, len)) return 1; sha1sum = vb2_private_key_sha1sum(key); - printf("Private key file: %s\n", state->in_filename); + printf("Private key file: %s\n", name); printf(" Vboot API: 2.1\n"); printf(" Desc: \"%s\"\n", key->desc ? key->desc : ""); entry = vb2_lookup_by_num(vb2_text_vs_sig, key->sig_alg); @@ -180,17 +179,17 @@ enum futil_file_type ft_recognize_pem(uint8_t *buf, uint32_t len) return FILE_TYPE_UNKNOWN; } -int futil_cb_show_pem(struct futil_traverse_state_s *state) +int ft_show_pem(const char *name, uint8_t *buf, uint32_t len, void *data) { RSA *rsa_key; uint8_t *keyb, *digest; uint32_t keyb_len; int i, bits; - printf("Private Key file: %s\n", state->in_filename); + printf("Private Key file: %s\n", name); /* We're called only after ft_recognize_pem, so this should work. */ - rsa_key = rsa_from_buffer(state->my_area->buf, state->my_area->len); + rsa_key = rsa_from_buffer(buf, len); if (!rsa_key) DIE; |