# 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. import logging, re from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error from autotest_lib.client.cros import service_stopper # Expected results of 'tpmc getX' commands. TPMC_EXPECTED_1_2 = { 'getvf': # volatile (ST_CLEAR) flags set([('deactivated', '0'), ('physicalPresence', '0'), ('physicalPresenceLock', '1'), ('bGlobalLock', '1')]), 'getpf': # permanent flags set([('disable', '0'), ('ownership', '1'), ('deactivated', '0'), ('physicalPresenceHWEnable', '0'), ('physicalPresenceCMDEnable', '1'), ('physicalPresenceLifetimeLock', '1'), ('nvLocked', '1')])} TPMC_EXPECTED_2_0 = { 'getvf': # volatile (ST_CLEAR) flags set([('phEnable', '0'), ('shEnable', '1'), ('ehEnable', '1'), ('phEnableNV', '1')]), 'getpf': # permanent flags set([('inLockout', '0')])} # Expected permissions for NV indexes. PERM_EXPECTED_1_2 = {'0x1007': '0x8001', '0x1008': '0x1'} PERM_EXPECTED_2_0 = {'0x1007': '0x60054c01', '0x1008': '0x60050001'} def missing_firmware_version(): """Check for empty fwid. @return True if no fwid else False. """ cmd = 'crossystem fwid' return not utils.system_output(cmd, ignore_status=True).strip() def __run_tpmc_cmd(subcommand): """Make this test more readable by simplifying commonly used tpmc command. @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...) @return String output (which may be empty). """ cmd = 'tpmc %s' % subcommand return utils.system_output(cmd, ignore_status=True).strip() def check_tpmc(subcommand, expected): """Runs tpmc command and checks the output against an expected result. The expected results take 2 different forms: 1. A regular expression that is matched. 2. A set of tuples that are matched. @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...) @param expected: Either a String re or the set of expected tuples. @raises error.TestError() for invalidly matching expected. """ error_msg = 'invalid response to tpmc %s' % subcommand if isinstance(expected, str): out = __run_tpmc_cmd(subcommand) if (not re.match(expected, out)): raise error.TestError('%s: %s' % (error_msg, out)) else: result_set = utils.set_from_keyval_output(__run_tpmc_cmd(subcommand)) if set(expected) <= result_set: return raise error.TestError('%s: expected=%s.' % (error_msg, sorted(set(expected) - result_set))) def check_perm(index, perm): return check_tpmc('getp %s' % index, '.*%s$' % perm) def is_tpm2(): """Check TPM version. @return True if the system has TPM2.0 else False. """ trunks_init_file = '/etc/init/trunksd.conf' cmd = 'ls %s' % trunks_init_file output = utils.system_output(cmd, ignore_status=True).strip() return output == trunks_init_file class hardware_TPMCheck(test.test): """Check that the state of the TPM is as expected.""" version = 1 def initialize(self): # Must stop the TCSD process to be able to collect TPM status, # then restart TCSD process to leave system in a known good state. # Must also stop services which depend on tcsd. # Note: for TPM2 the order of re-starting services (they are started # in the reversed listed order) is important: e.g. tpm_managerd must # start after trunksd, and cryptohomed after attestationd. self._services = service_stopper.ServiceStopper(['cryptohomed', 'chapsd', 'attestationd', 'tpm_managerd', 'tcsd', 'trunksd']) self._services.stop_services() def run_once(self): """Run a few TPM state checks.""" if missing_firmware_version(): logging.warning('no firmware version, skipping test') return if is_tpm2(): logging.info('Running on TPM 2.0') tpmc_expected = TPMC_EXPECTED_2_0 perm_expected = PERM_EXPECTED_2_0 else: logging.info('Running on TPM 1.2') tpmc_expected = TPMC_EXPECTED_1_2 perm_expected = PERM_EXPECTED_1_2 # Check volatile and permanent flags for subcommand in ['getvf', 'getpf']: check_tpmc(subcommand, tpmc_expected[subcommand]) # Check space permissions for index in ['0x1007', '0x1008']: check_perm(index, perm_expected[index]) # Check kernel space UID check_tpmc('read 0x1008 0x5', '.* 4c 57 52 47$') def cleanup(self): self._services.restore_services()