systemd / systemd

The systemd System and Service Manager
https://systemd.io
GNU General Public License v2.0
12.9k stars 3.71k forks source link

TPM2 code may select a PCR bank that is not used by EFI firmware #20684

Closed anssih closed 2 years ago

anssih commented 2 years ago

systemd version the issue has been seen with

3da57008e743 (master from a few weeks ago - no TPM2 changes to master since)

20134 is a different issue and the fix for that does not help here

Used distribution

Buildroot 2021.02.3

Linux kernel version used (uname -a)

5.4.123

CPU architecture issue was seen on

x86_64

Expected behaviour you didn't see

On some systems the TPM2 supports both SHA-1 and SHA-256 banks, but only the SHA-1 bank is active in EFI, i.e. the EFI firmware is only extending hashes in the SHA1 bank, and the SHA-256 bank is zero (banks 17-22 are FF, others 00, per spec). TPM2 event log shows SHA-1 extensions but no SHA-256 extensions.

The TCG EFI Protocol Specification has a notion of "Active PCR banks". The system I'm testing on responds to GetCapability with a structure with HashAlgorithmBitmap set to 0x1 (only SHA-1 supported). So only SHA-1 bank is supported and therefore active. The capability structure has brokenness, though, see full output further below.

The SHA-256 bank can still be extended in Linux via e.g. tpm2_pcrevent, as expected - it is just not active in EFI.

In this specific case, the expected behavior would probably be for systemd-cryptenroll to use SHA-1 bank which is active in EFI, or produce a warning or error. It could probably try to detect empty banks, or somehow use the active banks information from EFI TCG protocol (which would have to be relayed to it somehow as it is a Boot Service). It could also look at event log in /sys/kernel/security/tpm0/binary_bios_measurements via tpm2-tss on whether it has SHA-256 extensions logged, but I do have another weird system here where the log does not have SHA-256 extensions even though SHA-256 bank seems active.

Or there could just be a simple warning message if the PCR bank is zero/one-filled.

Alternatively, it should be mentioned in systemd-cryptenroll man page that it does not verify that the PCR bank is actually used by the firmware, and just consider this as firmware weirdness or misconfiguration. (My actual use case does not actually require fixing this issue either, I just noticed it during testing.)

Unexpected behaviour you saw

systemd-cryptenroll seals the key against an empty SHA-256 bank, offering no security at all.

The encrypted volume can be opened regardless of system state.

(I checked with systemd-security@ that this is OK to publish)

Steps to reproduce the problem

On a system with an affected firmware and TPM2:

# truncate --size=50M testfile2
# touch key
# cryptsetup --batch-mode luksFormat testfile2 key
# PASSWORD= systemd-cryptenroll --tpm2-device=auto testfile2                        
New TPM2 token enrolled as key slot 1.
# cryptsetup luksDump testfile2 | grep tpm2-bank
        tpm2-bank:  sha256

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

GetCapability() returns the following EFI_TCG2_BOOT_SERVICE_CAPABILITY:

Size = 36  (struct completely filled, i.e. 36 == sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY))
StructureVersion = 1.0
ProtocolVersion = 1.0
HashAlgorithmBitmap = 0x01
SupportedEventLogs = 0x01
TPMPresentFlag = 0x01
NumberOfPCRBanks = not touched / uninitialized
ActivePcrBanks = not touched / uninitialized

The PCR bank fields being left alone is clearly broken behavior from firmware.

GetActivePcrBanks works in the same way, it does not return error but does not touch the output bitmap.

If the active-pcr-banks fields or calls were to be used somehow, the input should probably be zeroed first (which e.g. systemd src/boot/efi/measure.c does not currently do).

Enrollment:

# SYSTEMD_LOG_LEVEL=debug PASSWORD= systemd-cryptenroll --tpm2-device=auto testfile2
Allocating context for crypt device /root/testfile2.
Trying to open and read device /root/testfile2 with direct-io.
Initialising device-mapper backend library.
Trying to load LUKS2 crypt type from device /root/testfile2.
Crypto backend (gcrypt 1.9.3-unknown) initialized in cryptsetup library version 2.4.0.
Detected kernel Linux 5.4.123-XX x86_64.
Loading LUKS2 header (repair disabled).
Acquiring read lock for device /root/testfile2.
Verifying lock handle for /root/testfile2.
Device /root/testfile2 READ lock taken.
Trying to read primary LUKS2 header at offset 0x0.
Opening locked device /root/testfile2
Veryfing locked device handle (regular file)
LUKS2 header version 2 of size 16384 bytes, checksum sha256.
Checksum:f5cf280ca392b3f116cad55566519ec8eca1b1791d199b48ad77770c9306beb9 (on-disk)
Checksum:f5cf280ca392b3f116cad55566519ec8eca1b1791d199b48ad77770c9306beb9 (in-memory)
Trying to read secondary LUKS2 header at offset 0x4000.
Reusing open ro fd on device /root/testfile2
LUKS2 header version 2 of size 16384 bytes, checksum sha256.
Checksum:2f1c0d86046ded96052e71c42a1563283fe795a4648604494ebee0f6a5f60437 (on-disk)
Checksum:2f1c0d86046ded96052e71c42a1563283fe795a4648604494ebee0f6a5f60437 (in-memory)
Device size 52428800, offset 16777216.
Device /root/testfile2 READ lock released.
PBKDF argon2id, time_ms 2000 (iterations 0), max_memory_kb 1048576, parallel_threads 4.
Keyslot 0 priority 1 != 2 (required), skipped.
Trying to open LUKS2 keyslot 0.
Reading keyslot area [0x8000].
Acquiring read lock for device /root/testfile2.
Verifying lock handle for /root/testfile2.
Device /root/testfile2 READ lock taken.
Reusing open ro fd on device /root/testfile2
Device /root/testfile2 READ lock released.
Verifying key from keyslot 0, digest 0.
TPM successfully started up.
Creating primary key on TPM.
Successfully created primary key on TPM.
Starting authentication session.
TPM2 device supports SHA256 PCR banks, yay!
Configuring PCR policy.
Acquiring policy digest.
Session policy digest: 8b5682d81b29435d08d79278150611dc7e5923b2fefcce684a09577b40130a8b
Added 512 bytes of entropy to the kernel random pool.
Generating secret key data.
Creating HMAC key.
Marshalling private and public part of HMAC key.
Completed TPM2 key sealing in 977.054ms.
Requesting JSON for token 0.
Requesting JSON for token 1.
Requesting JSON for token 2.
Requesting JSON for token 3.
Requesting JSON for token 4.
Requesting JSON for token 5.
Requesting JSON for token 6.
Requesting JSON for token 7.
Requesting JSON for token 8.
Requesting JSON for token 9.
Requesting JSON for token 10.
Requesting JSON for token 11.
Requesting JSON for token 12.
Requesting JSON for token 13.
Requesting JSON for token 14.
Requesting JSON for token 15.
Requesting JSON for token 16.
Requesting JSON for token 17.
Requesting JSON for token 18.
Requesting JSON for token 19.
Requesting JSON for token 20.
Requesting JSON for token 21.
Requesting JSON for token 22.
Requesting JSON for token 23.
Requesting JSON for token 24.
Requesting JSON for token 25.
Requesting JSON for token 26.
Requesting JSON for token 27.
Requesting JSON for token 28.
Requesting JSON for token 29.
Requesting JSON for token 30.
Requesting JSON for token 31.
PCR policy hash not yet enrolled, enrolling now.
Unsealing for verification...
Unmarshalling private part of HMAC key.
Unmarshalling public part of HMAC key.
TPM successfully started up.
Starting authentication session.
Configuring PCR policy.
Acquiring policy digest.
Session policy digest: 8b5682d81b29435d08d79278150611dc7e5923b2fefcce684a09577b40130a8b
Creating primary key on TPM.
Successfully created primary key on TPM.
Loading HMAC key into TPM.
Unsealing HMAC key.
Completed TPM2 key unsealing in 337.861ms.
PBKDF pbkdf2-sha512, time_ms 0 (iterations 1000).
Adding new keyslot -1 using volume key.
Adding new keyslot -1 with volume key assigned to a crypt segment.
Selected keyslot 1.
Keyslot 1 assigned to digest 0.
Trying to allocate LUKS2 keyslot 1.
Found area 290816 -> 548864
Reusing PBKDF values (no benchmark flag is set).
Calculating attributes for LUKS2 keyslot 1.
Acquiring write lock for device /root/testfile2.
Verifying lock handle for /root/testfile2.
Device /root/testfile2 WRITE lock taken.
Checking context sequence id matches value stored on disk.
Reusing open ro fd on device /root/testfile2
Updating keyslot area [0x47000].
Opening locked device /root/testfile2
Veryfing locked device handle (regular file)
Device size 52428800, offset 16777216.
Device /root/testfile2 WRITE lock already held.
Trying to write LUKS2 header (16384 bytes) at offset 0.
Reusing open rw fd on device /root/testfile2
Checksum:4652c932ba0c1d8e022477b9f03497840b1073a1ee699c4443c597450db078f3 (in-memory)
Trying to write LUKS2 header (16384 bytes) at offset 16384.
Reusing open rw fd on device /root/testfile2
Checksum:a795ac4992d01cbd4044d24e11dc3bba1b549515dd0488ba3a4387c75341561a (in-memory)
Device /root/testfile2 WRITE lock released.
Adding token text <{"type":"systemd-tpm2","keyslots":["1"],"tpm2-blob":"AH4AIOKQwlbWzgcB8u47Z1vY0tnUhBHWdWjn/ozKT0No5vfKABDpYX+GPaC82ucWBXGuRsV0Gz/mkJMu4G0sC8teJno+w+Z/hzGYmZVLnmZqj+Bgkm5Lv8CjvuuNn/8kzifEIooC4oZzMiWZOFcrbLOlxX8tT3ZLvjbL2+Q1fRsATgAIAAsAAAASACCLVoLYGylDXQjXkngVBhHcflkjsv78zmhKCVd7QBMKiwAQACBRsW7X3ISlbVAsLLyksowsFjqrunhFrCxafKYZuDlEMQ==","tpm2-pcrs":[7],"tpm2-pcr-bank":"sha256","tpm2-policy-hash":"8b5682d81b29435d08d79278150611dc7e5923b2fefcce684a09577b40130a8b"}>
Updating JSON for token -1.
Trying to load /usr/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so.
Loading symbol cryptsetup_token_open@CRYPTSETUP_TOKEN_1.0.
Loading symbol cryptsetup_token_buffer_free@CRYPTSETUP_TOKEN_1.0.
Loading symbol cryptsetup_token_validate@CRYPTSETUP_TOKEN_1.0.
Loading symbol cryptsetup_token_dump@CRYPTSETUP_TOKEN_1.0.
Loading symbol cryptsetup_token_open_pin@CRYPTSETUP_TOKEN_1.0.
/usr/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so: undefined symbol: cryptsetup_token_open_pin, version CRYPTSETUP_TOKEN_1.0
Loading symbol cryptsetup_token_version@CRYPTSETUP_TOKEN_1.0.
Token handler systemd-tpm2-1.0 systemd-v249 (249) loaded successfully.
Device size 52428800, offset 16777216.
Acquiring write lock for device /root/testfile2.
Verifying lock handle for /root/testfile2.
Device /root/testfile2 WRITE lock taken.
Checking context sequence id matches value stored on disk.
Reusing open ro fd on device /root/testfile2
Trying to write LUKS2 header (16384 bytes) at offset 0.
Reusing open rw fd on device /root/testfile2
Checksum:716d0053655841bb0ce5a35b9f5395ccb57314751f788cb402e24d06cb47edf5 (in-memory)
Trying to write LUKS2 header (16384 bytes) at offset 16384.
Reusing open rw fd on device /root/testfile2
Checksum:ba0514e36e8253529fc79ee48ef9b0fadd022f0a1721af7ff4e5b8ee081863f8 (in-memory)
Device /root/testfile2 WRITE lock released.
New TPM2 token enrolled as key slot 1.
Releasing crypt device /root/testfile2 context.
Releasing device-mapper backend.
Closing read only fd for /root/testfile2.
Closing read write fd for /root/testfile2.
Unloading systemd-tpm2 token handler.

Banks:

# tpm2_pcrread  
sha1:
  0 : 0x3DCAEA25DC86554D94B94AA5BC8F735A49212AF8
  1 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236
  2 : 0x61A89EE9A50479A1089877598C051EAA7C612361
  3 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236
  4 : 0xF4E0097AD917E905CCC59263B1DCC8132D888BFF
  5 : 0x9AADA2B39C6CB43AE398703FE479C153D41A8FD1
  6 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236
  7 : 0x4037336FA7BC0EABE3778FCFFF5FCD0EE6ADCDE3
  8 : 0x0EED6008416E64702746053CAD58C0DDF48829D1
  9 : 0x0000000000000000000000000000000000000000
  10: 0x0000000000000000000000000000000000000000
  11: 0x0000000000000000000000000000000000000000
  12: 0x0000000000000000000000000000000000000000
  13: 0x0000000000000000000000000000000000000000
  14: 0x0000000000000000000000000000000000000000
  15: 0x0000000000000000000000000000000000000000
  16: 0x0000000000000000000000000000000000000000
  17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  23: 0x0000000000000000000000000000000000000000
sha256:
  0 : 0x0000000000000000000000000000000000000000000000000000000000000000
  1 : 0x0000000000000000000000000000000000000000000000000000000000000000
  2 : 0x0000000000000000000000000000000000000000000000000000000000000000
  3 : 0x0000000000000000000000000000000000000000000000000000000000000000
  4 : 0x0000000000000000000000000000000000000000000000000000000000000000
  5 : 0x0000000000000000000000000000000000000000000000000000000000000000
  6 : 0x0000000000000000000000000000000000000000000000000000000000000000
  7 : 0x0000000000000000000000000000000000000000000000000000000000000000
  8 : 0x0000000000000000000000000000000000000000000000000000000000000000
  9 : 0x0000000000000000000000000000000000000000000000000000000000000000
  10: 0x0000000000000000000000000000000000000000000000000000000000000000
  11: 0x0000000000000000000000000000000000000000000000000000000000000000
  12: 0x0000000000000000000000000000000000000000000000000000000000000000
  13: 0x0000000000000000000000000000000000000000000000000000000000000000
  14: 0x0000000000000000000000000000000000000000000000000000000000000000
  15: 0x0000000000000000000000000000000000000000000000000000000000000000
  16: 0x0000000000000000000000000000000000000000000000000000000000000000
  17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  23: 0x0000000000000000000000000000000000000000000000000000000000000000
poettering commented 2 years ago

We could of course read the active banks from the EFI stub we provide (i.e. from EFI code before transitioning to the Linux kernel), and store it in some efi var. But that would only work if people actually use our efi stub, not sure all distros do that.

poettering commented 2 years ago

I prepped a patch for this in #20716. Would be great if you could give this a whirl.

(Should suffice building the git tree, then invoking systemd-cryptenroll/systemd-cryptsetup from the build tree, for testing. No need to install)

anssih commented 2 years ago

Seems to work on the affected system:

# truncate --size=50M testfile2
# touch key
# cryptsetup --batch-mode luksFormat testfile2 key
# PASSWORD= /opt/systemd/bin/systemd-cryptenroll --tpm2-device=auto testfile2
TPM2 device supports both SHA1 and SHA256 PCR banks, but only SHA1 PCRs are valid, falling back to SHA1 bank. This reduces the security level substantially.
New TPM2 token enrolled as key slot 1.
# cryptsetup luksDump testfile2 | grep tpm2-bank
        tpm2-bank:  sha1