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
prev 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