| # Copyright 2017 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 |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server import autotest |
| from autotest_lib.server.cros.faft.cr50_test import Cr50Test |
| |
| |
| class firmware_FWMPDisableCCD(Cr50Test): |
| """A test that uses cryptohome to set the FWMP flags and verifies that |
| cr50 disables/enables console unlock.""" |
| version = 1 |
| |
| FWMP_DEV_DISABLE_CCD_UNLOCK = (1 << 6) |
| GSCTOOL_ERR = 'Error: rv 7, response 7' |
| |
| def initialize(self, host, cmdline_args, full_args): |
| """Initialize servo check if cr50 exists""" |
| super(firmware_FWMPDisableCCD, self).initialize(host, cmdline_args, |
| full_args) |
| self.fast_ccd_open(enable_testlab=True) |
| |
| |
| def try_set_ccd_level(self, level, fwmp_disabled_ccd): |
| """Try setting the ccd level with a password. |
| |
| The normal Cr50 ccd path requires entering dev mode. Entering dev mode |
| may be disabled by the FWMP flags. Entering dev mode also erases the |
| FWMP. The test uses a password to get around the dev mode requirement. |
| Send CCD commands from the commandline with the password. Check the |
| output to make sure if it fails, it failed because of the FWMP. |
| |
| @param level: the desired ccd level: 'open' or 'unlock'. |
| @param fwmp_disabled_ccd: True if 'ccd set $LEVEL' should fail |
| """ |
| # Make sure the console is locked |
| self.cr50.set_ccd_level('lock') |
| try: |
| self.cr50.set_ccd_level(level, self.CCD_PASSWORD) |
| if fwmp_disabled_ccd: |
| raise error.TestFail('FWMP failed to prevent %r' % level) |
| except error.TestFail, e: |
| logging.info(e) |
| if fwmp_disabled_ccd: |
| if ("FWMP disabled 'ccd open'" in str(e) or |
| 'Console unlock not allowed' in str(e)): |
| logging.info('FWMP successfully disabled ccd %s', level) |
| return |
| else: |
| raise error.TestFail('FWMP disabled %s in unexpected ' |
| 'manner %r' % (level, str(e))) |
| raise |
| |
| |
| def open_cr50_and_setup_ccd(self): |
| """Configure cr50 ccd for the test. |
| |
| Open Cr50. Reset ccd, so the capabilities are reset and the password is |
| cleared. Set OpenNoTPMWipe to Always, so the FWMP won't be cleared |
| during open. |
| """ |
| # Clear the password and relock the console |
| self.cr50.send_command('ccd testlab open') |
| self.cr50.send_command('ccd reset') |
| # Set this so when we run the open test, it won't clear the FWMP |
| self.cr50.set_cap('OpenNoTPMWipe', 'Always') |
| |
| |
| def cr50_check_lock_control(self, flags): |
| """Verify cr50 lock enable/disable works as intended based on flags. |
| |
| If flags & self.FWMP_DEV_DISABLE_CCD_UNLOCK is true, lock disable should |
| fail. |
| |
| This will only run during a test with access to the cr50 console |
| |
| @param flags: A string with the FWMP settings. |
| """ |
| fwmp_disabled_ccd = not not (self.FWMP_DEV_DISABLE_CCD_UNLOCK & |
| int(flags, 16)) |
| |
| start_state = self.cr50.get_ccd_info('TPM') |
| if ('fwmp_lock' in start_state) != fwmp_disabled_ccd: |
| raise error.TestFail('Unexpected fwmp state with flags %s' % flags) |
| |
| logging.info('Flags are set to %s ccd is%s permitted', flags, |
| ' not' if fwmp_disabled_ccd else '') |
| if not self.faft_config.has_powerbutton: |
| logging.info('Can not test ccd without power button') |
| return |
| |
| self.open_cr50_and_setup_ccd() |
| # Try setting password after FWMP has been created. Setting password is |
| # always allowed. Open and unlock should still be blocked. Opening cr50 |
| # requires the device is in dev mode unless there's a password set. FWMP |
| # flags may disable dev mode. Set a password so we can get around this. |
| self.set_ccd_password(self.CCD_PASSWORD) |
| |
| # run ccd commands with the password. ccd open and unlock should fail |
| # when the FWMP has disabled ccd. |
| self.try_set_ccd_level('open', fwmp_disabled_ccd) |
| self.try_set_ccd_level('unlock', fwmp_disabled_ccd) |
| |
| # Clear the password. |
| self.open_cr50_and_setup_ccd() |
| self.cr50.send_command('ccd lock') |
| |
| |
| def check_fwmp(self, flags, clear_fwmp, check_lock=True): |
| """Set the flags and verify ccd lock/unlock |
| |
| Args: |
| flags: A string to used set the FWMP flags. If None, skip running |
| firmware_SetFWMP. |
| clear_fwmp: True if the flags should be reset. |
| check_lock: Check ccd open |
| """ |
| if clear_fwmp: |
| self.clear_fwmp() |
| |
| logging.info('setting flags to %s', flags) |
| if flags: |
| autotest.Autotest(self.host).run_test('firmware_SetFWMP', |
| flags=flags, fwmp_cleared=clear_fwmp, |
| check_client_result=True) |
| |
| # Verify ccd lock/unlock with the current flags works as intended. |
| if check_lock: |
| self.cr50_check_lock_control(flags if flags else '0') |
| |
| |
| def run_once(self): |
| """Verify FWMP disable with different flag values""" |
| # Skip checking ccd open, so the DUT doesn't reboot |
| self.check_fwmp('0xaa00', True, check_lock=False) |
| # Verify that the flags can be changed on the same boot |
| self.check_fwmp('0xbb00', False) |
| |
| # Verify setting FWMP_DEV_DISABLE_CCD_UNLOCK disables ccd |
| self.check_fwmp(hex(self.FWMP_DEV_DISABLE_CCD_UNLOCK), True) |
| |
| # 0x41 is the flag setting when dev boot is disabled. Make sure that |
| # nothing unexpected happens. |
| self.check_fwmp('0x41', True) |
| |
| # Clear the TPM owner and verify lock can still be enabled/disabled when |
| # the FWMP has not been created |
| self.check_fwmp(None, True) |