public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Laszlo Ersek" <lersek@redhat.com>
To: devel@edk2.groups.io, ard.biesheuvel@arm.com
Cc: "Philippe Mathieu-Daudé" <philmd@redhat.com>
Subject: Re: [edk2-devel] [PATCH v3 14/14] OvmfPkg: use generic QEMU image loader for secure boot enabled builds
Date: Wed, 10 Jun 2020 11:22:49 +0200	[thread overview]
Message-ID: <7d7d1cbc-d1ca-c3dd-c2ac-00084d79decf@redhat.com> (raw)
In-Reply-To: <dc97b28e-677d-2f1b-79d7-a9ef6fcb3648@arm.com>

On 06/09/20 12:45, Ard Biesheuvel wrote:
> On 6/9/20 11:51 AM, Laszlo Ersek via groups.io wrote:
>> Hi Ard,
>>
>> On 03/05/20 14:46, Ard Biesheuvel wrote:
>>> The QemuLoadImageLib implementation we currently use for all OVMF
>>> builds copies the behavior of the QEMU loader code that precedes it,
>>> which is to disregard UEFI secure boot policies entirely when it comes
>>> to loading kernel images that have been specified on the QEMU command
>>> line. This behavior deviates from ArmVirtQemu based builds, which do
>>> take UEFI secure boot policies into account, and refuse to load images
>>> from the command line that cannot be authenticated.
>>>
>>> The disparity was originally due to the fact that the QEMU command line
>>> kernel loader did not use LoadImage and StartImage at all, but this
>>> changed recently, and now, there are only a couple of reasons left to
>>> stick with the legacy loader:
>>> - it permits loading images that lack a valid PE/COFF header,
>>> - it permits loading X64 kernels on IA32 firmware running on a X64
>>>    capable system.
>>>
>>> Since every non-authentic PE/COFF image can trivially be converted into
>>> an image that lacks a valid PE/COFF header, the former case can simply
>>> not be supported in a UEFI secure boot context. The latter case is
>>> highly
>>> theoretical, given that one could easily switch to native X64
>>> firmware in
>>> a VM scenario.
>>>
>>> That leaves us with little justification to use the legacy loader at all
>>> when UEFI secure boot policies are in effect, so let's switch to the
>>> generic loader for UEFI secure boot enabled builds.
>>>
>>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
>>> ---
>>>   OvmfPkg/OvmfPkgIa32.dsc    | 4 ++++
>>>   OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++
>>>   OvmfPkg/OvmfPkgX64.dsc     | 4 ++++
>>>   3 files changed, 12 insertions(+)
>>>
>>> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
>>> index ec21d2f3f6cb..8916255df4df 100644
>>> --- a/OvmfPkg/OvmfPkgIa32.dsc
>>> +++ b/OvmfPkg/OvmfPkgIa32.dsc
>>> @@ -363,7 +363,11 @@ [LibraryClasses.common.DXE_DRIVER]
>>>     PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>>>     MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>>>    
>>> QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
>>> +!if $(SECURE_BOOT_ENABLE) == TRUE
>>> + 
>>> QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
>>>
>>> +!else
>>>    
>>> QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>>>
>>> +!endif
>>>   !if $(TPM_ENABLE) == TRUE
>>>    
>>> Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf
>>>
>>>    
>>> Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>>>
>>> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
>>> index 3c80a18c6086..342ff96cc279 100644
>>> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
>>> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
>>> @@ -367,7 +367,11 @@ [LibraryClasses.common.DXE_DRIVER]
>>>     PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>>>     MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>>>    
>>> QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
>>> +!if $(SECURE_BOOT_ENABLE) == TRUE
>>> + 
>>> QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
>>>
>>> +!else
>>>    
>>> QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>>>
>>> +!endif
>>>   !if $(TPM_ENABLE) == TRUE
>>>    
>>> Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf
>>>
>>>    
>>> Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>>>
>>> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
>>> index 63f1f935f4f3..1fb2de5e0121 100644
>>> --- a/OvmfPkg/OvmfPkgX64.dsc
>>> +++ b/OvmfPkg/OvmfPkgX64.dsc
>>> @@ -367,7 +367,11 @@ [LibraryClasses.common.DXE_DRIVER]
>>>     PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>>>     MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>>>    
>>> QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
>>> +!if $(SECURE_BOOT_ENABLE) == TRUE
>>> + 
>>> QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
>>>
>>> +!else
>>>    
>>> QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>>>
>>> +!endif
>>>   !if $(TPM_ENABLE) == TRUE
>>>    
>>> Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf
>>>
>>>    
>>> Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>>>
>>>
>>
>> this patch (commit ced77332cab6) breaks the following use case (on X64):
>>
>> (1) we're defining (installing) a new libvirt domain with
>>
>>    virt-install --location NETWORK-URL
>>
>> where NETWORK-URL identifies a remote (e.g. https) "exploded" Linux
>> distro tree,
>>
>> (2) in the variable store template file, from which the new domain will
>> have its variable store instantiated, the Microsoft certs had been
>> enrolled (by running EnrollDefaultKeys),
>>
>> (3) the guest kernel ("vmlinuz") downloaded in step (1) is only signed
>> by the distro vendor, not by Microsoft.
>>
>> When installing a distro with "virt-install" from a UEFI-bootable ISO
>> image (exposed to the guest e.g. via virtio-scsi), that is, not from a
>> remote "exploded" OS tree like above, then "shim" is booted first. Shim
>> is accepted by an MS certificate. Then shim/grub boot the guest kernel,
>> and thanks to shim, the vendor signature on that kernel is accepted.
>> This is why "shim" exists.
>>
>> However, when installing the guest with "virt-install --location
>> NETWORK-URL", then virt-install first downloads the broken-out vmlinuz
>> and initrd files from the remote (e.g. https) OS tree, to local
>> temporary files, and launches the guest (for installation) with a direct
>> (fw_cfg) kernel boot. Because "vmlinuz" is only signed by the OS vendor,
>> and "shim" is not used at all, and the varstore only has the MS certs,
>> the guest (installer) now fails to boot.
>>
>> This used to work before, and there's an argument to be made that it
>> wasn't insecure. Because -- as I understand it anyway --, the usage is
>> not unlike "audit mode / deployed mode":
>>
>> - When you boot the guest for the very first time, you make the guest
>> firmware blindly trust the guest kernel, in effect -- because, you are
>> providing the kernel from a source whose trustworthiness is guaranteed
>> by other means (for example, you pass such an https NETWORK-URL to
>> virt-install on the host side that you trust).
>>
>> - Once the guest is installed, and either rebooted from within, or shut
>> down altogether and relaunched, then the fw-cfg "bypass" won't happen
>> again. It's also not needed any more, because with the installation
>> completed, "shim" has also become part of the boot process (from the
>> virtual disk).
>>
>> Do you have any ideas to make this scenario work again? Or else, can we
>> predicate this patch for the DSC files on a build flag that's different
>> from SECURE_BOOT_ENABLE?
>>
>> (Put more formally: right now, the EFI_SECURITY_VIOLATION branch, and
>> the EFI_ACCESS_DENIED branch that I posted for TianoCore#2785 earlier,
>> in X86QemuLoadImageLib, are dead code. Because, those conditions are
>> only possible when SECURE_BOOT_ENABLE is defined -- but in that case, we
>> (currently) don't use X86QemuLoadImageLib at all.)
>>
> 
> Hmm, I did wonder about that and then forgot again ...
> 
>> I do see that the *exact* virt-install scenario above has never worked
>> in ArmVirtQemu / AARCH64. (Given that ArmVirtQemu has always used
>> gBS->LoadImage() for fw_cfg kernel boot.) However, to us (RH) anyway,
>> that difference between AARCH64 and X64 isn't a practical one. That's
>> because we have never set SECURE_BOOT_ENABLE for ArmVirtQemu in the
>> first place, due to not having an SMM-like pflash protection on AARCH64.
>>
>> ... Unfortunately, when I reviewed this patch, I failed to recall that
>> "virt-install --location NETWORK-URL" relied on fw_cfg kernel boot. And
>> I failed to realize that, by extension, virt-install relied on fw_cfg
>> kernel boot bypassing Secure Boot. :(
>>
> 
> I would argue that the cleanest way to deal with this is to introduce
> something like
> 
> gEfiSecurityPkgTokenSpaceGuid.PcdHttpsLoadedImageVerificationPolicy
> 
> so that you can mark HTTPS loaded images as trusted. In my opinion, a
> HTTPS loaded image is sufficiently different from the other classes of
> images that it warrants a separate policy.
> 
> But I suppose this could be a difficult sell, and it will take more time
> than you are willing to spend on it?

Primarily, I wouldn't suggest including "https" in the name of the PCD,
as "https" is just an example above. If NETWORK-URL is in the "http"
(not "https"), or even "ftp" scheme, but is served from localhost (for
example), it could be considered secure.

More generally, the guest firmware need not know where the kernel/initrd
over fw_cfg originate from, only that they should be trusted.

The PCD idea looks nice to me; I'd just make it an OvmfPkg PCD. Options:

(1) make it a FeaturePCD, and introduce a new build flag (-D) for
controlling it

(2) make it a FeaturePCD, and do not introduce a new flag (-D), instead,
let users rely on the already available "--pcd" option of "build" to
control it

(3) make it a dynamic boolean PCD, and use the recently introduced
fw_cfg control interface


In all three options, the OVMF DSC files would unconditionally use the
X86QemuLoadImageLib instance, we'd just predicate the fallback to the
legacy Linux/x86 boot protocol on:

  SecureBoot disabled || FallbackPcd

(where "SecureBoot disabled" means that the SecureBoot standard UEFI
variable doesn't exist, or has zero value).

I think option (2) means the least churn, and it would be "good enough"
functionally.

Does this look palatable to you?


> So what is the point of DEFER_EXECUTE_ON_SECURITY_VIOLATION anyway, if
> you cannot start the image anyway?

Honestly: I'm not sure!

> Because I would prefer it if we could
> start the image anyway, Unfortunately, the only way seems to be

Right; it seems like the EFI_SECURITY_VIOLATION status would get saved
in the private LoadImageStatus field, and that would cause StartImage()
to fail early as well. I couldn't find a spot to "re-set" LoadImageStatus.

> to use
> the loaded image protocol data (base and size in memory) and do another
> LoadImage() after adding the hash to "db", and this is also more
> elaborate than I would like.

Agreed. We shouldn't mess with "db".

> Of course, the *correct* fix is to add the vendor cert to db - you are
> loading a signed image from a known source, and this whole dance with
> shim and the MS certs is ridiculous, and I am still hoping we can avoid
> this mess on ARM

I considered this. It didn't look feasible to me at first sight (how
many vendors?), but now that you mention it and I've looked at it more
closely, it does look feasible. Selfishly (!), if I look at the guest OS
types officially supported on RHEL8 hosts:

- https://access.redhat.com/articles/973133
- https://access.redhat.com/articles/973163

then all we (RH) would need are two patches for (upstream)
OvmfPkg/EnrollDefaultKeys. Namely, we should enroll two more DB entries:

- SUSE's CA certificate to which the key(s) chain that they use for
signing their kernels

- Red Hat's CA certificate to which the key(s) chain that we use for
signing our kernels.

That would strictly be an improvement even for upstream OVMF (more
vendors accepted).

Ultimately I think the right way is to pick option (3) -- dynamic PCD --
*and* add the SUSE and RH CA certs to OvmfPkg/EnrollDefaultKeys. That
means SUSE and RH signed kernels can be booted at once, with secure
verification, and users would still have a QEMU command line option for
opting *into* the legacy boot protocol fallback.

Do you agree? Would you be OK with patches to this effect?

(I think I'd post the RH CA cert patch after some internal discussions,
and CC Gary so that he could follow suit with their own CA cert.)

Thanks!
Laszlo


  reply	other threads:[~2020-06-10  9:23 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-05 13:45 [PATCH v3 00/14] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 01/14] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 02/14] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 03/14] OvmfPkg: introduce QemuLoadImageLib library class Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 04/14] OvmfPkg: provide a generic implementation of QemuLoadImageLib Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 05/14] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Ard Biesheuvel
2020-03-05 13:45 ` [PATCH v3 06/14] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Ard Biesheuvel
2020-03-05 13:46 ` [PATCH v3 07/14] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line Ard Biesheuvel
2020-03-05 13:46 ` [PATCH v3 08/14] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block Ard Biesheuvel
2020-03-05 13:46 ` [PATCH v3 09/14] OvmfPkg: create protocol and GUID header for loaded x86 Linux kernels Ard Biesheuvel
2020-03-05 16:01   ` [edk2-devel] " Laszlo Ersek
2020-03-05 13:46 ` [PATCH v3 10/14] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Ard Biesheuvel
2020-03-05 18:03   ` [edk2-devel] " Laszlo Ersek
2020-03-05 13:46 ` [PATCH v3 11/14] OvmfPkg: add new QEMU kernel image loader components Ard Biesheuvel
2020-03-05 13:46 ` [PATCH v3 12/14] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Ard Biesheuvel
2020-03-05 21:15   ` [edk2-devel] " Laszlo Ersek
2020-03-05 21:20     ` Ard Biesheuvel
2020-03-05 23:42       ` Laszlo Ersek
2020-03-05 13:46 ` [PATCH v3 13/14] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Ard Biesheuvel
2020-03-05 13:46 ` [PATCH v3 14/14] OvmfPkg: use generic QEMU image loader for secure boot enabled builds Ard Biesheuvel
2020-06-09  9:51   ` [edk2-devel] " Laszlo Ersek
2020-06-09 10:45     ` Ard Biesheuvel
2020-06-10  9:22       ` Laszlo Ersek [this message]
2020-06-10  9:32         ` Ard Biesheuvel
2020-06-11 14:55           ` Laszlo Ersek
2020-06-11 15:05             ` Ard Biesheuvel
2020-06-11 18:13               ` Laszlo Ersek
2020-06-11 19:07                 ` Ard Biesheuvel
2020-03-06  2:01 ` [edk2-devel] [PATCH v3 00/14] Ovmf: use LoadImage/StartImage for loading command line images Bob Feng
2020-03-06  7:42   ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7d7d1cbc-d1ca-c3dd-c2ac-00084d79decf@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox