From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id C4FEA21CE740B for ; Thu, 6 Jul 2017 11:47:48 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C1FE885547; Thu, 6 Jul 2017 18:49:28 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com C1FE885547 Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx04.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=lersek@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com C1FE885547 Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-108.phx2.redhat.com [10.3.116.108]) by smtp.corp.redhat.com (Postfix) with ESMTP id EF14160F8A; Thu, 6 Jul 2017 18:49:27 +0000 (UTC) To: Jason Dickens , edk2-devel@lists.01.org References: <6703d38b-e99b-c11e-0126-ad24239dacee@grammatech.com> From: Laszlo Ersek Message-ID: Date: Thu, 6 Jul 2017 20:49:26 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.2.1 MIME-Version: 1.0 In-Reply-To: <6703d38b-e99b-c11e-0126-ad24239dacee@grammatech.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Thu, 06 Jul 2017 18:49:28 +0000 (UTC) Subject: Re: OVMF Secure Boot variable storage issue X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jul 2017 18:47:49 -0000 Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit 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