diff --git a/test/units/TEST-94-COCO.snp-direct.sh b/test/units/TEST-94-COCO.snp-direct.sh index 3bb9bda902..7933a8775f 100755 --- a/test/units/TEST-94-COCO.snp-direct.sh +++ b/test/units/TEST-94-COCO.snp-direct.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.1-or-later # -# SEV-SNP, direct kernel boot: launch a confidential guest with --linux=/--initrd=. +# SEV-SNP, direct kernel boot: launch confidential guests with --linux=/--initrd=. set -eux set -o pipefail @@ -16,10 +16,12 @@ fi MACHINE="coco-snp-direct-$$" WORKDIR="$(mktemp -d)" +mkdir -p "$WORKDIR/honest" "$WORKDIR/hostile" at_exit() { set +e jobs -p | xargs -r kill 2>/dev/null - machinectl terminate "$MACHINE" 2>/dev/null || : + machinectl terminate "$MACHINE-honest" 2>/dev/null || : + machinectl terminate "$MACHINE-hostile" 2>/dev/null || : rm -rf "$WORKDIR" } trap at_exit EXIT @@ -29,7 +31,7 @@ trap at_exit EXIT # - detect_virt: systemd-detect-virt --cvm detects SNP as the CVM type # - creds_vmspawn: credential transport by vmspawn is working (via initrd for SNP) # - creds_cmdline: credential transport via cmdline is working (cmdline is measured on SNP) -vmspawn_boot_coco "$MACHINE" "$COCO_TYPE" "$WORKDIR" 'detect_virt creds_vmspawn creds_cmdline' \ +vmspawn_boot_coco "$MACHINE-honest" "$COCO_TYPE" "$WORKDIR/honest" 'detect_virt creds_vmspawn creds_cmdline' \ --image="$IMAGE_DIR/image.raw" \ --linux="$IMAGE_DIR/image.vmlinuz" \ --initrd="$IMAGE_DIR/image.initrd" \ @@ -37,3 +39,22 @@ vmspawn_boot_coco "$MACHINE" "$COCO_TYPE" "$WORKDIR" 'detect_virt creds_vmspawn selinux=0 systemd.firstboot=no rw \ "systemd.set_credential=$COCO_CRED_CMDLINE_ID:$COCO_CRED_CMDLINE_VALUE" echo "SEV-SNP direct-boot guest correctly reported confidential virtualization and trusted credentials" + +# Offensive test: +# +# - creds_hostile_*: ensure the guest isn't tricked into consuming untrusted/unmeasured credentials. +QEMU_EXTRA=( + -smbios "type=11,value=io.systemd.credential:$COCO_CRED_SMBIOS_ID=$COCO_CRED_SMBIOS_VALUE" + -smbios "type=11,value=io.systemd.credential:$COCO_CRED_TRUSTED_ID=$COCO_CRED_SMBIOS_VALUE" + -fw_cfg "name=opt/io.systemd.credentials/$COCO_CRED_FWCFG_ID,string=$COCO_CRED_FWCFG_VALUE" +) + +SYSTEMD_VMSPAWN_QEMU_EXTRA="${QEMU_EXTRA[*]}" \ +vmspawn_boot_coco "$MACHINE-hostile" "$COCO_TYPE" "$WORKDIR/hostile" \ + 'detect_virt creds_vmspawn creds_hostile_fwcfg creds_hostile_snp_smbios creds_hostile_shadow' \ + --image="$IMAGE_DIR/image.raw" \ + --linux="$IMAGE_DIR/image.vmlinuz" \ + --initrd="$IMAGE_DIR/image.initrd" \ + --set-credential="$COCO_CRED_TRUSTED_ID:$COCO_CRED_TRUSTED_VALUE" \ + selinux=0 systemd.firstboot=no rw +echo "SEV-SNP direct-boot guest correctly ignored the host-injected credentials" diff --git a/test/units/TEST-94-COCO/fixtures.sh b/test/units/TEST-94-COCO/fixtures.sh index c690537b18..5676aabd14 100644 --- a/test/units/TEST-94-COCO/fixtures.sh +++ b/test/units/TEST-94-COCO/fixtures.sh @@ -20,3 +20,7 @@ COCO_CRED_TRUSTED_ID="coco.trusted" COCO_CRED_TRUSTED_VALUE="trusted-via-vmspawn" COCO_CRED_CMDLINE_ID="coco.cmdline" COCO_CRED_CMDLINE_VALUE="trusted-via-cmdline" +COCO_CRED_SMBIOS_ID="coco.hostile.smbios" +COCO_CRED_SMBIOS_VALUE="injected-via-smbios" +COCO_CRED_FWCFG_ID="coco.hostile.fwcfg" +COCO_CRED_FWCFG_VALUE="injected-via-fwcfg" diff --git a/test/units/TEST-94-COCO/guest-test.sh b/test/units/TEST-94-COCO/guest-test.sh index b7778c07db..c2f89258d8 100644 --- a/test/units/TEST-94-COCO/guest-test.sh +++ b/test/units/TEST-94-COCO/guest-test.sh @@ -13,6 +13,40 @@ fi # shellcheck source=test/units/TEST-94-COCO/fixtures.sh . "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/fixtures.sh" +# _coco_assert_cred_absent ID +# Assert no credential named ID reached the guest at all, neither trusted nor encrypted. +_coco_assert_cred_absent() { + local id="${1:?}" + local system_creds_dir=/run/credentials/@system + local encrypted_creds_dir=/run/credentials/@encrypted + + assert_fail test -e "$system_creds_dir/$id" + assert_fail test -e "$encrypted_creds_dir/$id" +} + +# _coco_assert_smbios_injected ID +# Assert the guest received an SMBIOS type #11 OEM string carrying credential ID. The scenario +# injected it, so a miss means the injection was lost on the way in and concluding anything from the +# credential's absence afterwards would be meaningless. +_coco_assert_smbios_injected() { + local id="${1:?}" raw + + [[ -d /sys/firmware/dmi/entries ]] || modprobe dmi-sysfs || : + + for raw in /sys/firmware/dmi/entries/11-*/raw; do + [[ -e "$raw" ]] || continue + # The entry is a blob of NUL-separated strings: --text to search it anyway, --fixed-strings + # because credential names contain dots. Redirect rather than -q, which would exit early and + # risk SIGPIPE on the reader. + if grep --text --fixed-strings "io.systemd.credential:$id=" "$raw" >/dev/null; then + return 0 + fi + done + + echo "FAIL: no SMBIOS OEM string for credential '$id' in the guest's DMI type #11 tables" >&2 + return 1 +} + # Assert the guest observes the confidential-virtualization technology the host launched it with. # COCO_TYPE is provided by the injected guest unit (Environment=, see lib.sh). testcase_coco_detect_virt() { @@ -34,3 +68,29 @@ testcase_coco_creds_cmdline() { assert_in "systemd.set_credential=$COCO_CRED_CMDLINE_ID:$COCO_CRED_CMDLINE_VALUE" "$(