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