Reworking kexec for signatures

Did you know...? LWN.net is a subscriber-supported publication; we rely on subscribers to keep the entire operation going. Please help out by buying a subscription and keeping LWN on the net.

The kernel execution (kexec) subsystem allows a running kernel to switch to a different kernel. This allows for faster booting, as the system firmware and bootloader are bypassed, but it can also be used to produce crash dumps using Kdump. However, as Matthew Garret explained on his blog, kexec could be used to circumvent UEFI secure boot restrictions, which led him to propose a way to disable kexec on secure boot systems. That was not terribly popular, but a more recent patch set would provide a path for kexec to only boot signed kernels, which would solve the problem Garrett was trying to address, without completely disabling the facility.

The kexec subsystem consists of the kexec_load() system call that loads a new kernel into memory, which can then be booted using the reboot() system call. There is also a kexec command that will both load the new kernel and boot it, without entering the system firmware (e.g. BIOS or UEFI) and bootloader.

But the UEFI firmware is what enforces the secure boot restrictions. Garrett was concerned that a Linux kernel could be used to boot an unsigned (and malicious) Windows operating system by way of kexec because it circumvents secure boot. That might lead Microsoft to blacklist the keys used to sign Linux bootloaders, which would make it difficult to boot Linux on commodity hardware. Using kexec that way could affect secure-booted Linux systems too, of course, though Microsoft might not be so quick to revoke keys under those circumstances.

In any case, Garrett eventually removed the kexec-disabling portion of his patch set (though he strongly suggested that distributions should still disable kexec if they are going to support secure boot). Those patches have not been merged (yet?). More recently, Vivek Goyal has put together a patch set that is intended to address Garrett's secure boot concerns, but would also protect systems that only allow loading signed kernel modules. As Garrett showed in his blog post, that restriction can be trivially bypassed by executing a new kernel that simply alters the sig_enforce sysfs parameter in the original kernel's memory and then jumps back to that original kernel.

Goyal's patches start down the path toward being able to restrict kexec so that it will only load signed code. To that end, this patch set defines a new system call:

long kexec_file_load(int kernel_fd, int initrd_fd, const char *cmdline_ptr, unsigned long cmdline_len, unsigned long flags);

kernel_fd

initrd_fd

cmdline_ptr

cmdline_len

long kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment *segments, unsigned long flags);

segments

kexec_file_load()

It will load the kernel executable from thefile descriptor and will associate the "initial ramdisk" (initrd) from thedescriptor. It will also associate the kernel command line passed asand. The initrd and command-line information will be used when the kernel is actually booted. This contrasts with the existing kexec system call:It expects to getthat have been parsed out of a kernel binary in user space and to just blindly load them into memory. As can be seen,puts the kernel in the loop so that it can (eventually) verify what is being loaded and executed.

As one of the segments that get loaded, there is a standalone executable object, called "purgatory", that runs between the two kernels. At reboot() time, the "exiting" kernel jumps to the purgatory code. Its main function is to check the SHA-256 hashes of the other segments that were loaded. If those have not been corrupted, booting can proceed. The purgatory code will copy some memory to a backup region and do some architecture-specific setup, then jump to the new kernel.

The purgatory code currently lives in kexec-tools, but if the kernel is to take responsibility for setting up the segments from the kernel binary and initrd, it will need a purgatory of its own. Goyal's patch set adds that code for x86 to arch/x86/purgatory/ .

Goyal also copied code from crypto/sha256_generic.c into the purgatory directory. It's clear he would rather simply just use the code directly from the crypto/ directory, but could not find a way to do so:

I could not #include sha256_generic.c file here as some of the function signature [required a] little tweaking. Original functions work with crypto API but these ones don't So instead of doing #include on sha256_generic.c I just copied relevant portions of code into arch/x86/purgatory/sha256.c. Now we shouldn't have to touch this code at all. Do let me know if there are better ways to handle it.

While the patch set is at version 3 (earlier versions: v2, v1), it is still a "request for comment" (RFC) patch. There are various unfinished pieces, with signature verification topping the list. So far, the new facility is only available for the x86_64 architecture and bzImage kernel images. Adding other architectures and support for the ELF kernel format still remain to be done. There is also a need for some documentation, including a man page.

Goyal did explain his vision for how the signature verification will work. It is based on David Howells's work on verifying the signatures for loadable kernel modules. Essentially, the signature will be verified when kexec_load_file() is called. That is also when the SHA-256 hashes for each segment are calculated and stored in the purgatory segment. So, all purgatory has to do is verify the hashes (which it already does to avoid running corrupted code) to ensure that only a properly signed kernel will be executed.

There have been plenty of comments on each version of the patch set, but most of those on v3 were technical suggestions for improving the code. So far, there have been no complaints about the overall idea, which means we may well see the ability to require cryptographic signatures on the kernels passed to kexec added as a feature sometime in the next year—hopefully sooner than that. It would be a nice feature to have when Garrett's secure boot patches get merged.