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 = {
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
22def missing_firmware_version():
23    """Check for empty fwid.
24
25    @return True if no fwid else False.
26    """
27    cmd = 'crossystem fwid'
28    return not utils.system_output(cmd, ignore_status=True).strip()
29
30
31def __run_tpmc_cmd(subcommand):
32    """Make this test more readable by simplifying commonly used tpmc command.
33
34    @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...)
35    @return String output (which may be empty).
36    """
37    cmd = 'tpmc %s' % subcommand
38    return utils.system_output(cmd, ignore_status=True).strip()
39
40
41def check_tpmc(subcommand, expected):
42    """Runs tpmc command and checks the output against an expected result.
43
44    The expected results take 2 different forms:
45    1. A regular expression that is matched.
46    2. A set of tuples that are matched.
47
48    @param subcommand: String of the tpmc subcommand (getvf, getpf, getp, ...)
49    @param expected: Either a String re or the set of expected tuples.
50    @raises error.TestError() for invalidly matching expected.
51    """
52    error_msg = 'invalid response to tpmc %s' % subcommand
53    if isinstance(expected, str):
54        out = __run_tpmc_cmd(subcommand)
55        if (not re.match(expected, out)):
56            raise error.TestError('%s: %s' % (error_msg, out))
57    else:
58        result_set = utils.set_from_keyval_output(__run_tpmc_cmd(subcommand))
59        if set(expected) <= result_set:
60            return
61        raise error.TestError('%s: expected=%s.' %
62                              (error_msg, sorted(set(expected) - result_set)))
63
64
65class hardware_TPMCheck(test.test):
66    """Check that the state of the TPM is as expected."""
67    version = 1
68
69
70    def initialize(self):
71        # Must stop the TCSD process to be able to collect TPM status,
72        # then restart TCSD process to leave system in a known good state.
73        # Must also stop services which depend on tcsd.
74        self._services = service_stopper.ServiceStopper(['cryptohomed',
75                                                         'chapsd', 'tcsd'])
76        self._services.stop_services()
77
78
79    def run_once(self):
80        """Run a few TPM state checks."""
81        if missing_firmware_version():
82            logging.warning('no firmware version, skipping test')
83            return
84
85        # Check volatile and permanent flags
86        for subcommand in ['getvf', 'getpf']:
87            check_tpmc(subcommand, TPMC_EXPECTED[subcommand])
88
89        # Check space permissions
90        check_tpmc('getp 0x1007', '.*0x8001')
91        check_tpmc('getp 0x1008', '.*0x1')
92
93        # Check kernel space UID
94        check_tpmc('read 0x1008 0x5', '.* 4c 57 52 47$')
95
96
97    def cleanup(self):
98        self._services.restore_services()
99