autotest: verify cr50 header is corrupted on login
Add test to verify the system invalidates the inactive RW header after
login.
BUG=none
BRANCH=none
TEST=none
Change-Id: Icb675ea7eafd24cdfc8f7c7c866dac8982f46ae5
Signed-off-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/496486
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/server/site_tests/firmware_Cr50InvalidateRW/control b/server/site_tests/firmware_Cr50InvalidateRW/control
new file mode 100644
index 0000000..793accc
--- /dev/null
+++ b/server/site_tests/firmware_Cr50InvalidateRW/control
@@ -0,0 +1,33 @@
+# 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.
+
+from autotest_lib.server import utils
+
+AUTHOR = "mruthven"
+NAME = "firmware_Cr50InvalidateRW"
+PURPOSE = "Verify the inactive cr50 header is corrupted after login"
+ATTRIBUTES = "suite:cr50_stress_experimental"
+TIME = "MEDIUM"
+TEST_TYPE = "server"
+
+DOC = """
+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.
+"""
+
+args_dict = utils.args_to_dict(args)
+
+def run(machine):
+ host = hosts.create_host(machine)
+
+ num_iterations = int(args_dict.get("num_iterations", 1))
+
+ job.run_test("firmware_Cr50InvalidateRW", host=host,
+ iterations=num_iterations)
+
+parallel_simple(run, machines)
diff --git a/server/site_tests/firmware_Cr50InvalidateRW/firmware_Cr50InvalidateRW.py b/server/site_tests/firmware_Cr50InvalidateRW/firmware_Cr50InvalidateRW.py
new file mode 100644
index 0000000..c394cab
--- /dev/null
+++ b/server/site_tests/firmware_Cr50InvalidateRW/firmware_Cr50InvalidateRW.py
@@ -0,0 +1,155 @@
+# 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):
+ 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)
+
+
+ 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):
+ # 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)
+
+ # 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 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)