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) |