• 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
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server import autotest
9from autotest_lib.server.cros.faft.cr50_test import Cr50Test
10
11
12class firmware_FWMPDisableCCD(Cr50Test):
13    """A test that uses cryptohome to set the FWMP flags and verifies that
14    cr50 disables/enables console unlock."""
15    version = 1
16
17    FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6)
18    GSCTOOL_ERR = 'Error: rv 7, response 7'
19    PASSWORD = 'Password'
20
21    def initialize(self, host, cmdline_args, full_args):
22        """Initialize servo check if cr50 exists"""
23        super(firmware_FWMPDisableCCD, self).initialize(host, cmdline_args,
24                full_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'))
31
32        logging.info('%sTesting CCD', '' if self.test_ccd_unlock else 'Not')
33        if self.test_ccd_unlock:
34            self.fast_open(enable_testlab=True)
35
36
37    def try_set_ccd_level(self, level, fwmp_disabled_ccd):
38        """Try setting the ccd level with a password.
39
40        The normal Cr50 ccd path requires entering dev mode. Entering dev mode
41        may be disabled by the FWMP flags. Entering dev mode also erases the
42        FWMP. The test uses a password to get around the dev mode requirement.
43        Send CCD commands from the commandline with the password. Check the
44        output to make sure if it fails, it failed because of the FWMP.
45
46        @param level: the desired ccd level: 'open' or 'unlock'.
47        @param fwmp_disabled_ccd: True if 'ccd set $LEVEL' should fail
48        """
49        # Make sure the console is locked
50        self.cr50.set_ccd_level('lock')
51        try:
52            self.cr50.set_ccd_level(level, self.PASSWORD)
53            if fwmp_disabled_ccd:
54                raise error.TestFail('FWMP failed to prevent %r' % level)
55        except error.TestFail, e:
56            logging.info(e)
57            if fwmp_disabled_ccd:
58                if ("FWMP disabled 'ccd open'" in str(e) or
59                    'Console unlock not allowed' in str(e)):
60                    logging.info('FWMP successfully disabled ccd %s', level)
61                    return
62                else:
63                    raise error.TestFail('FWMP disabled %s in unexpected '
64                                         'manner %r' % (level, str(e)))
65            raise
66
67
68    def open_cr50_and_setup_ccd(self):
69        """Configure cr50 ccd for the test.
70
71        Open Cr50. Reset ccd, so the capabilities are reset and the password is
72        cleared. Set OpenNoTPMWipe to Always, so the FWMP won't be cleared
73        during open.
74        """
75        # Clear the password and relock the console
76        self.cr50.send_command('ccd testlab open')
77        self.cr50.send_command('ccd reset')
78        # Set this so when we run the open test, it won't clear the FWMP
79        self.cr50.set_cap('OpenNoTPMWipe', 'Always')
80
81
82    def cr50_check_lock_control(self, flags):
83        """Verify cr50 lock enable/disable works as intended based on flags.
84
85        If flags & self.FWMP_DEV_DISABLE_CCD_UNLOCK is true, lock disable should
86        fail.
87
88        This will only run during a test with access to the cr50  console
89
90        @param flags: A string with the FWMP settings.
91        """
92        fwmp_disabled_ccd = not not (self.FWMP_DEV_DISABLE_CCD_UNLOCK &
93                               int(flags, 16))
94
95        start_state = self.cr50.get_ccd_info()['TPM']
96        if ('fwmp_lock' in start_state) != fwmp_disabled_ccd:
97            raise error.TestFail('Unexpected fwmp state with flags %x' % flags)
98
99        if not self.test_ccd_unlock:
100            return
101
102        logging.info('Flags are set to %s ccd is%s permitted', flags,
103                     ' not' if fwmp_disabled_ccd else '')
104
105        self.open_cr50_and_setup_ccd()
106        # Try setting password after FWMP has been created. Setting password is
107        # always allowed. Open and unlock should still be blocked. Opening cr50
108        # requires the device is in dev mode unless there's a password set. FWMP
109        # flags may disable dev mode. Set a password so we can get around this.
110        self.set_ccd_password(self.PASSWORD)
111
112        # run ccd commands with the password. ccd open and unlock should fail
113        # when the FWMP has disabled ccd.
114        self.try_set_ccd_level('open', fwmp_disabled_ccd)
115        self.try_set_ccd_level('unlock', fwmp_disabled_ccd)
116
117        # Clear the password.
118        self.open_cr50_and_setup_ccd()
119        self.cr50.send_command('ccd lock')
120
121
122    def check_fwmp(self, flags, clear_fwmp, check_lock=True):
123        """Set the flags and verify ccd lock/unlock
124
125        Args:
126            flags: A string to used set the FWMP flags. If None, skip running
127                   firmware_SetFWMP.
128            clear_fwmp: True if the flags should be reset.
129            check_lock: Check ccd open
130        """
131        if clear_fwmp:
132            self.clear_fwmp()
133
134        logging.info('setting flags to %s', flags)
135        if flags:
136            autotest.Autotest(self.host).run_test('firmware_SetFWMP',
137                    flags=flags, fwmp_cleared=clear_fwmp,
138                    check_client_result=True)
139
140        # Verify ccd lock/unlock with the current flags works as intended.
141        if check_lock:
142            self.cr50_check_lock_control(flags if flags else '0')
143
144
145    def run_once(self):
146        """Verify FWMP disable with different flag values"""
147        # Skip checking ccd open, so the DUT doesn't reboot
148        self.check_fwmp('0xaa00', True, check_lock=False)
149        # Verify that the flags can be changed on the same boot
150        self.check_fwmp('0xbb00', False)
151
152        # Verify setting FWMP_DEV_DISABLE_CCD_UNLOCK disables ccd
153        self.check_fwmp(hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK), True)
154
155        # 0x41 is the flag setting when dev boot is disabled. Make sure that
156        # nothing unexpected happens.
157        self.check_fwmp('0x41', True)
158
159        # Clear the TPM owner and verify lock can still be enabled/disabled when
160        # the FWMP has not been created
161        self.check_fwmp(None, True)
162