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, test 9from autotest_lib.client.common_lib.cros import tpm_utils 10 11 12class firmware_Cr50InvalidateRW(test.test): 13 """ 14 Verify the inactive Cr50 header on the first login after cryptohome 15 restarts. 16 17 There are two special cases this test covers: logging in after the TPM 18 owner is cleared and logging in as guest. 19 20 After the tpm owner is cleared, corrupting the header will be done on 21 the second login. During guest login the owner wont be cleared. 22 """ 23 version = 1 24 25 GET_CRYPTOHOME_MESSAGE ='grep cryptohomed /var/log/messages' 26 SUCCESS = 'Successfully invalidated inactive Cr50 RW' 27 FAIL = 'Invalidating inactive Cr50 RW failed' 28 NO_ATTEMPT = 'Did not try to invalidate header' 29 LOGIN_ATTEMPTS = 5 30 31 def initialize(self, host): 32 """Initialize servo and cr50""" 33 super(firmware_Cr50InvalidateRW, self).initialize() 34 35 self.host = host 36 self.client_at = autotest.Autotest(self.host) 37 38 self.last_message = None 39 # get the messages already in /var/log/messages so we don't use them 40 # later. 41 self.check_for_invalidated_rw() 42 43 44 def check_for_invalidated_rw(self): 45 """Use /var/log/messages to see if the rw header was invalidated. 46 47 Returns a string NO_ATTEMPT if cryptohome did not try to invalidate the 48 header or the cryptohome message if the attempt failed or succeeded. 49 """ 50 # Get the relevant messages from /var/log/messages 51 message_str = self.host.run(self.GET_CRYPTOHOME_MESSAGE, 52 verbose=False).stdout.strip() 53 54 # Remove the messages we have seen in the past 55 if self.last_message: 56 message_str = message_str.rsplit(self.last_message, 1)[-1] 57 messages = message_str.split('\n') 58 59 # Save the last message so we can identify new messages later 60 self.last_message = messages[-1] 61 62 rv = self.NO_ATTEMPT 63 # print all cryptohome messages. 64 for message in messages: 65 logging.debug(message) 66 # Return the message that is related to the RW invalidate attempt 67 if self.FAIL in message or self.SUCCESS in message: 68 rv = message 69 return rv 70 71 72 def login(self, use_guest): 73 """Run the test to login.""" 74 if use_guest: 75 self.client_at.run_test('login_CryptohomeIncognito') 76 else: 77 self.client_at.run_test('login_LoginSuccess') 78 79 80 def login_and_verify(self, use_guest=False, corrupt_login=None): 81 """Verify the header is only invalidated on the specified login. 82 83 login LOGIN_ATTEMPTS times. Verify that cryptohome only tries to corrupt 84 the inactive cr50 header on the specified login. If it tries on a 85 different login or fails to corrupt the header, raise an error. 86 87 Args: 88 use_guest: True to login as guest 89 corrupt_login: The login attempt that we expect the header to be 90 corrupted on 91 92 Raises: 93 TestError if the system attempts to corrupt the header on any login 94 that isn't corrupt_login or if an attepmt to corrupt the header 95 fails. 96 """ 97 for i in xrange(self.LOGIN_ATTEMPTS): 98 attempt = i + 1 99 100 self.login(use_guest) 101 result = self.check_for_invalidated_rw() 102 103 message = '%slogin %d: %s' % ('guest ' if use_guest else '', 104 attempt, result) 105 logging.info(message) 106 107 # Anytime the invalidate attempt fails raise an error 108 if self.FAIL in result: 109 raise error.TestError(message) 110 111 # The header should be invalidated only on corrupt_login. Raise 112 # an error if it was invalidated on some other login or if 113 # cryptohome did not try on the first one. 114 if (attempt == corrupt_login) != (self.SUCCESS in result): 115 raise error.TestError('Unexpected result %s' % message) 116 117 118 def restart_cryptohome(self): 119 """Restart cryptohome 120 121 Cryptohome only sends the command to corrupt the header once. Once it 122 has been sent it wont be sent again until cryptohome is restarted. 123 """ 124 self.host.run('restart cryptohomed') 125 126 127 def clear_tpm_owner(self): 128 """Clear the tpm owner.""" 129 logging.info('Clearing the TPM owner') 130 tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True) 131 132 133 def after_run_once(self): 134 """Print the run information after each successful run""" 135 logging.info('finished run %d', self.iteration) 136 137 138 def run_once(self, host): 139 """Login to validate ChromeOS corrupts the inactive header""" 140 # After clearing the tpm owner the header will be corrupted on the 141 # second login 142 self.clear_tpm_owner() 143 self.login_and_verify(corrupt_login=2) 144 145 # The header is corrupted on the first login after cryptohome is reset 146 self.restart_cryptohome() 147 self.login_and_verify(corrupt_login=1) 148 149 # Cryptohome is reset after reboot 150 self.host.reboot() 151 self.login_and_verify(corrupt_login=1) 152 153 # The header is not corrupted after guest login, but will be corrupted 154 # on the first login after that. 155 self.restart_cryptohome() 156 self.login_and_verify(use_guest=True) 157 self.login_and_verify(corrupt_login=1) 158