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

Unified Diff: vboot_firmware/lib/rollback_index.c

Issue 2786005: Protect the kernel version space from redefinition. (Closed) Base URL: ssh://git@chromiumos-git/vboot_reference.git
Patch Set: several small fixes 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
« no previous file with comments | « vboot_firmware/lib/include/tss_constants.h ('k') | vboot_firmware/stub/tlcl.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: vboot_firmware/lib/rollback_index.c
diff --git a/vboot_firmware/lib/rollback_index.c b/vboot_firmware/lib/rollback_index.c
index e15b07ed700fbc5881fe9a4743e4e999a2ebd727..3d1553ca0d08f6eb5d7181161cfb2d1a61c1ebdb 100644
--- a/vboot_firmware/lib/rollback_index.c
+++ b/vboot_firmware/lib/rollback_index.c
@@ -19,27 +19,44 @@ uint16_t g_firmware_version = 0;
uint16_t g_kernel_key_version = 0;
uint16_t g_kernel_version = 0;
-#define RETURN_ON_FAILURE(tpm_command) do { \
- uint32_t result; \
- if ((result = tpm_command) != TPM_SUCCESS) {\
- return result; \
- } \
+#define RETURN_ON_FAILURE(tpm_command) do { \
+ uint32_t result; \
+ if ((result = (tpm_command)) != TPM_SUCCESS) { \
+ return result; \
+ } \
} while (0)
+static uint32_t InitializeKernelVersionsSpaces(void) {
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
+ TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE));
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA,
+ KERNEL_SPACE_SIZE));
+ return TPM_SUCCESS;
+}
+
+static uint32_t GetSpacesInitialized(int* initialized) {
+ uint32_t space_holder;
+ uint32_t result;
+ result = TlclRead(TPM_IS_INITIALIZED_NV_INDEX,
+ (uint8_t*) &space_holder, sizeof(space_holder));
+ switch (result) {
+ case TPM_SUCCESS:
+ *initialized = 1;
+ break;
+ case TPM_E_BADINDEX:
+ *initialized = 0;
+ result = TPM_SUCCESS;
+ break;
+ }
+ return result;
+}
+
static uint32_t 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");
- 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;
- }
-
RETURN_ON_FAILURE(TlclSetNvLocked());
RETURN_ON_FAILURE(TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX,
@@ -47,23 +64,20 @@ static uint32_t InitializeSpaces(void) {
RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
(uint8_t*) &zero, sizeof(uint32_t)));
- RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
- kernel_perm, sizeof(uint32_t)));
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero,
- sizeof(uint32_t)));
+ 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_BACKUP_IS_VALID determines whether the backup value (1) or the
+ * KERNEL_MUST_USE_BACKUP determines whether the backup value (1) or the
* regular value (0) should be trusted.
*/
RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
firmware_perm, sizeof(uint32_t)));
RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
(uint8_t*) &zero, sizeof(uint32_t)));
- RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_BACKUP_IS_VALID_NV_INDEX,
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_MUST_USE_BACKUP_NV_INDEX,
firmware_perm, sizeof(uint32_t)));
- RETURN_ON_FAILURE(TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX,
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
(uint8_t*) &zero, sizeof(uint32_t)));
/* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM
@@ -80,99 +94,67 @@ static uint32_t InitializeSpaces(void) {
* the TPM, so do not attempt to do any more TPM operations, and particularly
* do not set bGlobalLock.
*/
-static void EnterRecovery(int unlocked) {
+void EnterRecovery(int unlocked) {
uint32_t combined_versions;
uint32_t backup_versions;
- uint32_t backup_is_valid;
+ uint32_t must_use_backup;
+ uint32_t result;
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));
- TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
- (uint8_t*) &backup_versions, sizeof(uint32_t));
- /* We could unconditional writes of both KERNEL_VERSIONS_BACKUP and
- * KERNEL_BACKUP_IS_VALID, but this is more robust.
- */
+ 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) {
- TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
- (uint8_t*) &combined_versions, sizeof(uint32_t));
+ 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;
+ }
}
- TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX,
- (uint8_t*) &backup_is_valid, sizeof(uint32_t));
- if (backup_is_valid != 1) {
- backup_is_valid = 1;
- TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid,
- sizeof(uint32_t));
+ 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. */
- LockFirmwareVersions();
+ 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;
+ }
+ /* TODO: reboot */
}
static uint32_t GetTPMRollbackIndices(void) {
- 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.
- */
- RETURN_ON_FAILURE(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) {
- RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
- (uint8_t*) &protected_combined_versions,
- sizeof(uint32_t)));
- }
- /* We recovered the backed-up versions and now we can reset the
- * BACKUP_IS_VALID flag.
- */
- RETURN_ON_FAILURE(TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX,
- (uint8_t*) &zero, 0));
-
- if (!TlclIsOwned()) {
- /* Must ForceClear and reboot to prevent from running into the 64-write
- * limit.
- */
- RETURN_ON_FAILURE(TlclForceClear());
- /* Reboot! No return */
- return 9999;
- }
- }
-
/* 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.
@@ -192,35 +174,118 @@ static uint32_t GetTPMRollbackIndices(void) {
return TPM_SUCCESS;
}
+/* Checks if the kernel version space has been mucked with. If it has,
+ * reconstructs it using the backup value.
+ */
+uint32_t RecoverKernelSpace(void) {
+ uint32_t perms = 0;
+ uint8_t buffer[KERNEL_SPACE_SIZE];
+ int read_OK = 0;
+ int perms_OK = 0;
+ uint32_t backup_combined_versions;
+ uint32_t must_use_backup;
+
+ RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &must_use_backup, sizeof(uint32_t)));
+ /* must_use_backup is true if the previous boot entered recovery mode. */
+
+ read_OK = TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &buffer,
+ KERNEL_SPACE_SIZE) == TPM_SUCCESS;
+ if (read_OK) {
+ RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_VERSIONS_NV_INDEX, &perms));
+ perms_OK = perms == TPM_NV_PER_PPWRITE;
+ }
+ if (!must_use_backup && read_OK && perms_OK &&
+ !Memcmp(buffer + sizeof(uint32_t), KERNEL_SPACE_UID,
+ KERNEL_SPACE_UID_SIZE)) {
+ /* Everything is fine. This is the normal, frequent path. */
+ return TPM_SUCCESS;
+ }
-uint32_t SetupTPM(void) {
+ /* Either we detected that something went wrong, or we cannot trust the
+ * PP-protected kernel space. Attempts to fix. It is not always necessary
+ * to redefine the space, but we might as well, since this path should be
+ * taken quite seldom (after recovery mode and after an attack).
+ */
+ RETURN_ON_FAILURE(InitializeKernelVersionsSpaces());
+ RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &backup_combined_versions,
+ sizeof(uint32_t)));
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
+ (uint8_t*) &backup_combined_versions,
+ sizeof(uint32_t)));
+ if (must_use_backup) {
+ uint32_t zero = 0;
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &zero, 0));
+
+ }
+ return TPM_SUCCESS;
+}
+
+static uint32_t BackupKernelSpace(void) {
+ uint32_t kernel_versions;
+ uint32_t backup_versions;
+ RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
+ (uint8_t*) &kernel_versions, sizeof(uint32_t)));
+ RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &backup_versions, sizeof(uint32_t)));
+ if (kernel_versions == backup_versions) {
+ return TPM_SUCCESS;
+ } else if (kernel_versions < backup_versions) {
+ /* This cannot happen. We're screwed. */
+ return TPM_E_INTERNAL_INCONSISTENCY;
+ }
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &kernel_versions, sizeof(uint32_t)));
+ return TPM_SUCCESS;
+}
+
+static uint32_t SetupTPM_(void) {
uint8_t disable;
uint8_t deactivated;
TlclLibInit();
RETURN_ON_FAILURE(TlclStartup());
RETURN_ON_FAILURE(TlclContinueSelfTest());
RETURN_ON_FAILURE(TlclAssertPhysicalPresence());
- /* Check that the TPM is enabled and activated. */
+ /* Checks that the TPM is enabled and activated. */
RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated));
if (disable || deactivated) {
RETURN_ON_FAILURE(TlclSetEnable());
RETURN_ON_FAILURE(TlclSetDeactivated(0));
- /* TODO: Reboot now */
+ /* TODO: Reboot */
return 9999;
}
/* We expect this to fail the first time we run on a device, indicating that
* the TPM has not been initialized yet. */
- if (GetTPMRollbackIndices() != TPM_SUCCESS) {
- /* If InitializeSpaces() fails (possibly because it had been executed
- * already), something is wrong. */
- RETURN_ON_FAILURE(InitializeSpaces());
- /* Try again. */
- RETURN_ON_FAILURE(GetTPMRollbackIndices());
+ if (RecoverKernelSpace() != TPM_SUCCESS) {
+ int initialized = 0;
+ RETURN_ON_FAILURE(GetSpacesInitialized(&initialized));
+ if (initialized) {
+ return TPM_E_ALREADY_INITIALIZED;
+ } else {
+ RETURN_ON_FAILURE(InitializeSpaces());
+ RETURN_ON_FAILURE(RecoverKernelSpace());
+ }
}
+ RETURN_ON_FAILURE(BackupKernelSpace());
+ RETURN_ON_FAILURE(GetTPMRollbackIndices());
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;
+ }
+}
+
uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
/* TODO: should verify that SetupTPM() has been called. Note that
@@ -257,15 +322,6 @@ uint32_t WriteStoredVersions(int type, uint16_t key_version, uint16_t version) {
RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
(uint8_t*) &combined_version,
sizeof(uint32_t)));
- break;
- }
- /* TODO(semenzato): change TlclIsOwned to return a TPM status directly and
- * the "owned" value by reference.
- */
- if (!TlclIsOwned()) {
- RETURN_ON_FAILURE(TlclForceClear());
- /* TODO: Reboot here. No return. */
- return 9999;
}
return TPM_SUCCESS;
}
« no previous file with comments | « vboot_firmware/lib/include/tss_constants.h ('k') | vboot_firmware/stub/tlcl.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698