Skip to content

Add a utility to choose between Secure Boot loaders.#294

Open
vathpela wants to merge 44 commits intorhboot:mainfrom
vathpela:sbchooser
Open

Add a utility to choose between Secure Boot loaders.#294
vathpela wants to merge 44 commits intorhboot:mainfrom
vathpela:sbchooser

Conversation

@vathpela
Copy link
Member

@vathpela vathpela commented Mar 3, 2026

This adds a utility to choose between bootloaders based on UEFI trust databases, either enrolled on the local system or specified manually.

The idea here is that a distro will provide several of the same shim binary in their packaging, i.e. shimx64.msft2011.efi, shimx64.msft2023.efi, shimx64.msft2011.msft2023.efi, and it'll sort them by how appropriate they are for the current system (or test databases provided on the command line), so then the OS packaging or installer can determine what to actually install as the bootloader.

Note that this is on top of #298 .

@vathpela vathpela force-pushed the sbchooser branch 10 times, most recently from cedce98 to 0bbee93 Compare March 4, 2026 19:25
vathpela added 3 commits March 4, 2026 14:28
This changes our abixml generation in a few ways:

- no longer include file locations
- no longer include architecture information
- uses hashes for the type-ids instead of enumerating them

This should enhance the ability to read updates to these files in the
future.

Signed-off-by: Peter Jones <pjones@redhat.com>
EFISECDB_SOURCES was missing from ALL_SOURCES.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
@vathpela vathpela marked this pull request as ready for review March 4, 2026 19:45
@vathpela vathpela requested a review from nfrayer March 4, 2026 19:45
vathpela added 13 commits March 6, 2026 12:18
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
It's 2026 and we have compilers that support '#pragma once' instead of
'#define' include guards.  It's really a lot nicer.

Unfortunately we can't really use it for the external headers, because
we need the names to make sure we're always including the development
versions instead of the system versions.

Signed-off-by: Peter Jones <pjones@redhat.com>
GCC says this buffer is too small.  I don't quite see how, but it
matters little.

Signed-off-by: Peter Jones <pjones@redhat.com>
There are a few changes to efi_signature_list_t debug prints here:
- at a few places, add display sizes in hex as well, to make comparing
  to x509 dumps easier.
- at the place where we complain about not having valid x509 and then
  do nothing with that, the address it's comparing is wrong.  This leads
  to making it look like the "invalid" x509 is related to the problem
  you're debugging, when it isn't related and may not even be invalid.
  This patch fixes that location.

Signed-off-by: Peter Jones <pjones@redhat.com>
enums in C are absolutely hopeless in many ways, and one of them is that
they don't get their own namespace.  Unfortunately that means if you
named a value in an enum "SHA256" (for example) and you want to include
a header that also names something entirely different "SHA256", it
doesn't work.

This changes the name of our enum values in libefisec's public API so
that they're prefixed appropriately so as not to conflict with e.g.
openssl.

Hopefully this doesn't break any consumers, but it's how it always
should have been.  Unfortunately (and for reasons that aren't quite
clear to me) these values show up in our public ABI, and their /names/
show up in the .abixml files, so it might be a (minor) breaking change.

Signed-off-by: Peter Jones <pjones@redhat.com>
I don't think there was ever a reason not to export efi_secdb_visit_entries,
and I need it exported, so here it is exported.

Signed-off-by: Peter Jones <pjones@redhat.com>
In the /private/ headers, we need to be sure to always use quote
includes so that we always get headers from the development tree, not
the installed efivar-devel package or similar.

Signed-off-by: Peter Jones <pjones@redhat.com>
This changes the private efisec.h in a couple of ways:
- tells clangd we're exporting all the headers we import, so its useless
  "complain about your headers" feature stops complaining.
- includes the exported headers directly, so they'll never get included
  via some other path.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
This code initially supported sha256 and sha512, but not sha384.  That's
wrong, so this adds sha384.

Signed-off-by: Peter Jones <pjones@redhat.com>
This changes the annotated output for certs from:

0000002c                                       30 82 05 a4              |0...|  esl[0].signature[0].data (end:0x000005d4)
00000030  30 82 03 8c a0 03 02 01  02 02 13 33 00 00 00 16  |0..........3....|
00000040  36 bf 36 89 9f 15 75 cc  00 00 00 00 00 16 30 0d  |6.6...u.......0.|
00000050  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 5a 31  |..*.H........0Z1|

to:

0000002c                                                                        esl[0].signature[0].data (end:0x000005d4)
0000002c                                       30 82 05 a4              |0...|  /C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023
00000030  30 82 03 8c a0 03 02 01  02 02 13 33 00 00 00 16  |0..........3....|
00000040  36 bf 36 89 9f 15 75 cc  00 00 00 00 00 16 30 0d  |6.6...u.......0.|
00000050  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 5a 31  |..*.H........0Z1|

and for simple digests from:

0000002c                                       80 b4 d9 69              |...i|  esl[0].signature[0].data (end:0x0000004c)
00000030  31 bf 0d 02 fd 91 a6 1e  19 d1 4f 1d a4 52 e6 6d  |1.........O..R.m|
00000040  b2 40 8c a8 60 4d 41 1f  92 65 9f 0a              |.@..`MA..e..|

to:

0000002c                                                                        esl[0].signature[0].data (end:0x0000004c)
0000002c                                       80 b4 d9 69              |...i|  SHA256:80b4d96931bf0d02fd91a61e19d14f1da452e66db2408ca8604d411f92659f0a
00000030  31 bf 0d 02 fd 91 a6 1e  19 d1 4f 1d a4 52 e6 6d  |1.........O..R.m|
00000040  b2 40 8c a8 60 4d 41 1f  92 65 9f 0a              |.@..`MA..e..|

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
vathpela added 23 commits March 9, 2026 13:23
This adds loading for db and dbx, either from efivars (default) or from
files on the command line.

Signed-off-by: Peter Jones <pjones@redhat.com>
This parses the db and dbx entries, extracts the relevant authenticode
hashes and x509 details, and provides the comparitor for determining
which certificate has the highest security strength based on its digest
and encryption algorithms.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds the main functions to parse a PE file.

Signed-off-by: Peter Jones <pjones@redhat.com>
This actually uses the PE loader and adds command line support for
loading some of them.

Signed-off-by: Peter Jones <pjones@redhat.com>
This allows us to use the sbchooser utility with a workflow like "sort".

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds the authenticode hash algorithm itself, and also the code to
generate sha256, sha384, and sha512 hashes on a pe_file_t.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds checking for binaries with their hashes in db and/or dbx, and
helper functions to do something with the data which are currently
unused.

That includes the initial "scoring" of PE binaries, in this case just
based on if the hashes are in dbx or db.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds the infrastructure to sort according to how much we prefer
each PE file.

Currently the sort criteria is:

- revoked comes after non-revoked
- trusted comes before non-trusted
- lower security strength comes after higher security strength

And then revoked binaries and untrusted binaries are filtered on the
output pass.

Signed-off-by: Peter Jones <pjones@redhat.com>
db.msft2011					- db with the 2011 MS UEFI CA
db.msft2023					- db with 2023 UEFI CA
db.shim-13-0.2.fedora.x64.sha256		- db for shim-13 by sha256
db.shim-13-0.2.fedora.x64.sha384		- db for shim-13 by sha384
db.shim-13-0.2.fedora.x64.sha512		- db for shim-13 by sha512
db.shim-15-7.el7_2.x64.sha256			- db for shim-15-7 by sha256
db.shim-15-7.el7_2.x64.sha384			- db for shim-15-7 by sha384
db.shim-15-7.el7_2.x64.sha512			- db for shim-15-7 by sha512
shim-13-0.2.fedora.x64.nosigs.efi		- shim-13 with no signatures
shim-15-7.el7_2.x64.msft2011.efi		- shim-15-7 with 2011 sigs
shim-15-7.el7_2.x64.nosigs.efi			- shim-15-7 without sigs
shim-16.1-4.el10.x64.msft2011.efi		- shim with 2011 sig
shim-16.1-4.el10.x64.msft2023.efi		- shim with 2023 sig
shim-16.1-4.el10.x64.msft2011.msft2023.efi	- shim with both sigs

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-13-0.2.fedora.x64.nosigs.efi
  shim-15-7.el7_2.x64.nosigs.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The 2023 UEFI CA cert
  The sha512 digest of shim-15-7.el7_2.x64.nosigs.efi
  The sha256 digest of shim-13-0.2.fedora.x64.nosigs.efi

This should produce the following output (annotated here):

shim-15-7.el7_2.x64.nosigs.efi - allowed by sha512 digest
shim-15-7.el7_2.x64.nosigs.efi - same
shim-13-0.2.fedora.x64.nosigs.efi - allowed by sha256
shim-13-0.2.fedora.x64.nosigs.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
This parses the PE signatures and their participant certificates, but
it's not a hater.  It doesn't judge.  That's for later.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds helpers to determine if our certificates participating in PE
signatures are trusted or revoked by db or dbx.  They're not used just
yet.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds more helpers to propagate the trust information from our
certificates up to our signatures, and adds that evaluation to our PE
binary before doing comparisons.

A signature is trusted if any of the certificates participating in the
signature are trusted and none are revoked.

Signed-off-by: Peter Jones <pjones@redhat.com>
This evaluates how many bits of security each signature has, and adds
that and our certificate and signature trust metrics to the sorting
function for PEs.

Signed-off-by: Peter Jones <pjones@redhat.com>
This propagates the not_before and not_after dates on each certificate
up through signatures to the PE files themselves as
"earliest_not_before" and "latest_not_after", and adds those as
comparisons of last resort when sorting PE binaries.

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-13-0.2.fedora.x64.nosigs.efi
  shim-15-7.el7_2.x64.nosigs.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The 2023 UEFI CA cert
  The sha512 digest of shim-15-7.el7_2.x64.nosigs.efi

This should produce the following output (annotated here):

shim-15-7.el7_2.x64.nosigs.efi - allowed by sha512 digest
shim-15-7.el7_2.x64.nosigs.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-13-0.2.fedora.x64.nosigs.efi
  shim-15-7.el7_2.x64.nosigs.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The 2023 UEFI CA cert
  The sha512 digest of shim-15-7.el7_2.x64.nosigs.efi
  The sha256 digest of shim-13-0.2.fedora.x64.nosigs.efi

And a "dbx" which includes:
  The sha256 digest of shim-13-0.2.fedora.x64.nosigs.efi

This should produce the following output (annotated here):

shim-15-7.el7_2.x64.nosigs.efi - allowed by sha512 digest
shim-15-7.el7_2.x64.nosigs.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-13-0.2.fedora.x64.nosigs.efi
  shim-15-7.el7_2.x64.msft2011.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The sha512 digest of shim-13-0.2.fedora.x64.nosigs.efi

This should produce the following output (annotated here):

shim-13-0.2.fedora.x64.nosigs.efi - allowed by sha512 digest (strength 256)
shim-13-0.2.fedora.x64.nosigs.efi - same
shim-15-7.el7_2.x64.msft2011.efi - allowed by cert (strength 112)
shim-15-7.el7_2.x64.msft2011.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-15-7.el7_2.x64.msft2011.efi
  shim-16.1-4.el10.x64.msft2011.msft2023.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The 2023 UEFI CA cert

and a "dbx" which includes:
  The 2011 UEFI CA cert

This should produce the following output (annotated here):

shim-16.1-4.el10.x64.msft2011.msft2023.efi - allowed by 2023 cert
shim-16.1-4.el10.x64.msft2011.msft2023.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-16.1-4.el10.x64.msft2011.efi
  shim-16.1-4.el10.x64.msft2011.msft2023.efi
  shim-16.1-4.el10.x64.msft2023.efi

Against a "db" which includes:
  The 2011 UEFI CA cert
  The 2023 UEFI CA cert

and a "dbx" which includes:
  The 2011 UEFI CA cert

This should produce the following output (annotated here):

shim-16.1-4.el10.x64.msft2011.msft2023.efi - allowed by 2023 cert
shim-16.1-4.el10.x64.msft2011.msft2023.efi - same
shim-16.1-4.el10.x64.msft2023.efi - allowed by 2023 cert but starts later
shim-16.1-4.el10.x64.msft2023.efi - same

Signed-off-by: Peter Jones <pjones@redhat.com>
On some truly buggy machines, I still think[0] we might see a case where
only the first signature is trusted.  This lets us build the support for
a user to permanently tell us to only use the first signature.

[0] Despite /some/ growing amount of evidence

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-16.1-4.el10.x64.msft2011.efi
  shim-16.1-4.el10.x64.msft2011.msft2023.efi
  shim-16.1-4.el10.x64.msft2023.efi

Against a "db" which includes:
  The 2023 UEFI CA cert

This test puts "--first-sig-only" on the sbchooser command line, so all
but the first signature on each input should be ignored.

This should produce the following output (annotated here):

shim-16.1-4.el10.x64.msft2023.efi - allowed by 2023 cert
shim-16.1-4.el10.x64.msft2023.efi - same

Note that shim-16.1-4.el10.x64.msft2011.msft2023.efi is not included,
because its first signature is with the 2011 cert.

Signed-off-by: Peter Jones <pjones@redhat.com>
This adds an explainer mode to sbchooser, which attempts to tell the
user which PE binaries are allowed based on which db entries they are
trusted or revoked by.

Signed-off-by: Peter Jones <pjones@redhat.com>
"pesign -u 1 -r -i shimx64.msft2011.msft2023.efi -o shimx64.msft2011.efi"
leaves a security directory with one byte of padding at the end, causing
the parser here to read off the end of the binary and treat that as the
cert length, which makes it spin forever.

This patch checks for if there's actually enough size for the wincert
header and stops if there's not.

Additionally, I don't see anything in the spec that says it couldn't be
zero-padded quite a bit more, so if we find wincert->length to be 0, we
also stop iterating.

Signed-off-by: Peter Jones <pjones@redhat.com>
This test submits the following shims, each listed twice, in a random
order:

  shim-16.1-6.x64.onesig.efi
  shim-16.1-6.x64.onesig.efi

Against a "db" which includes:
  The 2011 UEFI CA cert

and a "dbx" which includes:
  nothing

This should produce the following output:

shim-16.1-6.x64.onesig.efi is trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is trusted by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in db
shim-16.1-6.x64.onesig.efi is trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is trusted by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in db

Signed-off-by: Peter Jones <pjones@redhat.com>
Comment on lines +17 to +18
"Usage: %s [OPTION...]\n"
" -d, --db=<db file> UEFI trusted key database\n"

Choose a reason for hiding this comment

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

Suggested change
"Usage: %s [OPTION...]\n"
" -d, --db=<db file> UEFI trusted key database\n"
"Usage: %s [OPTION...]\n"
"\nOptions:\n"
" -d, --db=<db file> UEFI trusted key database\n"

Harmonizes better with the other man pages.

Also missing --explain from the help.

@marta-lewandowska
Copy link

sbchooser is working well for me now. Thank you for the changes. I also like the --explain option: it works well.

@Foxboron
Copy link

Fwiw, I ran this on my local machine with self-enrolled keys. Not the intended purpose for this tool, but it works and the output is sensible.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants