diff options
author | Luca Boccassi <bluca@debian.org> | 2023-07-14 23:47:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-14 23:47:37 +0200 |
commit | d3634ac46b43794bbe7dbd30bbdda20086f72855 (patch) | |
tree | 7caa92e78127158f9ef89d9bd2f327f4e8b8648a | |
parent | compare: fix typo (diff) | |
parent | efi: don't pull kernel cmdline from SMBIOS in a confidential VM (diff) | |
download | systemd-d3634ac46b43794bbe7dbd30bbdda20086f72855.tar.xz systemd-d3634ac46b43794bbe7dbd30bbdda20086f72855.zip |
Merge pull request #28301 from berrange/cvm-lockdown
Avoid using SMBIOS for kernel cmdline injection in sd-stub in confidential VMs
-rw-r--r-- | src/basic/confidential-virt.c | 67 | ||||
-rw-r--r-- | src/boot/efi/stub.c | 26 | ||||
-rw-r--r-- | src/boot/efi/vmm.c | 117 | ||||
-rw-r--r-- | src/boot/efi/vmm.h | 2 | ||||
-rw-r--r-- | src/fundamental/confidential-virt-fundamental.h | 72 |
5 files changed, 207 insertions, 77 deletions
diff --git a/src/basic/confidential-virt.c b/src/basic/confidential-virt.c index e00576a713..b6521cf5bf 100644 --- a/src/basic/confidential-virt.c +++ b/src/basic/confidential-virt.c @@ -5,81 +5,16 @@ #endif #include <errno.h> #include <fcntl.h> -#include <stdint.h> #include <stdlib.h> #include <unistd.h> +#include "confidential-virt-fundamental.h" #include "confidential-virt.h" #include "fd-util.h" #include "missing_threads.h" #include "string-table.h" #include "utf8.h" -#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1) - -/* - * AMD64 Architecture Programmer’s Manual Volume 3: - * General-Purpose and System Instructions. - * Chapter: E4.1 - Maximum Extended Function Number and Vendor String - * https://www.amd.com/system/files/TechDocs/24594.pdf - */ -#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000) - -/* - * AMD64 Architecture Programmer’s Manual Volume 3: - * General-Purpose and System Instructions. - * Chapter: E4.17 - Encrypted Memory Capabilities - * https://www.amd.com/system/files/TechDocs/24594.pdf - */ -#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f) - -/* - * AMD64 Architecture Programmer’s Manual Volume 3: - * General-Purpose and System Instructions. - * Chapter: 15.34.10 - SEV_STATUS MSR - * https://www.amd.com/system/files/TechDocs/24593.pdf - */ -#define MSR_AMD64_SEV UINT32_C(0xc0010131) - -/* - * Intel® TDX Module v1.5 Base Architecture Specification - * Chapter: 11.2 - * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html - */ - -#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21) - -/* Requirements for Implementing the Microsoft Hypervisor Interface - * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs - */ -#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000) - -#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003) - -#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C) - -#define CPUID_HYPERV_MIN UINT32_C(0x40000005) -#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff) - -#define CPUID_SIG_AMD "AuthenticAMD" -#define CPUID_SIG_INTEL "GenuineIntel" -#define CPUID_SIG_INTEL_TDX "IntelTDX " -#define CPUID_SIG_HYPERV "Microsoft Hv" - -/* ecx bit 31: set => hyperpvisor, unset => bare metal */ -#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31) - -/* Linux include/asm-generic/hyperv-tlfs.h */ -#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */ -#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */ - -#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf) -#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2 - -#define EAX_SEV (UINT32_C(1) << 1) -#define MSR_SEV (UINT64_C(1) << 0) -#define MSR_SEV_ES (UINT64_C(1) << 1) -#define MSR_SEV_SNP (UINT64_C(1) << 2) #if defined(__x86_64__) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index fd4f668eb6..8fac41258e 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -489,17 +489,21 @@ static EFI_STATUS run(EFI_HANDLE image) { log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); - const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); - if (extra) { - _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra); - cmdline = xasprintf("%ls %ls", tmp, extra16); - - /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific - * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable - * measurements that are not under control of the machine owner. */ - m = false; - (void) tpm_log_load_options(extra16, &m); - parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); + /* SMBIOS OEM Strings data is controlled by the host admin and not covered + * by the VM attestation, so MUST NOT be trusted when in a confidential VM */ + if (!is_confidential_vm()) { + const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); + if (extra) { + _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra); + cmdline = xasprintf("%ls %ls", tmp, extra16); + + /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific + * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable + * measurements that are not under control of the machine owner. */ + m = false; + (void) tpm_log_load_options(extra16, &m); + parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); + } } export_variables(loaded_image); diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c index 951b4e3766..60e216d54c 100644 --- a/src/boot/efi/vmm.c +++ b/src/boot/efi/vmm.c @@ -4,6 +4,7 @@ # include <cpuid.h> #endif +#include "confidential-virt-fundamental.h" #include "device-path-util.h" #include "drivers.h" #include "efi-string.h" @@ -307,3 +308,119 @@ const char* smbios_find_oem_string(const char *name) { return NULL; } + +#if defined(__i386__) || defined(__x86_64__) +static uint32_t cpuid_leaf(uint32_t eax, char ret_sig[static 13], bool swapped) { + /* zero-init as some queries explicitly require subleaf == 0 */ + uint32_t sig[3] = {}; + + if (swapped) + __cpuid_count(eax, 0, eax, sig[0], sig[2], sig[1]); + else + __cpuid_count(eax, 0, eax, sig[0], sig[1], sig[2]); + + memcpy(ret_sig, sig, sizeof(sig)); + ret_sig[12] = 0; /* \0-terminate the string to make string comparison possible */ + + return eax; +} + +static uint64_t msr(uint32_t index) { + uint64_t val; +#ifdef __x86_64__ + uint32_t low, high; + asm volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(index) : "memory"); + val = ((uint64_t)high << 32) | low; +#else + asm volatile ("rdmsr" : "=A"(val) : "c"(index) : "memory"); +#endif + return val; +} + +static bool detect_hyperv_sev(void) { + uint32_t eax, ebx, ecx, edx, feat; + char sig[13] = {}; + + feat = cpuid_leaf(CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false); + + if (feat < CPUID_HYPERV_MIN || feat > CPUID_HYPERV_MAX) + return false; + + if (memcmp(sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0) + return false; + + __cpuid(CPUID_HYPERV_FEATURES, eax, ebx, ecx, edx); + + if (ebx & CPUID_HYPERV_ISOLATION && !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) { + __cpuid(CPUID_HYPERV_ISOLATION_CONFIG, eax, ebx, ecx, edx); + + if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) == CPUID_HYPERV_ISOLATION_TYPE_SNP) + return true; + } + + return false; +} + +static bool detect_sev(void) { + uint32_t eax, ebx, ecx, edx; + uint64_t msrval; + + __cpuid(CPUID_GET_HIGHEST_FUNCTION, eax, ebx, ecx, edx); + + if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES) + return false; + + __cpuid(CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES, eax, ebx, ecx, edx); + + /* bit 1 == CPU supports SEV feature + * + * Note, Azure blocks this CPUID leaf from its SEV-SNP + * guests, so we must fallback to trying some HyperV + * specific CPUID checks. + */ + if (!(eax & EAX_SEV)) + return detect_hyperv_sev(); + + msrval = msr(MSR_AMD64_SEV); + + if (msrval & (MSR_SEV_SNP | MSR_SEV_ES | MSR_SEV)) + return true; + + return false; +} + +static bool detect_tdx(void) { + uint32_t eax, ebx, ecx, edx; + char sig[13] = {}; + + __cpuid(CPUID_GET_HIGHEST_FUNCTION, eax, ebx, ecx, edx); + + if (eax < CPUID_INTEL_TDX_ENUMERATION) + return false; + + cpuid_leaf(CPUID_INTEL_TDX_ENUMERATION, sig, true); + + if (memcmp(sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0) + return true; + + return false; +} +#endif /* ! __i386__ && ! __x86_64__ */ + +bool is_confidential_vm(void) { +#if defined(__i386__) || defined(__x86_64__) + char sig[13] = {}; + + if (!cpuid_in_hypervisor()) + return false; + + cpuid_leaf(0, sig, true); + + if (memcmp(sig, CPUID_SIG_AMD, sizeof(sig)) == 0) + return detect_sev(); + if (memcmp(sig, CPUID_SIG_INTEL, sizeof(sig)) == 0) + return detect_tdx(); +#endif /* ! __i386__ && ! __x86_64__ */ + + return false; +} diff --git a/src/boot/efi/vmm.h b/src/boot/efi/vmm.h index 061ad692ec..df48af3779 100644 --- a/src/boot/efi/vmm.h +++ b/src/boot/efi/vmm.h @@ -8,4 +8,6 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir); bool in_hypervisor(void); +bool is_confidential_vm(void); + const char* smbios_find_oem_string(const char *name); diff --git a/src/fundamental/confidential-virt-fundamental.h b/src/fundamental/confidential-virt-fundamental.h new file mode 100644 index 0000000000..986923e1c2 --- /dev/null +++ b/src/fundamental/confidential-virt-fundamental.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <stdint.h> + +/* Keep CVM detection logic in this file at feature parity with + * that in src/efi/boot/vmm.c */ + +#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: E4.1 - Maximum Extended Function Number and Vendor String + * https://www.amd.com/system/files/TechDocs/24594.pdf + */ +#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: E4.17 - Encrypted Memory Capabilities + * https://www.amd.com/system/files/TechDocs/24594.pdf + */ +#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f) + +/* + * AMD64 Architecture Programmer’s Manual Volume 3: + * General-Purpose and System Instructions. + * Chapter: 15.34.10 - SEV_STATUS MSR + * https://www.amd.com/system/files/TechDocs/24593.pdf + */ +#define MSR_AMD64_SEV UINT32_C(0xc0010131) + +/* + * Intel® TDX Module v1.5 Base Architecture Specification + * Chapter: 11.2 + * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html + */ + +#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21) + +/* Requirements for Implementing the Microsoft Hypervisor Interface + * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs + */ +#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000) + +#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003) + +#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C) + +#define CPUID_HYPERV_MIN UINT32_C(0x40000005) +#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff) + +#define CPUID_SIG_AMD "AuthenticAMD" +#define CPUID_SIG_INTEL "GenuineIntel" +#define CPUID_SIG_INTEL_TDX "IntelTDX " +#define CPUID_SIG_HYPERV "Microsoft Hv" + +/* ecx bit 31: set => hyperpvisor, unset => bare metal */ +#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31) + +/* Linux include/asm-generic/hyperv-tlfs.h */ +#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */ +#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */ + +#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf) +#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2 + +#define EAX_SEV (UINT32_C(1) << 1) +#define MSR_SEV (UINT64_C(1) << 0) +#define MSR_SEV_ES (UINT64_C(1) << 1) +#define MSR_SEV_SNP (UINT64_C(1) << 2) |