Locking down hibernation for the winter

· LWN.net

8 min read Original article ↗
[+] Proceed to the article

When a computer hibernates, the kernel writes the contents of memory to disk and then shuts the system down. Resuming from hibernation involves reading that data back into memory, and then having the kernel pick up where it left off. Unfortunately, the kernel does not have any way to know that the data it is reading back has not been tampered with, which is why lockdown, an optional kernel feature that attempts to prevent modifications to the running kernel, disables hibernation. Matthew Garrett, who has been working on lockdown for many years, has shared a patch set that aims to fix this problem. His solution must also protect against downgrade attacks (where an attacker reboots the computer with an older kernel version to bypass new protections), which complicates the implementation.

The basic idea of Garrett's patch set is not complicated: sign the saved hibernation data with a cryptographic key that only the kernel has access to. The problem turns out to be figuring out how to obtain such a key. A static, known key is useless for this purpose, since it could be used to forge the signature. A key stored in the computer's trusted platform module (TPM) is a better choice. The kernel sits between the TPM and user space, and so it can forbid user space to read the chosen key, keeping it secure. Additionally, the TPM can be configured to withhold the key when booted from a different kernel, although this is not as straightforward as it may seem, because older kernels will still, for example, possess correct secure-boot signatures.

The primary motivation behind involving the TPM is to avoid downgrade attacks. Given that older kernels won't know to forbid TPM access, an attacker could, theoretically:

  1. From a newer kernel with lockdown enabled, trigger hibernation.
  2. Boot an older kernel, with resume and swap disabled.
  3. Ask the older kernel for access to the chosen TPM key.
  4. Tamper with the saved hibernation data and re-sign it.
  5. Reboot into the newer kernel, allowing the system to resume from the tampered data.

One of the core features of a TPM is the ability to lock down secrets such that they can only be read when the hardware is in a known state. The TPM relies on platform-configuration registers (PCRs) to keep track of hardware state. These registers cannot be written directly. Instead, the TPM resets them to zero when the system boots, and then software running on the system can provide values that are hashed with the current state of the PCR to produce a new value.

So, for example, when the computer's firmware starts executing, it can check a signature on the bootloader, and add a marker to a specific PCR to indicate that it validated the signature against a particular secure-boot key. Then the bootloader can do the same thing to check the state of the operating system and the attached hardware, and so on. In theory, this ensures that the PCR can only reach a particular state when the operating system and hardware have not been tampered with.

In practice, the implementation is more complicated. There are multiple PCRs that are used for different purposes. For example, systemd uses PCR 11 to indicate which boot phase the system is in. This can be used to ensure that a key stored in the TPM (a disk-encryption key, for example) is only accessible while the initial ramdisk (initrd) is running. When the system is about to transition to the normal root filesystem, systemd writes "leave-initrd" to the PCR, changing its hash in a way that cannot be undone until the system is rebooted. The process of writing an additional piece of information to a PCR to invalidate a key associated with it is called "capping" the PCR.

If the kernel had a PCR that it capped prior to starting user space, it could extend that PCR with a new value, bind a key to the resulting PCR state, and thereby ensure that the key was only readable by new versions of the kernel and not by old versions. The kernel does not currently cap any PCRs, however, leaving that up to user space. So, a malicious user-space application running on an old kernel could still extend a PCR with the value that the new kernel expects, thereby putting the TPM into the "new kernel" state even on an old kernel.

The solution is "something horrifying" that Garrett came up with. PCR 5 is used by the UEFI firmware to perform hardware measurements, and is capped when ExitBootServices() is called, to prevent later components from faking hardware information. The kernel starts executing its boot stub before UEFI exits, since it may need to communicate with the firmware to bring the system up. Therefore, Garrett has added code to the boot stub to insert a marker into PCR 5 indicating that the kernel supports blocking user-space access to a TPM key before the firmware caps it. Since user space is not started until after that point, it has no way to fake the marker, even on an older kernel.

On its own, however, this is not enough. The state of PCR 5 only demonstrates that the computer booted with a new-enough kernel; it doesn't prevent user-space from creating its own key that is bound to that state of PCR 5, and then signing the hibernation data with that key. When the new kernel boots after hibernation, it would see a signature by a key bound to PCR 5, but it would still not be able to prove that key was created inside the kernel (and therefore hidden from user space).

So, Garrett's patch set goes further. Interactions with the TPM can be divided into individual sessions. His code allows those sessions to be audited, generating a signed digest. Those digests are signed by the TPM itself, which means that having a signed digest proves that, at some point, those commands really were sent to that particular TPM, and produced those particular results.

Combined with the use of PCR 5, the entire procedure to produce a signed hibernation image becomes:

  1. The new kernel boots, writing a marker to PCR 5.
  2. The firmware caps PCR 5, preventing user space from mimicking the kernel's marker.
  3. The kernel starts a new session with the TPM, where it checks that PCR 5 is in that state and then generates a new signing key.
  4. The transcript of the session is hashed, signed by the TPM, and stored alongside the hibernation data.
  5. The hibernation data is signed with the newly created key.

Then, on reboot, the kernel goes through a number of verification steps before loading the hibernation image:

  1. The kernel checks that the signed transcript digest was signed by the current TPM.
  2. It checks that replaying the transcript produces the same digest.
  3. It checks that the transcript holds the correct commands to the TPM, and only those.
  4. It checks that the transcript read PCR 5, and that the value matches the current value of PCR 5.
  5. Finally, it checks that the key produced by the transcript is a valid signing key that was used to sign the hibernation data.

The end result of all of this work is that users running lockdown kernels should be able to enable hibernation, and trust that the kernel cannot be tampered with between the computer shutting down and resuming from hibernation. Attackers cannot change the data directly, because it is signed; cannot access the key used to do the signing, because the kernel prevents it; cannot work around that restriction with an old kernel, because the TPM binds the key to a state that only new kernels can produce; and cannot create their own key that would be accepted, because they cannot forge the key-creation transcript.

Garrett's patch set has received a small amount of comment. James Bottomley asked about using a hardware feature called "localities" (an access-control mechanism that restricts operations to specific CPU privilege levels) to implement something like this more simply. Garrett agreed that localities "are very clearly the Right Way to deal with this," but did not think that the kernel could make use of them, because hardware support is typically limited to specialty devices. Most consumer devices have a TPM, but not one that supports the necessary extensions.

Bottomley also pointed out that having the firmware cap PCR 5 is not universal; his XPS-13 9380 Core i7 (released in 2019) does not cap that register. Garrett was disappointed to hear that, but still thought the patch set was worthwhile, given that newer computers do tend to cap PCR 5. Also, the implementation could potentially be swapped out for other hardware-specific solutions, if needed.

The patch set will need a second version to respond to the reviews. Garrett has been working on enabling hibernation under lockdown since 2021, but this time it seems that he's settled on a design that systematically prevents downgrade attacks. It may not be too much longer until his goal is finally realized.



Did you like this article?? Subscribe now at the special discounted rate to get a lot more like it.