/* 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 #include #include #include #include #include "fmap.h" #include "file_type.h" #include "file_type_bios.h" #include "futility.h" #include "futility_options.h" #include "host_common.h" #include "vb1_helper.h" #include "vb2_common.h" static const char * const fmap_name[] = { "GBB", /* BIOS_FMAP_GBB */ "FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */ "FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */ "VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */ "VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */ }; BUILD_ASSERT(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS); static const char * const fmap_oldname[] = { "GBB Area", /* BIOS_FMAP_GBB */ "Firmware A Data", /* BIOS_FMAP_FW_MAIN_A */ "Firmware B Data", /* BIOS_FMAP_FW_MAIN_B */ "Firmware A Key", /* BIOS_FMAP_VBLOCK_A */ "Firmware B Key", /* BIOS_FMAP_VBLOCK_B */ }; BUILD_ASSERT(ARRAY_SIZE(fmap_oldname) == NUM_BIOS_COMPONENTS); static 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) { VB2_DEBUG("%s 0x%x + 0x%x > 0x%x\n", ah->area_name, ah->area_offset, ah->area_size, len); ah->area_offset = 0; ah->area_size = 0; } } /** Show functions **/ int ft_show_gbb(const char *name, uint8_t *buf, uint32_t len, void *data) { struct vb2_gbb_header *gbb = (struct vb2_gbb_header *)buf; struct bios_state_s *state = (struct bios_state_s *)data; int retval = 0; uint32_t maxlen = 0; if (!len) { printf("GBB header: %s \n", 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", 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"); struct vb2_packed_key *pubkey = (struct vb2_packed_key *)(buf + gbb->rootkey_offset); if (packed_key_looks_ok(pubkey, gbb->rootkey_size)) { 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_pubkey(pubkey, " "); } else { retval = 1; printf(" Root Key: \n"); } pubkey = (struct vb2_packed_key *)(buf + gbb->recovery_key_offset); if (packed_key_looks_ok(pubkey, gbb->recovery_key_size)) { 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_pubkey(pubkey, " "); } else { retval = 1; printf(" Recovery Key: \n"); } if (!retval && state) state->area[BIOS_FMAP_GBB].is_valid = 1; return retval; } /* * This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image. * * 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. */ static int fmap_show_fw_main(const char *name, uint8_t *buf, uint32_t len, void *data) { struct bios_state_s *state = (struct bios_state_s *)data; if (!len) { printf("Firmware body: %s \n", name); return 1; } printf("Firmware body: %s\n", name); printf(" Offset: 0x%08x\n", state->area[state->c].offset); printf(" Size: 0x%08x\n", len); state->area[state->c].is_valid = 1; return 0; } /* Functions to call to show the bios components */ static int (*fmap_show_fn[])(const char *name, uint8_t *buf, uint32_t len, void *data) = { ft_show_gbb, fmap_show_fw_main, fmap_show_fw_main, ft_show_fw_preamble, ft_show_fw_preamble, }; BUILD_ASSERT(ARRAY_SIZE(fmap_show_fn) == NUM_BIOS_COMPONENTS); 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]; enum bios_component c; int retval = 0; struct bios_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 (c = 0; c < NUM_BIOS_COMPONENTS; c++) { /* We know one of these will work, too */ if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) || fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &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 = c; state.area[c].offset = ah->area_offset; state.area[c].buf = buf + ah->area_offset; state.area[c].len = ah->area_size; VB2_DEBUG("showing FMAP area %d (%s)," " offset=0x%08x len=0x%08x\n", c, ah_name, ah->area_offset, ah->area_size); /* Go look at it. */ if (fmap_show_fn[c]) retval += fmap_show_fn[c](ah_name, state.area[c].buf, state.area[c].len, &state); } } return retval; } /** Sign functions **/ /* * 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. */ static int fmap_sign_fw_main(const char *name, uint8_t *buf, uint32_t len, void *data) { struct bios_state_s *state = (struct bios_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 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. */ static int fmap_sign_fw_preamble(const char *name, uint8_t *buf, uint32_t len, void *data) { static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]; static struct vb2_workbuf wb; vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); struct vb2_keyblock *keyblock = (struct vb2_keyblock *)buf; struct bios_state_s *state = (struct bios_state_s *)data; /* * If we have a valid keyblock and fw_preamble, then we can use them to * determine the size of the firmware body. Otherwise, we'll have to * just sign the whole region. */ if (VB2_SUCCESS != vb2_verify_keyblock_hash(keyblock, len, &wb)) { fprintf(stderr, "Warning: %s keyblock is invalid. " "Signing the entire FW FMAP region...\n", name); goto whatever; } if (!packed_key_looks_ok(&keyblock->data_key, keyblock->data_key.key_offset + keyblock->data_key.key_size)) { fprintf(stderr, "Warning: %s public key is invalid. " "Signing the entire FW FMAP region...\n", name); goto whatever; } uint32_t more = keyblock->keyblock_size; struct vb2_fw_preamble *preamble = (struct vb2_fw_preamble *)(buf + more); uint32_t fw_size = preamble->body_signature.data_size; struct bios_area_s *fw_body_area = 0; 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 (!sign_option.flags_specified) sign_option.flags = preamble->flags; break; case BIOS_FMAP_VBLOCK_B: fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B]; break; default: FATAL("Can only handle VBLOCK_A or VBLOCK_B\n"); } if (fw_size > fw_body_area->len) { fprintf(stderr, "%s says the firmware is larger than we have\n", name); return 1; } /* Update the firmware size */ fw_body_area->len = fw_size; whatever: state->area[state->c].is_valid = 1; return 0; } static int write_new_preamble(struct bios_area_s *vblock, struct bios_area_s *fw_body, struct vb2_private_key *signkey, struct vb2_keyblock *keyblock) { struct vb2_signature *body_sig; struct vb2_fw_preamble *preamble; body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, signkey); if (!body_sig) { fprintf(stderr, "Error calculating body signature\n"); return 1; } preamble = vb2_create_fw_preamble(sign_option.version, (struct vb2_packed_key *)sign_option.kernel_subkey, body_sig, signkey, sign_option.flags); if (!preamble) { fprintf(stderr, "Error creating firmware preamble.\n"); free(body_sig); return 1; } /* Write the new keyblock */ uint32_t more = keyblock->keyblock_size; memcpy(vblock->buf, keyblock, more); /* and the new preamble */ memcpy(vblock->buf + more, preamble, preamble->preamble_size); free(preamble); free(body_sig); return 0; } static int write_loem(const char *ab, struct bios_area_s *vblock) { char filename[PATH_MAX]; int n; n = snprintf(filename, sizeof(filename), "%s/vblock_%s.%s", sign_option.loemdir ? sign_option.loemdir : ".", ab, sign_option.loemid); if (n >= sizeof(filename)) { fprintf(stderr, "LOEM args produce bogus filename\n"); return 1; } FILE *fp = fopen(filename, "w"); if (!fp) { fprintf(stderr, "Can't open %s for writing: %s\n", filename, strerror(errno)); return 1; } if (1 != fwrite(vblock->buf, vblock->len, 1, fp)) { fprintf(stderr, "Can't write to %s: %s\n", filename, strerror(errno)); fclose(fp); return 1; } if (fclose(fp)) { fprintf(stderr, "Failed closing loem output: %s\n", strerror(errno)); return 1; } return 0; } /* This signs a full BIOS image after it's been traversed. */ static int sign_bios_at_end(struct bios_state_s *state) { 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 (!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; } /* Do A & B differ ? */ if (fw_a->len != fw_b->len || memcmp(fw_a->buf, fw_b->buf, fw_a->len)) { /* Yes, must use DEV keys for A */ if (!sign_option.devsignprivate || !sign_option.devkeyblock) { fprintf(stderr, "FW A & B differ. DEV keys are required.\n"); return 1; } retval |= write_new_preamble(vblock_a, fw_a, sign_option.devsignprivate, sign_option.devkeyblock); } else { retval |= write_new_preamble(vblock_a, fw_a, sign_option.signprivate, sign_option.keyblock); } /* FW B is always normal keys */ retval |= write_new_preamble(vblock_b, fw_b, sign_option.signprivate, sign_option.keyblock); if (sign_option.loemid) { retval |= write_loem("A", vblock_a); retval |= write_loem("B", vblock_b); } return retval; } /* Functions to call while preparing to sign the bios */ static int (*fmap_sign_fn[])(const char *name, uint8_t *buf, uint32_t len, void *data) = { 0, fmap_sign_fw_main, fmap_sign_fw_main, fmap_sign_fw_preamble, fmap_sign_fw_preamble, }; BUILD_ASSERT(ARRAY_SIZE(fmap_sign_fn) == NUM_BIOS_COMPONENTS); int ft_sign_bios(const char *name, uint8_t *buf, uint32_t len, void *data) { FmapHeader *fmap; FmapAreaHeader *ah = 0; char ah_name[FMAP_NAMELEN + 1]; enum bios_component c; int retval = 0; struct bios_state_s state; memset(&state, 0, sizeof(state)); /* We've already checked, so we know this will work. */ fmap = fmap_find(buf, len); for (c = 0; c < NUM_BIOS_COMPONENTS; c++) { /* We know one of these will work, too */ if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) || fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &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 = c; state.area[c].buf = buf + ah->area_offset; state.area[c].len = ah->area_size; VB2_DEBUG("%s() examining FMAP area %d (%s)," " offset=0x%08x len=0x%08x\n", c, ah_name, ah->area_offset, ah->area_size); /* Go look at it, but abort on error */ if (fmap_sign_fn[c]) retval += fmap_sign_fn[c](ah_name, state.area[c].buf, state.area[c].len, &state); } } retval += sign_bios_at_end(&state); return retval; } enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len) { FmapHeader *fmap; enum bios_component c; fmap = fmap_find(buf, len); if (!fmap) return FILE_TYPE_UNKNOWN; for (c = 0; c < NUM_BIOS_COMPONENTS; c++) if (!fmap_find_by_name(buf, len, fmap, fmap_name[c], 0)) break; if (c == NUM_BIOS_COMPONENTS) return FILE_TYPE_BIOS_IMAGE; for (c = 0; c < NUM_BIOS_COMPONENTS; c++) if (!fmap_find_by_name(buf, len, fmap, fmap_oldname[c], 0)) break; if (c == NUM_BIOS_COMPONENTS) return FILE_TYPE_OLD_BIOS_IMAGE; return FILE_TYPE_UNKNOWN; }