rhboot / shim

UEFI shim loader
Other
873 stars 293 forks source link

RFE: add support for multiple second stage loaders #472

Open keszybz opened 2 years ago

keszybz commented 2 years ago

If a distribution wants to support multiple second stage bootloaders, it is hard with the current hardcoded filename (either grub<arch>.efi or the override of DEFAULT_LOADER set at compilation time). Please add support for having multiple second-stage bootloaders installed. The goal is to decouple shim from the second stage bootloader, so that the user can install one or more of the bootloader packages and the new efi binary will be picked up automatically, but at the same time, the bootloader packages don't step on each other's toes and can be updated / installed / removed independently.

I think this could be implemented as a glob or a prefix pattern so it's possible to add new second-stage bootloaders without modifying shim itself. E.g. Fedora could set DEFAULT_LOADERS=fedora-*.efi, and then shim would list all efi binaries present in ESP, and for the ones that match the glob, try them in turn. Or if a glob is too hard to implement, DEFAULT_LOADER_PREFIX=fedora-, and then try to boot anything that starts with the prefix and ends with .efi.

Conan-Kudo commented 2 years ago

Do we have a restriction to follow DOS 8.3 filenames (since the ESP is a FAT filesystem)?

keszybz commented 2 years ago

In pratice, it's usually VFAT, so there's no issue with long names. FAT12, you'd have 8 characters for the file name. Whoever does the installation and wants to support that would have to shorten the names. Continuing the example above, 'fdra' would be reasonable, leaving 4 characters for the boot loader name.

julian-klode commented 2 years ago

grub is the only 2nd stage loader that is allowed to be signed with the shim MS trust chain, so it's a bit pointless.

Conan-Kudo commented 2 years ago

Why is that the case?

keszybz commented 2 years ago

grub is the only 2nd stage loader that is allowed to be signed with the shim MS trust chain, so it's a bit pointless.

At least fwupd is regularly signed and used a second-stage bootloader with shim. Also, kernels are signed, and could be used as such, afaik. And obviously there are other bootloaders out there. If you have some references to the contrary, please link.

Also, note that this is about the filename. What matter for security is whether there is a signature, not what the name is used.

julian-klode commented 2 years ago

Well fwupd is an application not a loader. I don't see much point if you can have shim fall back from grub to fwupd or a kernel binary.

Regarding bootloader signing: grub is being signed, signing any additional bootloader increases the attack surface, hence only grub will be signed (the exception is that you can load a kernel directly; you can combine the kernel with systemd-boot stub too, as the entire binary is signed - however, systemd-boot stub is becoming increasingly complex (e.g. sysexts) and at some point one might have to pull the plug on that too and use a fork of an older version).

Note that we only have talked about distributions that already ship grub bootloader. We have not discussed signing alternative bootloaders for new distributions without grub; however, at least systemd-boot and pxelinux are barred from signing in general due to implementation quality concerns fundamental disagreement about how secure boot works.

e.g. if you sign systemd-boot or pxelinux with your key inside the shim, your next shim review will be rejected

julian-klode commented 2 years ago

One thing that comes to my mind where this is useful though is to provide A/B/... grub updates, such that shim can boot previous grub if new one is broken. But then it also needs to record if an bootloader failed to boot so it skips it next time?

Also we still like need a meta shim or something so that we can fallback to an older shim if a newer one is broken. Though I guess one can always setup the old shim as the fallback loader and have it chainload the new shim (for new keys, etc). And then configure latest shim as boot entry. Needs more thinking about. Well you can't exactly chainload due to breaking TPM measurements.

Also sure, grub is not being signed because it's great code, but for legacy reasons. We're stuck with it :/

keszybz commented 2 years ago

however, at least systemd-boot and pxelinux are barred from signing in general due to implementation quality concerns.

This has been mentioned a few times. I hope you understand that this is not very constructive and sounds like abusing your gatekeeper status to prevent newer and better projects from being used. If you have any concerns about the design or implementation of sd-boot, state them. You can write to systemd-devel@list.freedesktop.org, systemd-security@redhat.com, or file an issue in under https://github.com/systemd/systemd/issues/new. But making vague statements that can't be confirmed or denied is not in the spirit of open-source and not OK in general.

One thing that comes to my mind where this is useful though is to provide A/B/... grub updates

Yep. It might be also useful for development and such, where you might have "known good" version of grub (or something else), and an experimental one (even without automatic A/B switching).

Conan-Kudo commented 2 years ago

This has been mentioned a few times. I hope you understand that this is not very constructive and sounds like abusing your gatekeeper status to prevent newer and better projects from being used.

It's hard to state whether systemd-boot is better either, but it is bad that there's no reasonable way to have other projects become eligible. I would like @srs5694's rEFInd to be eligible too. It does implement shim protocol and he's basically one of the major experts of UEFI boot out there.

poettering commented 2 years ago

Well fwupd is an application not a loader.

systemd-boot isn't actually a "loader" either. It cannot load code into memory and run it. It doesn't do cryptographic verification or anything like that. The only thing it does is invoke other UEFI PE binaries throught the official UEFI PE APIs. That makes it a lot simpler and safer than Grub, because it carries zero code that validates or parses actual binary files, it defers to the firmware and shim that does that anyway.

Hence, it's a fancy menu for invoking UEFI PE binaries only. I'd call this an "application", not a "loader". Hence, by your own standards it should be OK.

Also note that systemd-boot is tiny compared to the other components discussed. It's a fraction of the codebase of grub, it's tiny compare to shim itself (even if if you discount the embedded openssl it carries, still just 1/3rd of the codebase of it.)

It appears the standards applied here are impossibly high, … and political in nature. In particular the vague comments on code quality are quite upsetting. I have the suspicion systemd's code checking/CI stuff is more comprehensive than shim's. And given that systemd is apparently good enough for all major distro's PID 1, I see no reason why systemd-boot should be too low quality, as you appear to suggest. It's written and maintaintained by the same people, and shares code with userspace.

I find it quite upsetting that you are OK with signing projects with a lot more complex codebases, that are generally understood to be problematic (grub, …) but a tiny, self contained tool such as systemd-boot is supposedly too terrible to touch.

poettering commented 2 years ago

e.g. if you sign systemd-boot or pxelinux with your key inside the shim, your next shim review will be rejected

A big distributor already does that actually already, to circumvent Grub. To my knowledge you have not cut them off yet.

Conan-Kudo commented 2 years ago

I would also argue that fwupd is considerably more dangerous than either rEFInd or sd-boot. If it's acceptable for shim to support launching fwupd and grub2, then it should be possible for shim to launch rEFInd and sd-boot.

julian-klode commented 2 years ago

e.g. if you sign systemd-boot or pxelinux with your key inside the shim, your next shim review will be rejected A big distributor already does that actually already, to circumvent Grub. To my knowledge you have not cut them off yet.

We sign the stub as part of a combined kernel binary in Ubuntu, and Cisco gets a fork of the stub eventually accepted, but those do not execute more binaries. Basically extends the kernel instead.

I would also argue that fwupd is considerably more dangerous than either rEFInd or sd-boot. If it's acceptable for shim to support launching fwupd and grub2, then it should be possible for shim to launch rEFInd and sd-boot.

fwupd adds very important features though, rEFInd and sd-boot implement subsets of grub, but don't add much value.

I find it quite upsetting that you are OK with signing projects with a lot more complex codebases, that are generally understood to be problematic (grub, …) but a tiny, self contained tool such as systemd-boot is supposedly too terrible to touch.

I'd very much prefer systemd-boot over grub myself. grub is begrudgingly accepted for legacy reasons :/ Certain people disagree very strongly in principle, and I understand the argument that signing it in addition to grub just increases the attack surface of a given shim without providing additional features.

Conan-Kudo commented 2 years ago

I would also argue that fwupd is considerably more dangerous than either rEFInd or sd-boot. If it's acceptable for shim to support launching fwupd and grub2, then it should be possible for shim to launch rEFInd and sd-boot.

fwupd adds very important features though, rEFInd and sd-boot implement subsets of grub, but don't add much value.

That's not actually true. In the case of rEFInd, @srs5694 made easily one of the most advanced EFI boot managers out there based on his experience of UEFI boot. One of the most important features of rEFInd that no other boot manager I know of can do is provide the ability to activate native UEFI services in the firmware despite the firmware UI not existing (such as managing UEFI firmware boot order, triggering UEFI network boot, renaming boot entries, etc.). It also provides several mechanisms for intelligent discovery and handling of multi-boot scenarios that I haven't seen any other boot manager do.

These features do not exist in GRUB and I'm not sure they ever will.

vathpela commented 2 years ago

Look. We do not need to discuss sd-boot or rEFInd, or what is an application vs a loader vs whatever in this thread. There are plenty of places for those discussions, but this is really not it. If you've got shim-review issues, take them up with reviewers, which you may notice is not attached to this repo.

None of that has any bearing on the feature being requested here, which is fundamentally a request to make shim be able to look for more than one loader, and we can limit our discussions to how to do that and what it would look like. I think it's a valid request, because it's easy for me to see a situation where we might want to load e.g. some test/validation application, and then launch a loader once it has completed, or to do A/B like @julian-klode suggested. But I do have some implementation concerns, which I'll follow up with.

Take anything else to a place more appropriate than shim's issues queue.

vathpela commented 2 years ago

Or if a glob is too hard to implement, DEFAULT_LOADER_PREFIX=fedora-, and then try to boot anything that starts with the prefix and ends with .efi.

Either of those is hard to implement for the network boot case, because we can't get directory listings. So probably the thing to do is define it like boot variables, boot${ARCH}####.efi, where #### is four hex characters, and when searching, start at 0000, and iterate 0001, ... 000f, 0010, etc. until we get an EFI_NOT_FOUND.

One thing that comes to my mind where this is useful though is to provide A/B/... grub updates, such that shim can boot previous grub if new one is broken. But then it also needs to record if an bootloader failed to boot so it skips it next time?

How would this wind up working with e.g. luks unlocking with tpm. Since any application we load will get measured, that failure will have a chain of extends with the intermediate failures, and you're not going to get keys unsealed. It seems like we need a reboot in this scenario to keep the measurements from locking us out.

martinezjavier commented 2 years ago

So probably the thing to do is define it like boot variables

I'm worried that using that approach could shorter the EFI storage life expectancy by making too many writes. Another option is just to use a config file in the ESP that contains the current EFI binary and previous one to boot. We could even use a CSV for that to prevent adding yet another parser in shim, since already uses CSV files for the fallback when there's no boot variables.

This wouldn't be less secure since the policy to boot a binary or not should be determined by dbx, SbatLevel, etc so an attacker getting write access to this A/B config file couldn't do more harm than getting write access to EFI variables and adding a fallback BOOTX64.CSV file.

How would this wind up working with e.g. luks unlocking with tpm

I think this is orthogonal to the feature requested here. The system should be designed in a way that the A/B update mechanism and TPM object sealing will work in tandem. This could be achieved for example by using TPM 2.0 Authorized Policies instead of static PCR values when sealing objects, but as said it should be up to the system to make sure that booting a different shim would be safe. This is also true with Secure Boot, updating shim with a version that couldn't be booted would lead to a failure.

julian-klode commented 2 years ago

I'm worried that using that approach could shorter the EFI storage life expectancy by making too many writes.

@martinezjavier We do not need variables, the proposal from @vathpela was to just name the files in a numbered fashion and try them until we fail to find.

However, this is also problematic in a sense as the range needs to be contiguous. It doesn't have to be for EFI boot variables because there's a BootOrder.

And well I guess one can just write the bootloaders to the boox64.csv. Once fb is integrated, shim should just try all the entries configured there when invoked via removable media path.