#!/bin/sh -u # 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. # # Run TPM diagnostics in recovery mode, and attempt to fix problems. This is # specific to devices with chromeos firmware. # # Most of the diagnostics examine the TPM state and try to fix it. This may # require clearing TPM ownership. tpmc=${USR_BIN:=/usr/bin}/tpmc crossystem=${USR_BIN}/crossystem dot_recovery=${DOT_RECOVERY:=/mnt/stateful_partition/.recovery} awk=/usr/bin/awk initctl=/sbin/initctl daemon_was_running= err=0 tpm2_target() { # This is not an ideal way to tell if we are running on a tpm2 target, but # it will have to do for now. if [ -f "/etc/init/trunksd.conf" ]; then return 0 else return 1 fi } log() { echo "$*" } quit() { log "ERROR: $*" restart_daemon_if_needed log "exiting" exit 1 } log_tryfix() { log "$*: attempting to fix" } log_error() { err=$((err + 1)) log "ERROR: $*" } log_warn() { log "WARNING: $*" } tpm_clear_and_reenable () { $tpmc clear # The below commands are are no-op on tpm2, but let's keep them here for # both TPM versions in case they are implemented in the future for # version 2. $tpmc enable $tpmc activate } reset_space () { local index=$1 local permissions=$2 local size=$3 local bytes="$4" if ! tpm2_target; then # definespace is not yet supported for tpm2 (crosbug.com/p/59361), let's # just rely on the firmware having created the required spaces for now. if ! $tpmc definespace $index $size $permissions; then log "could not redefine space $index" return 1 fi fi # do not quote "$bytes", as we mean to expand it here if ! $tpmc write $index $bytes; then log "writing to $index failed" return 1 fi log "space $index was recreated successfully" } restart_daemon_if_needed() { if [ "$daemon_was_running" = 1 ]; then log "Restarting ${DAEMON}..." $initctl start "${DAEMON}" >/dev/null fi } # ------------ # MAIN PROGRAM # ------------ # Sanity check: are we executing in a recovery image? if [ -e $dot_recovery ]; then quit "This is a developer utility, it should never run on a (production) recovery image" fi # Did the firmware keep the TPM unlocked? if ! $($crossystem mainfw_type?recovery); then quit "You must put a test image on a USB stick and boot it in recovery mode to run this" fi if tpm2_target; then DAEMON="trunksd" else DAEMON="tcsd" fi # TPM daemon may or may not be running log "Stopping ${DAEMON}..." if $initctl stop "${DAEMON}" >/dev/null 2>/dev/null; then daemon_was_running=1 log "done" else daemon_was_running=0 log "(was not running)" fi # Is the state of the PP enable flags correct? if ! tpm2_target; then if ! ($tpmc getpf | grep -q "physicalPresenceLifetimeLock 1" && $tpmc getpf | grep -q "physicalPresenceHWEnable 0" && $tpmc getpf | grep -q "physicalPresenceCMDEnable 1"); then log_tryfix "bad state of physical presence enable flags" if $tpmc ppfin; then log "physical presence enable flags are now correctly set" else quit "could not set physical presence enable flags" fi fi # Is physical presence turned on? if $tpmc getvf | grep -q "physicalPresence 0"; then log_tryfix "physical presence is OFF, expected ON" # attempt to turn on physical presence if $tpmc ppon; then log "physical presence is now on" else quit "could not turn physical presence on" fi fi else if ! $tpmc getvf | grep -q 'phEnable 1'; then quit "Platform Hierarchy is disabled, TPM can't be recovered" fi fi # I never learned what this does, but it's probably good just in case... tpm_clear_and_reenable # Reset firmware and kernel spaces to default (rollback version 1/1) reset_space 0x1007 0x8001 0xa "02 00 01 00 01 00 00 00 00 4f" || \ log_error "could not fix firmware space" reset_space 0x1008 0x1 0xd "02 4c 57 52 47 01 00 01 00 00 00 00 55" || \ log_error "could not fix kernel space" restart_daemon_if_needed if [ "$err" -eq 0 ]; then log "TPM has successfully been reset to factory defaults" else log_error "TPM was not fully recovered." exit 1 fi