| Index: tpm_handler.py
|
| diff --git a/tpm_handler.py b/tpm_handler.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..526dbf4976e38e1ae7ef785c7c747368cfe0a8ee
|
| --- /dev/null
|
| +++ b/tpm_handler.py
|
| @@ -0,0 +1,161 @@
|
| +#!/usr/bin/python
|
| +# Copyright (c) 2010 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.
|
| +
|
| +'''A module containing TPM handler class used by SAFT.'''
|
| +import os
|
| +import re
|
| +import shutil
|
| +
|
| +FW_NV_ADDRESS = 0x1007
|
| +KERNEL_NV_ADDRESS = 0x1008
|
| +COMMENT_PATTERN = '#saft'
|
| +
|
| +class TpmError(Exception):
|
| + pass
|
| +
|
| +class TpmNvRam(object):
|
| + '''An object representing TPM NvRam.
|
| +
|
| + Attributes:
|
| + addr: a number, NvRAm address in TPM.
|
| + size: a number, count of bites in this NvRam section.
|
| + os_if: an instance of the OS interface (chromeos_interface or a mock
|
| + object).
|
| + version_offset: - a number, offset into the NvRam contents where the the
|
| + versions are stored. The total version field size is 4 bytes, the
|
| + first two bytes are the body version, the second two bytes are the key
|
| + version. Numbers are stored in little endian format.
|
| + pattern: optional, a tuple of two elements, the first element is the
|
| + offset of the pattern expected to be present in the NvRam, and the
|
| + second element is an array of bytes the pattern must match.
|
| + contents: an array of bytes, the contents of the NvRam.
|
| + '''
|
| +
|
| + def __init__(self, addr, size, version_offset, data_pattern=None):
|
| + self.addr = addr
|
| + self.size = size
|
| + self.os_if = None
|
| + self.version_offset = version_offset
|
| + self.pattern = data_pattern
|
| + self.contents = []
|
| +
|
| + def init(self, os_if):
|
| + self.os_if = os_if
|
| + cmd = 'tpmc read 0x%x 0x%x' % (self.addr, self.size)
|
| + nvram_data = self.os_if.run_shell_command_get_output(cmd)[0].split()
|
| + self.contents = [int(x, 16) for x in nvram_data]
|
| + if self.pattern:
|
| + pattern_offset = self.pattern[0]
|
| + pattern_data = self.pattern[1]
|
| + contents_pattern = self.contents[pattern_offset:pattern_offset +
|
| + len(pattern_data)]
|
| + if contents_pattern != pattern_data:
|
| + raise TpmError('Nvram pattern does not match')
|
| +
|
| + def get_body_version(self):
|
| + return self.contents[
|
| + self.version_offset + 1] * 256 + self.contents[self.version_offset]
|
| +
|
| + def get_key_version(self):
|
| + return self.contents[
|
| + self.version_offset + 3] * 256 + self.contents[
|
| + self.version_offset + 2]
|
| +
|
| + def set_body_version(self, new_version):
|
| + new_contents = list(self.contents)
|
| + new_contents[self.version_offset + 1] = (new_version >> 8) & 0xff
|
| + new_contents[self.version_offset] = new_version & 0xff
|
| + if new_contents != self.contents:
|
| + self.contents = new_contents
|
| + cmd = 'tpmc write 0x%x %s' % (
|
| + self.addr, ' '.join('0x%2.2x' % x for x in new_contents))
|
| + self.os_if.run_shell_command(cmd)
|
| + else:
|
| + self.os_if.log('no version change for 0x%x' % self.addr)
|
| +
|
| +class TpmHandler(object):
|
| + '''An object to control TPM device's NVRAM.
|
| +
|
| + Attributes:
|
| + cros_if: an instance of the OS interface (chromeos_interface or a mock
|
| + object).
|
| + nvrams: A dictionary where the keys are the nvram names, and the values
|
| + are instances of TpmNvRam objects, providing access to the
|
| + appropriate TPM NvRam sections.
|
| + '''
|
| +
|
| + def __init__(self):
|
| + self.cros_if = None
|
| + self.nvrams = {
|
| + 'kernel': TpmNvRam(KERNEL_NV_ADDRESS, 13, 5, (
|
| + 1, [0x4c, 0x57, 0x52, 0x47])),
|
| + 'bios': TpmNvRam(FW_NV_ADDRESS, 10, 2)
|
| + }
|
| +
|
| + def init(self, cros_if):
|
| + self.cros_if = cros_if
|
| + status = self.cros_if.run_shell_command_get_output(
|
| + 'initctl status tcsd')[0]
|
| + if status.startswith('tcsd start/running'):
|
| + self.cros_if.run_shell_command('stop tcsd')
|
| +
|
| + for nvram in self.nvrams.itervalues():
|
| + nvram.init(self.cros_if)
|
| +
|
| + def get_fw_version(self):
|
| + return self.nvrams['bios'].get_body_version()
|
| +
|
| + def get_kernel_version(self):
|
| + return self.nvrams['kernel'].get_body_version()
|
| +
|
| + def set_kernel_version(self, new_version):
|
| + return self.nvrams['kernel'].set_body_version(new_version)
|
| +
|
| + def _version_good(self, nvram, version_a, version_b):
|
| + return self.nvrams[nvram].get_body_version() == max(
|
| + version_a, version_b)
|
| +
|
| + def kernel_version_good(self, version_a, version_b):
|
| + return self._version_good('kernel', version_a, version_b)
|
| +
|
| + def fw_version_good(self, version_a, version_b):
|
| + return self._version_good('bios', version_a, version_b)
|
| +
|
| + def enable_write_access(self, config_file_name='/etc/init/tcsd.conf'):
|
| + '''Enable TPM write access on the next recovery mode restart.
|
| +
|
| + Comment out from the TPM upstart script the following lines locking
|
| + the TPM (and thus preventing write access):
|
| +
|
| + tpmc block || logger "tpmc block: status $?"
|
| + tpmc pplock || logger "tpmc pplock: status $?"
|
| +
|
| + Save the original file in the state directory for future recovery.
|
| + '''
|
| +
|
| + pattern = re.compile('^\s+tpmc\s+(b|pp)lock\s+\|\|')
|
| + new_file_name = self.cros_if.state_dir_file(
|
| + os.path.basename(config_file_name))
|
| + shutil.copyfile(config_file_name, new_file_name + '.bak')
|
| + config_file = open(config_file_name, 'r')
|
| + new_file = open(new_file_name, 'w')
|
| + for line in config_file.readlines():
|
| + if pattern.search(line):
|
| + new_file.write('%s%s' % (COMMENT_PATTERN, line))
|
| + else:
|
| + new_file.write(line)
|
| + new_file.close()
|
| + config_file.close()
|
| + shutil.move(new_file_name, config_file_name)
|
| +
|
| + def disable_write_access(self, config_file_name='/etc/init/tcsd.conf'):
|
| + '''Disable TPM write access.
|
| +
|
| + Restore previously edited startup file to lock the TPM on the next
|
| + restart.
|
| + '''
|
| + backup_file_name = self.cros_if.state_dir_file(
|
| + os.path.basename(config_file_name) + '.bak')
|
| + shutil.move(backup_file_name, config_file_name)
|
|
|