I’ve posted about my work on EFI GELI support. This project is actually the first step in a larger series of changes that I’ve been sketching out since April. The goal of the larger effort is to implement tamper-resilience features at the OS level for FreeBSD. The full-disk encryption capabilities provided by GELI boot support represent the first step in this process.
Before I talk about the work I’m planning to do, it’s worth discussing the goals and the rationale for them. One of the keys to effective security is an accurate and effective threat model; another is identifying the scope of the security controls to be put in place. This kind of thinking is important for this project in particular, where it’s easy to conflate threats stemming from vulnerable or malicious hardware with vulnerabilities at the OS level.
Regarding terminology: “tamper-resistance” means the ability of a device to resist a threat agent who seeks to gain access to the device while it is inactive (in a suspended or powered-off state) in order to exfiltrate data or install malware of some kind. I specifically use the term “tamper-resilience” to refer to tamper-resistance features confined to the OS layer to acknowledge the fact that these features fundamentally cannot defeat threats based on hardware or firmware.
In our threat model, we have the following assets:
- The operating system kernel, modules, and boot programs.
- Specifically, a boot/resume program to be loaded by hardware, which must be stored as plaintext.
- The userland operating system programs and configuration data.
- The user’s data.
We assume a single threat agent with the following capabilities:
- Access and write to any permanent storage medium (such as a disk) while the device is suspended or powered off.
- Make copies of any volatile memory (such as RAM) while the device is suspended.
- Defeat any sort of physical security or detection mechanisms to do so.
Specifically, the following capabilities are considered out-of-scope (they are to be handled by other mechanisms):
- Accessing the device while powered on and in use.
- Attacks based on hardware or firmware tampering.
- Attacks based on things like bug devices, reading EM radiation (van Eyck phreaking), and the like.
- Attacks based on causing users to install malware while using the device.
Thus, the threat model is based on an attacker gaining access to the device while powered-off or suspended and tampering with it at the OS level and up.
It is important to note that hardware/firmware tampering is a real and legitimate threat, and one deserving of effort. However, it is a separate and parallel concern that requires its own effort. Moreover, if the OS level has weaknesses, no amount of hardware or firmware hardening can compensate for it.
The tamper resilience plan is based around the notion of protecting as much data as possible through authenticated encryption, using cryptographic verification to ensure that any part of the boot/resume process whose program must be stored as plaintext is not tampered with, and ensuring that no other data is accessible as plaintext while suspended or powered off.
The work on this breaks down into roughly three phases, one of which I’ve already finished.
Data Protection and Integrity
All data aside from the boot program to be loaded by the hardware (known in FreeBSD as boot1) can be effectively protected at rest by a combination of ZFS with SHA256 verification and the GELI disk encryption scheme. Full-disk encryption protects data from theft, and combining it with ZFS’ integrity checks based on a cryptographically-secure hash function prevents an attacker from tampering with the contents (this can actually be done even on encrypted data without an authentication scheme in play).
There is always at least one program that must remain unprotected by full-disk encryption: the boot entry-point program. Fortunately, the EFI platform provides a mechanism for ensuring the integrity of the boot program. EFI secure boot uses public-key crypto to allow the boot program to be signed by a private key and verified by a public key that is provided to the firmware. If the verification fails, then the firmware informs the user that their boot program has been tampered with and aborts the boot.
In an open-source OS like FreeBSD, this presents an effective protection scheme along with full-disk encryption. On most desktops and laptops, we build the kernel and boot loaders on the machine itself. We can simply store a machine-specific signing key on the encrypted partition and use it to sign the boot loader for that machine. The only way an attacker could forge the signature would be to gain access to the signing key, which is stored on an encrypted partition. Thus, the attacker would have to already have access to the encrypted volume in order to forge a signature and tamper with the boot program.
To achieve the baseline level of protection, we need to ensure that the plaintext boot program is signed, and that it verifies the signature of a boot stage that is stored on an encrypted volume. Because of the way the EFI boot process works, it is enough to sign the EFI boot1 and loader programs. The loader program is typically stored on the boot device itself (which would be encrypted), and loaded by the EFI LOAD_IMAGE_PROTOCOL interface, which performs signature verification. Thus, it should be possible to achieve baseline protection without having to modify boot1 and loader beyond what I’ve already done.
There is, of course, a case for doing signature verification on the kernel and modules. One can even imagine signature verification on userland programs. However, this is out-of-scope for the discussion here.
Suspend/resume represents the most significant tamper weakness at the present. Suspend/resume in FreeBSD is currently only implemented for the suspend-to-memory sleep state. This means that an attacker who gains access to the device while suspended effectively has access to the device at runtime. More specifically, they have all of the following:
- Access to the entire RAM memory state
- Sufficient data to decrypt all mounted filesystems
- Sufficient data to decrypt any encrypted swap partitions
- Possibly the signing key for signing kernels
There really isn’t a way to protect a system that’s suspended to memory. Even if you were to implement what amounts to suspend-to-disk by unmounting all filesystems and booting the kernel and all programs out to an encrypted disk storage, you still resume by starting execution at a specified memory address. The attacker can just implant malware in that process if they have the ability to tamper with RAM.
Thus, the only secure way to do suspend/resume is to tackle suspend-to-disk support for FreeBSD. Of course, it also has to be done securely. The scheme I have in mind for doing so looks something like this:
- Allow users to specify a secure suspend partition and set a resume password. This can be done with a standard GELI partition.
- Use the dump functionality to write out the entire kernel state to the suspend partition (because we intend to resume, we can’t do the usual trick of dumping to the swap space, as we need the data that’s stored there)
- Alternatively, since the dump is being done voluntarily, it might be possible to write out to a filesystem (normally, dumps are done in response to a kernel panic, so the filesystem drivers are assumed to be corrupted).
- Have the suspend-to-disk functionality sign the dumped state with a resume key (this can be the signing key for boot1, or it can be another key that’s generated during the build process)
- Make boot1 aware of whatever it needs to know for detecting when resuming from disk and have it request a password, load the encrypted dumped state, and resume.
There are, of course, a lot of issues to be resolved in doing this sort of thing, and I imagine it will take quite some time to implement fully.
Once these three things are implemented, we’d have a baseline of tamper-resilience in FreeBSD. Of course, there are ways we could go further. For one, signed kernels and modules are a good idea. There has also been talk of a signed executable and libraries framework.
My GELI EFI work is complete and waiting for testing before going through the integration process. There are already some EFI signing utilities in existence. I’m currently testing too many things to feel comfortable about trying out EFI signing (and I want to have a second laptop around before I do that kind of thing!); however, I plan on getting the baseline signed boot1 and loader scheme working, then trying to alter the build process to support automatically generating signed boot1 and loader programs.
The kernel crypto framework currently lacks public-key crypto support, and it needs some work anyway. I’ve started working on a design for a new crypto library which I intend to replace the boot_crypto code in my GELI work and eventually the code in the kernel. I’ve also heard of others working on integrating LibreSSL. I view this as a precursor to the more advanced work like secure suspend/resume and kernel/module signing.
However, we’re currently in the middle of the 11 release process and there are several major outstanding projects (my GELI work, the i915 graphics work). In general, I’m reluctant to move forward until those things calm down a bit.