Skip to content

Conversation

@vdagonneau-anssi
Copy link
Contributor

For now, it enrolls db, KEK and then PK as long as these
keys are available at the root on the ESP and Secure Boot
is in Setup Mode. The files should be named db.auth,
KEK.auth and PK.auth.

You also need to add the auto-enroll yes option to your
systemd-boot config file.

The files need to be prepared with cert-to-efi-sig-list
and then signed with sign-efi-sig-list.

Here is a short example to generate your own keys and the
right files for auto-enrollement.

keys="PK KEK DB"
for key in ${keys}; do
	openssl req -new -x509 -subj "/CN=${key}/ -keyout "${key}.key" -out "${key}.crt"
	openssl x509 -outform DER -in "${key}.crt" -out "${key}.cer"
	cert-to-efi-sig-list -g "${uuid}" "${key}.crt" "${key}.esl"
done

sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth
sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth
sign-efi-sig-list -c KEK.crt -k KEK.key db DB.esl DB.auth

Once these keys are auto-enrolled, the UEFI binaries on the ESP
NEED to be signed in order to run. You can sign the binaries
with the sbsign tool, for example:

sbsign --key DB.key --cert DB.crt bzImage --output $ESP/bzImage

@vdagonneau-anssi
Copy link
Contributor Author

I see that the pull request #18716 already covers that need but since it has not been merged I felt like I could propose an alternative implementation.

This would be a very cool feature to have in systemd.

Copy link
Member

@poettering poettering left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the concept and the simplicity of this very much.

I wonder about the right names for this stuff. i.e. I think in the long run we should probably synthesize boot menu entries from these things if people want that. While the initial patch doesn't have to implement that the way we organize the directory hierarchy should prepare for this I think, so that we can extend this later naturally.

Also, shouldn't the suffix for the key files be ".esl"? What made you pick ".auth"?

So, taking inspiration from https://systemd.io/BOOT_LOADER_SPECIFICATION maybe we should organize the directories like this:

below the ESP define a directory /loader/keys/. Below that dir there are a bunch of mor dirs, one for each set of keys to enroll. each of these dirs would eventually synthesize one additional boot menu entry, i.e. if you have a dir called "foobar" this would create a boot menu entry "Enroll 'foo' keys" or so. And one of those dirs would be special: the one called "auto", which would be the one we implicitly roll before showing the menu.

example:

$ESP/loader/keys/auto/db.esl
$ESP/loader/keys/auto/kek.esl
$ESP/loader/keys/auto/pk.esl
$ESP/loader/keys/Linux Only/db.esl
$ESP/loader/keys/Linux Only/kek.esl
$ESP/loader/keys/Linux Only/pk.esl
$ESP/loader/keys/Linux and Windows/db.esl
$ESP/loader/keys/Linux and Windows/kek.esl
$ESP/loader/keys/Linux and Windows/pk.esl

The first three keys would be auto-enrolled when we find the sysem in setup mode

the other six keys would result in a boot menu entry "Enroll Linux Only Keys" and "Enroll Linux and Windows Keys".

(Of course, actually putting both the "auto" dir and the other two in the same ESP is kinda pointless, since the autoenrollment of the the "auto" one means we left setupmode already, so consider this an illustrative maximally populated example)

The usecase for doing enrollment via the boot menu is that this allows image builders to provide two sets of keys: one where the PK only signs the vendor's own kek entries, and one where it signs both the vendor's kek and the windows one. The user then can pick one of the two options in the boot menu, depending on whether they want to allow exclusively the image to run it, or want to dual boot with window.

As mentioned, it's totally fine if the initial patch doesn't do the menu thing (though the additional work for it is pretty easy I think). The main point I am trying to make is that we should for the auto-enroll logic already use a path that fits into this, i.e. use /loader/keys/auto/ for that path.

Does that make sense?

@poettering
Copy link
Member

@DaanDeMeyer would be great if you could have a look too, and comment?

@vdagonneau
Copy link

I like the concept and the simplicity of this very much.

I wonder about the right names for this stuff. i.e. I think in the long run we should probably synthesize boot menu entries from these things if people want that. While the initial patch doesn't have to implement that the way we organize the directory hierarchy should prepare for this I think, so that we can extend this later naturally.

Thank you for your feedback. My goal with that patch was to get the most common case done: enrolling the complete set of db, KEK and PK at once. I do agree though that some extensibility would be nice.

Also, shouldn't the suffix for the key files be ".esl"? What made you pick ".auth"?

The suffix here is ".auth" instead of just ".esl" because the files provided need to be signed efi sig lists instead of just efi sig lists.

One of the reason why this PR is relatively small is because I'm only accepting fully prepared signed efi sig lists. The easiest way to generate these files in userspace I found is with efitools. The manpage for the sign-efi-sig-list has examples using the ".esl" extension for unsigned sig lists and the ".auth" extension for signed sig lists. I decided to follow that convention.

I'm open to changing it however.

Man page for reference: https://manpages.ubuntu.com/manpages/bionic/man1/sign-efi-sig-list.1.html

So, taking inspiration from https://systemd.io/BOOT_LOADER_SPECIFICATION maybe we should organize the directories like this:

below the ESP define a directory /loader/keys/. Below that dir there are a bunch of mor dirs, one for each set of keys to enroll. each of these dirs would eventually synthesize one additional boot menu entry, i.e. if you have a dir called "foobar" this would create a boot menu entry "Enroll 'foo' keys" or so. And one of those dirs would be special: the one called "auto", which would be the one we implicitly roll before showing the menu.

example:

$ESP/loader/keys/auto/db.esl
$ESP/loader/keys/auto/kek.esl
$ESP/loader/keys/auto/pk.esl
$ESP/loader/keys/Linux Only/db.esl
$ESP/loader/keys/Linux Only/kek.esl
$ESP/loader/keys/Linux Only/pk.esl
$ESP/loader/keys/Linux and Windows/db.esl
$ESP/loader/keys/Linux and Windows/kek.esl
$ESP/loader/keys/Linux and Windows/pk.esl

The first three keys would be auto-enrolled when we find the sysem in setup mode

the other six keys would result in a boot menu entry "Enroll Linux Only Keys" and "Enroll Linux and Windows Keys".

(Of course, actually putting both the "auto" dir and the other two in the same ESP is kinda pointless, since the autoenrollment of the the "auto" one means we left setupmode already, so consider this an illustrative maximally populated example)

The usecase for doing enrollment via the boot menu is that this allows image builders to provide two sets of keys: one where the PK only signs the vendor's own kek entries, and one where it signs both the vendor's kek and the windows one. The user then can pick one of the two options in the boot menu, depending on whether they want to allow exclusively the image to run it, or want to dual boot with window.

As mentioned, it's totally fine if the initial patch doesn't do the menu thing (though the additional work for it is pretty easy I think). The main point I am trying to make is that we should for the auto-enroll logic already use a path that fits into this, i.e. use /loader/keys/auto/ for that path.

Does that make sense?

It makes perfect sense. I'm going to address your other points first but I'll definitely submit changes to implement that folder hierarchy and the menu options.

@poettering
Copy link
Member

One of the reason why this PR is relatively small is because I'm only accepting fully prepared signed efi sig lists. The easiest way to generate these files in userspace I found is with efitools. The manpage for the sign-efi-sig-list has examples using the ".esl" extension for unsigned sig lists and the ".auth" extension for signed sig lists. I decided to follow that convention.

makes sense

@poettering
Copy link
Member

(btw, we want to merge "perfect" code, i.e. PRs that consist of logical steps, but not necessarily historical ones. i.e. we don't want commits that fix up earlier commits in the same PR. we want perfect bisectability, where each commit makes sense on its own. hence please eventually squash these commits)

@poettering poettering added the reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks label Aug 9, 2021
Copy link
Contributor

@medhefgo medhefgo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, something got messed up in your commit log. There's a ton of unrelated commits on the PR for some reason.

@poettering
Copy link
Member

there's something wrong with the git rebase?

@vdagonneau-anssi
Copy link
Contributor Author

Yes. Sorry about the messed up rebase and the poor status of the PR yesterday. I do appreciate your feedback and the work you are putting in this review.

I addressed some of your comments in the newest commit.

I am still missing a proper policy of what to do when the key set is not complete (missing one of PK, KEK or db) or they cannot be enrolled for some other reason.

The way I'm doing it now is that I am enrolling them one by one in order (db -> KEK -> PK) and error out as soon as any error (file read or var write) happens.

This makes sure that the PK is only enrolled after every other key has been successfully enrolled which is somewhat critical.

If the enrollement fails earlier (e.g. db passes but KEK fails) we still overwrote db but we will not set a PK without the KEK.

Now from my understanding it is somewhat OK to 'fuck up' the db because when the user switched to SetupMode I think db gets cleared anyway. Also as I am overwriting the db, next boot will perform OK if the KEK has been fixed.

That is my understanding and I'd welcome any input on that.

@vdagonneau-anssi vdagonneau-anssi force-pushed the auto-enrollement branch 2 times, most recently from 4f344a1 to 1d33c42 Compare July 13, 2022 13:18
Copy link
Contributor

@medhefgo medhefgo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really really don't like the splitting of boot.c. The stuff that's split out is not even used anywhere else and it would also be inconsistent with the other source files (pe.c for example). So I'd rather not see that commit.

@vdagonneau-anssi vdagonneau-anssi force-pushed the auto-enrollement branch 2 times, most recently from 47f63d9 to 1d33c42 Compare July 18, 2022 15:56
@vdagonneau-anssi
Copy link
Contributor Author

Thanks for the review, especially @medhefgo. Seems like systemd-boot is getting cleaned up quite a bit. I think I addressed most of the remaining issues.

I personally find the enum and the _MAX pretty ugly but I do think it makes the code much clearer so I'd rather keep it.

If you find it worth merging, I'll start working on integrating the key generation part into bootctl.

@vdagonneau-anssi
Copy link
Contributor Author

vdagonneau-anssi commented Jul 18, 2022

I also submitted a proposal for a talk at Linux Plumbers Conference on Secure Boot variables auto enrollment in the System Boot and Security MC. I am not sure if I'll be able to be there physically but I'll be open to discuss there in September.

Link: https://lpc.events/event/16/sessions/147/

@medhefgo
Copy link
Contributor

You're either not addressing stuff or messed up the force push. Also, please remove the boot.h split (I really don't like it).

@vdagonneau-anssi
Copy link
Contributor Author

Sorry, I messed up the force push. I definitely deleted the boot.h.

@WhyNotHugo

This comment was marked as outdated.

@patrakov

This comment was marked as outdated.

@vdagonneau-anssi

This comment was marked as outdated.

@WhyNotHugo

This comment was marked as outdated.

Copy link
Contributor

@medhefgo medhefgo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got a few nitpicks but otherwise looks good.

@DaanDeMeyer DaanDeMeyer added this to the v252 milestone Jul 28, 2022
…boot.

***DANGER*** NOTE ***DANGER***

This feature might result in your device becoming soft-brick as outlined
below, please use this feature carefully.

***DANGER*** NOTE ***DANGER***

If secure-boot-enrollment is set to no, then no action whatsoever is performed,
no matter the files on the ESP.

If secure boot keys are found under $ESP/loader/keys and secure-boot-enrollment
is set to either manual or force then sd-boot will generate enrollment entries
named after the directories they are in. The entries are shown at the very bottom
of the list and can be selected by the user from the menu. If the user selects it,
the user is shown a screen allowing for cancellation before a timeout. The enrollment
proceeds if the action is not cancelled after the timeout.

Additionally, if the secure-boot-enroll option is set to 'force' then the keys
located in the directory named 'auto' are going to be enrolled automatically. The user
is still going to be shown a screen allowing them to cancel the action if they want to,
however the enrollment will proceed automatically after a timeout without
user cancellation.

After keys are enrolled, the system reboots with secure boot enabled therefore, it is
***critical*** to ensure that everything needed for the system to boot is signed
properly (sd-boot itself, kernel, initramfs, PCI option ROMs).

This feature currently only allows loading the most simple set of variables: PK, KEK
and db.

The files need to be prepared with cert-to-efi-sig-list and then signed with
sign-efi-sig-list.

Here is a short example to generate your own keys and the right files for
auto-enrollement.

`
keys="PK KEK DB"
uuid="{$(systemd-id128 new -u)}"
for key in ${keys}; do
	openssl req -new -x509 -subj "/CN=${key}/ -keyout "${key}.key" -out "${key}.crt"
	openssl x509 -outform DER -in "${key}.crt" -out "${key}.cer"
	cert-to-efi-sig-list -g "${uuid}" "${key}.crt" "${key}.esl.nosign"
done

sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl.nosign PK.esl
sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl.nosign KEK.esl
sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl.nosign db.esl
`

Once these keys are enrolled, all the files needed for boot ***NEED*** to be signed in
order to run. You can sign the binaries with the sbsign tool, for example:

`
sbsign --key db.key --cert db.crt bzImage --output $ESP/bzImage
`

Example:

Assuming the system has been put in Setup Mode:

`
$ESP/loader/keys/auto/db.esl
$ESP/loader/keys/auto/KEK.esl
$ESP/loader/keys/auto/PK.esl
$ESP/loader/keys/Linux Only/db.esl
$ESP/loader/keys/Linux Only/KEK.esl
$ESP/loader/keys/Linux Only/PK.esl
$ESP/loader/keys/Linux and Windows/db.esl
$ESP/loader/keys/Linux and Windows/KEK.esl
$ESP/loader/keys/Linux and Windows/PK.esl
`

If auto-enroll is set, then the db, KEK and then PK are enrolled from the 'auto'
directory.

If not, three new boot entries are available to the user in order to enroll either the
'Linux Only', 'Linux And Windows' or 'auto' set of keys.
@patrakov
Copy link

patrakov commented Aug 1, 2022

Somewhat off-topic - what is the preferred tool to view which secure boot keys have been enrolled on a particular machine?

@vdagonneau-anssi
Copy link
Contributor Author

Somewhat off-topic - what is the preferred tool to view which secure boot keys have been enrolled on a particular machine?

From userspace and if you don't mind the UEFI cruft you can usually look at the efivarfs if you have it mounted.

@patrakov
Copy link

patrakov commented Aug 1, 2022

Somewhat off-topic - what is the preferred tool to view which secure boot keys have been enrolled on a particular machine?

From userspace and if you don't mind the UEFI cruft you can usually look at the efivarfs if you have it mounted.

Well, I was looking for something more human-readable. E.g., something that would recognize Microsoft certificates, other common certificates, or print the CN of anything custom.

@poettering
Copy link
Member

I also submitted a proposal for a talk at Linux Plumbers Conference on Secure Boot variables auto enrollment in the System Boot and Security MC. I am not sure if I'll be able to be there physically but I'll be open to discuss there in September.

Link: https://lpc.events/event/16/sessions/147/

Excellent! Thanks for doing that!

@poettering poettering merged commit e6b0cfa into systemd:main Aug 3, 2022
@medhefgo
Copy link
Contributor

medhefgo commented Aug 3, 2022

Just as I was testing this you merged this, so therefore we have #24189 now.

@vdagonneau-anssi I've changed the expected file ending from .esl to .auth as that is what is used in the sign-efi-sig-list as well as the arch wiki on secure boot. You got any objections to that?

@vdagonneau-anssi
Copy link
Contributor Author

Looks good to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.