public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: Jason Dickens <jdickens@grammatech.com>, edk2-devel@lists.01.org
Subject: Re: OVMF Secure Boot variable storage issue
Date: Thu, 6 Jul 2017 20:49:26 +0200	[thread overview]
Message-ID: <b5d97191-91f0-2180-f7b3-80acbd173a61@redhat.com> (raw)
In-Reply-To: <6703d38b-e99b-c11e-0126-ad24239dacee@grammatech.com>

Hi Jason,

On 07/06/17 19:31, Jason Dickens wrote:
> All,
> 
> I'm trying to understand why the secure boot variables (PK, KEK, db,
> etc) when using the OVMF build are not retained across reboot?

They *are* retained across reboot, you just have to use a virtualization
platform that provides a working pflash (persistent flash) device model.

With regards to the variable store, you can use OVMF in three different
QEMU configurations. In chronological order (which also matches
increasing usefulness order):

(1) -bios OVMF.fd

With this option, the unified firmware image called "OVMF.fd" (which
contains both the firmware executable and the variable store) is mapped
into the guest address space as ROM.

In this case, the lowest level of the variable driver stack -- the flash
block driver -- is provided by "OvmfPkg/EmuVariableFvbRuntimeDxe", which
emulates the "flash" in a normal RAM block, pre-reserved by
"OvmfPkg/PlatformPei", at an address that does not vary across reboots.

Whenever there is a write to the flash, EmuVariableFvbRuntimeDxe does
the following things:

- updates the in-memory (fake) flash,
- calls out to Library/EmuVariableFvbLib (PlatformFvbDataWritten()),
- that function signals an event identified by PcdEmuVariableEvent
  before ExitBootServices(), and does nothing after ExitBootServices().

>From the other side, "OvmfPkg/Library/PlatformBootManagerLib", which is
built into "MdeModulePkg/Universal/BdsDxe", does the following things:

- Using OvmfPkg/Library/NvVarsFileLib -- which in turn uses
  OvmfPkg/Library/SerializeVariablesLib --, PlatformBootManagerLib
  de-serializes variable content from the \NvVars binary file from
  any EFI system partition that it finds, and restores the content to
  the UEFI variables with the SetVariable() runtime service.

- PlatformBootManagerLib also configures the event mentioned above,
  setting up PcdEmuVariableEvent. Whenever EmuVariableFvbRuntimeDxe
  signals the event (upon a flash write), BdsDxe will call out to
  NvVarsFileLib, and serialize the full set of variables (using the
  GetVariable() runtime service)) to said \NvVars file.

Problems with this approach:

- Variable updates after ExitBootServices() are only stored in memory.
  This is because at that point, the ESP is owned (mounted) by the
  operating system, so the firmware must not write to it.

- If the VM is powered down with variable updates pending in memory,
  those updates are lost.

- If the VM is rebooted instead, then the next startup of the firmware
  will sync out the memory contents to disk, with the same
  SaveNvVarsToFs() function that is used by the serialization logic
  described above.

- If a DXE driver specifies an explicit dependency on the read-only
  variable protocol (or installs a protocol notify for it), it may
  validly attempt to read variables before BDS restores variables from
  \NvVars. The result is a GetVariable() runtime service that lies to
  callers. (This issue is exacerbated when considering read-only
  variable access in the PEI phase.)

- Authenticated variables cannot be restored by way of SetVariable()
  calls issued in an unspecified order, hence the deserialization
  doesn't work for them.

- I vaguely recall that the auth variables are also lost during an in-VM
  reboot (when de-serialization from \NvVars shouldn't be necessary). I
  think by now I've forgotten the details about this. It's possible that
  writing out & reloading always happens to and from \NvVars, and that
  loses the auth variables somehow.

  The following commits are related:

  - e678f9db899a ("OvmfPkg/SerializeVariablesLib: ignore secure variable
    restore errors", 2013-05-28)
  - db827286e283 ("OvmfPkg/SerializeVariablesLib: Relax check for the
    read-only variable", 2016-05-26)

On Xen, only "-bios" is usable.

"-bios" is incompatible with -D SMM_REQUIRE (the firmware will refuse
continuing after a specific point). The primary goal of SMM is to
protect the variable store from direct hardware access by the guest OS,
and that's impossible to do if the varstore lives in a normal RAM area
and in a file on the ESP.

(2) -pflash OVMF.fd

In this case, the same unified image is mapped into guest-phys address
space as a flash device. All of the emulation described above is
inactive. A real pflash driver is provided by
OvmfPkg/QemuFlashFvbServicesRuntimeDxe (which can be built both as a
runtime DXE driver and as an SMM driver, see the INF files in that
directory).

Variables behave as required by the UEFI spec and seen on physical
hardware. Updates are written back to the unified OVMF.fd file [*],
on-line, and variable material (auth and non-auth alike) simply survives
in the low-level flash representation.

[*] This also explains one drawback of "-pflash OVMF.fd": each virtual
machine has its own copy of the firmware executable, along with its
(justifiedly private) variable store. Updating the firmware executable
without losing variables is infeasible in practice.

This method is compatible with -D SMM_REQUIRE. For full protection, some
additional command line switches are necessary, but I'll mention those
under the next point.

(3) -drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on \
    -drive if=pflash,format=raw,unit=1,file=copy_of_OVMF_VARS.fd \

To the guest, this looks mostly the same (the only guest-visible
difference is that under (2), if the guest tried to reprogram the flash
area that holds the firmware binary, it would succeed, whereas in this
case the same attempt would fail -- see "readonly=on").

The firmware image is split in two parts. OVMF_CODE.fd is mapped as
read-only flash, and can be shared by multiple VMs. OVMF_VARS.fd is only
used as a *template* for creating private variable store files that
belong to individual VMs (see the filename "copy_of_OVMF_VARS.fd").

This method has all the benefits of (2), but we can also update
OVMF_CODE.fd centrally on a virtualization host.

When using a -D SMM_REQUIRE build, the following additions are needed:

* -machine q35

  SMM emulation is only available on pc-q35-2.5 and later

* -machine smm=on

  it's best to turn on SMM emulation explicitly

* -global driver=cfi.pflash01,property=secure,value=on

  this is the important setting that causes QEMU to actually restrict
  pflash writes to guest code that runs in System Management Mode.
  (I.e., firmware. The guest OS can itself raise an SMI if it wants to,
  but then control is transferred to the firmware just the same.)

> It seems
> that this code uses roughly the same SetVariable, GetVariable2 approach
> as say the PlatformConfig uses to store screen resolution (which is
> retained). Additionally, the NvVars file is being at least touched by
> the secure boot configuration. So why are none of the keys retained on
> the next reboot?
> 
> I know this was an issue in the past, but I haven't found the resolution?

Secure Boot with "-bios OVMF.fd" has never been resolved, due to the
reasons described above:
- inherent insecurity of the emulation,
- basic UEFI-non-compliance if the VM is powered off with runtime
  variable updates pending in the memory buffer, or if a DXE driver
  tries to consume variables before BDS de-serializes them,
- de-serialization from disk, with SetVariable(), would have to conform
  to the auth variable particulars (attributes, order)

Implementing pflash in QEMU and KVM, and adding the matching driver
(QemuFlashFvbServicesRuntimeDxe) were the future-proof solution. Jordan
did those things in the QEMU-1.6 timeframe (second half of 2013, it seems).

Thanks
Laszlo


      parent reply	other threads:[~2017-07-06 18:47 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-06 17:31 OVMF Secure Boot variable storage issue Jason Dickens
2017-07-06 18:30 ` Bill Paul
2017-07-06 18:55   ` Jason Dickens
2017-07-12 14:00     ` Konrad Rzeszutek Wilk
2017-07-06 19:08   ` Laszlo Ersek
2017-07-06 18:49 ` Laszlo Ersek [this message]

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=b5d97191-91f0-2180-f7b3-80acbd173a61@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