| OLD | NEW | 
| (Empty) |  | 
 |    1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 
 |    2  * Use of this source code is governed by a BSD-style license that can be | 
 |    3  * found in the LICENSE file. | 
 |    4  */ | 
 |    5  | 
 |    6 /* Exhaustive testing for correctness and integrity of TPM locking code from | 
 |    7  * all interesting initial conditions. | 
 |    8  * | 
 |    9  * This program iterates through a large number of initial states of the TPM at | 
 |   10  * power on, and executes the code related to initialing the TPM and managing | 
 |   11  * the anti-rollback indices. | 
 |   12  * | 
 |   13  * This program must be run on a system with "TPM-agnostic" BIOS: that is, the | 
 |   14  * system must have a TPM (as of this date, the emulator isn't good enough, | 
 |   15  * because it doesn't support bGlobalLock), but the firmware should not issue a | 
 |   16  * TPM_Startup.  In addition, the TPM drivers must be loaded (tpm_tis, tpm, and | 
 |   17  * tpm_bios) but tcsd should NOT be running.  However, tcsd must be installed, | 
 |   18  * as well as the command tpm_takeownership from the TPM tools, and tpm-nvtool | 
 |   19  * from third-party/tpm. | 
 |   20  * | 
 |   21  * This program must be run as root.  It issues multiple reboots, saving and | 
 |   22  * restoring the state from a file.  Typically it works in two phases: on one | 
 |   23  * reboot it sets the TPM to a certain state, and in the next reboot it runs | 
 |   24  * the test. | 
 |   25  * | 
 |   26  * This program may take a long time to complete. | 
 |   27  * | 
 |   28  * A companion upstart file rbtest.conf contains test setup instructions.  Look | 
 |   29  * around for it. | 
 |   30  */ | 
 |   31  | 
 |   32 #include "rollback_index.h" | 
 |   33 #include "tlcl.h" | 
 |   34 #include "tss_constants.h" | 
 |   35 #include "utility.h" | 
 |   36  | 
 |   37 #include <stdarg.h> | 
 |   38 #include <stdio.h> | 
 |   39 #include <string.h> | 
 |   40 #include <sys/types.h> | 
 |   41 #include <syslog.h> | 
 |   42 #include <unistd.h> | 
 |   43  | 
 |   44 #define RETURN_ON_FAILURE(tpm_command) do {             \ | 
 |   45     uint32_t result;                                    \ | 
 |   46     if ((result = (tpm_command)) != TPM_SUCCESS) {      \ | 
 |   47       return result;                                    \ | 
 |   48     }                                                   \ | 
 |   49   } while (0) | 
 |   50  | 
 |   51 #define WRITE_BUCKET_NV_INDEX 0x1050 | 
 |   52 #define STATEPATH "/mnt/stateful_partition/var/spool/rbtest.state" | 
 |   53 #define TPM_ANY_FAILURE (-1) | 
 |   54  | 
 |   55 #define TPM_MAX_NV_WRITE_NOOWNER 64 | 
 |   56 #define MAX_NV_WRITES_AT_BOOT 18         /* see comment below */ | 
 |   57 #define INITIALIZATION_NV_WRITES 11      /* see below */ | 
 |   58  | 
 |   59 const int high_writecount = TPM_MAX_NV_WRITE_NOOWNER; | 
 |   60 /* The -1 below is to make sure there is at least one case when we don't hit | 
 |   61  * the write limit.  It is probably unnecessary, but we pay this cost to avoid | 
 |   62  * an off-by-one error. | 
 |   63  */ | 
 |   64 const int low_writecount = TPM_MAX_NV_WRITE_NOOWNER - MAX_NV_WRITES_AT_BOOT - 1; | 
 |   65 const int low_writecount_when_initialized = TPM_MAX_NV_WRITE_NOOWNER | 
 |   66   - MAX_NV_WRITES_AT_BOOT + INITIALIZATION_NV_WRITES; | 
 |   67  | 
 |   68 /* | 
 |   69  * This structure contains all TPM states of interest, and other testing | 
 |   70  * states.  It is saved and restored from a file across all reboots. | 
 |   71  * | 
 |   72  * OWNED/UNOWNED | 
 |   73  * ACTIVATED/DEACTIVATED | 
 |   74  * ENABLED/DISABLED | 
 |   75  * WRITE COUNT | 
 |   76  * | 
 |   77  * The write count tests hitting the write limit with an unowned TPM.  After | 
 |   78  * resetting the TPM we reset the write count to zero, then we perform | 
 |   79  * |writecount| writes to bring the count to the desired number. | 
 |   80  * | 
 |   81  * Low write counts are not interesting, because we know they cannot cause the | 
 |   82  * code to hit the limit during a single boot.  There are a total of N | 
 |   83  * SafeWrite and SafeDefineSpace call sites, where N = MAX_NV_WRITES_AT_BOOT. | 
 |   84  * Every call site can be reached at most once at every boot (there are no | 
 |   85  * loops or multiple nested calls).  So we only need to test N + 1 different | 
 |   86  * initial values of the NVRAM write count (between 64 - (N + 1)) and 64). | 
 |   87  * | 
 |   88  * A number of calls happen at initialization, so when the TPM_IS_INITIALIZED | 
 |   89  * space exists, we only need to start checking at TPM_MAX_NV_WRITE_NOOWNER - | 
 |   90  * MAX_NV_WRITES_AT_BOOT + INITIALIZATION_NV_WRITES. | 
 |   91  * | 
 |   92  * TPM_IS_INITIALIZED space exists/does not exist | 
 |   93  * KERNEL_MUST_USE_BACKUP = 0 or 1 | 
 |   94  * KERNEL_VERSIONS exists/does not | 
 |   95  * KERNEL_VERSIONS space has wrong permissions | 
 |   96  * KERNEL_VERSIONS does not contain the replacement-prevention value | 
 |   97  * KERNEL_VERSIONS and KERNEL_VERSIONS_BACKUP are the same/are not | 
 |   98  * DEVELOPER_MODE_NV_INDEX = 0 or 1 | 
 |   99  * | 
 |  100  * developer switch on/off | 
 |  101  * recovery switch on/off | 
 |  102  */ | 
 |  103  | 
 |  104 typedef struct RBTState { | 
 |  105   /* Internal testing state */ | 
 |  106   int advancing;  /* this is 1 if we are setting the TPM to the next initial | 
 |  107                      state, 0 if we are running the test. */ | 
 |  108  | 
 |  109   /* TPM state */ | 
 |  110   int writecount; | 
 |  111   int owned; | 
 |  112   int disable; | 
 |  113   int deactivated; | 
 |  114   int TPM_IS_INITIALIZED_exists; | 
 |  115   int KERNEL_MUST_USE_BACKUP; | 
 |  116   int KERNEL_VERSIONS_exists; | 
 |  117   int KERNEL_VERSIONS_wrong_permissions; | 
 |  118   int KERNEL_VERSIONS_wrong_value; | 
 |  119   int KERNEL_VERSIONS_same_as_backup; | 
 |  120   int DEVELOPER_MODE;   /* content of DEVELOPER_MODE space */ | 
 |  121   int developer;        /* setting of developer mode switch */ | 
 |  122   int recovery;         /* booting in recovery mode */ | 
 |  123 } RBTState; | 
 |  124  | 
 |  125 RBTState RBTS; | 
 |  126  | 
 |  127 /* Set to 1 if the TPM was cleared in this run, to avoid clearing it again | 
 |  128  * before we set the write count. | 
 |  129  */ | 
 |  130 int tpm_was_just_cleared = 0; | 
 |  131  | 
 |  132 const char* RBTS_format = | 
 |  133   "advancing=%d, owned=%d, disable=%d, activated=%d, " | 
 |  134   "writecount=%d, TII_exists=%d, KMUB=%d, " | 
 |  135   "KV_exists=%d, KV_wp=%d, KV_wv=%d, KV_sab=%d, DM=%d, dm=%d, rm=%d"; | 
 |  136  | 
 |  137 static void Log(const char* format, ...) { | 
 |  138   va_list ap; | 
 |  139   va_start(ap, format); | 
 |  140   vsyslog(LOG_INFO, format, ap); | 
 |  141   va_end(ap); | 
 |  142   va_start(ap, format); | 
 |  143   vfprintf(stderr, format, ap); | 
 |  144   va_end(ap); | 
 |  145   fprintf(stderr, "\n"); | 
 |  146 } | 
 |  147  | 
 |  148 static void reboot(void) { | 
 |  149   int status; | 
 |  150   Log("requesting reboot"); | 
 |  151   status = system("/sbin/reboot"); | 
 |  152   if (status != 0) { | 
 |  153     Log("reboot failed with status %d", status); | 
 |  154     exit(1); | 
 |  155   } | 
 |  156 } | 
 |  157  | 
 |  158 static void RollbackTest_SaveState(FILE* file) { | 
 |  159   rewind(file); | 
 |  160   fprintf(file, RBTS_format, | 
 |  161           RBTS.advancing, | 
 |  162           RBTS.owned, | 
 |  163           RBTS.disable, | 
 |  164           RBTS.deactivated, | 
 |  165           RBTS.writecount, | 
 |  166           RBTS.TPM_IS_INITIALIZED_exists, | 
 |  167           RBTS.KERNEL_MUST_USE_BACKUP, | 
 |  168           RBTS.KERNEL_VERSIONS_exists, | 
 |  169           RBTS.KERNEL_VERSIONS_wrong_permissions, | 
 |  170           RBTS.KERNEL_VERSIONS_wrong_value, | 
 |  171           RBTS.KERNEL_VERSIONS_same_as_backup, | 
 |  172           RBTS.DEVELOPER_MODE, | 
 |  173           RBTS.developer, | 
 |  174           RBTS.recovery); | 
 |  175 } | 
 |  176  | 
 |  177 static void RollbackTest_RestoreState(FILE* file) { | 
 |  178   if (fscanf(file, RBTS_format, | 
 |  179              &RBTS.advancing, | 
 |  180              &RBTS.owned, | 
 |  181              &RBTS.disable, | 
 |  182              &RBTS.deactivated, | 
 |  183              &RBTS.writecount, | 
 |  184              &RBTS.TPM_IS_INITIALIZED_exists, | 
 |  185              &RBTS.KERNEL_MUST_USE_BACKUP, | 
 |  186              &RBTS.KERNEL_VERSIONS_exists, | 
 |  187              &RBTS.KERNEL_VERSIONS_wrong_permissions, | 
 |  188              &RBTS.KERNEL_VERSIONS_wrong_value, | 
 |  189              &RBTS.KERNEL_VERSIONS_same_as_backup, | 
 |  190              &RBTS.DEVELOPER_MODE, | 
 |  191              &RBTS.developer, | 
 |  192              &RBTS.recovery) != sizeof(RBTS)/sizeof(int)) { | 
 |  193     Log("failed to restore state"); | 
 |  194     exit(1); | 
 |  195   } | 
 |  196 } | 
 |  197  | 
 |  198 static void RollbackTest_LogState(void) { | 
 |  199   Log(RBTS_format, | 
 |  200       RBTS.advancing, | 
 |  201       RBTS.owned, | 
 |  202       RBTS.disable, | 
 |  203       RBTS.deactivated, | 
 |  204       RBTS.writecount, | 
 |  205       RBTS.TPM_IS_INITIALIZED_exists, | 
 |  206       RBTS.KERNEL_MUST_USE_BACKUP, | 
 |  207       RBTS.KERNEL_VERSIONS_exists, | 
 |  208       RBTS.KERNEL_VERSIONS_wrong_permissions, | 
 |  209       RBTS.KERNEL_VERSIONS_wrong_value, | 
 |  210       RBTS.KERNEL_VERSIONS_same_as_backup, | 
 |  211       RBTS.DEVELOPER_MODE, | 
 |  212       RBTS.developer, | 
 |  213       RBTS.recovery); | 
 |  214 } | 
 |  215  | 
 |  216 /* Executes a TPM command from the shell. | 
 |  217  */ | 
 |  218 static void RollbackTest_TPMShellCommand(char* command) { | 
 |  219   int status; | 
 |  220   TlclCloseDevice(); | 
 |  221   status = system("/usr/sbin/tcsd"); | 
 |  222   if (status != 0) { | 
 |  223     Log("could not start tcsd"); | 
 |  224     exit(1); | 
 |  225   } | 
 |  226   status = system("/usr/bin/sleep 0.1"); | 
 |  227   status = system(command); | 
 |  228   if (status != 0) { | 
 |  229     Log("command %s returned 0x%x", command, status); | 
 |  230     exit(1); | 
 |  231   } | 
 |  232   status = system("/usr/bin/pkill tcsd"); | 
 |  233   if (status != 0) { | 
 |  234     Log("could not kill tcsd, status 0x%x", status); | 
 |  235     exit(1); | 
 |  236   } | 
 |  237   status = system("/usr/bin/sleep 0.1"); | 
 |  238   TlclOpenDevice(); | 
 |  239 } | 
 |  240  | 
 |  241 /* Sets or clears ownership. | 
 |  242  */ | 
 |  243 static uint32_t RollbackTest_SetOwnership(int ownership) { | 
 |  244   if (ownership) { | 
 |  245     /* Requesting owned state */ | 
 |  246     int owned = TlclIsOwned(); | 
 |  247     if (!owned) { | 
 |  248       Log("acquiring ownership"); | 
 |  249       RollbackTest_TPMShellCommand("/usr/sbin/tpm_takeownership -y -z"); | 
 |  250       Log("ownership acquired"); | 
 |  251     } | 
 |  252   } else { | 
 |  253     /* Requesting unowned state */ | 
 |  254     Log("clearing TPM"); | 
 |  255     RETURN_ON_FAILURE(TPMClearAndReenable()); | 
 |  256     tpm_was_just_cleared = 1; | 
 |  257   } | 
 |  258   return TPM_SUCCESS; | 
 |  259 } | 
 |  260  | 
 |  261 /* Removes a space.  This is a huge pain, because spaces can be removed only | 
 |  262  * when the TPM is owned. | 
 |  263  */ | 
 |  264 static uint32_t RollbackTest_RemoveSpace(uint32_t index) { | 
 |  265   char command[1024]; | 
 |  266   RollbackTest_SetOwnership(1); | 
 |  267   snprintf(command, sizeof(command), | 
 |  268            "/usr/bin/tpm-nvtool --release --index 0x%x --owner_password \"\"", | 
 |  269            index); | 
 |  270   Log("releasing space %x with command: %s", index, command); | 
 |  271   RollbackTest_TPMShellCommand(command); | 
 |  272   Log("space %x released", index); | 
 |  273   return TPM_SUCCESS; | 
 |  274 } | 
 |  275  | 
 |  276 /* Checks if the TPM is disabled/deactivated, and optionally enables/activates. | 
 |  277  * Does not disable/deactivate here because it might interfere with other | 
 |  278  * operations. | 
 |  279  */ | 
 |  280 static uint32_t RollbackTest_PartiallyAdjustFlags(uint8_t* disable, | 
 |  281                                                   uint8_t* deactivated) { | 
 |  282   RETURN_ON_FAILURE(TlclGetFlags(disable, deactivated)); | 
 |  283  | 
 |  284   if (*deactivated && !RBTS.deactivated) { | 
 |  285     /* Needs to enable before we can activate. */ | 
 |  286     RETURN_ON_FAILURE(TlclSetEnable()); | 
 |  287     *disable = 0; | 
 |  288     /* Needs to reboot after activating. */ | 
 |  289     RETURN_ON_FAILURE(TlclSetDeactivated(0)); | 
 |  290     reboot(); | 
 |  291   } | 
 |  292   /* We disable and deactivate at the end, if needed. */ | 
 |  293  | 
 |  294   if (*disable && !RBTS.disable) { | 
 |  295     RETURN_ON_FAILURE(TlclSetEnable()); | 
 |  296   } | 
 |  297   return TPM_SUCCESS; | 
 |  298 } | 
 |  299  | 
 |  300 /* Removes or creates the TPM_IS_INITIALIZED space. | 
 |  301  */ | 
 |  302 static uint32_t RollbackTest_AdjustIsInitialized(void) { | 
 |  303   int initialized; | 
 |  304   RETURN_ON_FAILURE(GetSpacesInitialized(&initialized)); | 
 |  305   if (RBTS.TPM_IS_INITIALIZED_exists && !initialized) { | 
 |  306     RETURN_ON_FAILURE(TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX, | 
 |  307                                       TPM_NV_PER_PPWRITE, sizeof(uint32_t))); | 
 |  308   } | 
 |  309   if (!RBTS.TPM_IS_INITIALIZED_exists && initialized) { | 
 |  310     RETURN_ON_FAILURE(RollbackTest_RemoveSpace(TPM_IS_INITIALIZED_NV_INDEX)); | 
 |  311   } | 
 |  312   return TPM_SUCCESS; | 
 |  313 } | 
 |  314  | 
 |  315 /* Sets or clears KERNEL_MUST_USE_BACKUP. | 
 |  316  */ | 
 |  317 static uint32_t RollbackTest_AdjustMustUseBackup(void) { | 
 |  318   uint32_t must_use_backup; | 
 |  319   RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, | 
 |  320                              (uint8_t*) &must_use_backup, | 
 |  321                              sizeof(must_use_backup))); | 
 |  322   if (RBTS.KERNEL_MUST_USE_BACKUP != must_use_backup) { | 
 |  323     RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, | 
 |  324                                 (uint8_t*) &must_use_backup, | 
 |  325                                 sizeof(must_use_backup))); | 
 |  326   } | 
 |  327   return TPM_SUCCESS; | 
 |  328 } | 
 |  329  | 
 |  330 /* Adjusts KERNEL_VERSIONS space. | 
 |  331  */ | 
 |  332 static uint32_t RollbackTest_AdjustKernelVersions(int* wrong_value) { | 
 |  333   uint8_t kdata[KERNEL_SPACE_SIZE]; | 
 |  334   int exists; | 
 |  335   uint32_t result; | 
 |  336  | 
 |  337   result = TlclRead(KERNEL_VERSIONS_NV_INDEX, kdata, sizeof(kdata)); | 
 |  338   if (result != TPM_SUCCESS && result != TPM_E_BADINDEX) { | 
 |  339     return result; | 
 |  340   } | 
 |  341   *wrong_value = Memcmp(kdata + sizeof(uint32_t), KERNEL_SPACE_UID, | 
 |  342                         KERNEL_SPACE_UID_SIZE);     /* for later use */ | 
 |  343   exists = result == TPM_SUCCESS; | 
 |  344   if (RBTS.KERNEL_VERSIONS_exists && !exists) { | 
 |  345     RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, | 
 |  346                                       TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE)); | 
 |  347   } | 
 |  348   if (!RBTS.KERNEL_VERSIONS_exists && exists) { | 
 |  349     RETURN_ON_FAILURE(RollbackTest_RemoveSpace(KERNEL_VERSIONS_NV_INDEX)); | 
 |  350   } | 
 |  351   return TPM_SUCCESS; | 
 |  352 } | 
 |  353  | 
 |  354 /* Adjusts permissions of KERNEL_VERSIONS space.  Updates |wrong_value| to | 
 |  355  * reflect that currently the space contains the wrong value (i.e. does not | 
 |  356  * contain the GRWL identifier). | 
 |  357  */ | 
 |  358 static uint32_t RollbackTest_AdjustKernelPermissions(int* wrong_value) { | 
 |  359   uint32_t perms; | 
 |  360  | 
 |  361   /* Wrong permissions */ | 
 |  362   RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_VERSIONS_NV_INDEX, &perms)); | 
 |  363   if (RBTS.KERNEL_VERSIONS_wrong_permissions && perms == TPM_NV_PER_PPWRITE) { | 
 |  364     /* Redefines with wrong permissions. */ | 
 |  365     RETURN_ON_FAILURE(RollbackTest_RemoveSpace(KERNEL_VERSIONS_NV_INDEX)); | 
 |  366     RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, | 
 |  367                                       TPM_NV_PER_PPWRITE | | 
 |  368                                       TPM_NV_PER_GLOBALLOCK, | 
 |  369                                       KERNEL_SPACE_SIZE)); | 
 |  370     *wrong_value = 1; | 
 |  371   } | 
 |  372   if (!RBTS.KERNEL_VERSIONS_wrong_permissions && | 
 |  373       perms != TPM_NV_PER_PPWRITE) { | 
 |  374     /* Redefines with right permissions. */ | 
 |  375     RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, | 
 |  376                                       TPM_NV_PER_PPWRITE, 0)); | 
 |  377     RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, | 
 |  378                                       TPM_NV_PER_PPWRITE, | 
 |  379                                       KERNEL_SPACE_SIZE)); | 
 |  380     *wrong_value = 1; | 
 |  381   } | 
 |  382   return TPM_SUCCESS; | 
 |  383 } | 
 |  384  | 
 |  385 static uint32_t RollbackTest_AdjustKernelValue(int wrong_value) { | 
 |  386   if (!RBTS.KERNEL_VERSIONS_wrong_value && wrong_value) { | 
 |  387     RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, | 
 |  388                                 KERNEL_SPACE_INIT_DATA, KERNEL_SPACE_SIZE)); | 
 |  389   } | 
 |  390   if (RBTS.KERNEL_VERSIONS_wrong_value && !wrong_value) { | 
 |  391     RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, | 
 |  392                                 (uint8_t*) "mickey mouse", | 
 |  393                                 KERNEL_SPACE_SIZE)); | 
 |  394   } | 
 |  395   return TPM_SUCCESS; | 
 |  396 } | 
 |  397  | 
 |  398 /* Adjusts value of KERNEL_VERSIONS_BACKUP space. | 
 |  399  */ | 
 |  400 static uint32_t RollbackTest_AdjustKernelBackup(void) { | 
 |  401   /* Same as backup */ | 
 |  402   uint32_t kv, kvbackup; | 
 |  403   RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX, | 
 |  404                              (uint8_t*) &kv, sizeof(kv))); | 
 |  405   RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, | 
 |  406                              (uint8_t*) &kvbackup, sizeof(kvbackup))); | 
 |  407   if (RBTS.KERNEL_VERSIONS_same_as_backup && kv != kvbackup) { | 
 |  408     kvbackup = kv; | 
 |  409     RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, | 
 |  410                                 (uint8_t*) &kvbackup, sizeof(kvbackup))); | 
 |  411   } | 
 |  412   if (!RBTS.KERNEL_VERSIONS_same_as_backup && kv == kvbackup) { | 
 |  413     kvbackup = kv + 1; | 
 |  414     RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, | 
 |  415                                 (uint8_t*) &kvbackup, sizeof(kvbackup))); | 
 |  416   } | 
 |  417   return TPM_SUCCESS; | 
 |  418 } | 
 |  419  | 
 |  420 /* Adjust the value in the developer mode transition space. | 
 |  421  */ | 
 |  422 static uint32_t RollbackTest_AdjustDeveloperMode(void) { | 
 |  423   uint32_t dev; | 
 |  424   /* Developer mode transitions */ | 
 |  425   RETURN_ON_FAILURE(TlclRead(DEVELOPER_MODE_NV_INDEX, | 
 |  426                              (uint8_t*) &dev, sizeof(dev))); | 
 |  427  | 
 |  428   if (RBTS.developer != dev) { | 
 |  429     dev = RBTS.developer; | 
 |  430     RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX, | 
 |  431                                 (uint8_t*) &dev, sizeof(dev))); | 
 |  432   } | 
 |  433   return TPM_SUCCESS; | 
 |  434 } | 
 |  435  | 
 |  436 /* Changes the unowned write count. | 
 |  437  */ | 
 |  438 static uint32_t RollbackTest_AdjustWriteCount(void) { | 
 |  439   int i; | 
 |  440   if (!RBTS.owned) { | 
 |  441     /* Sets the unowned write count, but only if we think that it will make a | 
 |  442      * difference for the test. In other words: we're trying to reduce the | 
 |  443      * number if initial states with some reasoning that we hope is correct. | 
 |  444      */ | 
 |  445     if (RBTS.writecount > low_writecount) { | 
 |  446       if (!tpm_was_just_cleared) { | 
 |  447         /* Unknown write count: must clear the TPM to reset to 0 */ | 
 |  448         RETURN_ON_FAILURE(TPMClearAndReenable()); | 
 |  449       } | 
 |  450       for (i = 0; i < RBTS.writecount; i++) { | 
 |  451         /* Changes the value to ensure that the TPM won't optimize away | 
 |  452          * writes. | 
 |  453          */ | 
 |  454         uint8_t b = (uint8_t) i; | 
 |  455         RETURN_ON_FAILURE(TlclWrite(WRITE_BUCKET_NV_INDEX, &b, 1)); | 
 |  456       } | 
 |  457     } | 
 |  458   } | 
 |  459   return TPM_SUCCESS; | 
 |  460 } | 
 |  461  | 
 |  462 /* Sets the TPM to the right state for the next test run. | 
 |  463  * | 
 |  464  * Functionally correct ordering is tricky.  Optimal ordering is even trickier | 
 |  465  * (no claim to this).  May succeed only partially and require a reboot to | 
 |  466  * continue (if the TPM was deactivated at boot). | 
 |  467  */ | 
 |  468 static uint32_t RollbackTest_SetTPMState(int initialize) { | 
 |  469   uint8_t disable, deactivated; | 
 |  470   int wrong_value = 0; | 
 |  471  | 
 |  472   /* Initializes if needed */ | 
 |  473   if (initialize) { | 
 |  474     TlclLibInit(); | 
 |  475     /* Don't worry if we're already started. */ | 
 |  476     (void) TlclStartup(); | 
 |  477     RETURN_ON_FAILURE(TlclContinueSelfTest()); | 
 |  478     RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); | 
 |  479   } | 
 |  480  | 
 |  481   RETURN_ON_FAILURE(RollbackTest_PartiallyAdjustFlags(&disable, &deactivated)); | 
 |  482   RETURN_ON_FAILURE(RollbackTest_AdjustIsInitialized()); | 
 |  483   RETURN_ON_FAILURE(RollbackTest_AdjustMustUseBackup()); | 
 |  484   RETURN_ON_FAILURE(RollbackTest_AdjustKernelVersions(&wrong_value)); | 
 |  485  | 
 |  486   if (RBTS.KERNEL_VERSIONS_exists) { | 
 |  487     /* Adjusting these states only makes sense when the kernel versions space | 
 |  488      * exists. */ | 
 |  489     RETURN_ON_FAILURE(RollbackTest_AdjustKernelPermissions(&wrong_value)); | 
 |  490     RETURN_ON_FAILURE(RollbackTest_AdjustKernelValue(wrong_value)); | 
 |  491     RETURN_ON_FAILURE(RollbackTest_AdjustKernelBackup()); | 
 |  492   } | 
 |  493  | 
 |  494   RETURN_ON_FAILURE(RollbackTest_AdjustDeveloperMode()); | 
 |  495   RETURN_ON_FAILURE(RollbackTest_SetOwnership(RBTS.owned)); | 
 |  496   /* Do not remove spaces between SetOwnership and AdjustWriteCount, as that | 
 |  497    * might change the ownership state.  Also do not issue any writes from now | 
 |  498    * on, because AdjustWriteCount tries to avoid unneccessary clears, and after | 
 |  499    * that, any writes will obviously change the write count. | 
 |  500    */ | 
 |  501   RETURN_ON_FAILURE(RollbackTest_AdjustWriteCount()); | 
 |  502  | 
 |  503   /* Finally, disables and/or deactivates.  Must deactivate before disabling | 
 |  504    */ | 
 |  505   if (!deactivated && RBTS.deactivated) { | 
 |  506     RETURN_ON_FAILURE(TlclSetDeactivated(1)); | 
 |  507   } | 
 |  508   /* It's better to do this last, even though most commands we use work with | 
 |  509    * the TPM disabled. | 
 |  510    */ | 
 |  511   if (!disable && RBTS.disable) { | 
 |  512     RETURN_ON_FAILURE(TlclClearEnable()); | 
 |  513   } | 
 |  514   return TPM_SUCCESS; | 
 |  515 } | 
 |  516  | 
 |  517 #define ADVANCE(rbts_field, min, max) do { \ | 
 |  518     if (RBTS.rbts_field == max) {          \ | 
 |  519       RBTS.rbts_field = min;               \ | 
 |  520     } else {                               \ | 
 |  521       RBTS.rbts_field++;                   \ | 
 |  522       return 0;                            \ | 
 |  523     }                                      \ | 
 |  524   } while (0) | 
 |  525  | 
 |  526 #define ADVANCEB(field) ADVANCE(field, 0, 1) | 
 |  527  | 
 |  528 static int RollbackTest_AdvanceState(void) { | 
 |  529   /* This is a generalized counter.  It advances an element of the RTBS | 
 |  530    * structure, and when it hits its maximum value, it resets the element and | 
 |  531    * moves on to the next element, similar to the way a decimal counter | 
 |  532    * increases each digit from 0 to 9 and back to 0 with a carry. | 
 |  533    * | 
 |  534    * Tip: put the expensive state changes at the end. | 
 |  535    */ | 
 |  536   ADVANCEB(developer); | 
 |  537   ADVANCEB(recovery); | 
 |  538   ADVANCEB(TPM_IS_INITIALIZED_exists); | 
 |  539   ADVANCEB(KERNEL_MUST_USE_BACKUP); | 
 |  540   if (RBTS.owned) { | 
 |  541     ADVANCEB(KERNEL_VERSIONS_exists); | 
 |  542     ADVANCEB(KERNEL_VERSIONS_wrong_permissions); | 
 |  543     ADVANCEB(KERNEL_VERSIONS_wrong_value); | 
 |  544     ADVANCEB(KERNEL_VERSIONS_same_as_backup); | 
 |  545   } | 
 |  546   ADVANCEB(DEVELOPER_MODE); | 
 |  547   /* The writecount is meaningful only when the TPM is not owned. */ | 
 |  548   if (!RBTS.owned) { | 
 |  549     ADVANCE(writecount, low_writecount, high_writecount); | 
 |  550     if (RBTS.TPM_IS_INITIALIZED_exists) { | 
 |  551       /* We don't have to go through the full range in this case. */ | 
 |  552       if (RBTS.writecount < low_writecount_when_initialized) { | 
 |  553         RBTS.writecount = low_writecount_when_initialized; | 
 |  554       } | 
 |  555     } | 
 |  556   } | 
 |  557   ADVANCEB(deactivated); | 
 |  558   ADVANCEB(disable); | 
 |  559   ADVANCEB(owned); | 
 |  560   if (RBTS.owned == 0) { | 
 |  561     /* overflow */ | 
 |  562     return 1; | 
 |  563   } | 
 |  564   return 0; | 
 |  565 } | 
 |  566  | 
 |  567 static void RollbackTest_InitializeState(void) { | 
 |  568   FILE* file = fopen(STATEPATH, "w"); | 
 |  569   if (file == NULL) { | 
 |  570     fprintf(stderr, "could not open %s for writing\n", STATEPATH); | 
 |  571     exit(1); | 
 |  572   } | 
 |  573   RBTS.writecount = low_writecount; | 
 |  574   RollbackTest_SaveState(file); | 
 |  575 } | 
 |  576  | 
 |  577 uint32_t RollbackTest_Test(void) { | 
 |  578   uint16_t key_version, version; | 
 |  579  | 
 |  580   if (RBTS.recovery) { | 
 |  581     if (RBTS.developer) { | 
 |  582       /* Developer Recovery mode */ | 
 |  583       RETURN_ON_FAILURE(RollbackKernelRecovery(1)); | 
 |  584     } else { | 
 |  585       /* Normal Recovery mode */ | 
 |  586       RETURN_ON_FAILURE(RollbackKernelRecovery(0)); | 
 |  587     } | 
 |  588   } else { | 
 |  589     if (RBTS.developer) { | 
 |  590       /* Developer mode */ | 
 |  591       key_version = 0; | 
 |  592       version = 0; | 
 |  593       RETURN_ON_FAILURE(RollbackFirmwareSetup(1)); | 
 |  594       RETURN_ON_FAILURE(RollbackFirmwareLock()); | 
 |  595     } else { | 
 |  596       /* Normal mode */ | 
 |  597       key_version = 0; | 
 |  598       version = 0; | 
 |  599       RETURN_ON_FAILURE(RollbackFirmwareSetup(0)); | 
 |  600       RETURN_ON_FAILURE(RollbackFirmwareLock()); | 
 |  601       RETURN_ON_FAILURE(RollbackKernelRead(&key_version, &version)); | 
 |  602       RETURN_ON_FAILURE(RollbackKernelWrite(key_version, version)); | 
 |  603       RETURN_ON_FAILURE(RollbackKernelLock()); | 
 |  604     } | 
 |  605   } | 
 |  606   return TPM_SUCCESS; | 
 |  607 } | 
 |  608  | 
 |  609 /* One-time call to create the WRITE_BUCKET space. | 
 |  610  */ | 
 |  611 static uint32_t RollbackTest_InitializeTPM(void) { | 
 |  612   TlclLibInit(); | 
 |  613   RETURN_ON_FAILURE(TlclStartup()); | 
 |  614   RETURN_ON_FAILURE(TlclContinueSelfTest()); | 
 |  615   RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); | 
 |  616   RETURN_ON_FAILURE(TlclDefineSpace(WRITE_BUCKET_NV_INDEX, | 
 |  617                                     TPM_NV_PER_PPWRITE, 1)); | 
 |  618   RETURN_ON_FAILURE(RollbackTest_SetTPMState(0)); | 
 |  619   return TPM_SUCCESS; | 
 |  620 } | 
 |  621  | 
 |  622 static void RollbackTest_Initialize(void) { | 
 |  623   Log("initializing"); | 
 |  624   RollbackTest_InitializeState(); | 
 |  625   if (RollbackTest_InitializeTPM() != TPM_SUCCESS) { | 
 |  626     Log("couldn't initialize TPM"); | 
 |  627     exit(1); | 
 |  628   } | 
 |  629 } | 
 |  630  | 
 |  631 /* Advances the desired TPM state and sets the TPM to the new state. | 
 |  632  */ | 
 |  633 static void RollbackTest_Advance(FILE* file) { | 
 |  634   uint32_t result; | 
 |  635   Log("advancing state"); | 
 |  636   if (RollbackTest_AdvanceState()) { | 
 |  637     Log("done"); | 
 |  638     exit(0); | 
 |  639   } | 
 |  640   result = RollbackTest_SetTPMState(1); | 
 |  641   if (result == TPM_SUCCESS) { | 
 |  642     RBTS.advancing = 0; | 
 |  643     RollbackTest_SaveState(file); | 
 |  644     reboot(); | 
 |  645   } else { | 
 |  646     Log("SetTPMState failed with 0x%x\n", result); | 
 |  647     exit(1); | 
 |  648   } | 
 |  649 } | 
 |  650  | 
 |  651 /* Performs the test for the current TPM state, and verify that the outcome | 
 |  652  * matches the expectations. | 
 |  653  */ | 
 |  654 static void RollbackTest_RunOneTest(FILE* file) { | 
 |  655   uint32_t result; | 
 |  656   uint32_t expected_result = TPM_SUCCESS; | 
 |  657  | 
 |  658   if (!RBTS.KERNEL_VERSIONS_exists || | 
 |  659       RBTS.KERNEL_VERSIONS_wrong_permissions || | 
 |  660       RBTS.KERNEL_VERSIONS_wrong_value) { | 
 |  661     expected_result = TPM_E_CORRUPTED_STATE; | 
 |  662   } | 
 |  663  | 
 |  664   if (!RBTS.KERNEL_VERSIONS_exists && !RBTS.TPM_IS_INITIALIZED_exists) { | 
 |  665     /* The space will be recreated */ | 
 |  666     expected_result = TPM_SUCCESS; | 
 |  667   } | 
 |  668  | 
 |  669   if ((!RBTS.TPM_IS_INITIALIZED_exists || !RBTS.KERNEL_VERSIONS_exists) | 
 |  670       && RBTS.owned) { | 
 |  671     /* Cannot create spaces without owner authorization */ | 
 |  672     expected_result = TPM_E_OWNER_SET; | 
 |  673   } | 
 |  674  | 
 |  675   if (RBTS.TPM_IS_INITIALIZED_exists && !RBTS.KERNEL_VERSIONS_exists) { | 
 |  676     expected_result = TPM_ANY_FAILURE; | 
 |  677   } | 
 |  678  | 
 |  679   result = RollbackTest_Test(); | 
 |  680  | 
 |  681   if (result == expected_result || | 
 |  682       (result != TPM_SUCCESS && expected_result == TPM_ANY_FAILURE)) { | 
 |  683     Log("test succeeded with 0x%x\n", result); | 
 |  684     RBTS.advancing = 1; | 
 |  685     RollbackTest_SaveState(file); | 
 |  686     reboot(); | 
 |  687   } else { | 
 |  688     Log("test failed with 0x%x, expecting 0x%x\n", result, expected_result); | 
 |  689     exit(1); | 
 |  690   } | 
 |  691 } | 
 |  692  | 
 |  693 static FILE* RollbackTest_OpenState(void) { | 
 |  694   FILE* file = fopen(STATEPATH, "r+"); | 
 |  695   if (file == NULL) { | 
 |  696     Log("%s could not be opened", STATEPATH); | 
 |  697     exit(1); | 
 |  698   } | 
 |  699   return file; | 
 |  700 } | 
 |  701  | 
 |  702 /* Sync saved state with TPM state. | 
 |  703  */ | 
 |  704 static void RollbackTest_Sync(void) { | 
 |  705   FILE *file = RollbackTest_OpenState(); | 
 |  706   uint32_t result; | 
 |  707   RollbackTest_RestoreState(file); | 
 |  708   Log("Syncing state"); | 
 |  709   result = RollbackTest_SetTPMState(1); | 
 |  710   if (result != TPM_SUCCESS) { | 
 |  711     Log("Sync failed with %x", result); | 
 |  712     exit(1); | 
 |  713   } | 
 |  714 } | 
 |  715  | 
 |  716 /* Runs one testing iteration and advances the testing state. | 
 |  717  */ | 
 |  718 static void RollbackTest_Run(void) { | 
 |  719   FILE* file = RollbackTest_OpenState(); | 
 |  720   RollbackTest_RestoreState(file); | 
 |  721   RollbackTest_LogState(); | 
 |  722   if (RBTS.advancing) { | 
 |  723     RollbackTest_Advance(file); | 
 |  724   } else { | 
 |  725     RollbackTest_RunOneTest(file); | 
 |  726   } | 
 |  727 } | 
 |  728  | 
 |  729 int main(int argc, char** argv) { | 
 |  730  | 
 |  731   openlog("rbtest", LOG_CONS | LOG_PERROR, LOG_USER); | 
 |  732  | 
 |  733   if (geteuid() != 0) { | 
 |  734     fprintf(stderr, "rollback-test: must run as root\n"); | 
 |  735     exit(1); | 
 |  736   } | 
 |  737  | 
 |  738   if (argc == 2 && strcmp(argv[1], "initialize") == 0) { | 
 |  739     RollbackTest_Initialize(); | 
 |  740   } else if (argc == 2 && strcmp(argv[1], "sync") == 0) { | 
 |  741     RollbackTest_Sync(); | 
 |  742   } else if (argc == 1) { | 
 |  743     RollbackTest_Run(); | 
 |  744   } else { | 
 |  745     fprintf(stderr, "usage: rollback-test [ initialize ]\n"); | 
 |  746     exit(1); | 
 |  747   } | 
 |  748   return 0; | 
 |  749 } | 
| OLD | NEW |