Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to unseal HMAC key in TPM: tpm:error(2.0): PCR have changed since checked #24906

Closed
fritzbauer opened this issue Oct 3, 2022 · 6 comments · Fixed by #25612
Closed
Labels
bug 🐛 Programming errors, that need preferential fixing cryptsetup tpm2
Milestone

Comments

@fritzbauer
Copy link

fritzbauer commented Oct 3, 2022

systemd version the issue has been seen with

systemd 251.4-1

Used distribution

Archlinux

Linux kernel version used

5.19.12-xanmod1-1-alderlake

CPU architectures issue was seen on

x86_64

Component

systemd-cryptsetup

Expected behaviour you didn't see

I am using an encrypted LUKS2 root partition and enrolled an unlock key to the TPM bound to PCR-7. I expect that an intrd using systemd-cryptenroll automatically unlooks the luks partition and boots to the login prompt.

Unexpected behaviour you saw

I experience different behavior, which leads me to the suspicion that the error is due to a race condition:

  • In some cases I see the luks passphrase prompt. If I just hit enter without typing anything it boots successfully.
  • In other cases I see the luks passphrase prompt. If I hit enter, I see the prompt a second time, if I hit enter again I am redirected to the emergency shell. If I hit Ctrl-D instead of entering the password, the system boots successfully.
  • In a few cases the system boots successfully, without any interactions.

In cases where I get the prompt I see the following output:

journalctl -xb -u systemd-cryptsetup@luks
systemd-cryptsetup[338]: WARNING:esys:src/tss2-esys/api/Esys_Unseal.c:295:Esys_Unseal_Finish() Received TPM Error
systemd-cryptsetup[338]: ERROR:esys:src/tss2-esys/api/Esys_Unseal.c:98:Esys_Unseal() Esys Finish ErrorCode (0x00000128)
systemd-cryptsetup[338]: Failed to unseal HMAC key in TPM: tpm:error(2.0): PCR have changed since checked

Steps to reproduce the problem

I assume it is hard to reproduce, since it seems it is tied to a race condition.
Trying to add as much information as possible here:

  • I experience the issue using dracut as well as mkinitcpio.
  • The error Failed to unseal HMAC key in TPM seems to be thrown in tpm2_unseal() in tpm2_util.c
  • The error PCR have changed since checked is the result received from the TPM as TPM2_RC_PCR_CHANGED.
    I found some explanation about the pcrUpdateCounter in the TPM specification Page 230 - November 8, 2019 - Level 00 Revision 01.5. If any PCR changes in the time between validating PCR7 and unsealing the HMAC key the TPM will return TPM_RC_PCR_CHANGED.
  • I was able to build systemd from the current master branch and include it in the initrd with mkinitcpio. I added additional logging to print the output of the command "tpm2_pcrread" prior to calling tpm2_load_pcr_signature() in luks2-tpm.c and another time after calling tpm2_unseal(). And indeed the hash of PCR-10 changed in the meantime.
  • I am not sure whether PCR-10 is among the PCRs which cause pcrUpdateCounter to be incremented once it gets changed, but the behavior indicates it.
  • I suspect IMA (Integrity Measurement Architecture) is changing this value in the meantime. At least my kconfig has CONFIG_IMA_MEASURE_PCR_IDX=10

Idea for solution:
I assume arbitrary PCR values could change anytime, so it might be nice to have some retry logic, if TPM2_RC_PCR_CHANGED is returned. Notably, according to the logs it is already trying twice. Sometimes the second attempt succeeds and sometimes both attempts fail with this error.

Probably not only the LUKS unlock is affected, but any code which calls tpm2_unseal().

Please let me know, if you want me to provide any additional information. Thanks for helping.

Additional program output to the terminal or log subsystem illustrating the issue

No response

@fritzbauer fritzbauer added the bug 🐛 Programming errors, that need preferential fixing label Oct 3, 2022
@poettering
Copy link
Member

hmmpf, i guess we really have to loop around this then. When I read the spec I didn#t see that and assumed this was only if a PCR we actually referenced would change...

@poettering poettering added the tpm2 label Oct 4, 2022
@poettering poettering added this to the v253 milestone Oct 4, 2022
@fritzbauer
Copy link
Author

It seems arbitrary which kernel has this issue and which does not. I had it with 5.19.12-xanmod1-1-alderlake and 5.19.11-xanmod1-1-alderlake, but it does not occur with 6.0.0-alderlake-xanmod1-1. A friend of mine with a similar setup was fine with 5.19.12, but it started occuring with 6.0.0 for him.

For anybody in the same situation: Disabling CONFIG_IMA in the kernel seems to workaround it (for this particular cause). At least the Gentoo Wiki about IMA states IMA should be enabled for development purposes only. However, it is enabled in xanmod kernel per default.

@aplanas
Copy link
Contributor

aplanas commented Oct 27, 2022

Disabling CONFIG_IMA in the kernel seems to workaround it

Ahmm ... seems that if even there is no ima policy set in the kernel cmdline, the kernel expand always PCR10 for the boot_aggregate (even if later do not log the readed files)

@fritzbauer
Copy link
Author

with 6.0.5-alderlake-xanmod1-1 PCR10 is always 0x00 for me with disabled IMA.

@vliaskov
Copy link
Contributor

What are our options to solve this for kernels with CONFIG_IMA enabled?

Although this refers to TPM2_PolicyPCR command, I am quoting part of the spec that describes when pcrUpdateCounter is updated :

When this command is executed, policySession→pcrUpdateCounter is checked to see if it has been
previously set (in the reference implementation, it has a value of zero if not previously set). If it has been
set, it will be compared with the current value of pcrUpdateCounter to determine if any PCR changes
have occurred. If the values are different, the TPM shall return TPM_RC_PCR_CHANGED.

NOTE 2: Since the pcrUpdateCounter is updated if any PCR is extended (except those specified not to do
so), this means that the command will fail even if a PCR not specified in the policy is updated. This
is an optimization for the purposes of conserving internal TPM memory . This would be a rare
occurrence, and, if this should occur, the policy could be reset using the TPM2_PolicyRestart
command and rerun.
  • Is it possible to specify PCRs (that are not of interest) to not update pcrUpdateCounter? That's what I am getting from the spec line Since the pcrUpdateCounter is updated if any PCR is extended (except those specified not to do so) but I am not an expert in tpm.

  • Would the other option be to have retry logic with TPM2_PolicyRestart ? Of course in this case, the unspecified PCRs can still change again, leading to more failures.

@poettering
Copy link
Member

So with systemd-pcrphase we are now measuring certain things to TPM ourselves, which means we'll trigger the issue ourselves.

I think we should just abort our operation when we see TPM2_RC_PCR_CHANGED and start the whole thing anew.

aafeijoo-suse added a commit to aafeijoo-suse/systemd that referenced this issue Dec 2, 2022
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906
aafeijoo-suse added a commit to aafeijoo-suse/systemd that referenced this issue Dec 7, 2022
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906
aafeijoo-suse added a commit to aafeijoo-suse/systemd that referenced this issue Dec 7, 2022
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906
keszybz pushed a commit that referenced this issue Dec 8, 2022
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes #24906
eworm-de pushed a commit to eworm-de/systemd that referenced this issue Feb 4, 2023
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906

(cherry picked from commit 0254e4d)
d-hatayama pushed a commit to d-hatayama/systemd that referenced this issue Feb 15, 2023
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906
Werkov pushed a commit to Werkov/systemd that referenced this issue Nov 1, 2023
…CHANGED

Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":

"pcrUpdateCounter – this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.

If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."

The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.

Fixes systemd#24906

(cherry picked from commit 0254e4d)

[antonio.feijoo: adjust context]
[antonio.feijoo: fixes bsc#1204944]
grydz added a commit to Cosmian/cosmian_vm that referenced this issue Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Programming errors, that need preferential fixing cryptsetup tpm2
Development

Successfully merging a pull request may close this issue.

4 participants