From 4dd73ad924a660b90bfa1d5d95e04566484e76e6 Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Tue, 30 Jun 2026 13:30:08 +0200 Subject: [PATCH] core: trust SMBIOS credentials under Intel TDX SMBIOS OEM strings are host-controlled and normally distrusted by PID1 in confidential VMs. Under TDX, however, the SMBIOS table (including Type 11) is measured into RTMR0 by the firmware (TDVF), so a remote verifier can detect host tampering with credentials delivered this way. Accept them there, while keeping fw_cfg distrusted as those items are not measured even on TDX. This lets systemd-vmspawn deliver credentials to TDX guests via the normal SMBIOS path, unlike SNP which requires the initrd cpio channel. Signed-off-by: Paul Meyer --- man/systemd-vmspawn.xml | 15 +++++++++------ src/core/import-creds.c | 9 ++++++++- src/vmspawn/vmspawn.c | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 64cfab8294c..d8afb9d0e15 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -378,12 +378,15 @@ When booting a UKI, the whole UKI PE is measured into RTMR 1, and the loaded sections are measured individually by systemd-stub into RTMR 2. For direct linux boot, firmware measures the kernel PE into RTMR 1, and the Linux EFI stub measures initrd and - command line into RTMR 2. A vTPM, if attached via , must be treated - as untrusted by the guest. To obtain TD Quotes for remote attestation, the guest is wired - to the host's local TDX Quote Generation Service automatically: the unix socket - up /run/tdx-qgs/qgs.socket is used if it exists, otherwise vsock port 4050 - on the host (cid 2). If the QGS is listening on neither channel, the guest's quote requests will - fail. + command line into RTMR 2. Credentials passed via or + are delivered through SMBIOS Type 11 OEM strings, which the + firmware (TDVF) measures into RTMR 0. This channel is measured but not confidential with respect + to the host or VMM, since the host assembles the SMBIOS table. A vTPM, if attached via + , must be treated as untrusted by the guest. To obtain TD Quotes for + remote attestation, the guest is wired to the host's local TDX Quote Generation Service + automatically: the unix socket up /run/tdx-qgs/qgs.socket is used if it + exists, otherwise vsock port 4050 on the host (cid 2). If the QGS is listening on neither + channel, the guest's quote requests will fail. diff --git a/src/core/import-creds.c b/src/core/import-creds.c index 98675d2fc52..b8db23b85a4 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -622,7 +622,14 @@ static int import_credentials_smbios(ImportCredentialsContext *c) { if (detect_container() > 0) /* don't access /sys/ in a container */ return 0; - if (detect_confidential_virtualization() > 0) /* don't trust firmware if confidential VMs */ + /* SMBIOS OEM strings are host-controlled, so by default we don't trust them in a confidential VM. + * Exception: under Intel TDX the SMBIOS table (including Type 11) is measured into RTMR0 by the + * firmware (TDVF's SmbiosMeasurementDxe) on direct boot, and additionally by systemd-stub on UKI + * boot, so a remote verifier can detect host tampering with these credentials. As with the kernel + * command line (also host-influenced but measured), we can't and needn't locally verify the + * measurement actually happened; the verifier anchors the firmware's behaviour via MRTD. */ + ConfidentialVirtualization cv = detect_confidential_virtualization(); + if (cv > 0 && cv != CONFIDENTIAL_VIRTUALIZATION_TDX) return 0; for (unsigned i = 0;; i++) { diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 2c35fef0320..00df10874ca 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -3579,7 +3579,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { * confidential VMs. Instead, package credentials into a cpio archive appended to the initrd * (mirroring what systemd-stub does for ESP credentials) so they enter the launch measurement * via QEMU's "kernel-hashes=on". The new initrd path requires a guest PID1 that knows about - * /.extra/system_credentials/, so we keep this scoped to SNP for now. Non-CoCo guests + * /.extra/system_credentials/, so we keep this scoped to SNP for now. Non-SNP guests * continue to use the SMBIOS path below, which works with older systemd versions too. * Must run after all credential-mutating calls above so the cpio captures the complete set. */ bool use_initrd_cpio = arg_confidential_computing == COCO_AMD_SEV_SNP && -- 2.47.3