Random bits from Greg Kroah-Hartman

Booting a Self-signed Linux Kernel

Sep 2nd, 2013

Now that The Linux Foundation is a member of the UEFI.org
group, I’ve been working on the procedures for how to boot a self-signed
Linux kernel on a platform so that you do not have to rely on any
external signing authority.

After digging through the documentation out there, it turns out to be
relatively simple in the end, so here’s a recipe for how I did this, and
how you can duplicate it yourself on your own machine.

We don’t need no stinkin bootloaders!

When building your kernel image, make sure the following options are set:

The first two options here enable EFI mode, and tell the kernel to build
itself as a EFI binary that can be run directly from the UEFI bios.
This means that no bootloader is involved at all in the system, the UEFI
bios just boots the kernel, no “intermediate” step needed at all. As
much as I love gummiboot, if you trust the kernel image you
are running is “correct”, this is the simplest way to boot a signed
kernel.

As no bootloader is going to be involved in the boot process, you need
to ensure that the kernel knows where the root partition is, what init
is going to be run, and anything else that the bootloader normally
passes to the kernel image. The option listed above,
CONFIG_CMDLINE should be set to whatever you want the kernel to
use as the command line.

Also, as we don’t have an initrd passed by the bootloader to the kernel,
if you want to use one, you need to build it into the kernel itself.
The option CONFIG_INITRAMFS_SOURCE should be set to your
pre-built cpio initramfs image you wish to use.

Note, if you don’t want to use an initrd/initramfs, don’t set this last
option. Also, currently it’s a bit of a pain to build the kernel, build
the initrd using dracut with the needed dracut modules and
kernel modules, and then rebuild the kernel adding the cpio image to the
kernel image. I’ll be working next on taking a pre-built kernel image,
tearing it apart and adding a cpio image directly to it, no need to
rebuild the kernel. Hopefully that can be done with only a minimal use
of libbfd

After setting these options, build the kernel and install it on your
boot partition (it is in FAT mode, so that UEFI can find it, right?) To
have UEFI boot it directly, you can place it in
/boot/EFI/boot/bootx64.efi, so that UEFI will treat it as the
“default” bootloader for the machine.

Lather, rinse, repeat

After you have a kernel image installed on your boot partition, it’s
time to test it.

Reboot the machine, and go into the BIOS. Usually this means pounding
on the F2 key as the boot starts up, but all machines are different,
so it might take some experimentation to determine which key your BIOS
needs. See this post from Matthew Garrett for the problems you
might run into trying to get into BIOS mode on UEFI-based laptops.

Traverse the BIOS settings and find the place where UEFI boot mode is
specified, and turn it the “Secure Boot” option OFF.

Save the option and reboot, the BIOS should find the kernel located at
boot/EFI/boot/bootx64.efi and boot it directly. If your kernel
command line and initramfs (if you used one) are set up properly, you
should now be up and running and able to use your machine as normal.

If you can’t boot properly, ensure that your kernel command line was set
correctly, or that your initramfs has the needed kernel modules in it.
This usually takes a few times back and forth to get all of the correct
settings properly configured.

Only after you can successfully boot the kernel directly from the BIOS,
in “insecure” mode should you move to the next step.

Keys to the system

Now that you have a working kernel image and system, it is time to start
messing with keys. There are three different types of UEFI keys that
you need to learn about, the “Platform Key” (known as a “PK”), the
“Key-Exchange Keys” (known as a “KEK”), and the “Signature Database Key”
(known as a “db”). For a simple description of what these keys mean,
see the Linux Foundation Whitepaper about UEFI Secure boot,
published back in 2011. For a more detailed description of the keys,
see the UEFI Specification directly.

For a very simple description, the “Platform Key” shows who “owns and
controls” the hardware platform. The “Key-Exchange keys” shows who is
allowed to update the hardware platform, and the “Signature Database
keys” show who is allowed to boot the platform in secure mode.

If you are interested in how to manipulate these keys, replace them, and
do neat things with them, see James Bottomley’s blog for
descriptions of the tools you can use and much more detail than I
provide here.

To manipulate the keys on the system, you need the the UEFI keytool USB
image from James’s website called sb-usb.img (md5sum
7971231d133e41dd667a184c255b599f). dd the image to a USB
drive, and boot the machine into the image.

Depending on the mode of the system (insecure or secure), you will be
dropped to the UEFI console, or be presented with a menu. If a command
line, type KeyTool to run the keytool binary. If a menu,
select the option to run KeyTool directly.

Save the keys

First thing to do, you should save the keys that are currently on the
system, in case something “bad” ever happens and you really want to be
able to boot another operating system in secure mode on the hardware.
Go through the menu options in the KeyTool program and save off
the PK, KEK, and db keys to the USB drive, or to the hard drive, or
another USB drive you plug into the system.

Take those keys and store them somewhere “safe”.

Clear the machine

Next you should remove all keys from the system. You can do this from
the KeyTool program directly, or just reboot into the BIOS and
select an option to “remove all keys”, if your BIOS provides this (some
do, and some don’t.)

Create and install your own keys

Now that you have an “empty” machine, with the previous keys saved off
somewhere else, you should download the sbsigntool and
efiutil packages and install them on your development system.
James has built all of the latest versions of these packages in the
openSUSE build system for all RPM and DEB-based Linux
distros. If you have a Gentoo-based system, I have checked the needed
versions into portage, so just grab them directly from there.

If you want to build these from source, the sbsigntool git tree
can be found here, and the efitools git tree is
here.

The efitoolsREADME is a great summary of how to
create new keys, and here is the commands it says to follow in order to
create your own set of keys:

The option -subj can contain a string with whatever name you
wish to have for your key, be it your company name, or the like. Other
fields can be specified as well to make the key more “descriptive”.

Then, take the PK key you have created, turn it into a EFI Signature
List file, and add a GUID to the key:

1

cert-to-efi-sig-list -g <my random guid> PK.crt PK.esl

Where my random guid is any valid guid you wish to use
(I’ve seen some companies use all ‘5’ as their guid, so I’d recommend
picking something else a bit more “random” to make look like you know
what you are doing with your key…).

Take these files you have created, put them on a USB disk, run the
KeyTool program and use it to add the db, KEK, and PK keys into
the BIOS. Note, apply the PK key last, as once it is installed, the
platform will be “locked” and you should not be able to add any other
keys to the system.

Fail to boot

Now that your own set of keys is installed in the system, flip the BIOS
back into “Secure boot” mode, and try to boot your previous-successful
Linux image again.

Hopefully it should fail with some type of warning, the laptop I did
this testing on provides this “informative” graphic:

Sign your kernel

Now that your kernel can’t boot, you need to sign it with the db key you
placed in your bios:

1

sbsign --key db.key --cert db.crt --output bzImage bzImage.signed

Take the bzImage.signed file and put it back in the boot
partition, copying over the unsigned /boot/EFI/boot/bootx64.efi file.

Profit!

Now, rebooting the machine should cause the UEFI bios to check the
signatures of the signed kernel image, and boot it properly.

Demo

I’ve recorded a video of a Gateway laptop booting a signed kernel, with
my own key, here. The demo tries to boot an unsigned kernel
image that is on the hard disk, but it fails. I plug in a signed kernel
that is on the USB disk, and it properly boots.

I did the test with a CoreOS image as it provides a very small
self-contained Linux system that allows for easy testing/building from a
development machine.

Future plans

Now that you have full control over your system, running only a Linux
kernel image that you sign yourself, a whole raft of possibilities open
up. Here’s a few that I can think off of the top of my head:

Linux signed system self-contained in the kernel image (with
initramfs) booting into ram, nothing on the disk other than the
original kernel image.

Signed kernel image initramfs validates the other partitions with a
public key to ensure they aren’t tampered before mounting and using
them (ChromeOS does this exact thing quite well). This passes the
“chain of trust” on to the filesystem image, giving you assurances
that you are running code you trust, on a platform you trust.

If you are interested in these types of things, I’ll be at the
Linux Plumbers Conference in a few weeks, where a bunch of
people will be discussing secure boot issues with Linux. I’ll also be
at
LinuxCon North America,
Europe, and
Korea if you want to talk about UEFI and Linux issues
there.

Recent Posts

About Greg

Greg is a Fellow at the
Linux Foundation
and is responsible for the Linux kernel stable releases. He is also the
maintainer of a variety of different kernel subsystems (USB, char/misc,
staging, etc.) and has written a few books about Linux kernel
development.