158 lines
5.6 KiB
Python
158 lines
5.6 KiB
Python
# 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, test
|
|
from autotest_lib.client.common_lib.cros import tpm_utils
|
|
|
|
|
|
class firmware_Cr50InvalidateRW(test.test):
|
|
"""
|
|
Verify the inactive Cr50 header on the first login after cryptohome
|
|
restarts.
|
|
|
|
There are two special cases this test covers: logging in after the TPM
|
|
owner is cleared and logging in as guest.
|
|
|
|
After the tpm owner is cleared, corrupting the header will be done on
|
|
the second login. During guest login the owner wont be cleared.
|
|
"""
|
|
version = 1
|
|
|
|
GET_CRYPTOHOME_MESSAGE ='grep cryptohomed /var/log/messages'
|
|
SUCCESS = 'Successfully invalidated inactive Cr50 RW'
|
|
FAIL = 'Invalidating inactive Cr50 RW failed'
|
|
NO_ATTEMPT = 'Did not try to invalidate header'
|
|
LOGIN_ATTEMPTS = 5
|
|
|
|
def initialize(self, host):
|
|
"""Initialize servo and cr50"""
|
|
super(firmware_Cr50InvalidateRW, self).initialize()
|
|
|
|
self.host = host
|
|
self.client_at = autotest.Autotest(self.host)
|
|
|
|
self.last_message = None
|
|
# get the messages already in /var/log/messages so we don't use them
|
|
# later.
|
|
self.check_for_invalidated_rw()
|
|
|
|
|
|
def check_for_invalidated_rw(self):
|
|
"""Use /var/log/messages to see if the rw header was invalidated.
|
|
|
|
Returns a string NO_ATTEMPT if cryptohome did not try to invalidate the
|
|
header or the cryptohome message if the attempt failed or succeeded.
|
|
"""
|
|
# Get the relevant messages from /var/log/messages
|
|
message_str = self.host.run(self.GET_CRYPTOHOME_MESSAGE,
|
|
verbose=False).stdout.strip()
|
|
|
|
# Remove the messages we have seen in the past
|
|
if self.last_message:
|
|
message_str = message_str.rsplit(self.last_message, 1)[-1]
|
|
messages = message_str.split('\n')
|
|
|
|
# Save the last message so we can identify new messages later
|
|
self.last_message = messages[-1]
|
|
|
|
rv = self.NO_ATTEMPT
|
|
# print all cryptohome messages.
|
|
for message in messages:
|
|
logging.debug(message)
|
|
# Return the message that is related to the RW invalidate attempt
|
|
if self.FAIL in message or self.SUCCESS in message:
|
|
rv = message
|
|
return rv
|
|
|
|
|
|
def login(self, use_guest):
|
|
"""Run the test to login."""
|
|
if use_guest:
|
|
self.client_at.run_test('login_CryptohomeIncognito')
|
|
else:
|
|
self.client_at.run_test('login_LoginSuccess')
|
|
|
|
|
|
def login_and_verify(self, use_guest=False, corrupt_login=None):
|
|
"""Verify the header is only invalidated on the specified login.
|
|
|
|
login LOGIN_ATTEMPTS times. Verify that cryptohome only tries to corrupt
|
|
the inactive cr50 header on the specified login. If it tries on a
|
|
different login or fails to corrupt the header, raise an error.
|
|
|
|
Args:
|
|
use_guest: True to login as guest
|
|
corrupt_login: The login attempt that we expect the header to be
|
|
corrupted on
|
|
|
|
Raises:
|
|
TestError if the system attempts to corrupt the header on any login
|
|
that isn't corrupt_login or if an attepmt to corrupt the header
|
|
fails.
|
|
"""
|
|
for i in xrange(self.LOGIN_ATTEMPTS):
|
|
attempt = i + 1
|
|
|
|
self.login(use_guest)
|
|
result = self.check_for_invalidated_rw()
|
|
|
|
message = '%slogin %d: %s' % ('guest ' if use_guest else '',
|
|
attempt, result)
|
|
logging.info(message)
|
|
|
|
# Anytime the invalidate attempt fails raise an error
|
|
if self.FAIL in result:
|
|
raise error.TestError(message)
|
|
|
|
# The header should be invalidated only on corrupt_login. Raise
|
|
# an error if it was invalidated on some other login or if
|
|
# cryptohome did not try on the first one.
|
|
if (attempt == corrupt_login) != (self.SUCCESS in result):
|
|
raise error.TestError('Unexpected result %s' % message)
|
|
|
|
|
|
def restart_cryptohome(self):
|
|
"""Restart cryptohome
|
|
|
|
Cryptohome only sends the command to corrupt the header once. Once it
|
|
has been sent it wont be sent again until cryptohome is restarted.
|
|
"""
|
|
self.host.run('restart cryptohomed')
|
|
|
|
|
|
def clear_tpm_owner(self):
|
|
"""Clear the tpm owner."""
|
|
logging.info('Clearing the TPM owner')
|
|
tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True)
|
|
|
|
|
|
def after_run_once(self):
|
|
"""Print the run information after each successful run"""
|
|
logging.info('finished run %d', self.iteration)
|
|
|
|
|
|
def run_once(self, host):
|
|
"""Login to validate ChromeOS corrupts the inactive header"""
|
|
# After clearing the tpm owner the header will be corrupted on the
|
|
# second login
|
|
self.clear_tpm_owner()
|
|
self.login_and_verify(corrupt_login=2)
|
|
|
|
# The header is corrupted on the first login after cryptohome is reset
|
|
self.restart_cryptohome()
|
|
self.login_and_verify(corrupt_login=1)
|
|
|
|
# Cryptohome is reset after reboot
|
|
self.host.reboot()
|
|
self.login_and_verify(corrupt_login=1)
|
|
|
|
# The header is not corrupted after guest login, but will be corrupted
|
|
# on the first login after that.
|
|
self.restart_cryptohome()
|
|
self.login_and_verify(use_guest=True)
|
|
self.login_and_verify(corrupt_login=1)
|