/* 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. * * High-level firmware API for loading and verifying rewritable firmware. * (Firmware portion) */ #include "sysincludes.h" #include "region.h" #include "gbb_access.h" #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 = cparams->gbb; VbPublicKey *root_key = NULL; 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 */ retval = VbGbbReadRootKey(cparams, &root_key); if (retval) { VBDEBUG(("No GBB\n")); retval = VBERROR_INVALID_GBB; goto LoadFirmwareExit; } /* 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 */ if ((0 != KeyBlockVerify(key_block, vblock_size, root_key, 0))) { VBDEBUG(("Key block verification failed.\n")); *check_result = VBSD_LF_CHECK_VERIFY_KEYBLOCK; continue; } /* 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. */ 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); continue; } /* 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 */ VBDEBUG(("Preamble flags %#x\n", VbGetFirmwarePreambleFlags(preamble))); 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)) { VBDEBUG(("No RO normal support.\n")); *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 */ 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); 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); continue; } /* Verify firmware data */ 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); continue; } VbExFree(body_digest); } /* 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: VbExFree(root_key); /* Store recovery request, if any */ VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? recovery : VBNV_RECOVERY_NOT_REQUESTED); /* If the system does not support RO_NORMAL and LoadFirmware() * encountered an error, update the shared recovery reason if * recovery was not previously requested. */ if (!(shared->flags & VBSD_BOOT_RO_NORMAL_SUPPORT) && VBNV_RECOVERY_NOT_REQUESTED == shared->recovery_reason && VBERROR_SUCCESS != retval) { VBDEBUG(("RO normal but we got an error.\n")); shared->recovery_reason = recovery; } return retval; }