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