From 29be59555f478c9d029593ccf0a2f64b6ae5843f Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 25 Oct 2023 14:48:30 +0200 Subject: [PATCH 1/6] test: split TEST-70-TPM2 into subtests --- test/units/testsuite-70.creds.sh | 16 + test/units/testsuite-70.cryptenroll.sh | 84 +++++ test/units/testsuite-70.cryptsetup.sh | 184 ++++++++++ test/units/testsuite-70.measure.sh | 130 +++++++ test/units/testsuite-70.pcrextend.sh | 58 ++++ test/units/testsuite-70.sh | 452 +------------------------ test/units/testsuite-70.tpm2-setup.sh | 18 + test/units/util.sh | 7 + 8 files changed, 501 insertions(+), 448 deletions(-) create mode 100755 test/units/testsuite-70.creds.sh create mode 100755 test/units/testsuite-70.cryptenroll.sh create mode 100755 test/units/testsuite-70.cryptsetup.sh create mode 100755 test/units/testsuite-70.measure.sh create mode 100755 test/units/testsuite-70.pcrextend.sh create mode 100755 test/units/testsuite-70.tpm2-setup.sh diff --git a/test/units/testsuite-70.creds.sh b/test/units/testsuite-70.creds.sh new file mode 100755 index 0000000000..e66bfd19c5 --- /dev/null +++ b/test/units/testsuite-70.creds.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +# Ensure that sandboxing doesn't stop creds from being accessible +echo "test" > /tmp/testdata +systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2 +# LoadCredentialEncrypted +systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata +# SetCredentialEncrypted +systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata + +rm -f /tmp/testdata diff --git a/test/units/testsuite-70.cryptenroll.sh b/test/units/testsuite-70.cryptenroll.sh new file mode 100755 index 0000000000..3f8c14e54e --- /dev/null +++ b/test/units/testsuite-70.cryptenroll.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +cryptenroll_wipe_and_check() {( + set +o pipefail + + : >/tmp/cryptenroll.out + systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out + grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out +)} + +# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it +if [[ "$(uname -m)" == "ppc64le" ]]; then + echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716" + exit 0 +fi + +export SYSTEMD_LOG_LEVEL=debug +IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)" + +truncate -s 20M "$IMAGE" +echo -n password >/tmp/password +# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages +chmod 0600 /tmp/password +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password + +# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff +systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE" +NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" +NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" +for _ in {0..9}; do + systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE" +done +PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" +# Do some basic checks before we start wiping stuff +systemd-cryptenroll "$IMAGE" +systemd-cryptenroll "$IMAGE" | grep password +systemd-cryptenroll "$IMAGE" | grep recovery +# Let's start wiping +cryptenroll_wipe_and_check "$IMAGE" --wipe=empty +(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty) +cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0 +PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password +systemd-cryptenroll "$IMAGE" | grep password +(! systemd-cryptenroll "$IMAGE" | grep recovery) +# We shouldn't be able to wipe all keyslots without enrolling a new key first +(! systemd-cryptenroll "$IMAGE" --wipe=all) +PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all +# Check if the newly (and only) enrolled password works +(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE") +(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE") +PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE" + +systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE" +systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE" +systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE" +systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE" +systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE" + +(! systemd-cryptenroll --fido2-with-client-pin=false) +(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo) +(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE") +(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE") +(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE") +(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE") +(! systemd-cryptenroll --recovery-key --password "$IMAGE") +(! systemd-cryptenroll --password --recovery-key "$IMAGE") +(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE") +(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE") +(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE") +(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE") +(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE") +(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE") +(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE") +(! systemd-cryptenroll --wipe-slot "$IMAGE") +(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE") +(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE") + +rm -f "$IMAGE" diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/testsuite-70.cryptsetup.sh new file mode 100755 index 0000000000..98a188fd1e --- /dev/null +++ b/test/units/testsuite-70.cryptsetup.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug + +cryptsetup_has_token_plugin_support() { + local plugin_path + + plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)" + cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]] +} + +tpm_check_failure_with_wrong_pin() { + local testIMAGE="${1:?}" + local badpin="${2:?}" + local goodpin="${3:?}" + + # We need to be careful not to trigger DA lockout; allow 2 failures + tpm2_dictionarylockout -s -n 2 + (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1) + # Verify the correct PIN works, to be sure the failure wasn't a DA lockout + PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + # Clear/reset the DA lockout counter + tpm2_dictionarylockout -c +} + +at_exit() { + # Evict the TPM primary key that we persisted + if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then + tpm2_evictcontrol -c "$PERSISTENT_HANDLE" + fi +} + +trap at_exit EXIT + +# Prepare a fresh disk image +IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)" + +truncate -s 20M "$IMAGE" +echo -n passphrase >/tmp/passphrase +# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages +chmod 0600 /tmp/passphrase +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase + +# Unlocking via keyfile +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE" + +# Enroll unlock with default PCR policy +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check with wrong PCR +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +# Enroll unlock with PCR+PIN policy +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" +PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout +for _ in {0..3}; do + tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 +done + +# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto) +if cryptsetup_has_token_plugin_support; then + PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1 + systemd-cryptsetup detach test-volume + + # Check failure with wrong PIN + for _ in {0..3}; do + tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 + done +else + echo 'cryptsetup has no LUKS2 token plugin support, skipping' +fi + +# Check failure with wrong PCR (and correct PIN) +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +# Enroll unlock with PCR 0+7 +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check with wrong PCR 0 +tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +if tpm_has_pcr sha256 12; then + # Enroll using an explicit PCR value (that does match current PCR value) + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Same as above plus more PCRs without the value or alg specified + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Same as above plus more PCRs with hash alg specified but hash value not specified + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 + CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" + (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + rm -f /tmp/pcr.dat +fi + +# Use default (0) seal key handle +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Use SRK seal key handle +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Test invalid ranges: pcr, nv, session, permanent +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE") # PCR +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent + +# Use non-SRK persistent seal key handle (by creating/persisting new key) +PRIMARY=/tmp/primary.ctx +tpm2_createprimary -c "$PRIMARY" +PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle) +PERSISTENT="0x${PERSISTENT_LINE##*0x}" +tpm2_flushcontext -t + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT#0x}" "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT" "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +rm -f "$IMAGE" "$PRIMARY" diff --git a/test/units/testsuite-70.measure.sh b/test/units/testsuite-70.measure.sh new file mode 100755 index 0000000000..3336f38330 --- /dev/null +++ b/test/units/testsuite-70.measure.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug +SD_MEASURE="/usr/lib/systemd/systemd-measure" + +if [[ ! -x "${SD_MEASURE:?}" ]]; then + echo "$SD_MEASURE not found, skipping the test" + exit 0 +fi + +IMAGE="$(mktemp /tmp/systemd-measure-XXX.image)" + +echo HALLO >/tmp/tpmdata1 +echo foobar >/tmp/tpmdata2 + +cat >/tmp/result </tmp/result.json </tmp/result </tmp/result.json </dev/null; then + MEASURE_BANKS+=("--bank=sha1") +fi + +# Sign current PCR state with it +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig" +dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64 +systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem" +systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata + +# Invalidate PCR, decrypting should fail now +tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null) + +# Sign new PCR state, decrypting should work now. +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2" +systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata + +# Now, do the same, but with a cryptsetup binding +truncate -s 20M "$IMAGE" +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase +# Ensure that an unrelated signature, when not requested, is not used +touch /run/systemd/tpm2-pcr-signature.json +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE" +# Reset and use the signature now +rm -f /run/systemd/tpm2-pcr-signature.json +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE" + +# Check if we can activate that (without the token module stuff) +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2 + +# Check if we can activate that (and a second time with the token module stuff enabled) +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2 + +# After extending the PCR things should fail +tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) +(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) + +# But once we sign the current PCRs, we should be able to unlock again +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3" +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 +systemd-cryptsetup detach test-volume2 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 +systemd-cryptsetup detach test-volume2 + +# Test --append mode and de-duplication. With the same parameters signing should not add a new entry +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4" +cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4" + +# Sign one more phase, this should +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5" +(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5") + +# Should still be good to unlock, given the old entry still exists +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1 +systemd-cryptsetup detach test-volume2 + +# Adding both signatures once more should not change anything, due to the deduplication +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6" +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7" +cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7" + +rm -f "$IMAGE" diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/testsuite-70.pcrextend.sh new file mode 100755 index 0000000000..0c391626c2 --- /dev/null +++ b/test/units/testsuite-70.pcrextend.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug +SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend" + +if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then + echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests" + exit 0 +fi + +# Let's measure the machine ID +tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15 +mv /etc/machine-id /etc/machine-id.save +echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id +SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id +mv /etc/machine-id.save /etc/machine-id +tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15 + +# And check it matches expectations +diff /tmp/newpcr15 \ + <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256) + +rm -f /tmp/oldpcr15 /tmp/newpcr15 + +# Check that the event log record was properly written: +test "$(jq --seq --slurp '.[0].pcr' < /run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')" +test "$(jq --seq --slurp --raw-output '.[0].digests[1].digest' < /run/log/systemd/tpm2-measure.log) *stdin" == "$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)" + +# And similar for the boot phase measurement into PCR 11 +tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11 +# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...) +mkdir -p /run/systemd/system/systemd-pcrextend.socket.d +cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf </tmp/passphrase -# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages -chmod 0600 /tmp/passphrase -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/passphrase - -# Unlocking via keyfile -systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$img" - -# Enroll unlock with default PCR policy -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check with wrong PCR -tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1) - -# Enroll unlock with PCR+PIN policy -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$img" -PIN=123456 systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout -for _ in {0..3}; do - tpm_check_failure_with_wrong_pin "$img" 123457 123456 -done - -# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto) -if cryptsetup_has_token_plugin_support; then - PIN=123456 systemd-cryptsetup attach test-volume "$img" - headless=1 - systemd-cryptsetup detach test-volume - - # Check failure with wrong PIN - for _ in {0..3}; do - tpm_check_failure_with_wrong_pin "$img" 123457 123456 - done -else - echo 'cryptsetup has no LUKS2 token plugin support, skipping' -fi - -# Check failure with wrong PCR (and correct PIN) -tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! PIN=123456 systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1) - -# Enroll unlock with PCR 0+7 -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check with wrong PCR 0 -tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1) - -if tpm_has_pcr sha256 12; then - # Enroll using an explicit PCR value (that does match current PCR value) - systemd-cryptenroll --wipe-slot=tpm2 "$img" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img" - systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Same as above plus more PCRs without the value or alg specified - systemd-cryptenroll --wipe-slot=tpm2 "$img" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$img" - systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Same as above plus more PCRs with hash alg specified but hash value not specified - systemd-cryptenroll --wipe-slot=tpm2 "$img" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$img" - systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value - systemd-cryptenroll --wipe-slot=tpm2 "$img" - tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 - CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img" - (! systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1) - tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE" - systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - rm -f /tmp/pcr.dat -fi - -# Use default (0) seal key handle -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Use SRK seal key handle -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Test invalid ranges: pcr, nv, session, permanent -systemd-cryptenroll --wipe-slot=tpm2 "$img" -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$img") # PCR -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$img") # NV index -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$img") # HMAC/loaded session -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$img") # Policy/saved session -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$img") # Permanent - -# Use non-SRK persistent seal key handle (by creating/persisting new key) -primary=/tmp/primary.ctx -tpm2_createprimary -c "$primary" -persistent_line=$(tpm2_evictcontrol -c "$primary" | grep persistent-handle) -persistent="0x${persistent_line##*0x}" -tpm2_flushcontext -t - -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${persistent#0x}" "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$img" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$persistent" "$img" -systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -tpm2_evictcontrol -c "$persistent" -persistent="" -rm -f "$primary" - -rm -f "${img:?}" - -if [[ -x "$SD_MEASURE" ]]; then - echo HALLO >/tmp/tpmdata1 - echo foobar >/tmp/tpmdata2 - - cat >/tmp/result </tmp/result.json </tmp/result </tmp/result.json </dev/null; then - MEASURE_BANKS+=("--bank=sha1") - fi - - # Sign current PCR state with it - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig" - dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64 - systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem" - systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata - - # Invalidate PCR, decrypting should fail now - tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 - (! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null) - - # Sign new PCR state, decrypting should work now. - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2" - systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata - - # Now, do the same, but with a cryptsetup binding - truncate -s 20M "$img" - cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/passphrase - # Ensure that an unrelated signature, when not requested, is not used - touch /run/systemd/tpm2-pcr-signature.json - systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$img" - # Reset and use the signature now - rm -f /run/systemd/tpm2-pcr-signature.json - systemd-cryptenroll --wipe-slot=tpm2 "$img" - systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$img" - - # Check if we can activate that (without the token module stuff) - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2 - - # Check if we can activate that (and a second time with the token module stuff enabled) - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2 - - # After extending the PCR things should fail - tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 - (! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) - (! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) - - # But once we sign the current PCRs, we should be able to unlock again - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3" - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 - systemd-cryptsetup detach test-volume2 - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 - systemd-cryptsetup detach test-volume2 - - # Test --append mode and de-duplication. With the same parameters signing should not add a new entry - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4" - cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4" - - # Sign one more phase, this should - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5" - (! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5") - - # Should still be good to unlock, given the old entry still exists - SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$img" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1 - systemd-cryptsetup detach test-volume2 - - # Adding both signatures once more should not change anything, due to the deduplication - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6" - "$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7" - cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7" - - rm -f "$img" -else - echo "$SD_MEASURE or PCR sysfs files not found, skipping signed PCR policy test case" -fi - -if [[ -x "$SD_PCREXTEND" ]] && tpm_has_pcr sha256 11 && tpm_has_pcr sha256 15; then - # Let's measure the machine ID - tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15 - mv /etc/machine-id /etc/machine-id.save - echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id - SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id - mv /etc/machine-id.save /etc/machine-id - tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15 - - # And check it matches expectations - diff /tmp/newpcr15 \ - <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256) - - rm -f /tmp/oldpcr15 /tmp/newpcr15 - - # Check that the event log record was properly written: - test "$(jq --seq --slurp '.[0].pcr' < /run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')" - test "$(jq --seq --slurp --raw-output '.[0].digests[1].digest' < /run/log/systemd/tpm2-measure.log) *stdin" == "$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)" - - # And similar for the boot phase measurement into PCR 11 - tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11 - # Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...) - mkdir -p /run/systemd/system/systemd-pcrextend.socket.d - cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf < /tmp/testdata -systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2 -# LoadCredentialEncrypted -systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata -# SetCredentialEncrypted -systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata -rm -f /tmp/testdata - -# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it -machine="$(uname -m)" -if [ "${machine}" = "ppc64le" ]; then - touch /testok - exit 0 -fi - -cryptenroll_wipe_and_check() {( - set +o pipefail - - : >/tmp/cryptenroll.out - systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out - grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out -)} - -img="/tmp/cryptenroll.img" -truncate -s 20M "$img" -echo -n password >/tmp/password -# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages -chmod 0600 /tmp/password -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/password - -# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff -systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$img" -NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$img" -NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$img" -for _ in {0..9}; do - systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$img" -done -PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$img" -# Do some basic checks before we start wiping stuff -systemd-cryptenroll "$img" -systemd-cryptenroll "$img" | grep password -systemd-cryptenroll "$img" | grep recovery -# Let's start wiping -cryptenroll_wipe_and_check "$img" --wipe=empty -(! cryptenroll_wipe_and_check "$img" --wipe=empty) -cryptenroll_wipe_and_check "$img" --wipe=empty,0 -PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$img" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password -systemd-cryptenroll "$img" | grep password -(! systemd-cryptenroll "$img" | grep recovery) -# We shouldn't be able to wipe all keyslots without enrolling a new key first -(! systemd-cryptenroll "$img" --wipe=all) -PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$img" --password --wipe=all -# Check if the newly (and only) enrolled password works -(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$img") -(! PASSWORD="" systemd-cryptenroll --recovery-key "$img") -PASSWORD=foo systemd-cryptenroll --recovery-key "$img" - -systemd-cryptenroll --fido2-with-client-pin=false "$img" -systemd-cryptenroll --fido2-with-user-presence=false "$img" -systemd-cryptenroll --fido2-with-user-verification=false "$img" -systemd-cryptenroll --tpm2-pcrs=8 "$img" -systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$img" - -(! systemd-cryptenroll --fido2-with-client-pin=false) -(! systemd-cryptenroll --fido2-with-user-presence=f "$img" /tmp/foo) -(! systemd-cryptenroll --fido2-with-client-pin=1234 "$img") -(! systemd-cryptenroll --fido2-with-user-presence=1234 "$img") -(! systemd-cryptenroll --fido2-with-user-verification=1234 "$img") -(! systemd-cryptenroll --tpm2-with-pin=1234 "$img") -(! systemd-cryptenroll --recovery-key --password "$img") -(! systemd-cryptenroll --password --recovery-key "$img") -(! systemd-cryptenroll --password --fido2-device=auto "$img") -(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$img") -(! systemd-cryptenroll --password --tpm2-device=auto "$img") -(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$img") -(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$img") -(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$img") -(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$img") -(! systemd-cryptenroll --tpm2-pcrs=key "$img") -(! systemd-cryptenroll --tpm2-pcrs=44+8 "$img") -(! systemd-cryptenroll --tpm2-pcrs=hello "$img") -(! systemd-cryptenroll --wipe-slot "$img") -(! systemd-cryptenroll --wipe-slot=10240000 "$img") -(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$img") - -# Run this, just to get sanitizer coverage. The tools should be idempotent, hence run the multiple times. -if [[ -x "$SD_TPM2SETUP" ]]; then - "$SD_TPM2SETUP" --early=yes - "$SD_TPM2SETUP" --early=yes - "$SD_TPM2SETUP" --early=no - "$SD_TPM2SETUP" --early=no -fi - -cleanup +run_subtests touch /testok diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/testsuite-70.tpm2-setup.sh new file mode 100755 index 0000000000..40dce3bf2d --- /dev/null +++ b/test/units/testsuite-70.tpm2-setup.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug +SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup" + +if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then + echo "$SD_TPM2SETUP not found, skipping the test" + exit 0 +fi + +# Run this, just to get sanitizer coverage. The tools should be idempotent, hence run the multiple times. +"$SD_TPM2SETUP" --early=yes +"$SD_TPM2SETUP" --early=yes +"$SD_TPM2SETUP" --early=no +"$SD_TPM2SETUP" --early=no diff --git a/test/units/util.sh b/test/units/util.sh index fee642fa9d..e47d32c2b3 100755 --- a/test/units/util.sh +++ b/test/units/util.sh @@ -182,3 +182,10 @@ cgroupfs_supports_user_xattrs() { setfattr --name="$xattr" --value=254 /sys/fs/cgroup [[ "$(getfattr --name="$xattr" --absolute-names --only-values /sys/fs/cgroup)" -eq 254 ]] } + +tpm_has_pcr() { + local algorithm="${1:?}" + local pcr="${2:?}" + + [[ -f "/sys/class/tpm/tpm0/pcr-$algorithm/$pcr" ]] +} From 45534eefe6b8b5acec09f26dd698350ed6648e4e Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 25 Oct 2023 17:40:05 +0200 Subject: [PATCH 2/6] test: make the TPM event log checking a bit more robust Don't hardcode the event number, so the test works correctly even if someone wrote to the event log before us. Also, explicitly pick the sha256 bank when checking digests, as the indexing may vary depending on current TPM's capabilities. --- test/units/testsuite-70.pcrextend.sh | 40 ++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/testsuite-70.pcrextend.sh index 0c391626c2..cae2d28fd9 100755 --- a/test/units/testsuite-70.pcrextend.sh +++ b/test/units/testsuite-70.pcrextend.sh @@ -14,6 +14,23 @@ if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sh exit 0 fi +at_exit() { + if [[ $? -ne 0 ]]; then + # Dump the event log on fail, to make debugging a bit easier + jq --seq --slurp Date: Wed, 25 Oct 2023 17:42:07 +0200 Subject: [PATCH 3/6] test: add a couple more tests for systemd-pcrextend --- test/test-functions | 2 ++ test/units/testsuite-70.pcrextend.sh | 48 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/test/test-functions b/test/test-functions index 60db47c86a..b659c98529 100644 --- a/test/test-functions +++ b/test/test-functions @@ -169,6 +169,7 @@ BASICTOOLS=( echo env false + findmnt flock getconf getent @@ -188,6 +189,7 @@ BASICTOOLS=( login losetup lsattr + lsblk lz4cat mkfifo mknod diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/testsuite-70.pcrextend.sh index cae2d28fd9..318fce02a1 100755 --- a/test/units/testsuite-70.pcrextend.sh +++ b/test/units/testsuite-70.pcrextend.sh @@ -23,6 +23,41 @@ at_exit() { trap at_exit EXIT +# Temporarily override sd-pcrextend's sanity checks +export SYSTEMD_FORCE_MEASURE=1 + +"$SD_PCREXTEND" --help +"$SD_PCREXTEND" --version +"$SD_PCREXTEND" foo +"$SD_PCREXTEND" --machine-id +"$SD_PCREXTEND" --tpm2-device=list +"$SD_PCREXTEND" --tpm2-device=auto foo +"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 foo +"$SD_PCREXTEND" --bank=sha256 foo +"$SD_PCREXTEND" --bank=sha256 --bank=sha256 foo +"$SD_PCREXTEND" --graceful foo +"$SD_PCREXTEND" --pcr=15 foo +"$SD_PCREXTEND" --file-system=/ +"$SD_PCREXTEND" --file-system=/tmp --file-system=/ +"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=11 + +if tpm_has_pcr sha1 11; then + "$SD_PCREXTEND" --bank=sha1 --pcr=11 foo +fi + +(! "$SD_PCREXTEND") +(! "$SD_PCREXTEND" "") +(! "$SD_PCREXTEND" foo bar) +(! "$SD_PCREXTEND" --bank= foo) +(! "$SD_PCREXTEND" --tpm2-device= foo) +(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo) +(! "$SD_PCREXTEND" --pcr= foo) +(! "$SD_PCREXTEND" --pcr=-1 foo) +(! "$SD_PCREXTEND" --pcr=1024 foo) +(! "$SD_PCREXTEND" --foo=bar) + +unset SYSTEMD_FORCE_MEASURE + # Note: since we're reading the TPM event log as json-seq, the same rules apply to the output # as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012). # LF is usually eaten by bash, but RS needs special handling. @@ -73,4 +108,17 @@ DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)" DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" Date: Wed, 25 Oct 2023 17:52:19 +0200 Subject: [PATCH 4/6] tpm2-setup: terminate the option array Otherwise bad things happen: $ build/systemd-tpm2-setup --foo Segmentation fault (core dumped) --- src/tpm2-setup/tpm2-setup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index 580a18cbbb..ebb956ab21 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -63,6 +63,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "early", required_argument, NULL, ARG_EARLY }, + {} }; int c, r; From 181a80e602c6a4884bc34bcc0266981f87f7e39d Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 25 Oct 2023 18:04:26 +0200 Subject: [PATCH 5/6] tpm2-setup: drop the COMMAND placeholder from the help Since systemd-tpm2-setup doesn't expect any arguments. --- src/tpm2-setup/tpm2-setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index ebb956ab21..2f23fb162d 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -32,7 +32,7 @@ static int help(int argc, char *argv[], void *userdata) { if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] COMMAND\n" + printf("%1$s [OPTIONS...]\n" "\n%5$sSet up the TPM2 Storage Root Key (SRK).%6$s\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" From d08b67e55e7dac951e32b071ec5f5660a1c9053f Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 25 Oct 2023 17:57:08 +0200 Subject: [PATCH 6/6] test: slightly extend systemd-tpm2-setup's coverage --- test/units/testsuite-70.tpm2-setup.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/testsuite-70.tpm2-setup.sh index 40dce3bf2d..faf6fe7847 100755 --- a/test/units/testsuite-70.tpm2-setup.sh +++ b/test/units/testsuite-70.tpm2-setup.sh @@ -11,8 +11,17 @@ if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then exit 0 fi -# Run this, just to get sanitizer coverage. The tools should be idempotent, hence run the multiple times. +"$SD_TPM2SETUP" --help +"$SD_TPM2SETUP" --version +"$SD_TPM2SETUP" --tpm2-device=list +"$SD_TPM2SETUP" --tpm2-device=auto +"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0 "$SD_TPM2SETUP" --early=yes "$SD_TPM2SETUP" --early=yes "$SD_TPM2SETUP" --early=no "$SD_TPM2SETUP" --early=no + +(! "$SD_TPM2SETUP" "") +(! "$SD_TPM2SETUP" --tpm2-device=) +(! "$SD_TPM2SETUP" --tpm2-device=/dev/null) +(! "$SD_TPM2SETUP" --foo=bar)