• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 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
6import time
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib.cros import tpm_utils
10from autotest_lib.server import autotest
11from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
12
13
14class firmware_FWMPDisableCCD(FirmwareTest):
15    """A test that uses cryptohome to set the FWMP flags and verifies that
16    cr50 disables/enables console unlock."""
17    version = 1
18
19    FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6)
20    GSCTOOL_ERR = 'Error: rv 7, response 7'
21
22    def initialize(self, host, cmdline_args, ccd_lockout):
23        """Initialize servo check if cr50 exists"""
24        super(firmware_FWMPDisableCCD, self).initialize(host, cmdline_args)
25
26        self.host = host
27        # Test CCD if servo has access to Cr50, is running with CCD v1, and has
28        # testlab mode enabled.
29        self.test_ccd_unlock = (hasattr(self, 'cr50') and
30            self.cr50.has_command('ccdstate') and not ccd_lockout)
31
32        logging.info('%sTesting CCD Unlock', '' if self.test_ccd_unlock else
33            'Not ')
34
35
36    def try_ccd_unlock(self, fwmp_disabled_unlock):
37        """Try unlocking cr50 using gsctool
38
39        The FWMP flags may disable ccd. If they do, unlocking or opening CCD
40        should fail.
41
42        Unlocking has a lot of extra restrictions using the cr50 console, so
43        run it using tpm vendor commands.
44
45        @param fwmp_disabled_unlock: True if the unlock process should fail
46        """
47        self.cr50.send_command('ccd lock')
48
49        result = self.host.run('gsctool -U -a',
50                ignore_status=fwmp_disabled_unlock)
51
52        if fwmp_disabled_unlock and result.stderr.strip() != self.GSCTOOL_ERR:
53            raise error.TestFail('Unexpected gsctool response %r' % result)
54
55        state = self.cr50.get_ccd_level()
56        expected_state = 'lock' if fwmp_disabled_unlock else 'unlock'
57        if state != expected_state:
58            raise error.TestFail('Unexpected ccd state after unlock. expected '
59                    '%s got %s' % (expected_state, state))
60
61
62    def try_ccd_open(self, fwmp_disabled_unlock):
63        """Try opening cr50 using the console
64
65        The FWMP flags may disable ccd. If they do, unlocking or opening CCD
66        should fail.
67
68        @param fwmp_disabled_unlock: True if open should fail
69        """
70        self.cr50.send_command('ccd lock')
71        response = 'Console unlock%s allowed.*>' % (
72                ' not' if fwmp_disabled_unlock else '')
73        logging.info(self.cr50.send_command_get_output('ccd open', [response]))
74
75        # Wait long enough for ccd open to timeout
76        time.sleep(10)
77
78
79    def cr50_check_lock_control(self, flags):
80        """Verify cr50 lock enable/disable works as intended based on flags.
81
82        If flags & self.FWMP_DEV_DISABLE_CCD_UNLOCK is true, lock disable should
83        fail.
84
85        This will only run during a test with access to the cr50  console
86
87        @param flags: A string with the FWMP settings.
88        """
89        if not self.test_ccd_unlock:
90            return
91
92        fwmp_disabled_unlock = (self.FWMP_DEV_DISABLE_CCD_UNLOCK &
93                               int(flags, 16))
94
95        logging.info('Flags are set to %s ccd level change is %s', flags,
96                     'disabled' if fwmp_disabled_unlock else 'enabled')
97
98        # The ccd privilege level can be changed to unlock or open. Make sure
99        # that the fwmp setting affects both the same.
100        self.try_ccd_unlock(fwmp_disabled_unlock)
101        self.try_ccd_open(fwmp_disabled_unlock)
102
103
104
105    def check_fwmp(self, flags, clear_tpm_owner):
106        """Set the flags and verify ccd lock/unlock
107
108        Args:
109            flags: A string to used set the FWMP flags
110            clear_tpm_owner: True if the TPM owner needs to be cleared before
111                             setting the flags and verifying ccd lock/unlock
112        """
113        if clear_tpm_owner:
114            logging.info('Clearing TPM owner')
115            tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True)
116
117        logging.info('setting flags to %s', flags)
118        autotest.Autotest(self.host).run_test('firmware_SetFWMP', flags=flags,
119                fwmp_cleared=clear_tpm_owner, check_client_result=True)
120
121        # Verify ccd lock/unlock with the current flags works as intended.
122        self.cr50_check_lock_control(flags)
123
124
125    def run_once(self, ccd_lockout):
126        """Verify FWMP disable with different flag values"""
127        self.check_fwmp('0xaa00', True)
128        # Verify that the flags can be changed on the same boot
129        self.check_fwmp('0xbb00', False)
130
131        # Verify setting FWMP_DEV_DISABLE_CCD_UNLOCK disables ccd
132        self.check_fwmp(hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK), True)
133
134        # 0x41 is the flag setting when dev boot is disabled. Make sure that
135        # nothing unexpected happens.
136        self.check_fwmp('0x41', True)
137
138        # Clear the TPM owner and verify lock can still be enabled/disabled when
139        # the FWMP has not been created
140        tpm_utils.ClearTPMOwnerRequest(self.host)
141        self.cr50_check_lock_control('0')
142