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

Unified Diff: tests/rollback_index_test.c

Issue 2857030: Exhaustive test for rollback code (Closed) Base URL: ssh://git@chromiumos-git/vboot_reference.git
Patch Set: Fix write count handling and improve comments. Created 10 years, 5 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 | « tests/rbtest.conf ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/rollback_index_test.c
diff --git a/tests/rollback_index_test.c b/tests/rollback_index_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..1fcbcc80ec222c3299c9ad1b9f792c707c7bd0ad
--- /dev/null
+++ b/tests/rollback_index_test.c
@@ -0,0 +1,749 @@
+/* Copyright (c) 2010 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.
+ */
+
+/* Exhaustive testing for correctness and integrity of TPM locking code from
+ * all interesting initial conditions.
+ *
+ * This program iterates through a large number of initial states of the TPM at
+ * power on, and executes the code related to initialing the TPM and managing
+ * the anti-rollback indices.
+ *
+ * This program must be run on a system with "TPM-agnostic" BIOS: that is, the
+ * system must have a TPM (as of this date, the emulator isn't good enough,
+ * because it doesn't support bGlobalLock), but the firmware should not issue a
+ * TPM_Startup. In addition, the TPM drivers must be loaded (tpm_tis, tpm, and
+ * tpm_bios) but tcsd should NOT be running. However, tcsd must be installed,
+ * as well as the command tpm_takeownership from the TPM tools, and tpm-nvtool
+ * from third-party/tpm.
+ *
+ * This program must be run as root. It issues multiple reboots, saving and
+ * restoring the state from a file. Typically it works in two phases: on one
+ * reboot it sets the TPM to a certain state, and in the next reboot it runs
+ * the test.
+ *
+ * This program may take a long time to complete.
+ *
+ * A companion upstart file rbtest.conf contains test setup instructions. Look
+ * around for it.
+ */
+
+#include "rollback_index.h"
+#include "tlcl.h"
+#include "tss_constants.h"
+#include "utility.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define RETURN_ON_FAILURE(tpm_command) do { \
+ uint32_t result; \
+ if ((result = (tpm_command)) != TPM_SUCCESS) { \
+ return result; \
+ } \
+ } while (0)
+
+#define WRITE_BUCKET_NV_INDEX 0x1050
+#define STATEPATH "/mnt/stateful_partition/var/spool/rbtest.state"
+#define TPM_ANY_FAILURE (-1)
+
+#define TPM_MAX_NV_WRITE_NOOWNER 64
+#define MAX_NV_WRITES_AT_BOOT 18 /* see comment below */
+#define INITIALIZATION_NV_WRITES 11 /* see below */
+
+const int high_writecount = TPM_MAX_NV_WRITE_NOOWNER;
+/* The -1 below is to make sure there is at least one case when we don't hit
+ * the write limit. It is probably unnecessary, but we pay this cost to avoid
+ * an off-by-one error.
+ */
+const int low_writecount = TPM_MAX_NV_WRITE_NOOWNER - MAX_NV_WRITES_AT_BOOT - 1;
+const int low_writecount_when_initialized = TPM_MAX_NV_WRITE_NOOWNER
+ - MAX_NV_WRITES_AT_BOOT + INITIALIZATION_NV_WRITES;
+
+/*
+ * This structure contains all TPM states of interest, and other testing
+ * states. It is saved and restored from a file across all reboots.
+ *
+ * OWNED/UNOWNED
+ * ACTIVATED/DEACTIVATED
+ * ENABLED/DISABLED
+ * WRITE COUNT
+ *
+ * The write count tests hitting the write limit with an unowned TPM. After
+ * resetting the TPM we reset the write count to zero, then we perform
+ * |writecount| writes to bring the count to the desired number.
+ *
+ * Low write counts are not interesting, because we know they cannot cause the
+ * code to hit the limit during a single boot. There are a total of N
+ * SafeWrite and SafeDefineSpace call sites, where N = MAX_NV_WRITES_AT_BOOT.
+ * Every call site can be reached at most once at every boot (there are no
+ * loops or multiple nested calls). So we only need to test N + 1 different
+ * initial values of the NVRAM write count (between 64 - (N + 1)) and 64).
+ *
+ * A number of calls happen at initialization, so when the TPM_IS_INITIALIZED
+ * space exists, we only need to start checking at TPM_MAX_NV_WRITE_NOOWNER -
+ * MAX_NV_WRITES_AT_BOOT + INITIALIZATION_NV_WRITES.
+ *
+ * TPM_IS_INITIALIZED space exists/does not exist
+ * KERNEL_MUST_USE_BACKUP = 0 or 1
+ * KERNEL_VERSIONS exists/does not
+ * KERNEL_VERSIONS space has wrong permissions
+ * KERNEL_VERSIONS does not contain the replacement-prevention value
+ * KERNEL_VERSIONS and KERNEL_VERSIONS_BACKUP are the same/are not
+ * DEVELOPER_MODE_NV_INDEX = 0 or 1
+ *
+ * developer switch on/off
+ * recovery switch on/off
+ */
+
+typedef struct RBTState {
+ /* Internal testing state */
+ int advancing; /* this is 1 if we are setting the TPM to the next initial
+ state, 0 if we are running the test. */
+
+ /* TPM state */
+ int writecount;
+ int owned;
+ int disable;
+ int deactivated;
+ int TPM_IS_INITIALIZED_exists;
+ int KERNEL_MUST_USE_BACKUP;
+ int KERNEL_VERSIONS_exists;
+ int KERNEL_VERSIONS_wrong_permissions;
+ int KERNEL_VERSIONS_wrong_value;
+ int KERNEL_VERSIONS_same_as_backup;
+ int DEVELOPER_MODE; /* content of DEVELOPER_MODE space */
+ int developer; /* setting of developer mode switch */
+ int recovery; /* booting in recovery mode */
+} RBTState;
+
+RBTState RBTS;
+
+/* Set to 1 if the TPM was cleared in this run, to avoid clearing it again
+ * before we set the write count.
+ */
+int tpm_was_just_cleared = 0;
+
+const char* RBTS_format =
+ "advancing=%d, owned=%d, disable=%d, activated=%d, "
+ "writecount=%d, TII_exists=%d, KMUB=%d, "
+ "KV_exists=%d, KV_wp=%d, KV_wv=%d, KV_sab=%d, DM=%d, dm=%d, rm=%d";
+
+static void Log(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ vsyslog(LOG_INFO, format, ap);
+ va_end(ap);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void reboot(void) {
+ int status;
+ Log("requesting reboot");
+ status = system("/sbin/reboot");
+ if (status != 0) {
+ Log("reboot failed with status %d", status);
+ exit(1);
+ }
+}
+
+static void RollbackTest_SaveState(FILE* file) {
+ rewind(file);
+ fprintf(file, RBTS_format,
+ RBTS.advancing,
+ RBTS.owned,
+ RBTS.disable,
+ RBTS.deactivated,
+ RBTS.writecount,
+ RBTS.TPM_IS_INITIALIZED_exists,
+ RBTS.KERNEL_MUST_USE_BACKUP,
+ RBTS.KERNEL_VERSIONS_exists,
+ RBTS.KERNEL_VERSIONS_wrong_permissions,
+ RBTS.KERNEL_VERSIONS_wrong_value,
+ RBTS.KERNEL_VERSIONS_same_as_backup,
+ RBTS.DEVELOPER_MODE,
+ RBTS.developer,
+ RBTS.recovery);
+}
+
+static void RollbackTest_RestoreState(FILE* file) {
+ if (fscanf(file, RBTS_format,
+ &RBTS.advancing,
+ &RBTS.owned,
+ &RBTS.disable,
+ &RBTS.deactivated,
+ &RBTS.writecount,
+ &RBTS.TPM_IS_INITIALIZED_exists,
+ &RBTS.KERNEL_MUST_USE_BACKUP,
+ &RBTS.KERNEL_VERSIONS_exists,
+ &RBTS.KERNEL_VERSIONS_wrong_permissions,
+ &RBTS.KERNEL_VERSIONS_wrong_value,
+ &RBTS.KERNEL_VERSIONS_same_as_backup,
+ &RBTS.DEVELOPER_MODE,
+ &RBTS.developer,
+ &RBTS.recovery) != sizeof(RBTS)/sizeof(int)) {
+ Log("failed to restore state");
+ exit(1);
+ }
+}
+
+static void RollbackTest_LogState(void) {
+ Log(RBTS_format,
+ RBTS.advancing,
+ RBTS.owned,
+ RBTS.disable,
+ RBTS.deactivated,
+ RBTS.writecount,
+ RBTS.TPM_IS_INITIALIZED_exists,
+ RBTS.KERNEL_MUST_USE_BACKUP,
+ RBTS.KERNEL_VERSIONS_exists,
+ RBTS.KERNEL_VERSIONS_wrong_permissions,
+ RBTS.KERNEL_VERSIONS_wrong_value,
+ RBTS.KERNEL_VERSIONS_same_as_backup,
+ RBTS.DEVELOPER_MODE,
+ RBTS.developer,
+ RBTS.recovery);
+}
+
+/* Executes a TPM command from the shell.
+ */
+static void RollbackTest_TPMShellCommand(char* command) {
+ int status;
+ TlclCloseDevice();
+ status = system("/usr/sbin/tcsd");
+ if (status != 0) {
+ Log("could not start tcsd");
+ exit(1);
+ }
+ status = system("/usr/bin/sleep 0.1");
+ status = system(command);
+ if (status != 0) {
+ Log("command %s returned 0x%x", command, status);
+ exit(1);
+ }
+ status = system("/usr/bin/pkill tcsd");
+ if (status != 0) {
+ Log("could not kill tcsd, status 0x%x", status);
+ exit(1);
+ }
+ status = system("/usr/bin/sleep 0.1");
+ TlclOpenDevice();
+}
+
+/* Sets or clears ownership.
+ */
+static uint32_t RollbackTest_SetOwnership(int ownership) {
+ if (ownership) {
+ /* Requesting owned state */
+ int owned = TlclIsOwned();
+ if (!owned) {
+ Log("acquiring ownership");
+ RollbackTest_TPMShellCommand("/usr/sbin/tpm_takeownership -y -z");
+ Log("ownership acquired");
+ }
+ } else {
+ /* Requesting unowned state */
+ Log("clearing TPM");
+ RETURN_ON_FAILURE(TPMClearAndReenable());
+ tpm_was_just_cleared = 1;
+ }
+ return TPM_SUCCESS;
+}
+
+/* Removes a space. This is a huge pain, because spaces can be removed only
+ * when the TPM is owned.
+ */
+static uint32_t RollbackTest_RemoveSpace(uint32_t index) {
+ char command[1024];
+ RollbackTest_SetOwnership(1);
+ snprintf(command, sizeof(command),
+ "/usr/bin/tpm-nvtool --release --index 0x%x --owner_password \"\"",
+ index);
+ Log("releasing space %x with command: %s", index, command);
+ RollbackTest_TPMShellCommand(command);
+ Log("space %x released", index);
+ return TPM_SUCCESS;
+}
+
+/* Checks if the TPM is disabled/deactivated, and optionally enables/activates.
+ * Does not disable/deactivate here because it might interfere with other
+ * operations.
+ */
+static uint32_t RollbackTest_PartiallyAdjustFlags(uint8_t* disable,
+ uint8_t* deactivated) {
+ RETURN_ON_FAILURE(TlclGetFlags(disable, deactivated));
+
+ if (*deactivated && !RBTS.deactivated) {
+ /* Needs to enable before we can activate. */
+ RETURN_ON_FAILURE(TlclSetEnable());
+ *disable = 0;
+ /* Needs to reboot after activating. */
+ RETURN_ON_FAILURE(TlclSetDeactivated(0));
+ reboot();
+ }
+ /* We disable and deactivate at the end, if needed. */
+
+ if (*disable && !RBTS.disable) {
+ RETURN_ON_FAILURE(TlclSetEnable());
+ }
+ return TPM_SUCCESS;
+}
+
+/* Removes or creates the TPM_IS_INITIALIZED space.
+ */
+static uint32_t RollbackTest_AdjustIsInitialized(void) {
+ int initialized;
+ RETURN_ON_FAILURE(GetSpacesInitialized(&initialized));
+ if (RBTS.TPM_IS_INITIALIZED_exists && !initialized) {
+ RETURN_ON_FAILURE(TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX,
+ TPM_NV_PER_PPWRITE, sizeof(uint32_t)));
+ }
+ if (!RBTS.TPM_IS_INITIALIZED_exists && initialized) {
+ RETURN_ON_FAILURE(RollbackTest_RemoveSpace(TPM_IS_INITIALIZED_NV_INDEX));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Sets or clears KERNEL_MUST_USE_BACKUP.
+ */
+static uint32_t RollbackTest_AdjustMustUseBackup(void) {
+ uint32_t must_use_backup;
+ RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &must_use_backup,
+ sizeof(must_use_backup)));
+ if (RBTS.KERNEL_MUST_USE_BACKUP != must_use_backup) {
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
+ (uint8_t*) &must_use_backup,
+ sizeof(must_use_backup)));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Adjusts KERNEL_VERSIONS space.
+ */
+static uint32_t RollbackTest_AdjustKernelVersions(int* wrong_value) {
+ uint8_t kdata[KERNEL_SPACE_SIZE];
+ int exists;
+ uint32_t result;
+
+ result = TlclRead(KERNEL_VERSIONS_NV_INDEX, kdata, sizeof(kdata));
+ if (result != TPM_SUCCESS && result != TPM_E_BADINDEX) {
+ return result;
+ }
+ *wrong_value = Memcmp(kdata + sizeof(uint32_t), KERNEL_SPACE_UID,
+ KERNEL_SPACE_UID_SIZE); /* for later use */
+ exists = result == TPM_SUCCESS;
+ if (RBTS.KERNEL_VERSIONS_exists && !exists) {
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
+ TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE));
+ }
+ if (!RBTS.KERNEL_VERSIONS_exists && exists) {
+ RETURN_ON_FAILURE(RollbackTest_RemoveSpace(KERNEL_VERSIONS_NV_INDEX));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Adjusts permissions of KERNEL_VERSIONS space. Updates |wrong_value| to
+ * reflect that currently the space contains the wrong value (i.e. does not
+ * contain the GRWL identifier).
+ */
+static uint32_t RollbackTest_AdjustKernelPermissions(int* wrong_value) {
+ uint32_t perms;
+
+ /* Wrong permissions */
+ RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_VERSIONS_NV_INDEX, &perms));
+ if (RBTS.KERNEL_VERSIONS_wrong_permissions && perms == TPM_NV_PER_PPWRITE) {
+ /* Redefines with wrong permissions. */
+ RETURN_ON_FAILURE(RollbackTest_RemoveSpace(KERNEL_VERSIONS_NV_INDEX));
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
+ TPM_NV_PER_PPWRITE |
+ TPM_NV_PER_GLOBALLOCK,
+ KERNEL_SPACE_SIZE));
+ *wrong_value = 1;
+ }
+ if (!RBTS.KERNEL_VERSIONS_wrong_permissions &&
+ perms != TPM_NV_PER_PPWRITE) {
+ /* Redefines with right permissions. */
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
+ TPM_NV_PER_PPWRITE, 0));
+ RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
+ TPM_NV_PER_PPWRITE,
+ KERNEL_SPACE_SIZE));
+ *wrong_value = 1;
+ }
+ return TPM_SUCCESS;
+}
+
+static uint32_t RollbackTest_AdjustKernelValue(int wrong_value) {
+ if (!RBTS.KERNEL_VERSIONS_wrong_value && wrong_value) {
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
+ KERNEL_SPACE_INIT_DATA, KERNEL_SPACE_SIZE));
+ }
+ if (RBTS.KERNEL_VERSIONS_wrong_value && !wrong_value) {
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
+ (uint8_t*) "mickey mouse",
+ KERNEL_SPACE_SIZE));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Adjusts value of KERNEL_VERSIONS_BACKUP space.
+ */
+static uint32_t RollbackTest_AdjustKernelBackup(void) {
+ /* Same as backup */
+ uint32_t kv, kvbackup;
+ RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
+ (uint8_t*) &kv, sizeof(kv)));
+ RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &kvbackup, sizeof(kvbackup)));
+ if (RBTS.KERNEL_VERSIONS_same_as_backup && kv != kvbackup) {
+ kvbackup = kv;
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &kvbackup, sizeof(kvbackup)));
+ }
+ if (!RBTS.KERNEL_VERSIONS_same_as_backup && kv == kvbackup) {
+ kvbackup = kv + 1;
+ RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
+ (uint8_t*) &kvbackup, sizeof(kvbackup)));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Adjust the value in the developer mode transition space.
+ */
+static uint32_t RollbackTest_AdjustDeveloperMode(void) {
+ uint32_t dev;
+ /* Developer mode transitions */
+ RETURN_ON_FAILURE(TlclRead(DEVELOPER_MODE_NV_INDEX,
+ (uint8_t*) &dev, sizeof(dev)));
+
+ if (RBTS.developer != dev) {
+ dev = RBTS.developer;
+ RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX,
+ (uint8_t*) &dev, sizeof(dev)));
+ }
+ return TPM_SUCCESS;
+}
+
+/* Changes the unowned write count.
+ */
+static uint32_t RollbackTest_AdjustWriteCount(void) {
+ int i;
+ if (!RBTS.owned) {
+ /* Sets the unowned write count, but only if we think that it will make a
+ * difference for the test. In other words: we're trying to reduce the
+ * number if initial states with some reasoning that we hope is correct.
+ */
+ if (RBTS.writecount > low_writecount) {
+ if (!tpm_was_just_cleared) {
+ /* Unknown write count: must clear the TPM to reset to 0 */
+ RETURN_ON_FAILURE(TPMClearAndReenable());
+ }
+ for (i = 0; i < RBTS.writecount; i++) {
+ /* Changes the value to ensure that the TPM won't optimize away
+ * writes.
+ */
+ uint8_t b = (uint8_t) i;
+ RETURN_ON_FAILURE(TlclWrite(WRITE_BUCKET_NV_INDEX, &b, 1));
+ }
+ }
+ }
+ return TPM_SUCCESS;
+}
+
+/* Sets the TPM to the right state for the next test run.
+ *
+ * Functionally correct ordering is tricky. Optimal ordering is even trickier
+ * (no claim to this). May succeed only partially and require a reboot to
+ * continue (if the TPM was deactivated at boot).
+ */
+static uint32_t RollbackTest_SetTPMState(int initialize) {
+ uint8_t disable, deactivated;
+ int wrong_value = 0;
+
+ /* Initializes if needed */
+ if (initialize) {
+ TlclLibInit();
+ /* Don't worry if we're already started. */
+ (void) TlclStartup();
+ RETURN_ON_FAILURE(TlclContinueSelfTest());
+ RETURN_ON_FAILURE(TlclAssertPhysicalPresence());
+ }
+
+ RETURN_ON_FAILURE(RollbackTest_PartiallyAdjustFlags(&disable, &deactivated));
+ RETURN_ON_FAILURE(RollbackTest_AdjustIsInitialized());
+ RETURN_ON_FAILURE(RollbackTest_AdjustMustUseBackup());
+ RETURN_ON_FAILURE(RollbackTest_AdjustKernelVersions(&wrong_value));
+
+ if (RBTS.KERNEL_VERSIONS_exists) {
+ /* Adjusting these states only makes sense when the kernel versions space
+ * exists. */
+ RETURN_ON_FAILURE(RollbackTest_AdjustKernelPermissions(&wrong_value));
+ RETURN_ON_FAILURE(RollbackTest_AdjustKernelValue(wrong_value));
+ RETURN_ON_FAILURE(RollbackTest_AdjustKernelBackup());
+ }
+
+ RETURN_ON_FAILURE(RollbackTest_AdjustDeveloperMode());
+ RETURN_ON_FAILURE(RollbackTest_SetOwnership(RBTS.owned));
+ /* Do not remove spaces between SetOwnership and AdjustWriteCount, as that
+ * might change the ownership state. Also do not issue any writes from now
+ * on, because AdjustWriteCount tries to avoid unneccessary clears, and after
+ * that, any writes will obviously change the write count.
+ */
+ RETURN_ON_FAILURE(RollbackTest_AdjustWriteCount());
+
+ /* Finally, disables and/or deactivates. Must deactivate before disabling
+ */
+ if (!deactivated && RBTS.deactivated) {
+ RETURN_ON_FAILURE(TlclSetDeactivated(1));
+ }
+ /* It's better to do this last, even though most commands we use work with
+ * the TPM disabled.
+ */
+ if (!disable && RBTS.disable) {
+ RETURN_ON_FAILURE(TlclClearEnable());
+ }
+ return TPM_SUCCESS;
+}
+
+#define ADVANCE(rbts_field, min, max) do { \
+ if (RBTS.rbts_field == max) { \
+ RBTS.rbts_field = min; \
+ } else { \
+ RBTS.rbts_field++; \
+ return 0; \
+ } \
+ } while (0)
+
+#define ADVANCEB(field) ADVANCE(field, 0, 1)
+
+static int RollbackTest_AdvanceState(void) {
+ /* This is a generalized counter. It advances an element of the RTBS
+ * structure, and when it hits its maximum value, it resets the element and
+ * moves on to the next element, similar to the way a decimal counter
+ * increases each digit from 0 to 9 and back to 0 with a carry.
+ *
+ * Tip: put the expensive state changes at the end.
+ */
+ ADVANCEB(developer);
+ ADVANCEB(recovery);
+ ADVANCEB(TPM_IS_INITIALIZED_exists);
+ ADVANCEB(KERNEL_MUST_USE_BACKUP);
+ if (RBTS.owned) {
+ ADVANCEB(KERNEL_VERSIONS_exists);
+ ADVANCEB(KERNEL_VERSIONS_wrong_permissions);
+ ADVANCEB(KERNEL_VERSIONS_wrong_value);
+ ADVANCEB(KERNEL_VERSIONS_same_as_backup);
+ }
+ ADVANCEB(DEVELOPER_MODE);
+ /* The writecount is meaningful only when the TPM is not owned. */
+ if (!RBTS.owned) {
+ ADVANCE(writecount, low_writecount, high_writecount);
+ if (RBTS.TPM_IS_INITIALIZED_exists) {
+ /* We don't have to go through the full range in this case. */
+ if (RBTS.writecount < low_writecount_when_initialized) {
+ RBTS.writecount = low_writecount_when_initialized;
+ }
+ }
+ }
+ ADVANCEB(deactivated);
+ ADVANCEB(disable);
+ ADVANCEB(owned);
+ if (RBTS.owned == 0) {
+ /* overflow */
+ return 1;
+ }
+ return 0;
+}
+
+static void RollbackTest_InitializeState(void) {
+ FILE* file = fopen(STATEPATH, "w");
+ if (file == NULL) {
+ fprintf(stderr, "could not open %s for writing\n", STATEPATH);
+ exit(1);
+ }
+ RBTS.writecount = low_writecount;
+ RollbackTest_SaveState(file);
+}
+
+uint32_t RollbackTest_Test(void) {
+ uint16_t key_version, version;
+
+ if (RBTS.recovery) {
+ if (RBTS.developer) {
+ /* Developer Recovery mode */
+ RETURN_ON_FAILURE(RollbackKernelRecovery(1));
+ } else {
+ /* Normal Recovery mode */
+ RETURN_ON_FAILURE(RollbackKernelRecovery(0));
+ }
+ } else {
+ if (RBTS.developer) {
+ /* Developer mode */
+ key_version = 0;
+ version = 0;
+ RETURN_ON_FAILURE(RollbackFirmwareSetup(1));
+ RETURN_ON_FAILURE(RollbackFirmwareLock());
+ } else {
+ /* Normal mode */
+ key_version = 0;
+ version = 0;
+ RETURN_ON_FAILURE(RollbackFirmwareSetup(0));
+ RETURN_ON_FAILURE(RollbackFirmwareLock());
+ RETURN_ON_FAILURE(RollbackKernelRead(&key_version, &version));
+ RETURN_ON_FAILURE(RollbackKernelWrite(key_version, version));
+ RETURN_ON_FAILURE(RollbackKernelLock());
+ }
+ }
+ return TPM_SUCCESS;
+}
+
+/* One-time call to create the WRITE_BUCKET space.
+ */
+static uint32_t RollbackTest_InitializeTPM(void) {
+ TlclLibInit();
+ RETURN_ON_FAILURE(TlclStartup());
+ RETURN_ON_FAILURE(TlclContinueSelfTest());
+ RETURN_ON_FAILURE(TlclAssertPhysicalPresence());
+ RETURN_ON_FAILURE(TlclDefineSpace(WRITE_BUCKET_NV_INDEX,
+ TPM_NV_PER_PPWRITE, 1));
+ RETURN_ON_FAILURE(RollbackTest_SetTPMState(0));
+ return TPM_SUCCESS;
+}
+
+static void RollbackTest_Initialize(void) {
+ Log("initializing");
+ RollbackTest_InitializeState();
+ if (RollbackTest_InitializeTPM() != TPM_SUCCESS) {
+ Log("couldn't initialize TPM");
+ exit(1);
+ }
+}
+
+/* Advances the desired TPM state and sets the TPM to the new state.
+ */
+static void RollbackTest_Advance(FILE* file) {
+ uint32_t result;
+ Log("advancing state");
+ if (RollbackTest_AdvanceState()) {
+ Log("done");
+ exit(0);
+ }
+ result = RollbackTest_SetTPMState(1);
+ if (result == TPM_SUCCESS) {
+ RBTS.advancing = 0;
+ RollbackTest_SaveState(file);
+ reboot();
+ } else {
+ Log("SetTPMState failed with 0x%x\n", result);
+ exit(1);
+ }
+}
+
+/* Performs the test for the current TPM state, and verify that the outcome
+ * matches the expectations.
+ */
+static void RollbackTest_RunOneTest(FILE* file) {
+ uint32_t result;
+ uint32_t expected_result = TPM_SUCCESS;
+
+ if (!RBTS.KERNEL_VERSIONS_exists ||
+ RBTS.KERNEL_VERSIONS_wrong_permissions ||
+ RBTS.KERNEL_VERSIONS_wrong_value) {
+ expected_result = TPM_E_CORRUPTED_STATE;
+ }
+
+ if (!RBTS.KERNEL_VERSIONS_exists && !RBTS.TPM_IS_INITIALIZED_exists) {
+ /* The space will be recreated */
+ expected_result = TPM_SUCCESS;
+ }
+
+ if ((!RBTS.TPM_IS_INITIALIZED_exists || !RBTS.KERNEL_VERSIONS_exists)
+ && RBTS.owned) {
+ /* Cannot create spaces without owner authorization */
+ expected_result = TPM_E_OWNER_SET;
+ }
+
+ if (RBTS.TPM_IS_INITIALIZED_exists && !RBTS.KERNEL_VERSIONS_exists) {
+ expected_result = TPM_ANY_FAILURE;
+ }
+
+ result = RollbackTest_Test();
+
+ if (result == expected_result ||
+ (result != TPM_SUCCESS && expected_result == TPM_ANY_FAILURE)) {
+ Log("test succeeded with 0x%x\n", result);
+ RBTS.advancing = 1;
+ RollbackTest_SaveState(file);
+ reboot();
+ } else {
+ Log("test failed with 0x%x, expecting 0x%x\n", result, expected_result);
+ exit(1);
+ }
+}
+
+static FILE* RollbackTest_OpenState(void) {
+ FILE* file = fopen(STATEPATH, "r+");
+ if (file == NULL) {
+ Log("%s could not be opened", STATEPATH);
+ exit(1);
+ }
+ return file;
+}
+
+/* Sync saved state with TPM state.
+ */
+static void RollbackTest_Sync(void) {
+ FILE *file = RollbackTest_OpenState();
+ uint32_t result;
+ RollbackTest_RestoreState(file);
+ Log("Syncing state");
+ result = RollbackTest_SetTPMState(1);
+ if (result != TPM_SUCCESS) {
+ Log("Sync failed with %x", result);
+ exit(1);
+ }
+}
+
+/* Runs one testing iteration and advances the testing state.
+ */
+static void RollbackTest_Run(void) {
+ FILE* file = RollbackTest_OpenState();
+ RollbackTest_RestoreState(file);
+ RollbackTest_LogState();
+ if (RBTS.advancing) {
+ RollbackTest_Advance(file);
+ } else {
+ RollbackTest_RunOneTest(file);
+ }
+}
+
+int main(int argc, char** argv) {
+
+ openlog("rbtest", LOG_CONS | LOG_PERROR, LOG_USER);
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "rollback-test: must run as root\n");
+ exit(1);
+ }
+
+ if (argc == 2 && strcmp(argv[1], "initialize") == 0) {
+ RollbackTest_Initialize();
+ } else if (argc == 2 && strcmp(argv[1], "sync") == 0) {
+ RollbackTest_Sync();
+ } else if (argc == 1) {
+ RollbackTest_Run();
+ } else {
+ fprintf(stderr, "usage: rollback-test [ initialize ]\n");
+ exit(1);
+ }
+ return 0;
+}
« no previous file with comments | « tests/rbtest.conf ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698