initrd: fix TPM1 counter auth regression and defend lock cascade failure#2117
Open
tlaurion wants to merge 1 commit into
Open
initrd: fix TPM1 counter auth regression and defend lock cascade failure#2117tlaurion wants to merge 1 commit into
tlaurion wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves Heads’ TPM error handling for TPM1 by correctly classifying tpmtotp “Defend lock running” output as an authorization-related failure (so it doesn’t immediately hard-fail), and adds a recovery path in tpm1_reset() to attempt clearing TPM1 defend-lock after forceclear by cycling physical presence.
Changes:
- Extend auth-failure grep patterns to include
defend(and unify inclusion of TPM2 auth hex codes in the shared retry helper). - Enhance
tpm1_reset()to detect “defend lock” aftertakeownand retry after cycling physical presence. - Expand TPM documentation to describe tool selection, auth retry detection, and TPM1 defend-lock behavior.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| initrd/bin/tpmr.sh | Treat “defend lock” as auth-related and add TPM1 defend-lock recovery logic during reset. |
| doc/tpm.md | Document TPM toolchain selection and the updated auth retry / defend-lock behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b20be90 to
a5a2ebb
Compare
Contributor
|
The fix did not work…will copy logs. |
Collaborator
Author
Found the bug and where the regression comes from. Damn that one was not easy. Pushing fix and updating the other pr |
PR #2068 (tpm_reseal_ux-integrity_report-detect_disk_and_tpm_swap, merged at d3d8053) changed increment_tpm_counter from hardcoded -pwdc '' (empty counter auth) to -pwdc "${tpm_passphrase:-}" (owner passphrase from cache/prompt), but left check_tpm_counter using empty -pwdc when called from kexec-sign-config.sh without a $3 passphrase argument. This caused every counter increment to compute SHA1(owner_pass) while the counter was created with SHA1("") - persistent TPM_AUTH_FAIL. Per TCG TPM Main Spec Part 3, TPM_CreateCounter uses owner auth (-pwdo) but TPM_IncrementCounter uses the counter's own authData, not the owner password. The correct design for Heads' rollback counter is empty auth: rollback security comes from the signed /boot/kexec_rollback.txt and TPM sealing, not counter access control. The repeated auth failures (3 per boot x ~5 boots via the _tpm_auth_retry loop) triggered TPM 1.2 dictionary-attack lockout (TPM_DEFEND_LOCK_RUNNING), which persisted through forceclear on some implementations, causing tpm takeown to fail and TPM reset to abort - a cascade failure from the counter auth mismatch. Changes: - initrd/bin/tpmr.sh (_tpm_auth_retry, tpm2_counter_inc, tpm2_seal, tpm1_seal): add 'defend' and '0x98e|0x149' to auth detection grep patterns so defend lock and TPM2 RC codes are treated as retryable auth failures rather than fatal errors - initrd/bin/tpmr.sh (tpm1_reset): detect defend lock after takeown failure and cycle physical presence to clear the lock state before retrying; full AC power cycle remains the fallback if software presence is insufficient - initrd/bin/tpmr.sh (tpm1_counter_increment): detect -pwdc '' and call tpm directly, bypassing _tpm_auth_retry which injected the owner passphrase. Use || return to survive set -e on expected auth failure. - initrd/etc/functions.sh (check_tpm_counter): pass -pwdc '' instead of -pwdc "${tpm_passphrase:-}" so counters use SHA1("") per TCG spec. Document that $3 is intentionally ignored. - initrd/etc/functions.sh (increment_tpm_counter): try -pwdc '' first for TPM1. If that fails on a readable counter (created by PR #2068 era code), prompt for owner passphrase and retry as migration fallback with clear WARN explaining the one-time migration and TPM reset option. - initrd/etc/functions.sh (increment_tpm_counter): remove the TPM1-specific owner-passphrase prompt block added by PR #2068 - initrd/etc/functions.sh (increment_tpm_counter): DIE-path fallback counter_create: -pwdc '' for consistency - initrd/bin/oem-factory-reset.sh: counter_create -pwdc '' for consistency with the empty-auth design - doc/tpm.md: document TPM1 boot chain, tpmtotp tool selection, auth retry patterns, defend lock recovery, and physical presence Signed-off-by: Thierry Laurion <insurgo@riseup.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a TPM1 regression introduced by PR #2068 (
tpm_reseal_ux-integrity_report-detect_disk_and_tpm_swap) whereincrement_tpm_counterwas changed from hardcoded-pwdc ''(empty counter auth) to-pwdc "${tpm_passphrase:-}"(owner passphrase from cache/prompt), butcheck_tpm_counter— when called fromkexec-sign-config.shwithout a$3passphrase argument — continued to create counters with-pwdc "". This caused every counter increment to compute SHA1(owner_pass) while the counter was created with SHA1(""), producing persistentTPM_AUTH_FAIL.Per TCG TPM Main Spec Part 3,
TPM_CreateCounteruses owner auth (-pwdo) butTPM_IncrementCounteruses the counter's own authData, not the owner password. The correct design for Heads' rollback counter is empty auth: rollback security comes from the signed/boot/kexec_rollback.txtand TPM sealing, not counter access control.The repeated auth failures (3 per boot x ~5 boots via the
_tpm_auth_retryloop) triggered TPM 1.2 dictionary-attack lockout (TPM_DEFEND_LOCK_RUNNING), which persisted throughforceclearon some implementations, causingtpm takeownto fail during TPM reset — a cascade failure from the counter auth mismatch.Note: the TPM2 path within
increment_tpm_counterwas not affected — it correctly used-pwdc ""(empty index auth first, owner auth fallback), consistent with howtpm2 nvdefinecreates counters without explicit NV index auth.Changes
initrd/bin/tpmr.sh— auth detection + defend lock recovery + pass-through fix'defend'and'0x98e|0x149'to all auth detection grep patterns so defend lock and TPM2 RC codes are retried rather than fataltpm1_reset(): detect "defend lock" intakeownoutput and cycle physical presence to clear the lock before retryingtpm1_counter_increment(): detect-pwdc ''and calltpmdirectly (bypassing_tpm_auth_retrywhich injected owner passphrase). Uses|| returnto preventset -efrom killing the script on expected auth failureinitrd/etc/functions.sh— counter auth fix + migration fallbackcheck_tpm_counter():-pwdc ''— counter created with SHA1("") per TCG spec. Document$3as intentionally ignored (was used by pre-fix code)increment_tpm_counter()TPM1: try-pwdc ''first. Migration fallback: prompt for owner passphrase, retry, then show clear WARN explaining one-time migration and TPM reset option-pwdc ''for consistencyinitrd/bin/oem-factory-reset.sh— consistency fixcounter_create:-pwdc ''instead of-pwdc "${TPM_PASS:-}"— OEM factory reset now also creates counters with empty authdoc/tpm.md— documentationTesting
Test scenarios to verify:
-pwdc ''reset_tpm()cycles physical presence and recovers