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 <katexochen0@gmail.com>
This commit is contained in:
Paul Meyer
2026-07-01 16:09:27 +02:00
parent d17873f98f
commit 4dd73ad924
3 changed files with 18 additions and 8 deletions
+9 -6
View File
@@ -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 <command>systemd-stub</command> 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 <option>--tpm=</option>, 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 <filename>/run/tdx-qgs/qgs.socket</filename> 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 <option>--set-credential=</option> or
<option>--load-credential=</option> 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
<option>--tpm=</option>, 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 <filename>/run/tdx-qgs/qgs.socket</filename> 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.
<xi:include href="version-info.xml" xpointer="v262"/></para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
+8 -1
View File
@@ -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++) {
+1 -1
View File
@@ -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 &&