diff options
Diffstat (limited to 'firmware/lib/vboot_firmware.c')
-rw-r--r-- | firmware/lib/vboot_firmware.c | 643 |
1 files changed, 341 insertions, 302 deletions
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c index ed9d3bc0..c2e8293f 100644 --- a/firmware/lib/vboot_firmware.c +++ b/firmware/lib/vboot_firmware.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -13,311 +13,350 @@ #include "vboot_common.h" #include "vboot_nvstorage.h" -/* Static variables for UpdateFirmwareBodyHash(). It's less than - * optimal to have static variables in a library, but in UEFI the - * caller is deep inside a different firmware stack and doesn't have a - * good way to pass the params struct back to us. */ +/* + * Static variables for UpdateFirmwareBodyHash(). It's less than optimal to + * have static variables in a library, but in UEFI the caller is deep inside a + * different firmware stack and doesn't have a good way to pass the params + * struct back to us. + */ typedef struct VbLoadFirmwareInternal { - DigestContext body_digest_context; - uint32_t body_size_accum; + DigestContext body_digest_context; + uint32_t body_size_accum; } VbLoadFirmwareInternal; +void VbUpdateFirmwareBodyHash(VbCommonParams *cparams, uint8_t *data, + uint32_t size) +{ + VbLoadFirmwareInternal *lfi = + (VbLoadFirmwareInternal*)cparams->vboot_context; -void VbUpdateFirmwareBodyHash(VbCommonParams* cparams, - uint8_t* data, uint32_t size) { - VbLoadFirmwareInternal* lfi = - (VbLoadFirmwareInternal*)cparams->vboot_context; - - DigestUpdate(&lfi->body_digest_context, data, size); - lfi->body_size_accum += size; + DigestUpdate(&lfi->body_digest_context, data, size); + lfi->body_size_accum += size; } - -int LoadFirmware(VbCommonParams* cparams, VbSelectFirmwareParams* fparams, - VbNvContext* vnc) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - VbPublicKey* root_key; - VbLoadFirmwareInternal* lfi; - - uint32_t try_b_count; - uint32_t lowest_version = 0xFFFFFFFF; - int good_index = -1; - int is_dev; - int index; - int i; - - int retval = VBERROR_UNKNOWN; - int recovery = VBNV_RECOVERY_RO_UNSPECIFIED; - - /* Clear output params in case we fail */ - shared->firmware_index = 0xFF; - - VBDEBUG(("LoadFirmware started...\n")); - - /* Must have a root key from the GBB */ - if (!gbb) { - VBDEBUG(("No GBB\n")); - retval = VBERROR_INVALID_GBB; - goto LoadFirmwareExit; - } - root_key = (VbPublicKey*)((uint8_t*)gbb + gbb->rootkey_offset); - - /* Parse flags */ - is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); - if (is_dev) - shared->flags |= VBSD_LF_DEV_SWITCH_ON; - - /* Read try-b count and decrement if necessary */ - VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count); - if (0 != try_b_count) { - VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1); - shared->flags |= VBSD_FWB_TRIED; - } - - /* Allocate our internal data */ - lfi = (VbLoadFirmwareInternal*)VbExMalloc(sizeof(VbLoadFirmwareInternal)); - cparams->vboot_context = (void*)lfi; - - /* Loop over indices */ - for (i = 0; i < 2; i++) { - VbKeyBlockHeader* key_block; - uint32_t vblock_size; - VbFirmwarePreambleHeader* preamble; - RSAPublicKey* data_key; - uint64_t key_version; - uint32_t combined_version; - uint8_t* body_digest; - uint8_t* check_result; - - /* If try B count is non-zero try firmware B first */ - index = (try_b_count ? 1 - i : i); - if (0 == index) { - key_block = (VbKeyBlockHeader*)fparams->verification_block_A; - vblock_size = fparams->verification_size_A; - check_result = &shared->check_fw_a_result; - } else { - key_block = (VbKeyBlockHeader*)fparams->verification_block_B; - vblock_size = fparams->verification_size_B; - check_result = &shared->check_fw_b_result; - } - - /* Check the key block flags against the current boot mode. Do this - * before verifying the key block, since flags are faster to check than - * the RSA signature. */ - if (!(key_block->key_block_flags & - (is_dev ? KEY_BLOCK_FLAG_DEVELOPER_1 : - KEY_BLOCK_FLAG_DEVELOPER_0))) { - VBDEBUG(("Developer flag mismatch.\n")); - *check_result = VBSD_LF_CHECK_DEV_MISMATCH; - continue; - } - /* RW firmware never runs in recovery mode. */ - if (!(key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)) { - VBDEBUG(("Recovery flag mismatch.\n")); - *check_result = VBSD_LF_CHECK_REC_MISMATCH; - continue; - } - - /* Verify the key block */ - VBPERFSTART("VB_VKB"); - if ((0 != KeyBlockVerify(key_block, vblock_size, root_key, 0))) { - VBDEBUG(("Key block verification failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_KEYBLOCK; - VBPERFEND("VB_VKB"); - continue; - } - VBPERFEND("VB_VKB"); - - /* Check for rollback of key version. */ - key_version = key_block->data_key.key_version; - if (!(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { - if (key_version < (shared->fw_version_tpm >> 16)) { - VBDEBUG(("Key rollback detected.\n")); - *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; - continue; - } - if (key_version > 0xFFFF) { - /* Key version is stored in 16 bits in the TPM, so key versions greater - * than 0xFFFF can't be stored properly. */ - VBDEBUG(("Key version > 0xFFFF.\n")); - *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; - continue; - } - } - - /* Get the key for preamble/data verification from the key block. */ - data_key = PublicKeyToRSA(&key_block->data_key); - if (!data_key) { - VBDEBUG(("Unable to parse data key.\n")); - *check_result = VBSD_LF_CHECK_DATA_KEY_PARSE; - continue; - } - - /* Verify the preamble, which follows the key block. */ - VBPERFSTART("VB_VPB"); - preamble = (VbFirmwarePreambleHeader*)((uint8_t*)key_block + - key_block->key_block_size); - if ((0 != VerifyFirmwarePreamble(preamble, - vblock_size - key_block->key_block_size, - data_key))) { - VBDEBUG(("Preamble verfication failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_PREAMBLE; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_VPB"); - continue; - } - VBPERFEND("VB_VPB"); - - /* Check for rollback of firmware version. */ - combined_version = (uint32_t)((key_version << 16) | - (preamble->firmware_version & 0xFFFF)); - if (combined_version < shared->fw_version_tpm && - !(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { - VBDEBUG(("Firmware version rollback detected.\n")); - *check_result = VBSD_LF_CHECK_FW_ROLLBACK; - RSAPublicKeyFree(data_key); - continue; - } - - /* Header for this firmware is valid */ - *check_result = VBSD_LF_CHECK_HEADER_VALID; - - /* Check for lowest key version from a valid header. */ - if (lowest_version > combined_version) - lowest_version = combined_version; - - /* If we already have good firmware, no need to read another one; - * we only needed to look at the versions to check for - * rollback. */ - if (-1 != good_index) { - RSAPublicKeyFree(data_key); - continue; - } - - /* Handle preamble flag for using the RO normal/dev code path */ - if (VbGetFirmwarePreambleFlags(preamble) & - VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) { - - /* Fail if calling firmware doesn't support RO normal */ - if (!(shared->flags & VBSD_BOOT_RO_NORMAL_SUPPORT)) { - *check_result = VBSD_LF_CHECK_NO_RO_NORMAL; - RSAPublicKeyFree(data_key); - continue; - } - - /* Indicate that we should use the RO normal code path */ - shared->flags |= VBSD_LF_USE_RO_NORMAL; - - } else { - VbError_t rv; - - /* Read the firmware data */ - VBPERFSTART("VB_RFD"); - DigestInit(&lfi->body_digest_context, data_key->algorithm); - lfi->body_size_accum = 0; - rv = VbExHashFirmwareBody(cparams, (index ? VB_SELECT_FIRMWARE_B : - VB_SELECT_FIRMWARE_A)); - if (VBERROR_SUCCESS != rv) { - VBDEBUG(("VbExHashFirmwareBody() failed for index %d\n", index)); - *check_result = VBSD_LF_CHECK_GET_FW_BODY; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_RFD"); - continue; - } - if (lfi->body_size_accum != preamble->body_signature.data_size) { - VBDEBUG(("Hash updated %d bytes but expected %d\n", - (int)lfi->body_size_accum, - (int)preamble->body_signature.data_size)); - *check_result = VBSD_LF_CHECK_HASH_WRONG_SIZE; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_RFD"); - continue; - } - VBPERFEND("VB_RFD"); - - /* Verify firmware data */ - VBPERFSTART("VB_VFD"); - body_digest = DigestFinal(&lfi->body_digest_context); - if (0 != VerifyDigest(body_digest, &preamble->body_signature, - data_key)) { - VBDEBUG(("Firmware body verification failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_BODY; - RSAPublicKeyFree(data_key); - VbExFree(body_digest); - VBPERFEND("VB_VFD"); - continue; - } - VbExFree(body_digest); - VBPERFEND("VB_VFD"); - } - - /* Done with the data key, so can free it now */ - RSAPublicKeyFree(data_key); - - /* If we're still here, the firmware is valid. */ - VBDEBUG(("Firmware %d is valid.\n", index)); - *check_result = VBSD_LF_CHECK_VALID; - if (-1 == good_index) { - /* Save the key we actually used */ - if (0 != VbSharedDataSetKernelKey(shared, &preamble->kernel_subkey)) { - VBDEBUG(("Unable to save kernel subkey to shared data.\n")); - continue; /* The firmware signature was good, but the public - * key was bigger that the caller can handle. */ - } - - /* Save the good index, now that we're sure we can actually use - * this firmware. That's the one we'll boot. */ - good_index = index; - shared->firmware_index = (uint8_t)index; - shared->fw_keyblock_flags = key_block->key_block_flags; - - /* If the good firmware's key version is the same as the tpm, - * then the TPM doesn't need updating; we can stop now. - * Otherwise, we'll check all the other headers to see if they - * contain a newer key. */ - if (combined_version == shared->fw_version_tpm) - break; - } - } - - /* Free internal data */ - VbExFree(lfi); - cparams->vboot_context = NULL; - - /* Handle finding good firmware */ - if (good_index >= 0) { - - /* Save versions we found */ - shared->fw_version_lowest = lowest_version; - if (lowest_version > shared->fw_version_tpm) - shared->fw_version_tpm = lowest_version; - - /* Success */ - VBDEBUG(("Will boot firmware index %d\n", (int)shared->firmware_index)); - retval = VBERROR_SUCCESS; - } else { - uint8_t a = shared->check_fw_a_result; - uint8_t b = shared->check_fw_b_result; - uint8_t best_check; - - /* No good firmware, so go to recovery mode. */ - VBDEBUG(("Alas, no good firmware.\n")); - recovery = VBNV_RECOVERY_RO_INVALID_RW; - retval = VBERROR_LOAD_FIRMWARE; - - /* If the best check result fits in the range of recovery reasons, provide - * more detail on how far we got in validation. */ - best_check = (a > b ? a : b) + VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN; - if (best_check >= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN && - best_check <= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MAX) - recovery = best_check; - } - -LoadFirmwareExit: - /* Store recovery request, if any */ - VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? - recovery : VBNV_RECOVERY_NOT_REQUESTED); - - return retval; +int LoadFirmware(VbCommonParams *cparams, VbSelectFirmwareParams *fparams, + VbNvContext *vnc) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + VbPublicKey *root_key; + VbLoadFirmwareInternal *lfi; + + uint32_t try_b_count; + uint32_t lowest_version = 0xFFFFFFFF; + int good_index = -1; + int is_dev; + int index; + int i; + + int retval = VBERROR_UNKNOWN; + int recovery = VBNV_RECOVERY_RO_UNSPECIFIED; + + /* Clear output params in case we fail */ + shared->firmware_index = 0xFF; + + VBDEBUG(("LoadFirmware started...\n")); + + /* Must have a root key from the GBB */ + if (!gbb) { + VBDEBUG(("No GBB\n")); + retval = VBERROR_INVALID_GBB; + goto LoadFirmwareExit; + } + root_key = (VbPublicKey *)((uint8_t *)gbb + gbb->rootkey_offset); + + /* Parse flags */ + is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); + if (is_dev) + shared->flags |= VBSD_LF_DEV_SWITCH_ON; + + /* Read try-b count and decrement if necessary */ + VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count); + if (0 != try_b_count) { + VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1); + shared->flags |= VBSD_FWB_TRIED; + } + + /* Allocate our internal data */ + lfi = (VbLoadFirmwareInternal *) + VbExMalloc(sizeof(VbLoadFirmwareInternal)); + cparams->vboot_context = lfi; + + /* Loop over indices */ + for (i = 0; i < 2; i++) { + VbKeyBlockHeader *key_block; + uint32_t vblock_size; + VbFirmwarePreambleHeader *preamble; + RSAPublicKey *data_key; + uint64_t key_version; + uint32_t combined_version; + uint8_t *body_digest; + uint8_t *check_result; + + /* If try B count is non-zero try firmware B first */ + index = (try_b_count ? 1 - i : i); + if (0 == index) { + key_block = (VbKeyBlockHeader *) + fparams->verification_block_A; + vblock_size = fparams->verification_size_A; + check_result = &shared->check_fw_a_result; + } else { + key_block = (VbKeyBlockHeader *) + fparams->verification_block_B; + vblock_size = fparams->verification_size_B; + check_result = &shared->check_fw_b_result; + } + + /* + * Check the key block flags against the current boot mode. Do + * this before verifying the key block, since flags are faster + * to check than the RSA signature. + */ + if (!(key_block->key_block_flags & + (is_dev ? KEY_BLOCK_FLAG_DEVELOPER_1 : + KEY_BLOCK_FLAG_DEVELOPER_0))) { + VBDEBUG(("Developer flag mismatch.\n")); + *check_result = VBSD_LF_CHECK_DEV_MISMATCH; + continue; + } + + /* RW firmware never runs in recovery mode. */ + if (!(key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)) { + VBDEBUG(("Recovery flag mismatch.\n")); + *check_result = VBSD_LF_CHECK_REC_MISMATCH; + continue; + } + + /* Verify the key block */ + VBPERFSTART("VB_VKB"); + if ((0 != KeyBlockVerify(key_block, vblock_size, + root_key, 0))) { + VBDEBUG(("Key block verification failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_KEYBLOCK; + VBPERFEND("VB_VKB"); + continue; + } + VBPERFEND("VB_VKB"); + + /* Check for rollback of key version. */ + key_version = key_block->data_key.key_version; + if (!(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { + if (key_version < (shared->fw_version_tpm >> 16)) { + VBDEBUG(("Key rollback detected.\n")); + *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; + continue; + } + if (key_version > 0xFFFF) { + /* + * Key version is stored in 16 bits in the TPM, + * so key versions greater than 0xFFFF can't be + * stored properly. + */ + VBDEBUG(("Key version > 0xFFFF.\n")); + *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; + continue; + } + } + + /* Get key for preamble/data verification from the key block. */ + data_key = PublicKeyToRSA(&key_block->data_key); + if (!data_key) { + VBDEBUG(("Unable to parse data key.\n")); + *check_result = VBSD_LF_CHECK_DATA_KEY_PARSE; + continue; + } + + /* Verify the preamble, which follows the key block. */ + VBPERFSTART("VB_VPB"); + preamble = (VbFirmwarePreambleHeader *) + ((uint8_t *)key_block + key_block->key_block_size); + if ((0 != VerifyFirmwarePreamble( + preamble, + vblock_size - key_block->key_block_size, + data_key))) { + VBDEBUG(("Preamble verfication failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_PREAMBLE; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_VPB"); + continue; + } + VBPERFEND("VB_VPB"); + + /* Check for rollback of firmware version. */ + combined_version = (uint32_t)((key_version << 16) | + (preamble->firmware_version & 0xFFFF)); + if (combined_version < shared->fw_version_tpm && + !(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { + VBDEBUG(("Firmware version rollback detected.\n")); + *check_result = VBSD_LF_CHECK_FW_ROLLBACK; + RSAPublicKeyFree(data_key); + continue; + } + + /* Header for this firmware is valid */ + *check_result = VBSD_LF_CHECK_HEADER_VALID; + + /* Check for lowest key version from a valid header. */ + if (lowest_version > combined_version) + lowest_version = combined_version; + + /* + * If we already have good firmware, no need to read another + * one; we only needed to look at the versions to check for + * rollback. + */ + if (-1 != good_index) { + RSAPublicKeyFree(data_key); + continue; + } + + /* Handle preamble flag for using the RO normal/dev code path */ + if (VbGetFirmwarePreambleFlags(preamble) & + VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) { + + /* Fail if calling firmware doesn't support RO normal */ + if (!(shared->flags & VBSD_BOOT_RO_NORMAL_SUPPORT)) { + *check_result = VBSD_LF_CHECK_NO_RO_NORMAL; + RSAPublicKeyFree(data_key); + continue; + } + + /* Use the RO normal code path */ + shared->flags |= VBSD_LF_USE_RO_NORMAL; + + } else { + VbError_t rv; + + /* Read the firmware data */ + VBPERFSTART("VB_RFD"); + DigestInit(&lfi->body_digest_context, + data_key->algorithm); + lfi->body_size_accum = 0; + rv = VbExHashFirmwareBody( + cparams, + (index ? VB_SELECT_FIRMWARE_B : + VB_SELECT_FIRMWARE_A)); + if (VBERROR_SUCCESS != rv) { + VBDEBUG(("VbExHashFirmwareBody() failed for " + "index %d\n", index)); + *check_result = VBSD_LF_CHECK_GET_FW_BODY; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_RFD"); + continue; + } + if (lfi->body_size_accum != + preamble->body_signature.data_size) { + VBDEBUG(("Hashed %d bytes but expected %d\n", + (int)lfi->body_size_accum, + (int)preamble->body_signature.data_size)); + *check_result = VBSD_LF_CHECK_HASH_WRONG_SIZE; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_RFD"); + continue; + } + VBPERFEND("VB_RFD"); + + /* Verify firmware data */ + VBPERFSTART("VB_VFD"); + body_digest = DigestFinal(&lfi->body_digest_context); + if (0 != VerifyDigest(body_digest, + &preamble->body_signature, + data_key)) { + VBDEBUG(("FW body verification failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_BODY; + RSAPublicKeyFree(data_key); + VbExFree(body_digest); + VBPERFEND("VB_VFD"); + continue; + } + VbExFree(body_digest); + VBPERFEND("VB_VFD"); + } + + /* Done with the data key, so can free it now */ + RSAPublicKeyFree(data_key); + + /* If we're still here, the firmware is valid. */ + VBDEBUG(("Firmware %d is valid.\n", index)); + *check_result = VBSD_LF_CHECK_VALID; + if (-1 == good_index) { + /* Save the key we actually used */ + if (0 != VbSharedDataSetKernelKey( + shared, &preamble->kernel_subkey)) { + /* + * The firmware signature was good, but the + * public key was bigger that the caller can + * handle. + */ + VBDEBUG(("Unable to save kernel subkey.\n")); + continue; + } + + /* + * Save the good index, now that we're sure we can + * actually use this firmware. That's the one we'll + * boot. + */ + good_index = index; + shared->firmware_index = (uint8_t)index; + shared->fw_keyblock_flags = key_block->key_block_flags; + + /* + * If the good firmware's key version is the same as + * the tpm, then the TPM doesn't need updating; we can + * stop now. Otherwise, we'll check all the other + * headers to see if they contain a newer key. + */ + if (combined_version == shared->fw_version_tpm) + break; + } + } + + /* Free internal data */ + VbExFree(lfi); + cparams->vboot_context = NULL; + + /* Handle finding good firmware */ + if (good_index >= 0) { + + /* Save versions we found */ + shared->fw_version_lowest = lowest_version; + if (lowest_version > shared->fw_version_tpm) + shared->fw_version_tpm = lowest_version; + + /* Success */ + VBDEBUG(("Will boot firmware index %d\n", + (int)shared->firmware_index)); + retval = VBERROR_SUCCESS; + + } else { + uint8_t a = shared->check_fw_a_result; + uint8_t b = shared->check_fw_b_result; + uint8_t best_check; + + /* No good firmware, so go to recovery mode. */ + VBDEBUG(("Alas, no good firmware.\n")); + recovery = VBNV_RECOVERY_RO_INVALID_RW; + retval = VBERROR_LOAD_FIRMWARE; + + /* + * If the best check result fits in the range of recovery + * reasons, provide more detail on how far we got in + * validation. + */ + best_check = (a > b ? a : b) + + VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN; + if (best_check >= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN && + best_check <= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MAX) + recovery = best_check; + } + + LoadFirmwareExit: + /* Store recovery request, if any */ + VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? + recovery : VBNV_RECOVERY_NOT_REQUESTED); + + return retval; } |