1# Copyright 2014 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, os
6
7from autotest_lib.client.common_lib import error
8
9
10_PASSWD_FILE = '/var/tmp/tpm_password'
11_RM_DIRS = ('/home/.shadow/* ' +
12            '/home/chronos/.oobe_completed ' +
13            '/home/chronos/Local\ State ' +
14            '/var/cache/app_pack ' +
15            '/var/cache/shill/default.profile ' +
16            '/var/lib/tpm ' +
17            '/var/lib/whitelist/* ')
18
19
20class NoTPMPasswordException(Exception):
21    """No TPM Password could be found."""
22    pass
23
24
25def TPMStatus(client):
26    """Returns a dictionary with TPM status.
27
28    @param client: client object to run commands on.
29    """
30    out = client.run('cryptohome --action=tpm_status').stdout.strip()
31    out = out.replace('TPM ', '')
32    lines = out.split('\n')
33    status = {}
34    for item in lines:
35        item = item.split(':')
36        if not item[0]:
37            continue
38        if len(item) == 1:
39            item.append('')
40        item = map(lambda x : x.strip(), item)
41        item[1] = True if item[1] == 'true' else item[1]
42        item[1] = False if item[1] == 'false' else item[1]
43        status[item[0]] = item[1]
44    return status
45
46
47def IsTPMAvailable(client):
48    """Returns True if the TPM is unowned and enabled.
49
50    @param client: client object to run commands on.
51    """
52    status = TPMStatus(client)
53    return status['Enabled'] and not status['Owned']
54
55
56def ClearTPMServer(client, out_dir):
57    """Clears the TPM and reboots from a server-side autotest.
58
59    @param client: client object to run commands on.
60    @param out_dir: temporary directory to store the retrieved password file.
61    """
62    if IsTPMAvailable(client):
63        logging.debug('TPM is not owned')
64        return
65
66    client.run('stop ui')
67    try:
68        password = TPMStatus(client)['Password']
69        if not password:
70            try:
71                client.get_file(_PASSWD_FILE, out_dir)
72            except error.AutoservRunError:
73                raise NoTPMPasswordException(
74                        'TPM Password file %s doesn\'t exist, falling back on '
75                        'clear_tpm_owner_request to clear the TPM. You may '
76                        'need to have the firmware clear the TPM, for instance '
77                        'by toggling the dev switch.' % _PASSWD_FILE)
78            with open(os.path.join(out_dir,
79                      os.path.basename(_PASSWD_FILE))) as f:
80                password = f.read().rstrip()
81        if not password:
82            raise NoTPMPasswordException(
83                    'TPM Password file %s empty, falling back on '
84                    'clear_tpm_owner_request to clear the TPM. You may need to '
85                    'have the firmware clear the TPM, for instance by toggling '
86                    'the dev switch.' % _PASSWD_FILE)
87
88        res = client.run('tpm_clear --pass ' + password).stdout.strip()
89        logging.warn(repr(res))
90    except NoTPMPasswordException as e:
91        logging.warn(e.args[0])
92        client.run('crossystem clear_tpm_owner_request=1')
93
94    CleanupAndReboot(client)
95
96
97def ClearTPMOwnerRequest(client):
98    """Clears the TPM using crossystem command.
99
100    @param client: client object to run commands on.
101    """
102    if not client.run('crossystem clear_tpm_owner_request=1',
103                      ignore_status=True).exit_status == 0:
104        raise error.TestFail('Unable to clear TPM.')
105
106    CleanupAndReboot(client)
107
108
109def CleanupAndReboot(client):
110    """Cleanup and reboot the device.
111
112    @param client: client object to run commands on.
113    """
114    client.run('sudo rm -rf ' + _RM_DIRS, ignore_status=True)
115    client.run('sync', ignore_status=True)
116    client.reboot()
117