summaryrefslogtreecommitdiff
path: root/utility/chromeos-tpm-recovery
blob: bcb1819f50bb9d4d95d7573ad475ebbd4b9968b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/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