Please consider subscribing to LWNSubscriptions are the lifeblood of LWN.net. If you appreciate this content and would like to see more of it, your subscription will help to ensure that LWN continues to thrive. Please visit this page to join up and keep LWN on the net.
September 11, 2013
By Matthew Garrett's reading of the secure boot requirements, a system running in secure boot mode must not allow any user to change the running kernel; not even root is empowered to do so. Just over one year ago, Matthew posted a set of patches that implemented the necessary restrictions. In secure boot mode (as defined by the absence of a new capability called, at that time, CAP_SECURE_FIRMWARE), the kernel would not allow the loading of unsigned kernel modules, direct access to I/O ports or I/O memory, or, most controversially, use of the kexec_load() system call to reboot directly into a new kernel. As one might expect, not everybody liked this type of restriction, which flies in the face of the longstanding Unix tradition of giving root enough rope to shoot itself in the foot.
So there were discussions around various aspects of these patches, but one of the biggest problems only came to light later. It seems that there is a fundamental flaw in the capability model: it is nearly impossible to add new capability bits without risking problems with applications that do not know about the new bits. In particular:
- Some capability-aware applications work by turning off every
capability that they do not think they need. If a new bit is added
controlling functionality that such an application uses, it will
unknowingly disable a necessary capability and cease to work properly.
From the point of view of users of this application, this kind of
change constitutes an incompatible ABI change.
- Other applications work in a blacklist-oriented mode, turning off capabilities that are known not to be needed. In essence, such an application simply sets the capability mask to zero, then sets the bits corresponding to the capabilities it wants. If some sort of security-related functionality is put behind a new bit that is unknown to this kind of application, that application will leave the capability enabled. That, in turn, could make the application insecure.
In this case, the biggest risk is that whitelist-style applications would inadvertently turn off CAP_SECURE_FIRMWARE, essentially putting themselves into secure boot mode even if the system as a whole is not running in that mode. That could cause things to break in mysterious ways. What it comes down to is that, if one is designing a capability-based system, one really must come up with the full list of needed capabilities at the outset. Back in 1998, when capabilities for Linux were being hashed out, nobody had UEFI secure boot in mind. So there is no relevant capability bit available, and adding one now is not really an option.
More recently, Matthew posted a new patch set that eliminates the new capability. Instead, all of the secure boot restrictions were tied to the existing flag controlling whether unsigned kernel modules can be loaded. Matthew's reasoning was that the restriction on module loading exists to prevent the loading of arbitrary code into the running kernel, so it made sense to lock down any other functionality that might make it possible to evade that restriction. Other developers disagreed, though, saying that they needed the ability to restrict module loading while still allowing other functionality — kexec_load() in particular — to be used normally. After some discussion, Matthew backed down and withdrew the patches.
Eventually he came back with what he called his final attempt at providing a kernel lockdown facility that wasn't tied to the secure boot mechanism itself. This time around, we have a new sysfs file at /sys/kernel/security/securelevel that accepts any of three values. If it is set to zero (the default), everything works as it always has, with no new restrictions. Setting it to one invokes "secure mode," in which all of the restrictions related to secure boot go into effect. Secure mode is also irrevocable; once it has been enabled, it cannot be disabled (short of compromising the kernel, at which point the battle is already lost). There is also an interesting "permanently insecure" mode obtained by setting securelevel to -1; the system's behavior is the same as with a setting of zero, but it is no longer possible to change the security level.
In the UEFI secure boot setting, the bootstrap code would take pains to set securelevel to one before allowing any processes to run. That helps to avoid race conditions where the system is subverted before the lockdown can be applied.
Some readers will, by now, have recognized that "securelevel" looks an awful lot like the BSD functionality that goes by the same name; it was clearly patterned after BSD's version. Amusingly, this is not the first time that securelevel has been considered for Linux; there was an extensive discussion on the subject in early 1998, when Alan Cox was pushing strongly for a securelevel feature. At that time, Linus rejected the feature because he had something much better in mind: capabilities. As is usually the case, Linus won out, and Linux got capabilities instead of securelevel.
More than fifteen years later, it seems that we might just end up with both
mechanisms. Thus far, Matthew's latest patch set has not resulted in many
screams of agony, so it might just pass review this time — though, at this
point, it is almost certainly too late for 3.12. Meanwhile, Vivek Goyal
has posted the first version of a signed kexec
patch set that would limit kexec_load() to signed images.
That would allow some useful features (kdump, for example) to continue to
work properly in the secure boot environment without leaving
kexec_load() completely open. That, too, will make the secure
boot restrictions a bit more palatable and increase their chances of being
merged.
| Index entries for this article | |
|---|---|
| Kernel | Capabilities |
| Kernel | Security/UEFI secure boot |
| Security | Linux kernel |
| Security | Secure boot |