mbox series

[v8,0/4] Allow guest access to EFI confidential computing secret area

Message ID 20220228114254.1099945-1-dovmurik@linux.ibm.com
Headers show
Series Allow guest access to EFI confidential computing secret area | expand

Message

Dov Murik Feb. 28, 2022, 11:42 a.m. UTC
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 exposes the secrets to userspace via securityfs
using a new efi_secret kernel module.  The module is autoloaded (by the
EFI driver) if the secret area is populated.

The first patch in EFI keeps the address of the secret area as passed in
the EFI configuration table.  The second 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 third patch auto-loads the efi_secret module during
startup if the injected secrets area is populated.  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:

# ls -la /sys/kernel/security/secrets/coco
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

# hd /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
00000000  74 68 65 73 65 2d 61 72  65 2d 74 68 65 2d 6b 61  |these-are-the-ka|
00000010  74 61 2d 73 65 63 72 65  74 73 00 01 02 03 04 05  |ta-secrets......|
00000020  06 07                                             |..|
00000022

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

# ls -la /sys/kernel/security/secrets/coco
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


---

v8 changes:
 - Change path of filesystem to <securityfs>/secrets/coco and fix the
   documentation accordingly (Thanks Gerd, Matthew)
 - Remove patch 2/5 (of v7) because the latest OVMF release (edk2-stable202202)
   already contains the fix to mark the launch secret page as EFI_RESERVED_TYPE.

v7: https://lore.kernel.org/linux-coco/20220201124413.1093099-1-dovmurik@linux.ibm.com/
v7 changes:
 - Improve description of efi_secret module in Kconfig.
 - Fix sparse warnings on pointer address space mismatch
   (Reported-by: kernel test robot <lkp@intel.com>)

v6: https://lore.kernel.org/linux-coco/20211129114251.3741721-1-dovmurik@linux.ibm.com/
v6 changes:
 - Autoload the efi_secret module if the secret area is populated
   (thanks Greg KH).
 - efi_secret: Depend on X86_64 because we use ioremap_encrypted() which
   is only defined for this arch.
 - efi_secret.c: Remove unneeded tableheader_guid local variable.
 - Documentation fixes.

v5: https://lore.kernel.org/linux-coco/20211118113359.642571-1-dovmurik@linux.ibm.com/
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/20211020061408.3447533-1-dovmurik@linux.ibm.com/
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 <lkp@intel.com>)

v3: https://lore.kernel.org/linux-coco/20211014130848.592611-1-dovmurik@linux.ibm.com/
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/20211007061838.1381129-1-dovmurik@linux.ibm.com
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/20210809190157.279332-1-dovmurik@linux.ibm.com/

RFC: https://lore.kernel.org/linux-coco/20210628183431.953934-1-dovmurik@linux.ibm.com/


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

 Documentation/ABI/testing/securityfs-secrets-coco |  51 +++
 Documentation/security/index.rst                  |   1 +
 Documentation/security/secrets/coco.rst           | 103 ++++++
 Documentation/security/secrets/index.rst          |   9 +
 arch/x86/platform/efi/efi.c                       |   3 +
 drivers/firmware/efi/Kconfig                      |  16 +
 drivers/firmware/efi/Makefile                     |   1 +
 drivers/firmware/efi/coco.c                       |  58 ++++
 drivers/firmware/efi/efi.c                        |   6 +
 drivers/virt/Kconfig                              |   3 +
 drivers/virt/Makefile                             |   1 +
 drivers/virt/coco/efi_secret/Kconfig              |  19 ++
 drivers/virt/coco/efi_secret/Makefile             |   2 +
 drivers/virt/coco/efi_secret/efi_secret.c         | 337 ++++++++++++++++++++
 include/linux/efi.h                               |  10 +
 15 files changed, 620 insertions(+)
 create mode 100644 Documentation/ABI/testing/securityfs-secrets-coco
 create mode 100644 Documentation/security/secrets/coco.rst
 create mode 100644 Documentation/security/secrets/index.rst
 create mode 100644 drivers/firmware/efi/coco.c
 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: 7e57714cd0ad2d5bb90e50b5096a0e671dec1ef3

Comments

Borislav Petkov March 24, 2022, 4:33 p.m. UTC | #1
On Mon, Feb 28, 2022 at 11:42:50AM +0000, Dov Murik wrote:
> 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 exposes the secrets to userspace via securityfs
> using a new efi_secret kernel module.  The module is autoloaded (by the
> EFI driver) if the secret area is populated.

Right, so this thing.

Tom and I were talking about SEV* guest debugging today and I believe
there might be another use case for this: SEV-ES guests cannot find out
from an attestation report - like SNP guests can - whether they're being
debugged or not so it would be very helpful if the fact that a -ES guest
is being debugged, could be supplied through such a secrets blob.

Because then, when I'm singlestepping the guest with gdb over the
gdbstub, the guest could determine based on those guest-owner previously
injected secrets whether it should allow debugging or not.

And this is where your set comes in.

However, I'm wondering if - instead of defining your own secrets structs
etc - you could use the SNP confidential computing blob machinery the
SNP set is adding. In particular:

https://lore.kernel.org/all/20220307213356.2797205-30-brijesh.singh@amd.com/

And you're adding another GUID but maybe you could simply use the SNP
thing called EFI_CC_BLOB_GUID and mimick that layout.

That should unify things more. And then guest kernel code could query
the blob also for debugging policy and so on.

Thoughts, opinions?
Dov Murik March 29, 2022, 8:28 p.m. UTC | #2
On 29/03/2022 21:30, Borislav Petkov wrote:

> 
> So now that I think of it, it would be even nicer if the fact whether
> guest debugging is allowed, were available to the guest *very early*
> during boot. Because I think the most important cases where you'd want
> to singlestep a SEV* guest with the qemu gdbstub is early guest kernel
> boot code. So it would be cool if we'd have access to the debugging
> setting that early.
> 
> Lemme have a look at your patches in detail to get an idea what's
> happening there.

Is efi_config_parse_tables() early enough?  That's where we learn for
the first time that the firmware has a launch-secrets area that we can
look at.

We can add there (say, next to the call to efi_tpm_eventlog_init()) a
code to:

1. map the secret area (ioremap_encrypted())
2. parse the table, look for the "sev debug enabled" GUID.
3. set the value of the kernel variable that we can later use anywhere.


Of course Ard might know about a better mechanism or place to do that.


-Dov
Borislav Petkov March 31, 2022, 9:19 a.m. UTC | #3
On Wed, Mar 30, 2022 at 09:11:54AM +0300, Dov Murik wrote:
> If that's the case, we don't need a secure channel and secret injection.
> You can use a simple "sev=debug" (or whatever) in the kernel
> command-line to indicate your needs.

Yeah, that would work for a normal SEV guest.

However, if it is an -ES guest, you need to somehow tell it as the guest
owner: "hey you're being debugged and that's fine."

Because if you want to singlestep the thing, you're going to land in
the #VC handler and destroy registers so you want to save them first if
you're being debugged and then shovel them out to the host somehow. And
that's another question but first things first.

And "if you're being debugged" needs to be somehow told the guest
through a secure channel so that the HV doesn't go and simply enable
debugging by booting with "sev=debug" and bypass it all.

And SNP has access to the policy in the attestation report, says Tom, so
that's possible there.

So we need a way to add the debugging aspect to the measurement and be
able to recreate that measurement quickly so that a simple debugging
session of a kernel in a guest can work pretty much the same with a SEV*
guest.

I'm still digging the details tho...
Dov Murik March 31, 2022, 9:05 p.m. UTC | #4
On 31/03/2022 12:19, Borislav Petkov wrote:
> On Wed, Mar 30, 2022 at 09:11:54AM +0300, Dov Murik wrote:
>> If that's the case, we don't need a secure channel and secret injection.
>> You can use a simple "sev=debug" (or whatever) in the kernel
>> command-line to indicate your needs.
> 
> Yeah, that would work for a normal SEV guest.
> 
> However, if it is an -ES guest, you need to somehow tell it as the guest
> owner: "hey you're being debugged and that's fine."
> 
> Because if you want to singlestep the thing, you're going to land in
> the #VC handler and destroy registers so you want to save them first if
> you're being debugged and then shovel them out to the host somehow. And
> that's another question but first things first.
> 
> And "if you're being debugged" needs to be somehow told the guest
> through a secure channel so that the HV doesn't go and simply enable
> debugging by booting with "sev=debug" and bypass it all.
> 

Note that the HV can also start the VM with SEV completely turned off.
Similarly, it can enable debugging and "fool" the guest.  Of course all
this tricks will affect the measurement, and then the Guest Owner will
know that something is wrong and won't inject the secrets.  If you don't
rely on secret injection anyway, then I think a kernel command-line
param is good enough.  (I might be missing a scenario though)


Maybe you can use KVM_SEV_GET_ATTESTATION_REPORT (ask the host to do it
for you).  But I think it returns only the launch digest, and you can't
figure out the SEV Policy field from it.



> And SNP has access to the policy in the attestation report, says Tom, so
> that's possible there.

True. But not in really early boot? This is all in the sev-guest
platform driver.


> 
> So we need a way to add the debugging aspect to the measurement and be
> able to recreate that measurement quickly so that a simple debugging
> session of a kernel in a guest can work pretty much the same with a SEV*
> guest.
> 
> I'm still digging the details tho...
>