Home | History | Annotate | Download | only in firmware_Cr50InvalidateRW
      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 
      5 import logging
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.server import autotest, test
      9 from autotest_lib.client.common_lib.cros import tpm_utils
     10 
     11 
     12 class 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