Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 94 additions & 33 deletions initrd/bin/tpmr
Original file line number Diff line number Diff line change
Expand Up @@ -657,22 +657,69 @@ tpm1_unseal() {
-hk 40000000
}

# cache_owner_password <password>
# Store the TPM owner password in SECRET_DIR for the current boot session.
# The original callers wrote the password to a file directly; the helper
# provides identical behaviour and keeps the code DRY.
cache_owner_password() {
TRACE_FUNC
mkdir -p "$SECRET_DIR"
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
printf '%s' "$1" >"$SECRET_DIR/tpm_owner_password"
}

# Reset a TPM2 device for Heads. (Previous versions in origin/master put the
# comment about caching directly in this function, which is preserved below.)
tpm2_reset() {
TRACE_FUNC
tpm_owner_password="$1"
mkdir -p "$SECRET_DIR"
# output TPM Owner Password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
DO_WITH_DEBUG tpm2 clear -c platform &>/dev/null
DO_WITH_DEBUG tpm2 changeauth -c owner "$(tpm2_password_hex "$tpm_owner_password")" &>/dev/null
DO_WITH_DEBUG tpm2 changeauth -c endorsement "$(tpm2_password_hex "$tpm_owner_password")" &>/dev/null
DO_WITH_DEBUG tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \
-c "$SECRET_DIR/primary.ctx" -P "$(tpm2_password_hex "$tpm_owner_password")" &>/dev/null
DO_WITH_DEBUG tpm2 evictcontrol -C owner -c "$SECRET_DIR/primary.ctx" "$PRIMARY_HANDLE" \
-P "$(tpm2_password_hex "$tpm_owner_password")" &>/dev/null
shred -u "$SECRET_DIR/primary.ctx" &>/dev/null
DO_WITH_DEBUG tpm2_startsession &>/dev/null
# (using cache_owner_password() to avoid duplicating the write logic)
cache_owner_password "$tpm_owner_password"

# 1. Ensure TPM2_Clear is allowed: clear disableClear via platform hierarchy.
# This makes future clears (Owner/Lockout) possible and avoids a 'no clear' stuck state.
if ! DO_WITH_DEBUG tpm2 clearcontrol -C platform c >/dev/null 2>&1; then
LOG "tpm2_reset: unable to clear disableClear via platform hierarchy"
return 1
fi

# 2. Factory-style clear via platform hierarchy.
# This destroys all previous owner/endorsement/lockout auth and user-created objects.
if ! DO_WITH_DEBUG tpm2 clear -c platform >/dev/null 2>&1; then
LOG "tpm2_reset: TPM2_Clear via platform hierarchy failed; TPM not reset"
return 1
fi

# 3. Re-own the TPM for Heads: set new owner and endorsement auth.
if ! DO_WITH_DEBUG tpm2 changeauth -c owner "$(tpm2_password_hex "$tpm_owner_password")" >/dev/null 2>&1; then
LOG "tpm2_reset: unable to set owner auth"
return 1
fi

if ! DO_WITH_DEBUG tpm2 changeauth -c endorsement "$(tpm2_password_hex "$tpm_owner_password")" >/dev/null 2>&1; then
LOG "tpm2_reset: unable to set endorsement auth"
return 1
fi

# 4. Create and persist Heads primary key.
if ! DO_WITH_DEBUG tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \
-c "$SECRET_DIR/primary.ctx" \
-P "$(tpm2_password_hex "$tpm_owner_password")" >/dev/null 2>&1; then
LOG "tpm2_reset: unable to create primary"
return 1
fi

if ! DO_WITH_DEBUG tpm2 evictcontrol -C owner -c "$SECRET_DIR/primary.ctx" "$PRIMARY_HANDLE" \
-P "$(tpm2_password_hex "$tpm_owner_password")" >/dev/null 2>&1; then
LOG "tpm2_reset: unable to persist primary"
shred -u "$SECRET_DIR/primary.ctx" >/dev/null 2>&1
return 1
fi

shred -u "$SECRET_DIR/primary.ctx" >/dev/null 2>&1

DO_WITH_DEBUG tpm2_startsession >/dev/null 2>&1

# Set the dictionary attack parameters. TPM2 defaults vary widely, we
# want consistent behavior on any TPM.
Expand All @@ -695,36 +742,50 @@ tpm2_reset() {
--max-tries=10 \
--recovery-time=3600 \
--lockout-recovery-time=0 \
--auth="session:$ENC_SESSION_FILE" >/dev/null 2>&1 || LOG "Unable to set dictionary lockout parameters"
--auth="session:$ENC_SESSION_FILE" >/dev/null 2>&1 \
|| LOG "tpm2_reset: unable to set dictionary lockout parameters"

# Set a random DA lockout password, so the DA lockout can't be cleared
# with a password. Heads doesn't offer dictionary attach reset, instead
# the TPM can be reset and new secrets sealed.
#
# 6. Set a random DA lockout password so DA reset requires another TPM reset.
# The default lockout password is empty, so we must set this, and we
# don't need to provide any auth (use the default empty password).
tpm2 changeauth -Q -c lockout \
"hex:$(dd if=/dev/urandom bs=32 count=1 status=none 2>/dev/null | xxd -p | tr -d ' \n')" >/dev/null 2>&1 || LOG "Unable to set lockout password"
"hex:$(dd if=/dev/urandom bs=32 count=1 status=none 2>/dev/null | xxd -p | tr -d ' \n')" \
>/dev/null 2>&1 || LOG "tpm2_reset: unable to set lockout password"
}

tpm1_reset() {
TRACE_FUNC
tpm_owner_password="$1"
mkdir -p "$SECRET_DIR"
# output tpm_owner_password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
# Make sure the TPM is ready to be reset
DO_WITH_DEBUG tpm physicalpresence -s &>/dev/null
DO_WITH_DEBUG tpm physicalenable &>/dev/null
DO_WITH_DEBUG tpm physicalsetdeactivated -c &>/dev/null
DO_WITH_DEBUG tpm forceclear &>/dev/null
DO_WITH_DEBUG tpm physicalenable &>/dev/null
DO_WITH_DEBUG --mask-position 3 tpm takeown -pwdo "$tpm_owner_password" &>/dev/null

# And now turn it all back on
DO_WITH_DEBUG tpm physicalpresence -s &>/dev/null
DO_WITH_DEBUG tpm physicalenable &>/dev/null
DO_WITH_DEBUG tpm physicalsetdeactivated -c &>/dev/null
# (using cache_owner_password() under the hood)
cache_owner_password "$tpm_owner_password"

# 1. Request physical presence and enable TPM.
DO_WITH_DEBUG tpm physicalpresence -s >/dev/null 2>&1 || LOG "tpm1_reset: unable to set physical presence"
DO_WITH_DEBUG tpm physicalenable >/dev/null 2>&1 || LOG "tpm1_reset: unable to physicalenable"

# Ensure TPM is not deactivated.
DO_WITH_DEBUG tpm physicalsetdeactivated -c >/dev/null 2>&1 || LOG "tpm1_reset: unable to clear deactivated state"

# 2. Force clear: this is the critical full reset for TPM 1.2.
if ! DO_WITH_DEBUG tpm forceclear >/dev/null 2>&1; then
LOG "tpm1_reset: tpm forceclear failed; TPM not reset (firmware policy or state blocking clear)"
return 1
fi

# Re-enable after clear (some platforms require this).
DO_WITH_DEBUG tpm physicalenable >/dev/null 2>&1 || LOG "tpm1_reset: unable to physicalenable after clear"

# 3. Take ownership with the new TPM owner password.
if ! DO_WITH_DEBUG --mask-position 3 tpm takeown -pwdo "$tpm_owner_password" >/dev/null 2>&1; then
LOG "tpm1_reset: tpm takeown failed after forceclear"
return 1
fi

# 4. Leave TPM enabled, present, and not deactivated.
DO_WITH_DEBUG tpm physicalpresence -s >/dev/null 2>&1 || LOG "tpm1_reset: unable to set physical presence (final)"
DO_WITH_DEBUG tpm physicalenable >/dev/null 2>&1 || LOG "tpm1_reset: unable to physicalenable (final)"
DO_WITH_DEBUG tpm physicalsetdeactivated -c >/dev/null 2>&1 || LOG "tpm1_reset: unable to clear deactivated state (final)"
}

# Perform final cleanup before boot and lock the platform heirarchy.
Expand Down