EFI GELI support ready for testers
Eric McCorkle
eric at metricspace.net
Sun May 29 14:10:24 UTC 2016
> On May 29, 2016, at 05:18, Konstantin Belousov <kostikbel at gmail.com> wrote:
>
>> On Sat, May 28, 2016 at 04:02:43PM -0400, Eric McCorkle wrote:
>>
>>>> On May 28, 2016, at 13:26, Konstantin Belousov <kostikbel at gmail.com> wrote:
>>>>
>>>>> On Sat, May 28, 2016 at 10:27:40AM -0400, Allan Jude wrote:
>>>>>> On 2016-05-28 04:36, Konstantin Belousov wrote:
>>>>>> On Fri, May 27, 2016 at 07:39:57PM -0400, Eric McCorkle wrote:
>>>>>> I am pleased to announce that my work to add support for GELI in the EFI boot loader (as well as perform more general refactoring) is now ready for testing. I am able to successfully detect multiple GELI partitions in boot1 and pass the keys into the kernel.
>>>>>
>>>>> Can somebody explain in which way this is useful ?
>>>>> Same question for the GELI code for non-EFI loader.
>>>>>
>>>>> BIOS cannot read raw data from the encrypted partition, so you need
>>>>> either old boot or the loader and some additional data on EFI boot
>>>>> partition anyway.
>>>>>
>>>>> Features adds significant amount of code, which must be maintained in
>>>>> parallel with the kernel code.
>>>>> _______________________________________________
>>>>> freebsd-hackers at freebsd.org mailing list
>>>>> https://lists.freebsd.org/mailman/listinfo/freebsd-hackers
>>>>> To unsubscribe, send any mail to "freebsd-hackers-unsubscribe at freebsd.org"
>>>>
>>>> The motivation for my work (GELI in boot2 and loader for non-EFI boot)
>>>> was supporting ZFS boot environments. Previously, when you use GELI you
>>>> needed to have two ZFS pools, one for an unencrypted /boot and one for
>>>> the encrypted /
>>>>
>>>> This breaks ZFS boot environments, since a snapshot of the root file
>>>> system won't include the correct kernel etc.
>>> Why cannot /boot included into the boot environment ?
>>> When I last saw Solaris, somewhere in its 10-th, any amount of
>>> filesystems could be added to the bootenv. But whatever the definition
>>> of bootenv is used, it exists at a level of the shell scripts and selecting
>>> the actual partition for loader. Throw thousand lines of code into
>>> the unstable and very hard to debug environment of loader looks somewhat
>>> unproductive.
>>
>> You misunderstand. Alan was talking about pure ZFS systems, where there is one big ZFS pool holding everything. You need to be able to access /boot obviously, but ZFS does not allow you to assign separate out data on to a single device. It creates a pool, which combines all devices.
>>
>> To have /boot unencrypted, you have to have a separate partition for just /boot, which is undesirable.
> Why it is undesirable ? You must have EFI boot partition by any means.
>
> And why it is simultaneously desirable to add a bunch of code to loader
> to hide this ?
>
It's undesirable because the whole point of ZFS is to have one ZFS volume for the whole system. The ESP is a minor annoyance that's unavoidable. But having to have a UFS volume or two ZFS systems just so I can keep the kernel, loader, and all the modules unencrypted is bad.
>>
>>>>
>>>> The final version of my geliboot took an extra effort to reuse the AES
>>>> code from sys/crypto/rijndael and sys/opencrypto and GELI directly from
>>>> sys/geom/eli to avoid maintaining a separate copy of that code in sys/boot
>>> Which means that kernel code must be also runnable in the strange and
>>> incompatible environment of the loader. I cannot see how this reduces
>>> code maintanability even a bit. I am already rather unhappy about ZFS
>>> kernel code being compiled as the userspace library, which hurt me
>>> and other people more than once. Now (?) the kernel code must be also
>>> verified for the loader.
>>
>> I mean, the loader has to be able to access the filesystems, there's no way around it (barring crazy coreboot stuff).
>>
>> Also, it should be possible to create a synthetic EFI test-harness that runs in *userland*. EFI is basically just a bunch of tables of function pointers, no bios interrupts or anything. As long as you implement the interfaces, any loader should also run in your synthetic environment. I had plans to look into this after this work is done.
> Of course EFI is not 'just a bunch of tables'. It might look so when you
> have tunnel vision and all you do is writing code making calls, but it
> is definitely not when you consider it as a part of platform.
From the standpoint of building a synthetic test environment to make it easy to test and debug boot and loader code, it is.
> UEFI makes it absurdly easy to distribute a code that gets executed on
> every boot before boot loaders. The most straighforward is to put UEFI
> driver binary into the UEFI FAT and register it into some variables (too
> lazy to remember details). You get this way both boot- and runtime-
> injection, officially supported. This was one of the reason for secure
> boot.
>
> To avoid trivial debunks, I put a note that of course drivers are not
> the only way for such code to appear on the system, without
> requiring any hardware modifications. The rich programming environment,
> with flat memory and full control over hardware, with easily explorable
> data structures, with documented and compatible ways to get code
> persistent are the characteristics of it.
>
> It is much easier to target such preboot env then to maintain dynamic
> list of the userspace escalation code.
Ok fine. But this is a risk anyone using EFI has to assume, and it doesn't end at the boot environment. There are plenty of nasty tricks an EFI boot environment can play that affect the OS as well. It's simply impossible for an OS to provide reliable security when the hardware or firmware is compromised, and this isn't limited to the boot loader.
This kind of thing is the reason for projects like coreboot gaining traction. If you really don't trust EFI, then the right answer is to use something like coreboot, or else don't use a system with EFI. Trying to compensate for this by keeping stuff out of the boot loader (and exposing vital parts of your system in a powered-off state) is futile, as a determined attacker can just set things up to compromise the kernel anyway.
>
>>>>
>>>> Hopefully the work I did to make sys/opencrypto and sys/geom/eli more
>>>> reusable outside of the kernel will make it easier for Eric to do the
>>>> same for the EFI version.
>>>>
>>>> The motivation for the EFI version is the same, ZFS boot environments,
>>>> plus the obvious security advantages of having the kernel stored
>>>> encrypted rather than not.
>>>
>>> Obvious security advantages ? Seriously ?
>>> What is a single 'security' advantage of hiding the kernel ?
>>
>> There are plenty.
>>
>> Right up at the top, an attacker able to access /boot is free to tamper with anything, and can insert back-doors into the kernel, loader, or any module. If you assume that the attacker can't write, then they can still scan the kernel and modules for signatures for known vulnerabilities (since the kernel itself has a commit id and detailed version info, they can compare this against a CVE database). Even if they can't do that for some reason, they could still potentially exfilt a kernel address map for use in rootkit attacks against your system. Lastly, general security principles say to minimize the attack surface, which would definitely mean you should protect the core of the system.
> I cannot believe that somebody considers hiding kernel binary even
> remotely viable 'security' measure. But let me accompany that for a
> second and see what attack surface does the attacker get from knowing
> the kernel binary ?
What you completely ignore in all this is the capability for an attacker to tamper with the unencrypted kernel, loader, and drivers.
> From inspecting quirks in the syscalls it is (almost trivially) easy
> to reconstruct the date of kernel sources up to probably month, without
> causing much observable havoc in the system. The kern.osreldate is around
> as well, not only as sysctl, but also having some impact on the runtime
> behaviour of several syscalls and image activators.
>
> Same fingerprints are available from the sizes of the objects in UMA
> zones, readily available from vmstat -z. Changes in the basic kernel
> structures layout are reflected in the size changes.
>
> Facilities like procstat -kk would show code reorganization and refactoring
> changes.
>
> The list above was written after about 10 minutes spent on thinking how
> to identify the system sources without having access to binaries. I
> believe another 10 minutes would give even more suggestions.
This is all academic. Yes, maybe someone can piece together what version of things I'm running. Of course, they'd have no idea what my address map looks like, unless they can get my compiler version and custom kernel config. Maybe there's some way to do that too.
But they get all of this with 100% accuracy by scanning the unencrypted boot partition, AND they can tamper with any of it. It's *just* less secure, plain and simple.
>
>
>>> Since you noted a 'security', I realized that your changes decrypt the
>>> keys in the pre-kernel environment. In other words, very sensitive
>>> material becoming accessible to strange and unknown code which is
>>> managed by firmware.
>>
>> The likelihood of such an attack, while something I considered, is very low. A key should look like random binary data (much like the GUIDs prevalent in EFI code). I seriously doubt someone could write a firmware module that could dig through the memory state of an arbitrary OS's boot loader and find keys. You'd basically have to put a full online binary analysis framework in the firmware, and have it run fast enough to not be noticed. That would be some serious Turing award-worthy work kept secret from the world solely to build firmware backdoors. I don't think even Lenovo would go that far.
>>
>> (I know I mentioned scanning the kernel as a threat above; that's an offline attack that can be done on a copy. Online undetectable analysis of a running program is WAY harder.)
> Nobody would scan for random data looking like keys. If attacker is
> interested in keys from the freebsd loader or password decrypting the
> keys, the address of the memory region where the material is stored and
> region' layout are immediately available for any code collocated in the
> common address space.
No, they don't necessarily know the memory location at all. The keys are stored either in static variables (i.e. no symbol to tell you where it is), stack frames, or malloc data structures (and the static variable ones can be moved if it's REALLY a concern).
Plus you've got other operating systems with their own full disk encryption methods, as well as every revision of the FreeBSD boot loader.
Unless you're specifically targeting an individual for attack (in which case, going by your proposal, I could just tamper with their unencrypted kernel, so why would I bother with EFI tricks), you'd have to have either a huge number of profiles for every version of every OS boot loader, or you'd have to have some kind of analysis framework.
> The hooks into the interfaces and pre-recorded sequence of calls for
> the initialization of loader give unique execution profile which allow
> to determine the password and key storage location.
And this changes on every platform, with every revision, and is wildly different for other OSes. Moreover, this sort of attack is in no way unique to GELI, and could just as easily be used to insert a backdoor into a kernel, or anything else.
> Exact attack scenario is not the point I am trying to make. Instead, I
> point out that UEFI execution environment is shared one, with no walls
> between software components from arbitrary sources. It is absolutely
> unsuitable for exposing sensitive material. Even more laughtable to
> simultaneosly hide kernel binary.
>
> In contrast with the kernel environment, which even with ME/BMC/ACPI/UEFI
> runtime components, is less penetrable by trying to own the execution
> state of the machine.
I see no reason why. Sure, it's bigger, but any method that could recover a key from a boot loader should also work on a kernel. These sorts of things have been demonstrated all the time in conference presentations, academic publications, and other venues.
>
>> Besides, if someone wants to do evil things in firmware, I think it's far more likely they'd just backdoor the network or USB controllers to let someone send an override packet and execute bundled code in kernel mode. That works every time for every OS and doesn't require surreptitiously picking apart arbitrary programs' data structures in real time.
> Of course backdoring hardware is order of magnitude harder and requires
> physical access to the machine. The realistic action by level of script
> kiddie is getting UEFI driver installed.
It seems you're considering only one kind of attacker, and specifically not the kind that are relevant to disk encryption.
The whole point of disk encryption IS to deal with people who have varying levels of physical access to your machine. By definition, the script kiddie of which you speak would be running their attacks when the encrypted partitions are all attached. So they'd have access to all the data anyway. The same holds for a more sophisticated attacker. Why would they bother with a fancy EFI malware when they already have access to all the data anyway.
I'll admit there may be some kind of persistent threat scenario where someone plants EFI malware to sniff keys at a later date, but this seems extraordinary rare. By contrast, data protection is a real thing: many organizations have requirements for full disk encryption, and there are people in the world who have to deal with getting harassed and having their computers seized and searched.
> To close this branch, backdored busmasters (but not ME) are
> trivially mitigated by enabling IOMMU, FWIW.
>
>> For what it's worth, I have considered possible hardening measures: using alias GUIDs to disguise interfaces and adding per-build padding or XOR masks to structures with keys to throw off scanners. However, I don't think the threat is realistic enough to warrant such measures.
> I do not see a way to comment about the measures, without adding
> the word 'theatre'.
Right, which is why I decided they aren't worth it.
>
>>> Putting aside ineradicable evil of Intel ME or its
>>> AMD twin, and SMM handler, you are exposing keys to whatever code happen
>>> to inhabit the preboot env, not neccessary originating from the firmware
>>> vendor. Secure Boot would not help there, since when the measurement
>>> indicate that untrusted component was there, it is too late. I.e.,
>>> you making much easier for malice hacker to steal the keys.
>>
>> I mean, the same argument holds for the kernel, if not moreso. You have ACPI bytecode, SMM, device blobs, anything that the boot environment chose to keep around for runtime by marking that memory as "unavailable", and of course, the evil that is Intel ME. This is an unavoidable risk of having keys in main memory ever.
> See above.
>
>> If you really want any kind of safety from this, you need to use a hardware crypto device that stores keys in a separate memory (which, I specifically designed things to make it easy to add support for one). Of course, you then have to trust the device...
>
> No, what I want is avoiding bad choices which are seemingly made just
> because code can be written. Code exposing sensitive material is written
> for very weak and leaking environment, at least without secure boot
> turned on. Arguments supporting that decision reference 'security' by
> e.g. hiding kernel binary, and undesire to have /boot partition. Please
> take a step outside and see bigger picture.
I'd say the same to you...
You are only considering one kind of scenario. Alan's and my work is aimed at protecting systems from attackers that have some level of physical access to the machines. There are people in the world who have to contend with this sort of thing, and the major competing OSes (Apple, Linux, MS, etc) all have their own full-disk encryption solutions.
Moreover, my work is aimed at tamper-resilience at the OS level. Yes, I know OS tamper-resistance can be undermined without trusted firmware, but we are seeing projects like coreboot and others working toward that end. Moreover, these kinds of efforts are themselves pointless when the OS does things like leaving the most sensitive part of the system wide open to tampering.
Speaking personally, I would like to see a system where I have full disk encryption combined with EFI secure boot with a unique platform key stored on my encrypted disk. Having an unencrypted boot partition in such a system just opens up more attacks, plain an simple. (I would also like to see an open source firmware and no ME, but that's out of scope here)
Finally, it's not like Alan's and my work *forces* anyone to use GELI. If you are really worried about some scenario where an attacker doesn't have physical access and needs to backdoor the firmware to sniff keys so they can access data they can't see despite being able to write to firmware, then fine, don't use GELI at boot. Better yet, use the compile flag to remove it from boot1 and loader completely. But if you want full disk encryption, you have the option of having it.
More information about the freebsd-hackers
mailing list