/* Copyright (c) 2012 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. * * High-level firmware API for loading and verifying rewritable firmware. * (Firmware portion) */ #include "gbb_header.h" #include "load_firmware_fw.h" #include "utility.h" #include "vboot_api.h" #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. */ typedef struct VbLoadFirmwareInternal { 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; 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; }