-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Description
systemd version the issue has been seen with
master (7728f6a)
Used distribution
Buildroot 2021.02.3 - should be reproducible anywhere
Linux kernel version used (uname -a)
5.4.123
CPU architecture issue was seen on
x86_64
Expected behaviour you didn't see
I expected that the systemd TPM2 primary key would not have dictionary attack protection enabled.
TPM provides a dictionary attack (DA) protection mechanism to prevent exhaustive searches of authorization values (e.g. passwords). The mechanism limits the amount of tries within a time period, causing TPM to enter DA lockout mode when this is exceeded which disallows further tries (TPM spec 2.0, r1p59 Part 1 sec 19.8 Dictionary Attack Protection).
The associated TPM failedTries counter will also be increased on an unclean system shutdown (19.8.6 Non-orderly Shutdown) as a properly timed shutdown could otherwise be used to prevent the counter increment after a real failed authentication attempt.
Depending on TPM configuration, the DA lockout mode may be lifted after a time period or it may require further explicit recovery action (see e.g. tpm2-software/tpm2-tools#1956).
In DA lockout mode, tpm2_seal() and tpm2_unseal() will fail in sym_Esys_Create() and sym_Esys_Load() with:
tpm:warn(2.0): authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode
The authValues of objects automatically get DA protection unless the caller opts out. tpm2-util creates the primary key with userWithAuth attribute but does not opt out of DA protection using noDA.
However, the authValue that is used for the primary key is always zero (primary_sensitive.sensitive.userAuth) as we rely on the PCR policy hash for protection instead, so dictionary attack protection for the authValue does not make sense as the attacker already knows it to be zero.
It seems also to not be generally desirable to have e.g. storage encryption stop working if the user fails an unrelated authentication prompt, or if the system is power-cycled enough times (e.g. several systems I checked go to lockout mode at 3 tries or power-cycles, with 1 try restored after every 1000 seconds - another system was more generous with 31 tries 600 seconds). There may be use cases where that is desirable, but such behavior should not be the default or at least not an undocumented default.
The TPM specification itself says (r1p59 Part 1 page 133, sec 19.8):
The reason for being able to exclude entities from DA protection is that lockout of all TPM use could make the system unstable. The OS may have uses for the TPM that should not be blocked due to authorization problems with keys associated with user-mode applications. The OS is expected to use a well-known or high-entropy authValue for any entities that it manages and an authValue of neither type needs DA-protection.
Systemd is using a "well-known authValue" (zero) as described in the quote.
Specification references: TPM spec 2.0, r1p59 (https://trustedcomputinggroup.org/resource/tpm-library-specification/):
- DA protection: Part 1 sec 19.8 Dictionary Attack Protection
- sensitive: Part 2 sec 11.1.16 TPM2B_SENSITIVE_CREATE
- create command: Part 3 sec 24.1 TPM2_CreatePrimary
Unexpected behaviour you saw
The DA protection was enabled, and after power cycling the machine 3 times the TPM2-sealed encrypted storage did not open anymore.
Steps to reproduce the problem
- Add a TPM2-sealed key to a LUKS volume with
systemd-cryptenroll --tpm2-device=auto volume. - Run
tpm2_getcap properties-variableand take note of theTPM2_PT_LOCKOUT_COUNTER,TPM2_PT_MAX_AUTH_FAIL,TPM2_PT_LOCKOUT_INTERVALvalues. - Power cycle the system MaxAuthFail times, or slightly more if the LockoutInterval happens to elapse during that time. LockoutCounter should reach MaxAuthFail in which case
inLockoutgoes to1. - TPM2-unsealing and opening the encrypted volume does not work anymore (see below example output).
Additional program output to the terminal or log subsystem illustrating the issue
# cryptsetup --token-only --token-type=systemd-tpm2 --verbose luksOpen testfile file1
WARNING:esys:src/tss2-esys/api/Esys_Load.c:324:Esys_Load_Finish() Received TPM Error
ERROR:esys:src/tss2-esys/api/Esys_Load.c:112:Esys_Load() Esys Finish ErrorCode (0x00000921)
Failed to load HMAC key in TPM: tpm:warn(2.0): authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode
Command failed with code -1 (wrong or missing parameters).
Fix discussion
I applied to following patch locally to solve our issue:
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index f066d17a91..88eee4c90c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -265,7 +265,7 @@ static int tpm2_make_primary(
.publicArea = {
.type = TPM2_ALG_ECC,
.nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
.parameters = {
.eccDetail = {
.symmetric = {Unfortunately, this changes the primary key and therefore breaks tpm2_unseal for existing values and is therefore not appropriate for upstream systemd. tpm2_seal/tpm2_unseal would need a new flag to detect whether the object was originally sealed with NODA or not.
I can also accept a fix where the systemd-cryptenroll documentation is updated to mention dictionary lockout, though I personally believe it is not appropriate here.