# 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 import time from autotest_lib.client.common_lib import error from autotest_lib.client.common_lib.cros import tpm_utils from autotest_lib.server import autotest from autotest_lib.server.cros.faft.firmware_test import FirmwareTest class firmware_FWMPDisableCCD(FirmwareTest): """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, ccd_lockout): """Initialize servo check if cr50 exists""" super(firmware_FWMPDisableCCD, self).initialize(host, cmdline_args) self.host = host # Test CCD if servo has access to Cr50, is running with CCD v1, and has # testlab mode enabled. self.test_ccd_unlock = (hasattr(self, 'cr50') and self.cr50.has_command('ccdstate') and not ccd_lockout) logging.info('%sTesting CCD Unlock', '' if self.test_ccd_unlock else 'Not ') def try_ccd_unlock(self, fwmp_disabled_unlock): """Try unlocking cr50 using gsctool The FWMP flags may disable ccd. If they do, unlocking or opening CCD should fail. Unlocking has a lot of extra restrictions using the cr50 console, so run it using tpm vendor commands. @param fwmp_disabled_unlock: True if the unlock process should fail """ self.cr50.send_command('ccd lock') result = self.host.run('gsctool -U -a', ignore_status=fwmp_disabled_unlock) if fwmp_disabled_unlock and result.stderr.strip() != self.GSCTOOL_ERR: raise error.TestFail('Unexpected gsctool response %r' % result) state = self.cr50.get_ccd_level() expected_state = 'lock' if fwmp_disabled_unlock else 'unlock' if state != expected_state: raise error.TestFail('Unexpected ccd state after unlock. expected ' '%s got %s' % (expected_state, state)) def try_ccd_open(self, fwmp_disabled_unlock): """Try opening cr50 using the console The FWMP flags may disable ccd. If they do, unlocking or opening CCD should fail. @param fwmp_disabled_unlock: True if open should fail """ self.cr50.send_command('ccd lock') response = 'Console unlock%s allowed.*>' % ( ' not' if fwmp_disabled_unlock else '') logging.info(self.cr50.send_command_get_output('ccd open', [response])) # Wait long enough for ccd open to timeout time.sleep(10) 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. """ if not self.test_ccd_unlock: return fwmp_disabled_unlock = (self.FWMP_DEV_DISABLE_CCD_UNLOCK & int(flags, 16)) logging.info('Flags are set to %s ccd level change is %s', flags, 'disabled' if fwmp_disabled_unlock else 'enabled') # The ccd privilege level can be changed to unlock or open. Make sure # that the fwmp setting affects both the same. self.try_ccd_unlock(fwmp_disabled_unlock) self.try_ccd_open(fwmp_disabled_unlock) def check_fwmp(self, flags, clear_tpm_owner): """Set the flags and verify ccd lock/unlock Args: flags: A string to used set the FWMP flags clear_tpm_owner: True if the TPM owner needs to be cleared before setting the flags and verifying ccd lock/unlock """ if clear_tpm_owner: logging.info('Clearing TPM owner') tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True) logging.info('setting flags to %s', flags) autotest.Autotest(self.host).run_test('firmware_SetFWMP', flags=flags, fwmp_cleared=clear_tpm_owner, check_client_result=True) # Verify ccd lock/unlock with the current flags works as intended. self.cr50_check_lock_control(flags) def run_once(self, ccd_lockout): """Verify FWMP disable with different flag values""" self.check_fwmp('0xaa00', True) # 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 tpm_utils.ClearTPMOwnerRequest(self.host) self.cr50_check_lock_control('0')