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 5import logging, re 6from autotest_lib.client.bin import test, utils 7from autotest_lib.client.common_lib import error 8from autotest_lib.client.cros import service_stopper 9 10 11# Expected results of 'tpmc getX' commands. 12TPMC_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 21TPMC_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. 29PERM_EXPECTED_1_2 = {'0x1007': '0x8001', '0x1008': '0x1'} 30PERM_EXPECTED_2_0 = {'0x1007': '0x60054c01', '0x1008': '0x60050001'} 31 32def 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 41def __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 51def 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 75def check_perm(index, perm): 76 return check_tpmc('getp %s' % index, '.*%s$' % perm) 77 78 79def 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 90class 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 # First check the TPM data version. If it is ver 1.0, then skip 135 # the kernel UID because it is not available. 136 try: 137 # Read the first two bytes of TPM kernel data, and check if it is 138 # in the format version 1.0. 139 # byte[0]: version. bit[7:4]=Major, bit[3:0]=Minor. 140 # byte[1]: byte size. 0x28, 40(0x28) bytes 141 check_tpmc('read 0x1008 0x2', '^10 28') 142 except error.TestError as e: 143 # If TPM ver 1.0 format pattern matching fails, then it should be in 144 # the old format. Let's check kernel space UID. 145 check_tpmc('read 0x1008 0x5', '^.* 4c 57 52 47$') 146 147 148 def cleanup(self): 149 self._services.restore_services() 150