diff --git a/tests/rollback_index_mock.c b/tests/rollback_index_mock.c index c7c6d6ff1c..9640e316b1 100644 --- a/tests/rollback_index_mock.c +++ b/tests/rollback_index_mock.c @@ -21,41 +21,32 @@ void SetupTPM(void) { #endif } -uint16_t GetStoredVersion(int type) { +void GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) { switch (type) { - case FIRMWARE_KEY_VERSION: - return g_firmware_key_version; + case FIRMWARE_VERSIONS: + *key_version = g_firmware_key_version; + *version = g_firmware_version; break; - case FIRMWARE_VERSION: - return g_firmware_version; - break; - case KERNEL_KEY_VERSION: - return g_kernel_key_version; - break; - case KERNEL_VERSION: - return g_kernel_version; + case KERNEL_VERSIONS: + *key_version = g_kernel_key_version; + *version = g_kernel_version; break; } - return 0; } -int WriteStoredVersion(int type, uint16_t version) { +int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { switch (type) { - case FIRMWARE_KEY_VERSION: - g_firmware_key_version = version; - break; - case FIRMWARE_VERSION: + case FIRMWARE_VERSIONS: + g_firmware_key_version = key_version; g_firmware_version = version; break; - case KERNEL_KEY_VERSION: - g_kernel_key_version = version; - break; - case KERNEL_VERSION: + case KERNEL_VERSIONS: + g_kernel_key_version = key_version; g_kernel_version = version; break; } #ifndef NDEBUG - debug("Rollback Index Library Mock: Stored Version written.\n"); + debug("Rollback Index Library Mock: Stored Versions written.\n"); #endif return 1; } diff --git a/vboot_firmware/include/rollback_index.h b/vboot_firmware/include/rollback_index.h index 2633ab7513..2096474d55 100644 --- a/vboot_firmware/include/rollback_index.h +++ b/vboot_firmware/include/rollback_index.h @@ -17,20 +17,20 @@ extern uint16_t g_kernel_key_version; extern uint16_t g_kernel_version; /* Rollback version types. */ -#define FIRMWARE_KEY_VERSION 0 -#define FIRMWARE_VERSION 1 -#define KERNEL_KEY_VERSION 2 -#define KERNEL_VERSION 3 +#define FIRMWARE_VERSIONS 0 +#define KERNEL_VERSIONS 1 /* TPM NVRAM location indices. */ -#define FIRMWARE_KEY_VERSION_NV_INDEX 0x1001 -#define FIRMWARE_VERSION_NV_INDEX 0x1002 -#define KERNEL_KEY_VERSION_NV_INDEX 0x1003 -#define KERNEL_VERSION_NV_INDEX 0x1004 +#define FIRMWARE_VERSIONS_NV_INDEX 0x1001 +#define KERNEL_VERSIONS_NV_INDEX 0x1002 +#define TPM_IS_INITIALIZED_NV_INDEX 0x1003 +#define KERNEL_VERSIONS_BACKUP_NV_INDEX 0x1004 +#define KERNEL_BACKUP_IS_VALID_NV_INDEX 0x1005 + void SetupTPM(void); -uint16_t GetStoredVersion(int type); -int WriteStoredVersion(int type, uint16_t version); +void GetStoredVersions(int type, uint16_t* key_version, uint16_t* version); +int WriteStoredVersions(int type, uint16_t key_version, uint16_t version); void LockFirmwareVersions(); void LockKernelVersionsByLockingPP(); diff --git a/vboot_firmware/lib/firmware_image_fw.c b/vboot_firmware/lib/firmware_image_fw.c index a8cb646a96..9999ed6663 100644 --- a/vboot_firmware/lib/firmware_image_fw.c +++ b/vboot_firmware/lib/firmware_image_fw.c @@ -252,6 +252,7 @@ int VerifyFirmwareDriver_f(uint8_t* root_key_blob, uint8_t firmwareA_is_verified = 0; /* Whether firmwareA verify succeeded. */ uint32_t min_lversion; /* Minimum of firmware A and firmware lversion. */ uint32_t stored_lversion; /* Stored logical version in the TPM. */ + uint16_t version, key_version; /* Temporary variables */ /* Initialize the TPM since we'll be reading the rollback indices. */ SetupTPM(); @@ -265,8 +266,8 @@ int VerifyFirmwareDriver_f(uint8_t* root_key_blob, firmwareA_lversion = GetLogicalFirmwareVersion(verification_headerA); firmwareB_lversion = GetLogicalFirmwareVersion(verification_headerB); min_lversion = Min(firmwareA_lversion, firmwareB_lversion); - stored_lversion = CombineUint16Pair(GetStoredVersion(FIRMWARE_KEY_VERSION), - GetStoredVersion(FIRMWARE_VERSION)); + GetStoredVersions(FIRMWARE_VERSIONS, &key_version, &version); + stored_lversion = CombineUint16Pair(key_version, version); /* Always try FirmwareA first. */ if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, verification_headerA, @@ -280,10 +281,9 @@ int VerifyFirmwareDriver_f(uint8_t* root_key_blob, if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, verification_headerB, firmwareB)) { - WriteStoredVersion(FIRMWARE_KEY_VERSION, - (uint16_t) (min_lversion >> 16)); - WriteStoredVersion(FIRMWARE_VERSION, - (uint16_t) (min_lversion & 0x00FFFF)); + WriteStoredVersions(FIRMWARE_VERSIONS, + (uint16_t) (min_lversion >> 16), + (uint16_t) (min_lversion & 0xFFFF)); stored_lversion = min_lversion; /* Update stored version as it's used * later. */ } diff --git a/vboot_firmware/lib/include/tss_constants.h b/vboot_firmware/lib/include/tss_constants.h index e14e2e0c38..69873a64b2 100644 --- a/vboot_firmware/lib/include/tss_constants.h +++ b/vboot_firmware/lib/include/tss_constants.h @@ -24,6 +24,8 @@ #define TPM_LARGE_ENOUGH_COMMAND_SIZE 256 /* saves space in the firmware */ #define TPM_SUCCESS ((uint32_t)0x00000000) +#define TPM_E_BADINDEX ((uint32_t)0x00000002) + #define TPM_NV_INDEX0 ((uint32_t)0x00000000) #define TPM_NV_INDEX_LOCK ((uint32_t)0xffffffff) #define TPM_NV_PER_WRITE_STCLEAR (((uint32_t)1)<<14) diff --git a/vboot_firmware/lib/kernel_image_fw.c b/vboot_firmware/lib/kernel_image_fw.c index 35ab330ab4..8c1ca31419 100644 --- a/vboot_firmware/lib/kernel_image_fw.c +++ b/vboot_firmware/lib/kernel_image_fw.c @@ -397,6 +397,7 @@ int VerifyKernelDriver_f(uint8_t* firmware_key_blob, kernel_entry* try_kernel[2]; /* Kernel in try order. */ int try_kernel_which[2]; /* Which corresponding kernel in the try order */ uint32_t try_kernel_lversion[2]; /* Their logical versions. */ + uint16_t kernel_version, kernel_key_version; /* Temporary variables */ /* [kernel_to_boot] will eventually contain the boot path to follow * and is returned to the caller. Initially, we set it to recovery. If @@ -415,8 +416,8 @@ int VerifyKernelDriver_f(uint8_t* firmware_key_blob, kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob); kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob); min_lversion = Min(kernelA_lversion, kernelB_lversion); - stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION), - GetStoredVersion(KERNEL_VERSION)); + GetStoredVersions(KERNEL_VERSIONS, &kernel_key_version, &kernel_version); + stored_lversion = CombineUint16Pair(kernel_key_version, kernel_version); /* TODO(gauravsh): The kernel entries kernelA and kernelB come from the * partition table - verify its signature/checksum before proceeding @@ -464,10 +465,9 @@ int VerifyKernelDriver_f(uint8_t* firmware_key_blob, if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob, try_kernel[1]->kernel_blob, dev_mode)) { - WriteStoredVersion(KERNEL_KEY_VERSION, - (uint16_t) (min_lversion >> 16)); - WriteStoredVersion(KERNEL_VERSION, - (uint16_t) (min_lversion & 0xFFFF)); + WriteStoredVersions(KERNEL_VERSIONS, + (uint16_t) (min_lversion >> 16), + (uint16_t) (min_lversion & 0xFFFF)); stored_lversion = min_lversion; /* Update stored version as it's * used later. */ } diff --git a/vboot_firmware/lib/rollback_index.c b/vboot_firmware/lib/rollback_index.c index 89e97a3ce9..55d97d9283 100644 --- a/vboot_firmware/lib/rollback_index.c +++ b/vboot_firmware/lib/rollback_index.c @@ -19,50 +19,138 @@ uint16_t g_firmware_version = 0; uint16_t g_kernel_key_version = 0; uint16_t g_kernel_version = 0; -static void InitializeSpaces(void) { - uint16_t zero = 0; +static int InitializeSpaces(void) { + uint32_t zero = 0; + uint32_t space_holder; uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE; uint32_t kernel_perm = TPM_NV_PER_PPWRITE; debug("Initializing spaces\n"); - TlclSetNvLocked(); /* useful only the first time */ - TlclDefineSpace(FIRMWARE_KEY_VERSION_NV_INDEX, - firmware_perm, sizeof(uint16_t)); - TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + if (TlclRead(TPM_IS_INITIALIZED_NV_INDEX, + (uint8_t*) &space_holder, sizeof(space_holder)) == TPM_SUCCESS) { + /* Spaces are already initialized, so this is an error */ + return 0; + } + + TlclSetNvLocked(); - TlclDefineSpace(FIRMWARE_VERSION_NV_INDEX, firmware_perm, sizeof(uint16_t)); - TlclWrite(FIRMWARE_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t)); + TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); - TlclDefineSpace(KERNEL_KEY_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t)); - TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, kernel_perm, sizeof(uint32_t)); + TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); - TlclDefineSpace(KERNEL_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t)); - TlclWrite(KERNEL_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); + /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel + * versions when entering recovery mode. The content of space + * KERNEL_BACKUP_IS_VALID determines whether the backup value (1) or the + * regular value (0) should be trusted. + */ + TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX, + firmware_perm, sizeof(uint32_t)); + TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, + (uint8_t*) &zero, sizeof(uint32_t)); + TlclDefineSpace(KERNEL_BACKUP_IS_VALID_NV_INDEX, + firmware_perm, sizeof(uint32_t)); + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, + (uint8_t*) &zero, sizeof(uint32_t)); + + /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM + * initialization has completed. Without it we cannot be sure that the last + * space to be created was also initialized (power could have been lost right + * after its creation). + */ + TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX, firmware_perm, sizeof(uint32_t)); + return 1; } -static void EnterRecovery(void) { - /* Temporary recovery stub. Currently just initalizes spaces. */ - InitializeSpaces(); +/* Enters the recovery mode. If |unlocked| is true, there is some problem with + * the TPM, so do not attempt to do any more TPM operations, and particularly + * do not set bGlobalLock. + */ +static void EnterRecovery(int unlocked) { + uint32_t combined_versions; + uint32_t one = 1; + + if (!unlocked) { + /* Saves the kernel versions and indicates that we should trust the saved + * ones. + */ + TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions, + sizeof(uint32_t)); + TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &combined_versions, + sizeof(uint32_t)); + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &one, + sizeof(uint32_t)); + /* Protects the firmware and backup kernel versions. */ + LockFirmwareVersions(); + } + debug("entering recovery mode"); + + /* and then what? */ } static int GetTPMRollbackIndices(void) { - /* We just perform the reads, making sure they succeed. A failure means that - * the rollback index locations are some how messed up and we must jump to - * recovery */ - if (TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, - (uint8_t*) &g_firmware_key_version, - sizeof(g_firmware_key_version)) || - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, - (uint8_t*) &g_firmware_key_version, - sizeof(g_firmware_key_version)) || - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, - (uint8_t*) &g_firmware_key_version, - sizeof(g_firmware_key_version)) || - TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, - (uint8_t*) &g_firmware_key_version, - sizeof(g_firmware_key_version))) + uint32_t backup_is_valid; + uint32_t firmware_versions; + uint32_t kernel_versions; + + if (TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid, + sizeof(uint32_t)) != TPM_SUCCESS) { + EnterRecovery(1); + } + if (backup_is_valid) { + /* We reach this path if the previous boot went into recovery mode and we + * made a copy of the kernel versions to protect them. + */ + uint32_t protected_combined_versions; + uint32_t unsafe_combined_versions; + uint32_t result; + uint32_t zero = 0; + if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, + (uint8_t*) &protected_combined_versions, + sizeof(uint32_t)) != TPM_SUCCESS) { + EnterRecovery(1); + } + result = TlclRead(KERNEL_VERSIONS_NV_INDEX, + (uint8_t*) &unsafe_combined_versions, sizeof(uint32_t)); + if (result == TPM_E_BADINDEX) { + /* Jeez, someone removed the space. This is either hostile or extremely + * incompetent. Foo to them. Politeness and lack of an adequate + * character set prevent me from expressing my true feelings. + */ + TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, TPM_NV_PER_PPWRITE, + sizeof(uint32_t)); + } else if (result != TPM_SUCCESS) { + EnterRecovery(1); + } + if (result == TPM_E_BADINDEX || + protected_combined_versions != unsafe_combined_versions) { + TlclWrite(KERNEL_VERSIONS_NV_INDEX, + (uint8_t*) &protected_combined_versions, sizeof(uint32_t)); + } + /* We recovered and now we can reset the BACKUP_IS_VALID flag. + */ + TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0); + } + + /* We perform the reads, making sure they succeed. A failure means that the + * rollback index locations are missing or somehow messed up. We let the + * caller deal with that. + */ + if (TPM_SUCCESS != TlclRead(FIRMWARE_VERSIONS_NV_INDEX, + (uint8_t*) &firmware_versions, + sizeof(firmware_versions)) || + TPM_SUCCESS != TlclRead(KERNEL_VERSIONS_NV_INDEX, + (uint8_t*) &kernel_versions, + sizeof(kernel_versions))) return 0; + + g_firmware_key_version = firmware_versions >> 16; + g_firmware_version = firmware_versions && 0xffff; + g_kernel_key_version = kernel_versions >> 16; + g_kernel_version = kernel_versions && 0xffff; + return 1; } @@ -84,61 +172,52 @@ void SetupTPM(void) { /* Check that the TPM is enabled and activated. */ if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) { debug("failed to get TPM flags"); - EnterRecovery(); + EnterRecovery(1); } if (disable || deactivated) { TlclSetEnable(); if (TlclSetDeactivated(0) != TPM_SUCCESS) { debug("failed to activate TPM"); - EnterRecovery(); + EnterRecovery(1); } } + /* We expect this to fail the first time we run on a device, indicating that + * the TPM has not been initialized yet. */ if (!GetTPMRollbackIndices()) { debug("failed to get rollback indices"); - EnterRecovery(); + if (!InitializeSpaces()) { + /* If InitializeSpaces() fails (possibly because it had been executed + * already), something is wrong. */ + EnterRecovery(1); + } } } - -uint16_t GetStoredVersion(int type) { +void GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) { switch (type) { - case FIRMWARE_KEY_VERSION: - return g_firmware_key_version; + case FIRMWARE_VERSIONS: + *key_version = g_firmware_key_version; + *version = g_firmware_version; break; - case FIRMWARE_VERSION: - return g_firmware_version; - break; - case KERNEL_KEY_VERSION: - return g_kernel_key_version; - break; - case KERNEL_VERSION: - return g_kernel_version; + case KERNEL_VERSIONS: + *key_version = g_kernel_key_version; + *version = g_kernel_version; break; } - return 0; } -int WriteStoredVersion(int type, uint16_t version) { +int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { + uint32_t combined_version = (key_version << 16) & version; switch (type) { - case FIRMWARE_KEY_VERSION: - return (TPM_SUCCESS == TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, - (uint8_t*) &version, - sizeof(uint16_t))); + case FIRMWARE_VERSIONS: + return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, + (uint8_t*) &combined_version, + sizeof(uint32_t))); break; - case FIRMWARE_VERSION: - return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSION_NV_INDEX, - (uint8_t*) &version, - sizeof(uint16_t))); - break; - case KERNEL_KEY_VERSION: - return (TPM_SUCCESS == TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, - (uint8_t*) &version, - sizeof(uint16_t))); - break; - case KERNEL_VERSION: - return (TPM_SUCCESS == TlclWrite(KERNEL_VERSION_NV_INDEX, - (uint8_t*) &version, - sizeof(uint16_t))); + case KERNEL_VERSIONS: + return (TPM_SUCCESS == TlclWrite(KERNEL_VERSIONS_NV_INDEX, + (uint8_t*) &combined_version, + sizeof(uint32_t))); break; } return 0; @@ -147,13 +226,13 @@ int WriteStoredVersion(int type, uint16_t version) { void LockFirmwareVersions() { if (TlclSetGlobalLock() != TPM_SUCCESS) { debug("failed to set global lock"); - EnterRecovery(); + EnterRecovery(1); } } void LockKernelVersionsByLockingPP() { if (TlclLockPhysicalPresence() != TPM_SUCCESS) { debug("failed to turn off PP"); - EnterRecovery(); + EnterRecovery(1); } } diff --git a/vboot_firmware/linktest/main.c b/vboot_firmware/linktest/main.c index cb7c5a9b79..757f65e9ac 100644 --- a/vboot_firmware/linktest/main.c +++ b/vboot_firmware/linktest/main.c @@ -9,6 +9,8 @@ int main(void) { + uint16_t x, y; + // cgptlib.h GptInit(0); GptNextKernelEntry(0, 0, 0); @@ -36,8 +38,8 @@ int main(void) // rollback_index.h SetupTPM(); - GetStoredVersion(0); - WriteStoredVersion(0, 0); + GetStoredVersions(0, &x, &y); + WriteStoredVersions(0, 0, 0); LockFirmwareVersions(); LockKernelVersionsByLockingPP();