Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Unified Diff: vboot_firmware/lib/rollback_index.c

Issue 2792009: Fix normal/recovery mode, and RO firmware vs. RW firmware behavior. (Closed) Base URL: ssh://git@chromiumos-git/vboot_reference.git
Patch Set: add a comment Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: vboot_firmware/lib/rollback_index.c
diff --git a/vboot_firmware/lib/rollback_index.c b/vboot_firmware/lib/rollback_index.c
index 3d1553ca0d08f6eb5d7181161cfb2d1a61c1ebdb..b09ea64a6d30c7b3cb4f18baa0657238e987e792 100644
--- a/vboot_firmware/lib/rollback_index.c
+++ b/vboot_firmware/lib/rollback_index.c
@@ -34,6 +34,10 @@ static uint32_t InitializeKernelVersionsSpaces(void) {
return TPM_SUCCESS;
}
+/* When the return value is TPM_SUCCESS, this function sets *|initialized| to 1
+ * if the spaces have been fully initialized, to 0 if not. Otherwise
+ * *|initialized| is not changed.
+ */
static uint32_t GetSpacesInitialized(int* initialized) {
uint32_t space_holder;
uint32_t result;
@@ -51,6 +55,8 @@ static uint32_t GetSpacesInitialized(int* initialized) {
return result;
}
+/* Creates the NVRAM spaces, and sets their initial values as needed.
+ */
static uint32_t InitializeSpaces(void) {
uint32_t zero = 0;
uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE;
@@ -67,9 +73,8 @@ static uint32_t InitializeSpaces(void) {
RETURN_ON_FAILURE(InitializeKernelVersionsSpaces());
/* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel
- * versions when entering recovery mode. The content of space
- * KERNEL_MUST_USE_BACKUP determines whether the backup value (1) or the
- * regular value (0) should be trusted.
+ * versions. The content of space KERNEL_MUST_USE_BACKUP determines whether
+ * only the backup value should be trusted.
*/
RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
firmware_perm, sizeof(uint32_t)));
@@ -79,6 +84,10 @@ static uint32_t InitializeSpaces(void) {
firmware_perm, sizeof(uint32_t)));
RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
(uint8_t*) &zero, sizeof(uint32_t)));
+ RETURN_ON_FAILURE(TlclDefineSpace(DEVELOPER_MODE_NV_INDEX,
+ firmware_perm, sizeof(uint32_t)));
+ RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_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
@@ -90,68 +99,18 @@ static uint32_t InitializeSpaces(void) {
return TPM_SUCCESS;
}
-/* 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.
- */
-void EnterRecovery(int unlocked) {
- uint32_t combined_versions;
- uint32_t backup_versions;
+static uint32_t SetDistrustKernelSpaceAtNextBoot(uint32_t distrust) {
uint32_t must_use_backup;
- uint32_t result;
-
- if (!unlocked) {
- /* Saves the kernel versions and indicates that we should trust the saved
- * ones.
- */
- if (TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions,
- sizeof(uint32_t)) != TPM_SUCCESS)
- goto recovery_mode;
- if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &backup_versions,
- sizeof(uint32_t)) != TPM_SUCCESS)
- goto recovery_mode;
- /* Avoids idempotent writes. */
- if (combined_versions != backup_versions) {
- result = TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
- (uint8_t*) &combined_versions, sizeof(uint32_t));
- if (result == TPM_E_MAXNVWRITES) {
- goto forceclear_and_reboot;
- } else if (result != TPM_SUCCESS) {
- goto recovery_mode;
- }
- }
-
- if (TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, (uint8_t*) &must_use_backup,
- sizeof(uint32_t)) != TPM_SUCCESS)
- goto recovery_mode;
- if (must_use_backup != 1) {
- must_use_backup = 1;
- result = TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
- (uint8_t*) &must_use_backup, sizeof(uint32_t));
- if (result == TPM_E_MAXNVWRITES) {
- goto forceclear_and_reboot;
- } else if (result != TPM_SUCCESS) {
- goto recovery_mode;
- }
- }
- /* Protects the firmware and backup kernel versions. */
- if (LockFirmwareVersions() != TPM_SUCCESS)
- goto recovery_mode;
- }
-
- recovery_mode:
- debug("entering recovery mode");
-
- /* TODO(nelson): code for entering recovery mode. */
-
- forceclear_and_reboot:
- if (TlclForceClear() != TPM_SUCCESS) {
- goto recovery_mode;
+ RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &must_use_backup, sizeof(uint32_t)));
+ if (must_use_backup != distrust) {
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &distrust, sizeof(uint32_t)));
}
- /* TODO: reboot */
+ return TPM_SUCCESS;
}
-static uint32_t GetTPMRollbackIndices(void) {
+static uint32_t GetTPMRollbackIndices(int type) {
uint32_t firmware_versions;
uint32_t kernel_versions;
@@ -159,17 +118,22 @@ static uint32_t GetTPMRollbackIndices(void) {
* rollback index locations are missing or somehow messed up. We let the
* caller deal with that.
*/
- RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
- (uint8_t*) &firmware_versions,
- sizeof(firmware_versions)));
- RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
- (uint8_t*) &kernel_versions,
- sizeof(kernel_versions)));
-
- 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;
+ switch (type) {
+ case FIRMWARE_VERSIONS:
+ RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
+ (uint8_t*) &firmware_versions,
+ sizeof(firmware_versions)));
+ g_firmware_key_version = firmware_versions >> 16;
+ g_firmware_version = 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 = kernel_versions >> 16;
+ g_kernel_version = kernel_versions && 0xffff;
+ break;
+ }
return TPM_SUCCESS;
}
@@ -241,7 +205,31 @@ static uint32_t BackupKernelSpace(void) {
return TPM_SUCCESS;
}
-static uint32_t SetupTPM_(void) {
+/* Checks for transitions between protected mode to developer mode. When going
+ * into developer mode, clear the TPM.
+ */
+static uint32_t CheckDeveloperModeTransition(uint32_t current_developer) {
+ uint32_t past_developer;
+ int must_clear;
+ RETURN_ON_FAILURE(TlclRead(DEVELOPER_MODE_NV_INDEX,
+ (uint8_t*) &past_developer,
+ sizeof(past_developer)));
+ must_clear = current_developer && !past_developer;
+ if (must_clear) {
+ RETURN_ON_FAILURE(TlclForceClear());
+ }
+ if (past_developer != current_developer) {
+ /* (Unauthorized) writes to the TPM succeed even when the TPM is disabled
+ * and deactivated.
+ */
+ RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX,
+ (uint8_t*) &current_developer,
+ sizeof(current_developer)));
+ }
+ return must_clear ? TPM_E_MUST_REBOOT : TPM_SUCCESS;
+}
+
+static uint32_t SetupTPM_(int mode, int developer_flag) {
uint8_t disable;
uint8_t deactivated;
TlclLibInit();
@@ -253,11 +241,11 @@ static uint32_t SetupTPM_(void) {
if (disable || deactivated) {
RETURN_ON_FAILURE(TlclSetEnable());
RETURN_ON_FAILURE(TlclSetDeactivated(0));
- /* TODO: Reboot */
- return 9999;
+ return TPM_E_MUST_REBOOT;
}
- /* We expect this to fail the first time we run on a device, indicating that
- * the TPM has not been initialized yet. */
+ /* We expect this to fail the first time we run on a device, because the TPM
+ * has not been initialized yet.
+ */
if (RecoverKernelSpace() != TPM_SUCCESS) {
int initialized = 0;
RETURN_ON_FAILURE(GetSpacesInitialized(&initialized));
@@ -269,32 +257,73 @@ static uint32_t SetupTPM_(void) {
}
}
RETURN_ON_FAILURE(BackupKernelSpace());
- RETURN_ON_FAILURE(GetTPMRollbackIndices());
+ 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;
}
-uint32_t SetupTPM(void) {
- uint32_t result = SetupTPM_();
- if (result == TPM_E_MAXNVWRITES) {
- /* ForceClears and reboots */
- RETURN_ON_FAILURE(TlclForceClear());
- /* TODO: reboot */
- return 9999;
- } else {
- return result;
+/* 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 (result == TPM_E_MAXNVWRITES) {
+ /* ForceClears and reboots */
+ RETURN_ON_FAILURE(TlclForceClear());
+ return TPM_E_MUST_REBOOT;
+ } else 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:
+ /* There are no TPM writes here, so no need to check for write limit errors.
+ */
+ 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 don't have access to the global variables. So I guess we DO need
- * to call SetupTPM() there, and have it be smart enough not to redo the
- * hardware init, but it still needs to re-read the flags... */
-
+ /* 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;

Powered by Google App Engine
This is Rietveld 408576698