RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1751993 - DxeImageVerificationLib handles "DENY execute on security violation" like "DEFER execute on security violation" [rhel8]
Summary: DxeImageVerificationLib handles "DENY execute on security violation" like "DE...
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: edk2
Version: 8.1
Hardware: Unspecified
OS: Unspecified
high
unspecified
Target Milestone: rc
: 8.2
Assignee: Laszlo Ersek
QA Contact: Xueqiang Wei
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2019-09-13 12:06 UTC by Laszlo Ersek
Modified: 2023-04-29 10:32 UTC (History)
8 users (show)

Fixed In Version: edk2-20190829git37eef91017ad-7.el8
Doc Type: If docs needed, set a value
Doc Text:
Clone Of: 1751990
Environment:
Last Closed: 2020-04-28 16:02:22 UTC
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Issue Tracker RHELPLAN-27335 0 None None None 2023-04-29 10:32:13 UTC
Red Hat Product Errata RHSA-2020:1712 0 None None None 2020-04-28 16:02:50 UTC
TianoCore 2129 0 None None None 2019-09-13 12:06:28 UTC

Description Laszlo Ersek 2019-09-13 12:06:29 UTC
+++ This bug was initially created as a clone of Bug #1751990 +++

* Description of problem:

- Launching a UEFI executable consists of two steps. First, the
  LoadImage() boot service is used to load the executable. Second, the
  StartImage() boot service is used to start the loaded executable.

- LoadImage() may return one of *two* error codes if Secure Boot
  verification fails for the executable being loaded: EFI_ACCESS_DENIED,
  or EFI_SECURITY_VIOLATION. The error code depends on firmware policy,
  as follows.

- If the firmware sets "*deny* execution on security violation", then
  EFI_ACCESS_DENIED is returned, and no allocated resources are retained
  for the image. In other words, the executable is never loaded.

- If the firmware sets "*defer* execution on security violation", then
  EFI_SECURITY_VIOLATION is returned, and the image *remains* loaded
  (with a number of other resources allocated). Here the intent is that
  the firmware may investigate the loaded image in platform-specific
  ways, and *override* the secure boot verification failure -- and
  finally start the image with StartImage().

- The issue is that "DxeImageVerificationLib", which is the component
  responsible for mapping the "deny" policy to EFI_ACCESS_DENIED, and
  "defer" to EFI_SECURITY_VIOLATION, has a bug. It maps "deny" to
  EFI_SECURITY_VIOLATION as well.

Consequences:

- If a firmware sets the "deny" policy, and (accordingly) does not
  specifically handle the EFI_SECURITY_VIOLATION return value from
  LoadImage(), it may *leak* loaded images that fail Secure Boot
  verification. The reason is that the firmware does not expect to have
  to call the UnloadImage() boot service in response to an *error* code
  returned by LoadImage().

- With the loaded image leaked, some resources may remain in RAM that
  could perhaps be utilized to mount a more complex attack. For example,
  a buffer overflow in another part of the firmware could be exploited
  for returning into executable code that is present in the leaked
  loaded image.

OVMF sets the "deny" policy for the following executable sources:

- executables loaded from removable media (such as network, CD-ROM)
- executables loaded from fixed media (such as hard disk)
- (in SEV guests only:) executables loaded from PCI(e) device ROM BARs
  (i.e., UEFI drivers loaded from PCI expansion ROMs)

* Version-Release number of selected component (if applicable):

edk2-20190308git89910a39dcfd-6.el8

* How reproducible:

Always, when enabling SB, and loading an unsigned or incorrectly signed
(or blacklisted) binary.

* Steps to Reproduce:

1. Define a new domain with SB enabled (using the "OVMF_VARS.secboot.fd"
   varstore template).

2. Attempt booting "UefiShell.iso".

3. Capture the UEFI console traffic on the serial port.

* Actual results:

> BdsDxe: loading Boot0003 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/[...]
> BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/[...]: Security Violation

* Expected results:

> BdsDxe: loading Boot0003 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/[...]
> BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/[...]: Access Denied

* Additional info:

It's not only the platform firmware that may call LoadImage(), and
therefore be susceptible to a leak on EFI_SECURITY_VIOLATION. UEFI boot
loaders would typically call LoadImage() too. Two examples (running in
guests):

(a) grub2:

- repository: https://github.com/rhboot/grub2
- branch:     master
- file:       grub-core/loader/efi/chainloader.c
- function:   grub_cmd_chainloader()

At commit 2a2e10c1b396, the code goes like:

>   status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
>                        boot_image, size,
>                        &image_handle);
>   if (status != GRUB_EFI_SUCCESS)
>     {
>       if (status == GRUB_EFI_OUT_OF_RESOURCES)
>         grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
>       else
>         grub_error (GRUB_ERR_BAD_OS, "cannot load image");
>
>       goto fail;
>     }

This code exhibits the leak. Even though the OVMF platform policy is set
to "deny execute on security violation", the LoadImage() boot service
will return EFI_SECURITY_VIOLATION rather than EFI_ACCESS_DENIED, in
case secure boot verification fails for the UEFI binary loaded for the
"chainloader" command. Then the above grub2 code will leak
"image_handle".

grub_cmd_chainloader() handles the "chainloader" command, which is put
to use in RHEL7 for example in "/etc/grub.d/30_os-prober", for
chain-loading UEFI boot loaders of other operating systems.

(b) systemd-boot (known earlier as "gummiboot"):

It is available in RHEL8 guests, for example: the "systemd-udev" package
provides:

  /usr/lib/systemd/boot/efi/systemd-bootx64.efi

AIUI this is an alternative to grub2, and like grub2, it can be
installed on the EFI system partition.

The source code is at:

- repository: https://github.com/systemd/systemd.git
- branch:     master
- file:       src/boot/efi/boot.c
- function:   image_start()

At commit 26fe3af8ae7d, we have

>         err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
>         if (EFI_ERROR(err)) {
>                 Print(L"Error loading %s: %r", entry->loader, err);
>                 uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
>                 return err;
>         }

Which is another affected call site -- it leaks "image" if "err" is
EFI_SECURITY_VIOLATION.


The applications and the firmware are both at fault here. The
applications have bugs; they do not handle EFI_SECURITY_VIOLATION from
LoadImage() the way the UEFI spec requires. On the other hand, edk2 is
at fault too, because in a "DENY execute on security violation" policy
(as opposed to "DEFER execute on security violation" policy),
EFI_SECURITY_VIOLATION should never be returned, only EFI_ACCESS_DENIED
should. (OVMF configures the DENY policy, not the DEFER policy.) And
with the EFI_ACCESS_DENIED error code, there is no leak, regardless of
application-level error handling.

Other UEFI applications (e.g. custom ones that customers run) could
invoke LoadImage() too.


Once the upstream bug (TianoCore#2129) is fixed, we should backport the
patches, so that in OVMF, LoadImage() only return EFI_ACCESS_DENIED on
SB verification failure. This will prevent leaks regardless of whether
the LoadImage() caller handles EFI_SECURITY_VIOLATION specifically.

Comment 3 Laszlo Ersek 2020-01-16 19:11:44 UTC
Posted upstream:
* [edk2-devel] [PATCH 00/11]
  SecurityPkg/DxeImageVerificationHandler: fix retval for "deny" policy

  https://edk2.groups.io/g/devel/message/53314
  http://mid.mail-archive.com/20200116190705.18816-1-lersek@redhat.com

Comment 4 Laszlo Ersek 2020-01-16 19:16:03 UTC
Xueqiang Wei, please see "Steps to Reproduce" / "Actual results" / "Expected results" in comment#0. Can you please grant qa_ack+? Thank you.

Comment 5 Laszlo Ersek 2020-01-31 09:31:25 UTC
(In reply to Laszlo Ersek from comment #3)
> Posted upstream:
> * [edk2-devel] [PATCH 00/11]
>   SecurityPkg/DxeImageVerificationHandler: fix retval for "deny" policy
> 
>   https://edk2.groups.io/g/devel/message/53314
>   http://mid.mail-archive.com/20200116190705.18816-1-lersek@redhat.com

Merge in progress via <https://github.com/tianocore/edk2/pull/324>, but the GitHub.com PR seems to stuck (all checks have passed, twice, but the mergify bot is not merging the branch).

Comment 6 Laszlo Ersek 2020-01-31 10:06:14 UTC
(In reply to Laszlo Ersek from comment #5)
> (In reply to Laszlo Ersek from comment #3)
> > Posted upstream:
> > * [edk2-devel] [PATCH 00/11]
> >   SecurityPkg/DxeImageVerificationHandler: fix retval for "deny" policy
> >
> >   https://edk2.groups.io/g/devel/message/53314
> >   http://mid.mail-archive.com/20200116190705.18816-1-lersek@redhat.com
>
> Merge in progress via <https://github.com/tianocore/edk2/pull/324>,
> but the GitHub.com PR seems to stuck (all checks have passed, twice,
> but the mergify bot is not merging the branch).

Now merged (same PR), commit range 83357313dd67..8b0932c19f31:

 1  1e0f973b65c3 SecurityPkg/DxeImageVerificationHandler: simplify
                 "VerifyStatus"
 2  eccb856f013a SecurityPkg/DxeImageVerificationHandler: remove "else"
                 after return/break
 3  61a9fa589a15 SecurityPkg/DxeImageVerificationHandler: keep PE/COFF
                 info status internal
 4  47650a5cab60 SecurityPkg/DxeImageVerificationHandler: narrow down
                 PE/COFF hash status
 5  f891b052c5ec SecurityPkg/DxeImageVerificationHandler: fix retval on
                 memalloc failure
 6  12a4ef58a8b1 SecurityPkg/DxeImageVerificationHandler: remove
                 superfluous Status setting
 7  c602e97446a8 SecurityPkg/DxeImageVerificationHandler: unnest
                 AddImageExeInfo() call
 8  fb02f5b2cd0b SecurityPkg/DxeImageVerificationHandler: eliminate
                 "Status" variable
 9  6d57592740cd SecurityPkg/DxeImageVerificationHandler: fix retval for
                 (FileBuffer==NULL)
10  6aa31db5ebeb SecurityPkg/DxeImageVerificationHandler: fix imgexec
                 info on memalloc fail
11  8b0932c19f31 SecurityPkg/DxeImageVerificationHandler: fix "defer"
                 vs. "deny" policies

Comment 11 Xueqiang Wei 2020-02-12 08:10:18 UTC
According to comment#0, reproduced it on edk2-ovmf-20190829git37eef91017ad-6.el8.noarch.

Retested on edk2-ovmf-20190829git37eef91017ad-7.el8.noarch, not hit this issue. So set status to VERIFIED.


Details:

Versions:
kernel-4.18.0-175.el8.x86_64
qemu-kvm-4.2.0-7.module+el8.2.0+5520+4e5817f3
edk2-ovmf-20190829git37eef91017ad-7.el8.noarch



Steps:

1. enable secure boot (using the "OVMF_VARS.secboot.fd"
   varstore template)
# cp /usr/share/edk2/ovmf/OVMF_VARS.secboot.fd /home/kvm_autotest_root/images/rhel810-64-virtio-scsi.qcow2.fd

2. Attempt booting "UefiShell.iso".
/usr/libexec/qemu-kvm \
    -S  \
    -name 'avocado-vt-vm1'  \
    -sandbox on  \
    -machine q35  \
    -nodefaults \
    -device VGA,bus=pcie.0,addr=0x1 \
    -m 15360  \
    -smp 12,maxcpus=12,cores=6,threads=1,dies=1,sockets=2  \
    -cpu 'Opteron_G5',+kvm_pv_unhalt  \
    -chardev socket,id=qmp_id_qmpmonitor1,path=/var/tmp/avocado_5pvzspmp/monitor-qmpmonitor1-20191217-033600-Mk8g6MnL,server,nowait \
    -mon chardev=qmp_id_qmpmonitor1,mode=control  \
    -chardev socket,id=qmp_id_catch_monitor,path=/var/tmp/avocado_5pvzspmp/monitor-catch_monitor-20191217-033600-Mk8g6MnL,server,nowait \
    -mon chardev=qmp_id_catch_monitor,mode=control \
    -device pvpanic,ioport=0x505,id=idVmoeBy \
    -chardev socket,nowait,path=/var/tmp/avocado_5pvzspmp/serial-serial0-20191217-033600-Mk8g6MnL,server,id=chardev_serial0 \
    -device isa-serial,id=serial0,chardev=chardev_serial0  \
    -chardev socket,id=seabioslog_id_20191217-033600-Mk8g6MnL,path=/var/tmp/avocado_5pvzspmp/seabios-20191217-033600-Mk8g6MnL,server,nowait \
    -device isa-debugcon,chardev=seabioslog_id_20191217-033600-Mk8g6MnL,iobase=0x402 \
    -device pcie-root-port,id=pcie.0-root-port-2,slot=2,chassis=2,addr=0x2,bus=pcie.0 \
    -device qemu-xhci,id=usb1,bus=pcie.0-root-port-2,addr=0x0 \
    -device pcie-root-port,id=pcie.0-root-port-3,slot=3,chassis=3,addr=0x3,bus=pcie.0 \
    -device virtio-scsi-pci,id=virtio_scsi_pci0,bus=pcie.0-root-port-3,addr=0x0 \
    -blockdev node-name=file_image1,driver=file,aio=threads,filename=/home/kvm_autotest_root/images/rhel810-64-virtio-scsi.qcow2,cache.direct=on,cache.no-flush=off \
    -blockdev node-name=drive_image1,driver=qcow2,cache.direct=on,cache.no-flush=off,file=file_image1 \
    -device scsi-hd,id=image1,drive=drive_image1,write-cache=on \
    -device pcie-root-port,id=pcie.0-root-port-4,slot=4,chassis=4,addr=0x4,bus=pcie.0 \
    -device virtio-net-pci,mac=9a:e9:9e:b5:c8:76,id=idoQ7AuT,netdev=idXQ4Zp4,bus=pcie.0-root-port-4,addr=0x0  \
    -netdev tap,id=idXQ4Zp4,vhost=on \
    -blockdev node-name=file_uefishell,driver=file,read-only=on,aio=threads,filename=/home/kvm_autotest_root/images/UefiShell.iso,cache.direct=on,cache.no-flush=off \
    -blockdev node-name=drive_uefishell,driver=raw,read-only=on,cache.direct=on,cache.no-flush=off,file=file_uefishell \
    -device scsi-cd,id=uefishell,drive=drive_uefishell,bootindex=0,write-cache=on \
    -device usb-tablet,id=usb-tablet1,bus=usb1.0,port=1  \
    -vnc :0  \
    -rtc base=utc,clock=host,driftfix=slew  \
    -boot menu=off,order=cdn,once=c,strict=off \
    -blockdev node-name=file_ovmf_code,driver=file,read-only=on,filename=/usr/share/OVMF/OVMF_CODE.secboot.fd \
    -blockdev node-name=file_ovmf_vars,driver=file,filename=/home/kvm_autotest_root/images/rhel810-64-virtio-scsi.qcow2.fd \
    -machine pflash0=file_ovmf_code,pflash1=file_ovmf_vars \
    -enable-kvm \
    -device pcie-root-port,id=pcie_extra_root_port_0,slot=5,chassis=5,addr=0x5,bus=pcie.0 \
    -monitor stdio \

3. Capture the UEFI console traffic on the serial port.
# nc -U /var/tmp/avocado_5pvzspmp/serial-serial0-20191217-033600-Mk8g6MnL


Tested with edk2-ovmf-20190829git37eef91017ad-6.el8.noarch, after step 3:

BdsDxe: loading Boot0001 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/Pci(0x3,0x0)/Pci(0x0,0x0)/Scsi(0x1,0x0)
BdsDxe: failed to load Boot0001 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/Pci(0x3,0x0)/Pci(0x0,0x0)/Scsi(0x1,0x0): Security Violation



Tested with edk2-ovmf-20190829git37eef91017ad-7.el8.noarch, after step 3:

BdsDxe: loading Boot0001 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/Pci(0x3,0x0)/Pci(0x0,0x0)/Scsi(0x1,0x0)
BdsDxe: failed to load Boot0001 "UEFI QEMU QEMU CD-ROM " from PciRoot(0x0)/Pci(0x3,0x0)/Pci(0x0,0x0)/Scsi(0x1,0x0): Access Denied

Comment 13 errata-xmlrpc 2020-04-28 16:02:22 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHSA-2020:1712


Note You need to log in before you can comment on or make changes to this bug.