Home | History | Annotate | Download | only in hardware_TPMCheck
      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 import logging, re
      6 from autotest_lib.client.bin import test, utils
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.cros import service_stopper
      9 
     10 
     11 # Expected results of 'tpmc getX' commands.
     12 TPMC_EXPECTED_1_2 = {
     13     'getvf': # volatile (ST_CLEAR) flags
     14      set([('deactivated', '0'), ('physicalPresence', '0'),
     15           ('physicalPresenceLock', '1'), ('bGlobalLock', '1')]),
     16     'getpf': # permanent flags
     17      set([('disable', '0'), ('ownership', '1'), ('deactivated', '0'),
     18           ('physicalPresenceHWEnable', '0'), ('physicalPresenceCMDEnable', '1'),
     19           ('physicalPresenceLifetimeLock', '1'), ('nvLocked', '1')])}
     20 
     21 TPMC_EXPECTED_2_0 = {
     22     'getvf': # volatile (ST_CLEAR) flags
     23      set([('phEnable', '0'), ('shEnable', '1'),
     24           ('ehEnable', '1'), ('phEnableNV', '1')]),
     25     'getpf': # permanent flags
     26      set([('inLockout', '0')])}
     27 
     28 # Expected permissions for NV indexes.
     29 PERM_EXPECTED_1_2 = {'0x1007': '0x8001', '0x1008': '0x1'}
     30 PERM_EXPECTED_2_0 = {'0x1007': '0x60054c01', '0x1008': '0x60050001'}
     31 
     32 def missing_firmware_version():
     33     """Check for empty fwid.
     34 
     35     @return True if no fwid else False.
     36     """
     37     cmd = 'crossystem fwid'
     38     return not utils.system_output(cmd, ignore_status=True).strip()
     39 
     40 
     41 def __run_tpmc_cmd(subcommand):
     42     """Make this test more readable by simplifying commonly used tpmc command.
     43 
     44     @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...)
     45     @return String output (which may be empty).
     46     """
     47     cmd = 'tpmc %s' % subcommand
     48     return utils.system_output(cmd, ignore_status=True).strip()
     49 
     50 
     51 def check_tpmc(subcommand, expected):
     52     """Runs tpmc command and checks the output against an expected result.
     53 
     54     The expected results take 2 different forms:
     55     1. A regular expression that is matched.
     56     2. A set of tuples that are matched.
     57 
     58     @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...)
     59     @param expected: Either a String re or the set of expected tuples.
     60     @raises error.TestError() for invalidly matching expected.
     61     """
     62     error_msg = 'invalid response to tpmc %s' % subcommand
     63     if isinstance(expected, str):
     64         out = __run_tpmc_cmd(subcommand)
     65         if (not re.match(expected, out)):
     66             raise error.TestError('%s: %s' % (error_msg, out))
     67     else:
     68         result_set = utils.set_from_keyval_output(__run_tpmc_cmd(subcommand))
     69         if set(expected) <= result_set:
     70             return
     71         raise error.TestError('%s: expected=%s.' %
     72                               (error_msg, sorted(set(expected) - result_set)))
     73 
     74 
     75 def check_perm(index, perm):
     76     return check_tpmc('getp %s' % index, '.*%s$' % perm)
     77 
     78 
     79 def is_tpm2():
     80     """Check TPM version.
     81 
     82     @return True if the system has TPM2.0 else False.
     83     """
     84     trunks_init_file = '/etc/init/trunksd.conf'
     85     cmd = 'ls %s' % trunks_init_file
     86     output = utils.system_output(cmd, ignore_status=True).strip()
     87     return output == trunks_init_file
     88 
     89 
     90 class hardware_TPMCheck(test.test):
     91     """Check that the state of the TPM is as expected."""
     92     version = 1
     93 
     94 
     95     def initialize(self):
     96         # Must stop the TCSD process to be able to collect TPM status,
     97         # then restart TCSD process to leave system in a known good state.
     98         # Must also stop services which depend on tcsd.
     99         # Note: for TPM2 the order of re-starting services (they are started
    100         # in the reversed listed order) is important: e.g. tpm_managerd must
    101         # start after trunksd, and cryptohomed after attestationd.
    102         self._services = service_stopper.ServiceStopper(['cryptohomed',
    103                                                          'chapsd',
    104                                                          'attestationd',
    105                                                          'tpm_managerd',
    106                                                          'tcsd', 'trunksd'])
    107         self._services.stop_services()
    108 
    109 
    110     def run_once(self):
    111         """Run a few TPM state checks."""
    112         if missing_firmware_version():
    113             logging.warning('no firmware version, skipping test')
    114             return
    115 
    116         if is_tpm2():
    117             logging.info('Running on TPM 2.0')
    118             tpmc_expected = TPMC_EXPECTED_2_0
    119             perm_expected = PERM_EXPECTED_2_0
    120         else:
    121             logging.info('Running on TPM 1.2')
    122             tpmc_expected = TPMC_EXPECTED_1_2
    123             perm_expected = PERM_EXPECTED_1_2
    124 
    125         # Check volatile and permanent flags
    126         for subcommand in ['getvf', 'getpf']:
    127             check_tpmc(subcommand, tpmc_expected[subcommand])
    128 
    129         # Check space permissions
    130         for index in ['0x1007', '0x1008']:
    131             check_perm(index, perm_expected[index])
    132 
    133         # Check kernel space UID
    134         check_tpmc('read 0x1008 0x5', '.* 4c 57 52 47$')
    135 
    136 
    137     def cleanup(self):
    138         self._services.restore_services()
    139