If you want to create your own secure boot loader, you should have a look at the shim UEFI boot loader. It implements all the requirements set force by Microsoft. Even Microsoft says that this is a good starting point. While the instructions presented here are specific to Ubuntu 14.04, they should be easy to adapt to other distributions.
In this blog post, we will have a look at the following tools.
- QEMU for experiments inside a virtual machine. You do not want to mess around with a real machine, as long as you do not know the tools by heart. Especially, because some UEFI BIOSes out there are just crap.
- All the tools we use that generate EFI applications require GNU EFI. Best to install it right now with:
$ sudo apt-get install gnu-efi
- efitools to lock down the virtual machine. efitools, however, require sbsigntool as a prerequisite for signing EFI applications.
- The shim secure boot loader which requires pesign as a prerequisite for signing PE/COFF binaries or EFI applications. Probably you get around with just pesign or sbsigntools but I did not (yet) bother to try that out.
Getting all the tools to work is actually not so hard, except that the devil is in the detail. Here are the problems I encountered and that had stalled me for a while; one of the reason, why I decided to create this blog entry:
- Be careful when setting up the toolchain inside a VirtualBox VM and use a shared folder from your host OS. The vboxsf driver does not implement all the mmap options which some of the tools such as pesign rely on. Thanks to Peter Jones for debugging this together with me.
- The current git version of sbsigntool seems to be having some alignment(?) problems and does not sign all of the EFI applications correctly. This seems to be the case for all EFI applications that use GNU EFI as part of their tool chain.
First, if you want to experiment with UEFI, you probable want to configure QEMU to boot a virtual machine with a UEFI BIOS. One such BIOS is available as part of the Tianocore distribution, the OMVF UEFI BIOS. I have previously written an explanation (albeit terse) of what needs to be done to compile this yourself.
The only thing that has changed is that nowadays you not only have to specify the BIOS directory but also the BIOS image to use.
$ qemu-system-x86_64 -L bios -bios bios/bios.bin -hda fat:hda -m 1024
Other useful options are the following if you want to write debug messages from the OVMF UEFI BIOS to a file called debug.log.
-debugcon file:debug.log -global isa-debugcon.iobase=0x402
As for disk images, there are two options, create a virtual disk image or use the QEMU fat driver. This is indicated by -hda fat:$directory. The fat driver automatically takes all the files in $directory and assembles a virtual fat filesystem to boot the virtual machine with. Only caveat, you cannot write to such a disk, but for the experiments below writing to disk is not necessary.
Below is a list of tools and the packages that require these tools. I am sure I may have missed a few dependencies because they will have already been installed at a certain point in time.
sbsigntool is an application to sign EFI applications.
Compilation. Before compiling the tool yourself, be careful, the sbsigntool copy from the git repository at git://kernel.ubuntu.com/jk/sbsigntool is broken. Some EFI applications are not signed correctly and my secure boot enabled OVMF BIOS refuses to load files signed with this version of sbsigntool. My guess is that files that need to be padded are not signed correctly. But maybe, my BIOS is overly strict.
The version of sbsigntool shipped with Ubuntu 14.04 so far has signed all my EFI applications correctly even those where the self-compiled version failed, so I suggest to use that one instead:
$ sudo apt-get install sbsigntool
Now that you have been warned, if you still insist, here is what you need to do to compile the sources yourself. First, let’s install the prerequisites:
$ sudo apt-get install binutils-dev $ sudo apt-get install libssl-dev $ sudo apt-get install uuid-dev $ sudo apt-get install help2man $ sudo apt-get install libfile-slurp-perl
Now, we can compile sbsigntools as follows:
$ apt-get install automake $ git clone git://kernel.ubuntu.com/jk/sbsigntool $ sbsigntool $ ./autogen.sh # this produces many warnings $ ./configure $ make
Usage. If you have a key and certificate, use the following commands to sign and verify whether an application is signed correctly:
$ sbsign --key DB.key --cert DB.crt --output HelloWorld-signed.efi HelloWorld.efi $ sbverify --cert DB.crt HelloWorld-signed.efi
That is all that is to know about sbsigntool.
Compilation. The efitools collections allow you to lock down a UEFI machine with specific keys (platform key PK, key exchange key KEK, and authorized database keys DB). efitools rely on sbsigntool to sign EFI applications. The commands below assume that the sbsign executable is in your path. If you compiled it yourself and it is in a sibbling directory, use PATH="../sbsigntool/src:$PATH" make instead of make. Note, that the PATH modification is only valid within the make process (i.e., it won’t be changed in your shell).
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git $ cd efitools $ make
Usage. efitools contain everything you need to get started. During the make process, it creates three keys necessary for secure boot: PK, KEK, and DB.
LockDown. The LockDown EFI application that will install the keys on a UEFI system that is in setup mode and enable secure boot. Note, that it will remove any existing keys in those databases, so be careful, especially when using it on a real system.
HelloWorld. It comes with signed and unsigned versions of a simple HelloWorld EFI application. Note that this application will independently of which version you execute display This file is used to prove you have managed To execute an unsigned binary in secure boot mode.
A sample usage is to start a QEMU virtual machine inside which we can execute the following commands:
> fs0: > LockDown.efi > HelloWorld.efi Error reported: Security Violation > HelloWorld-signed.efi HelloWorld ... This file is used to ...
Loader. Apart from demonstrating how to install our newly generated keys into a Secure Boot enabled machine, there is another use to this utility. It comes with the Loader EFI application that allows us to boot an unsigned binary even if secure boot is enabled. However, it warns the user before doing so. In order to get this to work create the following disk image.
- LockDown.efi – copy of efitools/LockDown.efi
- EFI/BOOT/bootX64.efi – copy of efitools/Loader-signed.efi
- EFI/BOOT/linux-loader.efi – copy of efitools/HelloWorld.efi
Run the QEMU machine and enter the BIOS menum by pressing Esc when the system comes up. Change the Boot Maintenance Manager, Boot Options, Change Boot Order option to start from the EFI Internal Shell. Exit the BIOS, and in the EFI Shell execute the following:
> fs0: > LockDown.efi > exit
Now you are back in the BIOS and change the boot order back and start the machine. You will now see Loader.efi being executed automatically, determining that linux-loader.efi is not signed and will ask you whether you want to execute it. Voilà, done. This is useful if you want to bootchain into an untrusted boot loader.
This is a tool to sign PE/COFF executables and can also be used to sign EFI applications.
Compilation. This tool itself requires a couple of other packages: libpopt-dev for command line option processing, libnss3-dev and libnspr4-dev for handling NetScape(?) certificate databases from the code, and libuuid-dev for handling UUIDs. While you are at it, also instal libnss3-tools. While not strictly required, it provides all the command line tools to manipulating certificate databases.
$ sudo apt-get install libpopt-dev $ sudo apt-get install libnspr4-dev libnss3-dev $ sudo apt-get install uuid-dev $ sudo apt-get install libnss3-tools
After installing dependencies, we need to -I/usr/include/nspr to the INCDIR variable in Make.defaults to pick up prerror.h correctly.
Now we can compile with:
$ git clone https://github.com/vathpela/pesign $ cd pesign $ vi Make.defaults # and see above $ make
Usage. I only compiled pesign, because I wanted to compile the shim boot loader which has this as prerequisite. It probably will also work as a good replacement for sbsigntool (whose git version does not work anyway). As a difference to sbsigntool, it requires that the keys/certificates are stored in a certificate database. The database can be maintained with certutil contained in the libnss3-tools package mentioned above.
This is the state-of-the-art UEFI Secure Boot Loader as used by RedHat or Ubuntu. Even Microsofts points to this as a good starting point if you want to develop your own secure boot loader. See my UEFI Resources blog entry for details.
Compilation. If you have installed the tools above, you should have all prerequisites needed for shim as well. It certainly depends on pesign for signing EFI applications and libnss3-tools for handling the certificate database.
$ sudo apt-get install libnss3-tools
Before compilation, you will have to change the EFI_PATH variable in the Makefile from /usr/lib64/gnuefi to /usr/lib.
$ git clone https://github.com/mjg59/shim $ cd shim $ vi Makefile $ PATH="../pesign/src:$PATH" make
Usage. The shim boot loader is full fledged UEFI Secure Boot Loader that allows users to manage which followup boot loaders may be loaded, signed, etc. For this it generates the following EFI applications.
shim. This needs to be signed with the UEFI DB key (if you are serious you should think of submitting this to Microsoft for signing). The shim EFI applications contains its own shim certificate. Simply because shim acts as a proxy and allows the owner of shim (typically the distribution provider) to specify which binaries are allowed to be executed without having to go to Microsoft for each of them.
MokManager. This program can be executed to allow the owner of the machine to specify which applications are allowed to be executed (MOK is Machine Owner’s Key). This way he does not have to contact the distribution owner or Microsoft, whenever he wishes to boot an experimental kernel.
Now, as a final sample, let us start a UEFI system with the shim boot loader. First, we need to sign the shim boot loader with our authorized database key.
sbsign --key ../efitools/DB.key --cert ../efitools/DB.crt --output shim-signed.efi shim.efi
Next, we need to prepare a directory with the following files:
- LockDown.efi – the one from efitools.
- HelloWorld.efi – the one from efitools.
- HelloWorld-signed.efi – the one from efitools.
- shim.efi – the shim executable we just compiled.
- shim-signed.efi – the shim executable we just signed.
- MokManager.efi – a copy of MokManager.efi.signed, the MokManager signed with the shim boot loader’s key.
- fallback.efi – a copy of fallback.efi.signed, the fallback application signed with the shim boot loader’s key.
Next, let’s execute the following inside a QEMU virtual machine with the above files.
> fs0: > LockDown.efi > shim.efi Error reported: Security Violation > shim-signed.efi
The above will then start the signed shim boot loader, which will not find a default boot loader and hence ask whether we want to start MokManager to enroll our own keys.
As a variation to the above, we can rename HelloWorld-signed.efi to grub.efi (the default boot loader used by shim defined in DEFAULT_LOADER in the Makefile). In this case, shim will automatically execute automatically our signed hello world application as the next stage boot loader.
As yet another variation, we could have renamed HelloWorld.efi to grub.efi. In this case the shim boot loader would have identified that this file is not correctly signed with any key valid at this stage, complain, and offer to execute the MokManager. With which we could enroll a the signature of the unsigned version of our hello world application.