tpm2: re-manufacture software TPM when state dir is incomplete
setup_swtpm() decided whether a software TPM had already been manufactured by checking whether the state directory was empty. But manufacture_swtpm() writes swtpm's config files before forking swtpm_setup, so an interrupted manufacture leaves the directory non-empty yet without a usable TPM. The next boot then mistook it for a complete TPM and started swtpm against a broken state directory. Keying off a swtpm state file like tpm2-00.permall is no better, as swtpm_setup gives no guarantee any single one is written atomically or last. Instead, have manufacture_swtpm() write a marker (.manufactured) as its very last step, once swtpm_setup has exited successfully, and gate on it: re-manufacture when it is missing in the initrd, and refuse rather than start a broken TPM outside it. Signed-off-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
@@ -3,12 +3,16 @@
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# Exercises the software TPM fallback (systemd-tpm2-swtpm.service) across a reboot. The VM boots in EFI mode
|
||||
# Exercises the software TPM fallback (systemd-tpm2-swtpm.service) across reboots. The VM boots in EFI mode
|
||||
# without a hardware/firmware TPM and with "systemd.tpm2_software_fallback=yes" (see the test's meson.build),
|
||||
# so systemd-tpm2-generator manufactures a software TPM on the ESP in the initrd and chainloads swtpm.
|
||||
#
|
||||
# boot 0: the TPM is manufactured in the initrd; seal a secret to it and stash the blob.
|
||||
# boot 1: the TPM state persisted on the ESP across the reboot, so the secret still unseals.
|
||||
# boot 1: the TPM state persisted on the ESP across the reboot, so the secret still unseals. Then mimic a
|
||||
# manufacture that was interrupted before it completed (drop everything but the config files, so the
|
||||
# ".manufactured" marker is gone) and reboot.
|
||||
# boot 2: setup_swtpm() must notice the missing marker and re-manufacture, rather than mistaking the
|
||||
# leftover config files for a complete TPM and starting swtpm against a stateless directory.
|
||||
#
|
||||
# See systemd-tpm2-swtpm.service(8).
|
||||
|
||||
@@ -17,6 +21,8 @@ set -o pipefail
|
||||
|
||||
CRED=/var/lib/systemd-tpm2-swtpm-test.cred
|
||||
PLAINTEXT="swtpm round-trip"
|
||||
# Marker (SWTPM_MANUFACTURED_MARKER) that manufacture_swtpm() indicates completion with.
|
||||
MARKER=.manufactured
|
||||
|
||||
if [[ -n "${ASAN_OPTIONS:-}" ]]; then
|
||||
# swtpm_setup is not built with sanitizers, but does NSS lookups that pull in the ASan-instrumented
|
||||
@@ -38,6 +44,15 @@ assert_swtpm_up() {
|
||||
assert_in '\+driver' "$(systemd-analyze has-tpm2 || :)"
|
||||
}
|
||||
|
||||
# Locate swtpm's state directory on the ESP.
|
||||
swtpm_state_dir() {
|
||||
local d
|
||||
for d in /boot/loader/swtpm /efi/loader/swtpm; do
|
||||
[[ -d "$d" ]] && { echo "$d"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
case "$REBOOT_COUNT" in
|
||||
0)
|
||||
assert_swtpm_up
|
||||
@@ -53,6 +68,31 @@ case "$REBOOT_COUNT" in
|
||||
# Persistence: the TPM state survived the reboot on the ESP, so the blob still unseals.
|
||||
echo -n "$PLAINTEXT" >/tmp/swtpm-plaintext
|
||||
systemd-creds decrypt --name= "$CRED" - | cmp /tmp/swtpm-plaintext -
|
||||
|
||||
# Mimic a manufacture interrupted after swtpm began writing TPM state but before the marker: stop
|
||||
# swtpm, drop everything except the three config files, then leave a partial/corrupt state file
|
||||
# behind. swtpm_setup --not-overwrite would refuse to recreate that, so recovery must clear it first.
|
||||
statedir="$(swtpm_state_dir)"
|
||||
systemctl stop systemd-tpm2-swtpm.service
|
||||
find "$statedir" -mindepth 1 -maxdepth 1 -type f \
|
||||
! -name swtpm-localca.conf ! -name swtpm-localca.options ! -name swtpm_setup.conf -delete
|
||||
echo "corrupt" >"$statedir/tpm2-00.permall"
|
||||
test -e "$statedir/swtpm_setup.conf"
|
||||
test ! -e "$statedir/$MARKER"
|
||||
systemctl_final reboot
|
||||
exec sleep infinity
|
||||
;;
|
||||
2)
|
||||
# setup_swtpm() must have re-manufactured instead of trusting the leftover config files: the marker is
|
||||
# back and swtpm_setup re-ran swtpm_localca, recreating issuer-certificate.pem. The TPM must also work.
|
||||
# Regression test for keying re-manufacture off an incomplete state directory.
|
||||
assert_swtpm_up
|
||||
statedir="$(swtpm_state_dir)"
|
||||
test -e "$statedir/$MARKER"
|
||||
test -e "$statedir/issuer-certificate.pem"
|
||||
echo -n "$PLAINTEXT" >/tmp/swtpm-plaintext
|
||||
systemd-creds encrypt --name= --with-key=tpm2 /tmp/swtpm-plaintext /tmp/swtpm-new.cred
|
||||
systemd-creds decrypt --name= /tmp/swtpm-new.cred - | cmp /tmp/swtpm-plaintext -
|
||||
touch /testok
|
||||
;;
|
||||
*)
|
||||
|
||||
Reference in New Issue
Block a user