2021-11-18 11:35:09

by Dov Murik

[permalink] [raw]
Subject: [PATCH v5 0/4] Allow guest access to EFI confidential computing secret area

Confidential computing (coco) hardware such as AMD SEV (Secure Encrypted
Virtualization) allows guest owners to inject secrets into the VMs
memory without the host/hypervisor being able to read them. In SEV,
secret injection is performed early in the VM launch process, before the
guest starts running.

OVMF already reserves designated area for secret injection (in its
AmdSev package; see edk2 commit 01726b6d23d4 "OvmfPkg/AmdSev: Expose the
Sev Secret area using a configuration table" [1]), but the secrets were
not available in the guest kernel.

The patch series keeps the address of the EFI-provided memory for
injected secrets, and optionally exposes the secrets to userspace via
securityfs using a new efi_secret kernel module.

The first patch in EFI keeps the address of the secret area as passed in
the EFI configuration table. The second patch is a quirk fix for older
firmwares didn't mark the secrets page as EFI_RESERVED_TYPE. The third
patch introduces the new efi_secret module that exposes the content of
the secret entries as securityfs files, and allows clearing out secrets
with a file unlink interface. The last patch documents the data flow of
confidential computing secret injection.

As a usage example, consider a guest performing computations on
encrypted files. The Guest Owner provides the decryption key (= secret)
using the secret injection mechanism. The guest application reads the
secret from the efi_secret filesystem and proceeds to decrypt the files
into memory and then performs the needed computations on the content.

In this example, the host can't read the files from the disk image
because they are encrypted. Host can't read the decryption key because
it is passed using the secret injection mechanism (= secure channel).
Host can't read the decrypted content from memory because it's a
confidential (memory-encrypted) guest.

This has been tested with AMD SEV and SEV-ES guests, but the kernel side
of handling the secret area has no SEV-specific dependencies, and
therefore might be usable (perhaps with minor changes) for any
confidential computing hardware that can publish the secret area via the
standard EFI config table entry.

To enable this functionality, set CONFIG_EFI_SECRET=m when building the
guest kernel.

Here is a simple example for usage of the efi_secret module in a guest
to which an EFI secret area with 4 secrets was injected during launch:

# modprobe efi_secret
# ls -la /sys/kernel/security/coco/efi_secret
total 0
drwxr-xr-x 2 root root 0 Jun 28 11:54 .
drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
-r--r----- 1 root root 0 Jun 28 11:54 e6f5a162-d67f-4750-a67c-5d065f2a9910

# xxd /sys/kernel/security/coco/efi_secret/e6f5a162-d67f-4750-a67c-5d065f2a9910
00000000: 7468 6573 652d 6172 652d 7468 652d 6b61 these-are-the-ka
00000010: 7461 2d73 6563 7265 7473 0001 0203 0405 ta-secrets......
00000020: 0607 ..

# rm /sys/kernel/security/coco/efi_secret/e6f5a162-d67f-4750-a67c-5d065f2a9910

# ls -la /sys/kernel/security/coco/efi_secret
total 0
drwxr-xr-x 2 root root 0 Jun 28 11:55 .
drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2


[1] https://github.com/tianocore/edk2/commit/01726b6d23d4

---

v5 changes:
- Simplify EFI code: instead of copying the secret area, the firmware
marks the secret area as EFI_RESERVED_TYPE, and then the uefi_init()
code just keeps the pointer as it appears in the EFI configuration
table. The use of reserved pages is similar to the AMD SEV-SNP
patches for handling SNP-Secrets and SNP-CPUID pages.
- In order to handle OVMF releases out there which mark the
confidential computing secrets page as EFI_BOOT_SERVICES_DATA, add
efi/libstub code that detects this and fixes the E820 map to reserve
this page.
- In the efi_secret module code, map the secrets page using
ioremap_encrypted (again, similar to the AMD SEV-SNP guest patches
for accessing SNP-Secrets and SNP-CPUID pages).
- Add documentation in Documentation/security/coco/efi_secret.

v4: https://lore.kernel.org/linux-coco/[email protected]/
v4 changes:
- Guard all the new EFI and efi-stub code (patches 1+2) with #ifdef
CONFIG_EFI_COCO_SECRET (thanks Greg KH). Selecting
CONFIG_EFI_SECRET=m (patch 3) will enable the EFI parts as well.
- Guard call to clflush_cache_range() with #ifdef CONFIG_X86
(Reported-by: kernel test robot <[email protected]>)

v3: https://lore.kernel.org/linux-coco/[email protected]/
v3 changes:
- Rename the module to efi_secret
- Remove the exporting of clean_cache_range
- Use clflush_cache_range in wipe_memory
- Document function wipe_memory
- Initialize efi.coco_secret to EFI_INVALID_TABLE_ADDR to correctly detect
when there's no secret area published in the EFI configuration tables

v2: https://lore.kernel.org/linux-coco/[email protected]
v2 changes:
- Export clean_cache_range()
- When deleteing a secret, call clean_cache_range() after explicit_memzero
- Add Documentation/ABI/testing/securityfs-coco-sev_secret

v1: https://lore.kernel.org/linux-coco/[email protected]/

RFC: https://lore.kernel.org/linux-coco/[email protected]/




Dov Murik (4):
efi: Save location of EFI confidential computing area
efi/libstub: Reserve confidential computing secret area
virt: Add efi_secret module to expose confidential computing secrets
docs: security: Add coco/efi_secret documentation

.../ABI/testing/securityfs-coco-efi_secret | 50 +++
Documentation/security/coco/efi_secret.rst | 103 ++++++
Documentation/security/coco/index.rst | 9 +
Documentation/security/index.rst | 1 +
arch/x86/platform/efi/efi.c | 3 +
drivers/firmware/efi/Kconfig | 16 +
drivers/firmware/efi/efi.c | 6 +
drivers/firmware/efi/libstub/x86-stub.c | 28 ++
drivers/virt/Kconfig | 3 +
drivers/virt/Makefile | 1 +
drivers/virt/coco/efi_secret/Kconfig | 11 +
drivers/virt/coco/efi_secret/Makefile | 2 +
drivers/virt/coco/efi_secret/efi_secret.c | 341 ++++++++++++++++++
include/linux/efi.h | 7 +
14 files changed, 581 insertions(+)
create mode 100644 Documentation/ABI/testing/securityfs-coco-efi_secret
create mode 100644 Documentation/security/coco/efi_secret.rst
create mode 100644 Documentation/security/coco/index.rst
create mode 100644 drivers/virt/coco/efi_secret/Kconfig
create mode 100644 drivers/virt/coco/efi_secret/Makefile
create mode 100644 drivers/virt/coco/efi_secret/efi_secret.c


base-commit: 42eb8fdac2fc5d62392dcfcf0253753e821a97b0
--
2.25.1



2021-11-18 11:36:27

by Dov Murik

[permalink] [raw]
Subject: [PATCH v5 4/4] docs: security: Add coco/efi_secret documentation

Add documentation for the efi_secret module which allows access
to Confidential Computing injected secrets.

Signed-off-by: Dov Murik <[email protected]>
---
Documentation/security/coco/efi_secret.rst | 103 +++++++++++++++++++++
Documentation/security/coco/index.rst | 9 ++
Documentation/security/index.rst | 1 +
3 files changed, 113 insertions(+)
create mode 100644 Documentation/security/coco/efi_secret.rst
create mode 100644 Documentation/security/coco/index.rst

diff --git a/Documentation/security/coco/efi_secret.rst b/Documentation/security/coco/efi_secret.rst
new file mode 100644
index 000000000000..bb7acae22e90
--- /dev/null
+++ b/Documentation/security/coco/efi_secret.rst
@@ -0,0 +1,103 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========
+efi_secret
+==========
+
+This document describes how Confidential Computing secret injection is handled
+from the firmware to the operating system.
+
+
+Introduction
+============
+
+Confidential Computing (coco) hardware such as AMD SEV (Secure Encrypted
+Virtualization) allows guest owners to inject secrets into the VMs
+memory without the host/hypervisor being able to read them. In SEV,
+secret injection is performed early in the VM launch process, before the
+guest starts running.
+
+The efi_secret kernel module allows userspace applications to access these
+secrets via securityfs.
+
+
+Secret data flow
+================
+
+The guest firmware may reserve a designated memory area for secret injection,
+and publish its location (base GPA and length) in the EFI configuration table
+under a ``LINUX_EFI_COCO_SECRET_AREA_GUID`` entry
+(``adf956ad-e98c-484c-ae11-b51c7d336447``). This memory area should be marked
+by the firmware as ``EFI_RESERVED_TYPE``, and therefore the kernel should not
+be use it for its own purposes.
+
+During the VM's launch, the virtual machine manager may inject a secret to that
+area. In AMD SEV and SEV-ES this is performed using the
+``KVM_SEV_LAUNCH_SECRET`` command (see [amd-mem-enc]_). The strucutre of the
+injected Guest Owner secret data should be a GUIDed table of secret values; the
+binary format is described in ``drivers/virt/coco/efi_secret/efi_secret.c``
+under "Structure of the EFI secret area".
+
+On kernel start, the kernel's EFI driver saves the location of the secret
+memory (taken from the EFI configuration table) in the ``efi.coco_secret``
+field.
+
+When a userspace application needs to access the secrets inside the guest VM,
+it loads the efi_secret kernel module (``CONFIG_EFI_SECRET=m``) which exposes
+the secrets via securityfs. The details of the efi_secret filesystem interface
+are in [efi-secret-abi]_.
+
+
+
+Application usage example
+=========================
+
+Consider a guest performing computations on encrypted files. The Guest Owner
+provides the decryption key (= secret) using the secret injection mechanism.
+The guest application reads the secret from the efi_secret filesystem and
+proceeds to decrypt the files into memory and then performs the needed
+computations on the content.
+
+In this example, the host can't read the files from the disk image
+because they are encrypted. Host can't read the decryption key because
+it is passed using the secret injection mechanism (= secure channel).
+Host can't read the decrypted content from memory because it's a
+confidential (memory-encrypted) guest.
+
+Here is a simple example for usage of the efi_secret module in a guest
+to which an EFI secret area with 4 secrets was injected during launch::
+
+ # modprobe efi_secret
+ # ls -la /sys/kernel/security/coco/efi_secret
+ total 0
+ drwxr-xr-x 2 root root 0 Jun 28 11:54 .
+ drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
+ -r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
+ -r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
+ -r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
+ -r--r----- 1 root root 0 Jun 28 11:54 e6f5a162-d67f-4750-a67c-5d065f2a9910
+
+ # xxd /sys/kernel/security/coco/efi_secret/e6f5a162-d67f-4750-a67c-5d065f2a9910
+ 00000000: 7468 6573 652d 6172 652d 7468 652d 6b61 these-are-the-ka
+ 00000010: 7461 2d73 6563 7265 7473 0001 0203 0405 ta-secrets......
+ 00000020: 0607 ..
+
+ # rm /sys/kernel/security/coco/efi_secret/e6f5a162-d67f-4750-a67c-5d065f2a9910
+
+ # ls -la /sys/kernel/security/coco/efi_secret
+ total 0
+ drwxr-xr-x 2 root root 0 Jun 28 11:55 .
+ drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
+ -r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
+ -r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
+ -r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
+
+
+References
+==========
+
+See [sev-api-spec]_ for more info regarding SEV ``LAUNCH_SECRET`` operation.
+
+.. [amd-mem-enc] :ref:`Documentation/virt/kvm/amd-memory-encryption <amdmemenc>`
+.. [efi-secret-abi] :ref:`Documentation/ABI/testing/securityfs-coco-efi_secret <efisecret>`
+.. [sev-api-spec] https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
diff --git a/Documentation/security/coco/index.rst b/Documentation/security/coco/index.rst
new file mode 100644
index 000000000000..56b803d4b33e
--- /dev/null
+++ b/Documentation/security/coco/index.rst
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
+Confidential Computing documentation
+====================================
+
+.. toctree::
+
+ efi_secret
diff --git a/Documentation/security/index.rst b/Documentation/security/index.rst
index 16335de04e8c..d4ef543825a7 100644
--- a/Documentation/security/index.rst
+++ b/Documentation/security/index.rst
@@ -17,3 +17,4 @@ Security Documentation
tpm/index
digsig
landlock
+ coco
--
2.25.1


2021-11-18 11:36:43

by Dov Murik

[permalink] [raw]
Subject: [PATCH v5 2/4] efi/libstub: Reserve confidential computing secret area

Some older firmware declare the confidential computing secret area as
EFI_BOOT_SERVICES_DATA region. Fix this up by treating this memory
region as EFI_RESERVED_TYPE, as it should be.

If that memory region is already EFI_RESERVED_TYPE then this has no
effect on the E820 map.

Signed-off-by: Dov Murik <[email protected]>
---
drivers/firmware/efi/libstub/x86-stub.c | 28 +++++++++++++++++++++++++
1 file changed, 28 insertions(+)

diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index f14c4ff5839f..dabfa33ae2b3 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -24,6 +24,7 @@
const efi_system_table_t *efi_system_table;
extern u32 image_offset;
static efi_loaded_image_t *image = NULL;
+static u64 efi_coco_secret_phys_addr = U64_MAX;

static efi_status_t
preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
@@ -443,6 +444,21 @@ static void add_e820ext(struct boot_params *params,
params->hdr.setup_data = (unsigned long)e820ext;
}

+#ifdef CONFIG_EFI_COCO_SECRET
+static void efi_set_coco_secret_phys_addr(void)
+{
+ struct linux_efi_coco_secret_area *secret_area =
+ get_efi_config_table(LINUX_EFI_COCO_SECRET_AREA_GUID);
+
+ if (!secret_area || secret_area->size == 0 || secret_area->size >= SZ_4G)
+ return;
+
+ efi_coco_secret_phys_addr = secret_area->base_pa;
+}
+#else
+static void efi_set_coco_secret_phys_addr(void) {}
+#endif
+
static efi_status_t
setup_e820(struct boot_params *params, struct setup_data *e820ext, u32 e820ext_size)
{
@@ -494,6 +510,16 @@ setup_e820(struct boot_params *params, struct setup_data *e820ext, u32 e820ext_s
e820_type = E820_TYPE_SOFT_RESERVED;
else
e820_type = E820_TYPE_RAM;
+#ifdef CONFIG_EFI_COCO_SECRET
+ if (d->phys_addr == efi_coco_secret_phys_addr)
+ /*
+ * Fix a quirk in firmwares which don't mark
+ * the EFI confidential computing area as
+ * EFI_RESERVED_TYPE, but instead as
+ * EFI_BOOT_SERVICES_DATA.
+ */
+ e820_type = E820_TYPE_RESERVED;
+#endif
break;

case EFI_ACPI_MEMORY_NVS:
@@ -793,6 +819,8 @@ unsigned long efi_main(efi_handle_t handle,

efi_retrieve_tpm2_eventlog();

+ efi_set_coco_secret_phys_addr();
+
setup_graphics(boot_params);

setup_efi_pci(boot_params);
--
2.25.1