| Index: firmware/lib/rollback_index.c
|
| diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c
|
| index 4fb7ec97e2c7980e04071df48ee3f6525023c2e4..4629e6e4810de9b8c551e2ee72e9756312fa3a0c 100644
|
| --- a/firmware/lib/rollback_index.c
|
| +++ b/firmware/lib/rollback_index.c
|
| @@ -12,10 +12,7 @@
|
| #include "tss_constants.h"
|
| #include "utility.h"
|
|
|
| -uint16_t g_firmware_key_version = 0;
|
| -uint16_t g_firmware_version = 0;
|
| -uint16_t g_kernel_key_version = 0;
|
| -uint16_t g_kernel_version = 0;
|
| +static int g_rollback_recovery_mode = 0;
|
|
|
| /* disable MSVC warning on const logical expression (as in } while(0);) */
|
| __pragma(warning (disable: 4127))
|
| @@ -133,34 +130,6 @@ static uint32_t SetDistrustKernelSpaceAtNextBoot(uint32_t distrust) {
|
| return TPM_SUCCESS;
|
| }
|
|
|
| -static uint32_t GetTPMRollbackIndices(int type) {
|
| - uint32_t firmware_versions;
|
| - uint32_t kernel_versions;
|
| -
|
| - /* 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.
|
| - */
|
| - switch (type) {
|
| - case FIRMWARE_VERSIONS:
|
| - RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
|
| - (uint8_t*) &firmware_versions,
|
| - sizeof(firmware_versions)));
|
| - g_firmware_key_version = (uint16_t) (firmware_versions >> 16);
|
| - g_firmware_version = (uint16_t) (firmware_versions & 0xffff);
|
| - break;
|
| - case KERNEL_VERSIONS:
|
| - RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
|
| - (uint8_t*) &kernel_versions,
|
| - sizeof(kernel_versions)));
|
| - g_kernel_key_version = (uint16_t) (kernel_versions >> 16);
|
| - g_kernel_version = (uint16_t) (kernel_versions & 0xffff);
|
| - break;
|
| - }
|
| -
|
| - return TPM_SUCCESS;
|
| -}
|
| -
|
| /* Checks if the kernel version space has been mucked with. If it has,
|
| * reconstructs it using the backup value.
|
| */
|
| @@ -244,9 +213,30 @@ static uint32_t CheckDeveloperModeTransition(uint32_t current_developer) {
|
| return TPM_SUCCESS;
|
| }
|
|
|
| -static uint32_t SetupTPM_(int mode, int developer_flag) {
|
| +/* SetupTPM starts the TPM and establishes the root of trust for the
|
| + * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a
|
| + * TPM hardware failure. 3 An unexpected TPM state due to some attack. In
|
| + * general we cannot easily distinguish the kind of failure, so our strategy is
|
| + * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM
|
| + * again, which executes (almost) the same sequence of operations. There is a
|
| + * good chance that, if recovery mode was entered because of a TPM failure, the
|
| + * failure will repeat itself. (In general this is impossible to guarantee
|
| + * because we have no way of creating the exact TPM initial state at the
|
| + * previous boot.) In recovery mode, we ignore the failure and continue, thus
|
| + * giving the recovery kernel a chance to fix things (that's why we don't set
|
| + * bGlobalLock). The choice is between a knowingly insecure device and a
|
| + * bricked device.
|
| + *
|
| + * As a side note, observe that we go through considerable hoops to avoid using
|
| + * the STCLEAR permissions for the index spaces. We do this to avoid writing
|
| + * to the TPM flashram at every reboot or wake-up, because of concerns about
|
| + * the durability of the NVRAM.
|
| + */
|
| +static uint32_t SetupTPM(int recovery_mode,
|
| + int developer_mode) {
|
| uint8_t disable;
|
| uint8_t deactivated;
|
| +
|
| TlclLibInit();
|
| RETURN_ON_FAILURE(TlclStartup());
|
| RETURN_ON_FAILURE(TlclContinueSelfTest());
|
| @@ -272,136 +262,83 @@ static uint32_t SetupTPM_(int mode, int developer_flag) {
|
| }
|
| }
|
| RETURN_ON_FAILURE(BackupKernelSpace());
|
| - RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(mode == RO_RECOVERY_MODE));
|
| - RETURN_ON_FAILURE(GetTPMRollbackIndices(FIRMWARE_VERSIONS));
|
| - RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS));
|
| -
|
| - RETURN_ON_FAILURE(CheckDeveloperModeTransition(developer_flag));
|
| -
|
| - /* As a courtesy (I hope) to the caller, lock the firmware versions if we are
|
| - * in recovery mode. The normal mode may need to update the firmware
|
| - * versions, so they cannot be locked here.
|
| - */
|
| - if (mode == RO_RECOVERY_MODE) {
|
| - RETURN_ON_FAILURE(LockFirmwareVersions());
|
| - }
|
| - return TPM_SUCCESS;
|
| -}
|
| -
|
| -/* SetupTPM starts the TPM and establishes the root of trust for the
|
| - * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a
|
| - * TPM hardware failure. 3 An unexpected TPM state due to some attack. In
|
| - * general we cannot easily distinguish the kind of failure, so our strategy is
|
| - * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM
|
| - * again, which executes (almost) the same sequence of operations. There is a
|
| - * good chance that, if recovery mode was entered because of a TPM failure, the
|
| - * failure will repeat itself. (In general this is impossible to guarantee
|
| - * because we have no way of creating the exact TPM initial state at the
|
| - * previous boot.) In recovery mode, we ignore the failure and continue, thus
|
| - * giving the recovery kernel a chance to fix things (that's why we don't set
|
| - * bGlobalLock). The choice is between a knowingly insecure device and a
|
| - * bricked device.
|
| - *
|
| - * As a side note, observe that we go through considerable hoops to avoid using
|
| - * the STCLEAR permissions for the index spaces. We do this to avoid writing
|
| - * to the TPM flashram at every reboot or wake-up, because of concerns about
|
| - * the durability of the NVRAM.
|
| - */
|
| -uint32_t SetupTPM(int mode, int developer_flag) {
|
| - switch (mode) {
|
| - case RO_RECOVERY_MODE:
|
| - case RO_NORMAL_MODE: {
|
| - uint32_t result = SetupTPM_(mode, developer_flag);
|
| - if (mode == RO_NORMAL_MODE) {
|
| - return result;
|
| - } else {
|
| - /* In recovery mode we want to keep going even if there are errors. */
|
| - return TPM_SUCCESS;
|
| - }
|
| - }
|
| - case RW_NORMAL_MODE:
|
| - RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS));
|
| - default:
|
| - return TPM_E_INTERNAL_INCONSISTENCY;
|
| - }
|
| -}
|
| -
|
| -uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
|
| - /* TODO: should verify that SetupTPM() has been called.
|
| - *
|
| - * Note that SetupTPM() does hardware setup AND sets global variables. When
|
| - * we get down into kernel verification, the hardware setup persists, but we
|
| - * lose the global variables.
|
| - */
|
| - switch (type) {
|
| - case FIRMWARE_VERSIONS:
|
| - *key_version = g_firmware_key_version;
|
| - *version = g_firmware_version;
|
| - break;
|
| - case KERNEL_VERSIONS:
|
| - *key_version = g_kernel_key_version;
|
| - *version = g_kernel_version;
|
| - break;
|
| - }
|
| -
|
| - return TPM_SUCCESS;
|
| -}
|
| + RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(recovery_mode));
|
| + RETURN_ON_FAILURE(CheckDeveloperModeTransition(developer_mode));
|
|
|
| -uint32_t WriteStoredVersions(int type, uint16_t key_version, uint16_t version) {
|
| - uint32_t combined_version = (key_version << 16) & version;
|
| - switch (type) {
|
| - case FIRMWARE_VERSIONS:
|
| - RETURN_ON_FAILURE(SafeWrite(FIRMWARE_VERSIONS_NV_INDEX,
|
| - (uint8_t*) &combined_version,
|
| - sizeof(uint32_t)));
|
| - break;
|
| -
|
| - case KERNEL_VERSIONS:
|
| - RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_NV_INDEX,
|
| - (uint8_t*) &combined_version,
|
| - sizeof(uint32_t)));
|
| + if (recovery_mode) {
|
| + /* In recovery mode global variables are usable. */
|
| + g_rollback_recovery_mode = 1;
|
| }
|
| return TPM_SUCCESS;
|
| }
|
|
|
| -uint32_t LockFirmwareVersions() {
|
| - return TlclSetGlobalLock();
|
| -}
|
| -
|
| -uint32_t LockKernelVersionsByLockingPP() {
|
| - return TlclLockPhysicalPresence();
|
| -}
|
| -
|
| /* disable MSVC warnings on unused arguments */
|
| __pragma(warning (disable: 4100))
|
|
|
| -/* NEW APIS! HELP ME LUIGI, YOU'RE MY ONLY HOPE! */
|
| +uint32_t RollbackFirmwareSetup(int developer_mode) {
|
| + return SetupTPM(0, developer_mode);
|
| +}
|
|
|
| -uint32_t RollbackFirmwareSetup(int developer_mode,
|
| - uint16_t* key_version, uint16_t* version) {
|
| +uint32_t RollbackFirmwareRead(uint16_t* key_version, uint16_t* version) {
|
| + uint32_t firmware_versions;
|
| + /* Gets firmware versions. */
|
| + RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &firmware_versions,
|
| + sizeof(firmware_versions)));
|
| + *key_version = (uint16_t) (firmware_versions >> 16);
|
| + *version = (uint16_t) (firmware_versions & 0xffff);
|
| return TPM_SUCCESS;
|
| }
|
|
|
| uint32_t RollbackFirmwareWrite(uint16_t key_version, uint16_t version) {
|
| - return TPM_SUCCESS;
|
| + uint32_t combined_version = (key_version << 16) & version;
|
| + return SafeWrite(FIRMWARE_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &combined_version,
|
| + sizeof(uint32_t));
|
| }
|
|
|
| uint32_t RollbackFirmwareLock(void) {
|
| - return TPM_SUCCESS;
|
| + return TlclSetGlobalLock();
|
| }
|
|
|
| uint32_t RollbackKernelRecovery(int developer_mode) {
|
| + uint32_t result = SetupTPM(1, developer_mode);
|
| + if (result == TPM_SUCCESS) {
|
| + RETURN_ON_FAILURE(TlclSetGlobalLock());
|
| + }
|
| return TPM_SUCCESS;
|
| }
|
|
|
| uint32_t RollbackKernelRead(uint16_t* key_version, uint16_t* version) {
|
| + uint32_t kernel_versions;
|
| + if (g_rollback_recovery_mode) {
|
| + *key_version = 0;
|
| + *version = 0;
|
| + } else {
|
| + /* Reads kernel versions from TPM. */
|
| + RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &kernel_versions,
|
| + sizeof(kernel_versions)));
|
| + *key_version = (uint16_t) (kernel_versions >> 16);
|
| + *version = (uint16_t) (kernel_versions & 0xffff);
|
| + }
|
| return TPM_SUCCESS;
|
| }
|
|
|
| uint32_t RollbackKernelWrite(uint16_t key_version, uint16_t version) {
|
| + if (!g_rollback_recovery_mode) {
|
| + uint32_t combined_version = (key_version << 16) & version;
|
| + return SafeWrite(KERNEL_VERSIONS_NV_INDEX,
|
| + (uint8_t*) &combined_version,
|
| + sizeof(uint32_t));
|
| + }
|
| return TPM_SUCCESS;
|
| }
|
|
|
| uint32_t RollbackKernelLock(void) {
|
| - return TPM_SUCCESS;
|
| + if (!g_rollback_recovery_mode) {
|
| + return TlclLockPhysicalPresence();
|
| + } else {
|
| + return TPM_SUCCESS;
|
| + }
|
| }
|
|
|