Commit Graph
231 Commits
Author SHA1 Message Date
Luca Boccassi 1441f2e992 Add padding to sbat section (#38621) 2025-08-19 17:49:07 +01:00
Zbigniew Jędrzejewski-Szmek ee45041da1 ukify: stop appending NUL to merged .sbat section
Our tools will strip trailing NULs, so this is not causing harm, except for
the corner case when the .sbat section is exactly of the maximum size and now
it wouldn't fit because we're one byte short.

But it also is not needed. Sections have an encoded size of the data and the
reader must use that and must be prepared to handle a text section that does
not end in NUL. Appending of the NUL was added in
c3f7501c4d, without any discussion of this
change. Since we didn't insert the NUL before, the tools must have been
prepared to work without it. I think it's better to keep the code clean and
not do this unnecessary step.
2025-08-19 11:50:28 +02:00
Zbigniew Jędrzejewski-Szmek ec1d031f3d ukify: fix insertion of padding in merged sections
The padding was done to expand the new section contents to the expected size of
the new section. And this then would be used for the content in the existing
section. The new section cannot be larger than the old section, but it can be
smaller. If the new section was smaller, then we'd not write enough padding and
the output file would be corrupted.

This was observed in CI when the .sbat section in the stub was padded to 1k.
The UKI with an .sbat section that was merged and was fairly short would hit
this scenario and be corrupted.
2025-08-19 11:08:35 +02:00
Zbigniew Jędrzejewski-Szmek 2ddbbb22db ukify: adjust comments 2025-08-19 10:46:36 +02:00
Zbigniew Jędrzejewski-Szmek dbd7b16819 ukify: extend error message when section is too small 2025-08-19 10:46:36 +02:00
Li Tian 776991a3f3 ukify: rstrip and escape binary null characters from 'inspect' output (#38607)
SBAT section of UKI may contain \u000 null characters. Rstrip them, and if there's anything left in the middle,
escape them so they are displayed as text.

Fixes #38606
2025-08-18 22:43:41 +01:00
Luca Boccassi 4f9f0e5041 ukify: drop NX bit from UKI if kernel doesn't have it
If the kernel is not NX_COMPAT ready (W^X memory compatible) then the
UKI should not be marked as NX_COMPAT ready either, as the kernel
section is the loadable code in the image.

https://microsoft.github.io/mu/WhatAndWhy/enhancedmemoryprotection/
https://www.kraxel.org/blog/2023/12/uefi-nx-linux-boot/

While the sd-stub EFI code itself is NX ready, it is more useful
to think of it as one unit of execution together with the kernel
it embeds, as that's what it is used for.

Fixes https://github.com/systemd/systemd/issues/38545
2025-08-14 03:49:20 +09:00
Luca Boccassi 3ddb2ee172 ukify: fix hwids section reproducibility
When there are multiple json files the glob list output is
not stable. Sort it before use.

Follow-up for 0333b9d589
2025-07-31 11:47:54 +01:00
DaanDeMeyer 1408505318 meson: Fix missing test dependencies
These test would fail when executed directly with meson test before
doing a build because the required dependencies are not declared, let's
fix that.
2025-07-14 13:07:29 +01:00
Zbigniew Jędrzejewski-Szmek 85830b0d62 ukify: fix version detection for aarch64 zboot kernels with gzip or lzma compression
Fixes https://github.com/systemd/systemd/issues/34780. The number in the header
is the size of the *compressed* data, so for gzip we'd read the initial part of
the decompressed data (equal to the size of the compressed data) and not find
the version string. Later on, Fedora switched to zstd compression, and there we
correctly use the number as the size of the compressed data, so we stopped
hitting the issue, but we should still fix it for older kernels.

I verified that the fix works for gzip-compressed kernels. I also made the same
change for the code for lzma compression. I'm pretty sure it is the right thing,
even though I don't have such a kernel at hand to test.

>>> ukify.Uname.scrape('/lib/modules/6.12.0-0.rc2.24.fc42.aarch64/vmlinuz')
Real-Mode Kernel Header magic not found
+ readelf --notes /lib/modules/6.12.0-0.rc2.24.fc42.aarch64/vmlinuz
readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
Found uname version: 6.12.0-0.rc2.24.fc42.aarch64
2025-07-10 13:37:07 +09:00
Luca Boccassi 6cc01c8cc4 ukify: fix parsing uname version with '+'
Debian started using '+' in the kernel uname version, which fails the
regex in ukify. Fix it.
2025-07-06 10:49:22 +01:00
Luca Boccassi 60bda55f5b ukify: when decompressing kernel before signing, call verify on decompressed file
Otherwise it will fail as it's an archive, not a PE file:

Invalid DOS header magic
Can't open image /boot/vmlinuz.old
/boot/vmlinuz.old is compressed and cannot be loaded by UEFI, decompressing
+ sbverify --list /boot/vmlinuz.old
=========================== short test summary info ============================
FAILED ../src/ukify/test/test_ukify.py::test_efi_signing_sbsign[3650] - subprocess.CalledProcessError: Command '['sbverify', '--list', PosixPath('/boot/vmlinuz.old')]' returned non-zero exit status 1.
FAILED ../src/ukify/test/test_ukify.py::test_efi_signing_sbsign[None] - subprocess.CalledProcessError: Command '['sbverify', '--list', PosixPath('/boot/vmlinuz.old')]' returned non-zero exit status 1.
FAILED ../src/ukify/test/test_ukify.py::test_inspect - subprocess.CalledProcessError: Command '['sbverify', '--list', PosixPath('/boot/vmlinuz.old')]' returned non-zero exit status 1.

Follow-up for 0dd03215f1
2025-07-04 12:39:43 +09:00
Luca Boccassi 7eb1cd2f0c Ukify tweaks (#36389) 2025-03-05 11:26:47 +00:00
Zbigniew Jędrzejewski-Szmek 6446c7d99b ukify: avoid treating invalid option as an argument
For some reason, argparse treats undefined options as positional args in
certain scenarios:

$ src/ukify/ukify.py --badopt='11'
ukify.py: error: unrecognized arguments: --badopt=11
$ src/ukify/ukify.py --badopt '11'
ukify.py: error: unrecognized arguments: --badopt
$ src/ukify/ukify.py --badopt '11 12'
Assuming obsolete command line syntax with no verb. Please use 'build'.
Traceback (most recent call last):
  File "/home/zbyszek/src/systemd/src/ukify/ukify.py", line 2497, in <module>
    main()
    ~~~~^^
  File "/home/zbyszek/src/systemd/src/ukify/ukify.py", line 2485, in main
    check_inputs(opts)
    ~~~~~~~~~~~~^^^^^^
  File "/home/zbyszek/src/systemd/src/ukify/ukify.py", line 671, in check_inputs
    value.open().close()
    ~~~~~~~~~~^^
  File "/usr/lib64/python3.13/pathlib/_local.py", line 537, in open
    return io.open(self, mode, buffering, encoding, errors, newline)
           ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '--badopt=11 12'

I suspect that this is some crap compat for Windows, where option parsing is
an even bigger mess than here.

Being told about positional args, when no positional args were specified is
confusing, so add a check for this.
2025-03-05 11:17:33 +01:00
Zbigniew Jędrzejewski-Szmek bd208a5421 ukify: drop dots from exception messages
In Python, exception messages are often embedded in surrounding text, so in
general they should not contain punctuation.

Also, no need to instantiate the exception object if no arguments are used.
2025-03-04 10:07:38 +01:00
Luca Boccassi cf4deeaf1e ukify: print all remaining log-like output to stderr
We want to be able to capture stdout for json and such, so convert
all remaining logging to stderr.
2025-02-17 22:20:32 +01:00
Daan De Meyer 7db71cd79f ukify: Fix --sign-profile= check for main profile
opts.profile is always an env file so we have to parse the default
value as well to check if it's in --sign-profile= or not.
2025-02-16 18:52:26 +01:00
Zbigniew Jędrzejewski-Szmek 40ff0dec08 test/ukify: deduplicate path specification 2025-02-14 17:27:12 +01:00
Luca Boccassi cdedc90caf ukify: do not insist on a stub being available when joining pcrsigs
It is not used in this case, so skip it, otherwise it will need to be
installed even if it is not used

Follow-up for 9876e88e23
2025-02-14 10:57:03 +00:00
Luca Boccassi 0dd03215f1 ukify: if the specified kernel is not a valid PE file try to decompress it
On some distros on some architectures (e.g.: Ubuntu arm64) the kernel is shipped as
a gzipped file, which the UEFI firmware does not understand.
If pefile fails to parse it, try to decompress it.
2025-02-14 10:57:03 +00:00
Luca Boccassi a6d51ae582 ukify: fix zboot parsing with zstd
The header starts with 'zstd', not 'zstd22':

$ ukify build --linux vmlinuz-6.13+unreleased-cloud-arm64 --initrd /boot/initrd.img-6.12.12-amd64 --output uki
Kernel version not specified, starting autodetection 😖.
Real-Mode Kernel Header magic not found
+ readelf --notes vmlinuz-6.13+unreleased-cloud-arm64
readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
Traceback (most recent call last):
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 2510, in <module>
    main()
    ~~~~^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 2499, in main
    make_uki(opts)
    ~~~~~~~~^^^^^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 1328, in make_uki
    opts.uname = Uname.scrape(linux, opts=opts)
                 ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 384, in scrape
    version = func(filename, opts=opts)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 374, in scrape_generic
    text = maybe_decompress(filename)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 221, in maybe_decompress
    return get_zboot_kernel(f)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 201, in get_zboot_kernel
    raise NotImplementedError(f'unknown compressed type: {comp_type!r}')
NotImplementedError: unknown compressed type: b'zstd\x00\x00'
2025-02-14 10:28:41 +00:00
Luca Boccassi fbc6fecf1a ukify: switch from zstd to zstandard
The zstd library does not support stream decompression, and it
requires the zstd header to contain extra metadata, that the kernel
build does not append:

$ file -k vmlinuz-6.13+unreleased-cloud-arm64
vmlinuz-6.13+unreleased-cloud-arm64: PE32+ executable (EFI application) Aarch64 (stripped to external PDB), for MS Windows, 2 sections\012- data
$ ukify build --linux vmlinuz-6.13+unreleased-cloud-arm64 --initrd /boot/initrd.img-6.12.12-amd64 --output uki
Kernel version not specified, starting autodetection 😖.
Real-Mode Kernel Header magic not found
+ readelf --notes vmlinuz-6.13+unreleased-cloud-arm64
readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
Traceback (most recent call last):
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 2508, in <module>
    main()
    ~~~~^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 2497, in main
    make_uki(opts)
    ~~~~~~~~^^^^^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 1326, in make_uki
    opts.uname = Uname.scrape(linux, opts=opts)
                 ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 382, in scrape
    version = func(filename, opts=opts)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 372, in scrape_generic
    text = maybe_decompress(filename)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 219, in maybe_decompress
    return get_zboot_kernel(f)
  File "/home/bluca/git/systemd/src/ukify/ukify.py", line 199, in get_zboot_kernel
    return cast(bytes, zstd.uncompress(f.read(size)))
                       ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
zstd.Error: Input data invalid or missing content size in frame header.

This appears to be by design:

https://github.com/sergey-dryabzhinsky/python-zstd/issues/53

Switch to python3-zstandard, which works.
2025-02-14 10:28:41 +00:00
Luca Boccassi 2ac8fcf656 ukify: add --pcr-certificate= parameter
Public keys and certificates are not the same, as the latter embeds more
information that the former, and other tools like sd-measure have distinct
parameters for each of them.
Add a new --pcr-certificate= parameter to ukify, and use it to pass certs
down to sd-measure, as an alternative to --pcr-public-key=. Do not allow
specifying both.
2025-02-09 22:24:00 +00:00
Luca Boccassi 32caed550f ukify: do not fail if pefile complains about hardcoded 256MB limit
pefile has an hardcoded limit to 256MB per section:

https://github.com/erocarrera/pefile/issues/396

When building an initrd with large firmware files and
lots of kernel modules, this limit can be reached.
Skip over those warnings.
2025-02-07 13:58:51 +00:00
Luca Boccassi 9876e88e23 ukify: add --pcrsig and --join-pcrsig arguments to append offline signature
Add a build parameter to take an existing UKI and attach a .pcrsig section
to it. This allows one to create a UKI with a .pcrpkey section with
--policy-digest to get the json output from sd-measure, sign the digest
offline, and attach the .pcrsig section with the signature later.
2025-02-07 13:58:51 +00:00
Daan De Meyer 38801c9129 ukify/measure: Revert changes to use SizeOfImage from Linux PE binary
With 19812661f1, we make sure at runtime
in the stub itself that SizeOfImage from the Linux EFISTUB PE binary is
taken into account, so there's no need to take this into account in ukify
itself. By reverting the ukify change, we again ensure that Misc_VirtualSize
reflects the actual size of the Linux EFISTUB PE binary in the .linux section
which lots of tooling depends on. It also makes sure we don't measure a bunch
of extra zeroes in the stub which should fix systemd-pcrlock measurements as
well.

This effectively reverts 2188c759f9 and
0005411352.

Fixes #35851
2025-02-04 14:11:38 +01:00
Zbigniew Jędrzejewski-Szmek 2e326aad7f ukify: Calculate section size more correctly (#36215)
We should only use Misc_VirtualSize if it's smaller than SizeOfRawData,
since in that case it'll be the non-aligned section size. Otherwise we
have to use SizeOfRawData to get the size on disk.
2025-02-03 10:17:37 +01:00
Ani Sinha 83bf58f39d uki: introduce support for a .efifw section
UKIs can be used to bundle uefi firmwares that can be measured and
used on a confidential computing environment. There can be more than one
firmware blob bundle, each one for a specific platform. Also firmware images
can themselves be containers like IGVM files that can in turn bundle the
actual firmware blob. This change is specifically for uefi firmwares, not
IGVM container files.

This change adds support to introduce a .efifw section in UKI that can be
used for firmware blobs/images. There can be multiple such sections and each
section can contain a single firmware image.

The matching .hwids entry for a specific platform can be used to select the
most appropriate firmware blob.

ukify tool has been also changed to support addition of a firmware image
in UKI.

Since firmware gets measured automatically, we do not need to measure it
separately as a part of the UKI.
2025-01-31 10:05:00 +01:00
Daan De Meyer 33b25fa11c ukify: Calculate section size more correctly
We should only use Misc_VirtualSize if it's smaller than SizeOfRawData,
since in that case it'll be the non-aligned section size. Otherwise we
have to use SizeOfRawData to get the size on disk.
2025-01-29 14:44:27 +01:00
Daan De Meyer 2d6ae4319c ukify: Fix two typing issues 2025-01-29 14:43:06 +01:00
Luca Boccassi c4c3eb03fd Minor fixes for ukify tests (#36197) 2025-01-28 18:49:42 +00:00
Luca Boccassi 25a253084a test: support slow test-ukify on Debian/Ubuntu
Kernels are installed in /boot/ so find them there too
2025-01-28 17:28:55 +00:00
Luca Boccassi bcca98c632 test: use local stub if available in test-ukify
It might not be available on a CI system in the system
path, so use the local one if it was built
2025-01-28 17:28:55 +00:00
Daan De Meyer b61efe6591 ukify: Add --sign-profile
Let's allow configuring which UKI profiles we generate signed PCR
measurements for since there are various types of profiles for
which we do not want to generate signed PCR measurements so that they
can not unlock the encrypted rootfs.

Fixes #36173.
2025-01-28 17:20:46 +00:00
Luca Boccassi d1429288ae ukify: add riscv32 to --efi-arch
It is supported in the arch mapping at the top and in elf2efi, so add it here too
2025-01-25 01:25:38 +00:00
Luca Boccassi abe0033556 ukify: add loongarch64 to --efi-arch 2025-01-25 01:25:38 +00:00
Luca Boccassi 606c5e7580 ukify: add --policy-digest option
Uses the newly added policy-digest verb of systemd-measure, for the same
purpose: build a UKI and get digests for the .pcrsig section out, so
that they can be offline signed and reattached
2025-01-21 09:19:49 +01:00
Luca Boccassi 7d64e2f368 ukify: print debug/progress messages to stderr
Otherwise json will be interleaved with plain text messages
2025-01-19 16:52:00 +00:00
Luca Boccassi 7bf31680ad ukify: pass through --json to systemd-measure
So that --measure --json prints usable json output
2025-01-19 16:22:38 +00:00
Daan De Meyer 96403d5121 tree-wide: Fix python formatting
The new release of ruff formats a few more things which causes linter
failures in CI so let's fix those formatting nits.
2025-01-10 14:58:56 +09:00
Ani Sinha 5d0ac3528b hwids: add a new uefi firmware type of device entry
This change adds a new uefi firmware type device entry for the .hwids section.
It also adds necessary changes for ukify.py.
2025-01-05 09:40:05 +05:30
Antonio Alvarez Feijoo 84624d8c99 ukify: fix typos 2024-12-30 10:50:25 +00:00
Thomas Hebb 32c3e1379d ukify: Fix regression in --no-sign-kernel flag
The man page says that --sign-kernel and --no-sign-kernel "override the
detection of whether to sign the Linux binary", so we should only
autodetect if neither are specified. But as of commit 02eabaffe9
("ukify: Add a unified interface for signing tools"), we autodetect even
when --no-sign-kernel is passed, which makes the flag useless.

The sign_kernel option is parsed using argparse.BooleanOptionalAction,
which sets it to either True, False, or None. commit 02eabaffe9
replaced `sign_kernel is None` with `not sign_kernel`. These are not the
same in Python, as the latter accepts False as well as None.

Restore the original check and fix type annotations accordingly.

Fixes: 02eabaffe9 ("ukify: Add a unified interface for signing tools")
2024-12-19 12:27:27 +09:00
Zbigniew Jędrzejewski-Szmek 544df97b41 ukify: ellipsize CN to not exceed maximum length
Currently the generation of the certificate fails if len(fqdn) >= 43.
Ellipsize the fqdn to let the tests pass in all cases.

Fixes https://github.com/systemd/systemd/issues/34581.
2024-12-02 16:55:57 +00:00
Zbigniew Jędrzejewski-Szmek ef20d06da6 ukify: Switch to JSON HWID description format (#35208)
Fixes #35176
2024-11-27 09:50:41 +01:00
Daan De Meyer d6047d9fb5 ukify: Fix typing error 2024-11-24 16:47:20 +01:00
Daan De Meyer a2aacbfad5 Move mypy.ini and ruff.toml to top level
This allows reusing them for integration-test-wrapper.py as well.
2024-11-24 16:47:20 +01:00
anonymix007 cf331f1c9b ukify: Switch to JSON HWID description format 2024-11-22 00:42:47 +03:00
Zbigniew Jędrzejewski-Szmek 5e7e4e4d49 ukify: fix parsing of SignTool configuration option
This partially reverts 02eabaffe9.
As noted in https://github.com/systemd/systemd/pull/35211:
> The configuration parsing simply stores the string as-is, rather than
> creating the appropriate object

One way to fix the issue would be to store the "appropriate object", i.e.
actually the class. But that makes the code very verbose, with the conversion
being done in two places. And that still doesn't fix the issue, because we need
to map the class objects back to the original name in error messages.

So instead, store the setting as a string and only map it to the class much
later. This makes the code simpler and fixes the error messages too.

Resolves https://github.com/systemd/systemd/pull/35193
2024-11-18 14:58:41 +00:00
anonymix007 61d6075775 ukify: Use new .hwids PE section format 2024-11-15 19:15:30 +00:00