Commit Graph

56 Commits

Author SHA1 Message Date
Lennart Poettering
be2e63159f Merge pull request #27358 from bluca/pe_mule
stub: allow loading and verifying kernel command line addons
2023-05-25 03:56:06 -07:00
Malte Poll
8d89d66ce8 ukify: fix handling signed kernel as file
The .linux section would contain the path to the signed kernel (instead of the signed kernel itself), since the python type of the variable is used to determine how it is handled when adding the pe sections.

Co-authored-by: Otto Bittner <cobittner@posteo.net>
2023-05-24 18:07:51 +01:00
Luca Boccassi
c67d5a027d ukify: add default .sbat section for addons
In order to ensure addons can always be revoked via SBAT, and it is not
left out by mistake, have a default metadata entry if none is specified
by the caller.
https://github.com/rhboot/shim/blob/main/SBAT.md
2023-05-24 11:09:03 +01:00
Luca Boccassi
f644ea3ed7 ukify: use empty stub for addons
Instead of picking up sd-stub, which is runnable, add an empty
addon stub that just returns an error if executed
2023-05-24 11:09:03 +01:00
Luca Boccassi
b6f2e68602 stub/measure: document and measure .uname UKI section 2023-05-24 11:06:36 +01:00
Luca Boccassi
fcb4ba6c14 Merge pull request #27539 from esposem/ukify_pesign
ukify: support pesign as alternative to sbsign
2023-05-11 10:45:59 +01:00
Emanuele Giuseppe Esposito
4e906270a3 src/ukify/test/test_ukify: fix skipped tests
Some tests are skipped because initrd extracted from bootctl
is "/boot/initramfs-5.14.0-284.el9.x86_64.img $tuned_initrd"
and not just "/boot/initramfs-5.14.0-284.el9.x86_64.img".
Therefore split and remove the additional garbage.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2023-05-10 09:20:46 -04:00
Emanuele Giuseppe Esposito
ff5618c7c0 src/ukify/test/test_ukify: add pesign unit test
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2023-05-10 09:20:46 -04:00
Emanuele Giuseppe Esposito
c1e8d1727b ukify: support pesign as alternative to sbsign
sbsign is not available everywhere, for example RHEL does not have it.
Add pesign as alternative to it.

pesign will use options "--secureboot-certificate-name" (mandatory) and
"--secureboot-certificate-dir" (optional), while sbsign will use
"--secureboot-private-key" and "--secureboot-certificate".

By default, use sbsign. If no key/cert is provided or sbsign is not found,
try pesign.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2023-05-10 09:18:27 -04:00
Emanuele Giuseppe Esposito
e673c5c2d9 ukify: typo in doc and print when package is missing
If a package is missing, a subprocess is started with None as
command argument. Error raised by subprocess is therefore not helpful
at all to understand what needs to be done to fix that error.

Also fix doc since systemd-stub will look for .cmdline files, and not
.cmdline.efi files.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2023-05-10 09:18:25 -04:00
Zbigniew Jędrzejewski-Szmek
7d3d147c4a test_ukify: print message when skipping whole test file 2023-05-09 08:11:10 +02:00
Zbigniew Jędrzejewski-Szmek
46886f130d test_ukify: add test for combining config and cmdline 2023-05-05 18:43:20 +02:00
Zbigniew Jędrzejewski-Szmek
55be961f48 test_ukify: rework how --flakes argument is appended
The usual approach is to put 'addopts = --flakes' in setup.cfg. Unfortunately
this fails badly when pytest-flakes is not installed:
  ERROR: usage: test_ukify.py [options] [file_or_dir] [file_or_dir] [...]
  test_ukify.py: error: unrecognized arguments: --flakes

pytest-flakes is not packaged everywhere, and this test is not very important,
so let's just do it only if pytest-flakes is available. We now detect if
pytest-flakes is available and only add '--flakes' conditionally. This
unfortunately means that when invoked via 'pytest' or directly as
'src/ukify/test/test_ukify.py', '--flakes' will not be appended automatically.
But I don't see a nice way to achieve previous automatic behaviour.

(I first considered making 'setup.cfg' templated. But then it is created
in the build directory, but we would need it in the source directory for
pytest to load it automatically. So to load the file, we'd need to give an
argument to pytest anyway, so we don't gain anything with this more complex
approach.)
2023-05-05 18:42:37 +02:00
Zbigniew Jędrzejewski-Szmek
041f536f9a test_ukify: propagate failure
Oops. This explains why the tests were "passing" in CI even
though a direct pytest invocation would fail.
2023-05-05 18:42:37 +02:00
Zbigniew Jędrzejewski-Szmek
a758f95c33 ukify: appease mypy
Note to self: PEP 585 introduced using collection types as types,
and is available since 3.9. PEP 604 allows writing unions with "|",
but is only available since 3.10, so not yet here because we maintain
compat with 3.9.
2023-05-05 18:42:37 +02:00
Zbigniew Jędrzejewski-Szmek
47a6df4da0 test_ukify: add tests for the new functionality 2023-05-05 18:42:37 +02:00
Zbigniew Jędrzejewski-Szmek
7081db294c ukify: PeError → PEError
We don't lowercase acronyms in systemd usually.
Remove unnused f'' prefix to avoid a pylint warning.
2023-05-05 18:42:37 +02:00
Zbigniew Jędrzejewski-Szmek
5143a47a81 ukify: rework option parsing to support a config file
In some ways this is similar to mkosi: we have a argparse.ArgumentParser()
with a bunch of options, and a configparser.ConfigParser() with an
overlapping set of options. Many options are settable in both places, but
not all. In mkosi, we define this in three places (a dataclass, and a
function for argparse, and a function for configparser). Here, we have one
huge list of ConfigItem instances. Each instance specifies the full metadata
for both parsers. Argparse generates a --help string for all the options,
and we also append a config file sample to --help based on the ConfigItem
data:

$ python src/ukify/ukify.py --help|tail -n 25
config file:
  [UKI]
  Linux = LINUX
  Initrd = INITRD…
  Cmdline = TEXT|@PATH
  OSRelease = TEXT|@PATH
  DeviceTree = PATH
  Splash = BMP
  PCRPKey = KEY
  Uname = VERSION
  EFIArch = ia32|x64|arm|aa64|riscv64
  Stub = STUB
  PCRBanks = BANK…
  SigningEngine = ENGINE
  SecureBootPrivateKey = SB_KEY
  SecureBootCertificate = SB_CERT
  SignKernel = SIGN_KERNEL

  [PCRSignature:NAME]
  PCRPrivateKey = PATH
  PCRPublicKey = PATH
  Phases = PHASE-PATH…

While writing this I needed to check the argument parsing, so I added
a --summary switch. It just pretty-prints the resulting option dictionary:

$ python src/ukify/ukify.py /efi//3a9d668b4db749398a4a5e78a03bffa5/6.2.11-300.fc38.x86_64/linux /efi//3a9d668b4db749398a4a5e78a03bffa5/6.2.11-300.fc38.x86_64/initrd --pcr-private-key=PRIV.key --pcr-public-key=PUB.key --config=man/ukify-example.conf --summary
Host arch 'x86_64', EFI arch 'x64'
{'_groups': [0, 'initrd', 'system'],
 'cmdline': 'A1 B2 C3',
 'config': 'man/ukify-example.conf',
 'devicetree': None,
 'efi_arch': 'x64',
 'initrd': [PosixPath('initrd1'),
            PosixPath('initrd2'),
            PosixPath('initrd3'),
            PosixPath('/efi/3a9d668b4db749398a4a5e78a03bffa5/6.2.11-300.fc38.x86_64/initrd')],
 'linux': PosixPath('/efi/3a9d668b4db749398a4a5e78a03bffa5/6.2.11-300.fc38.x86_64/linux'),
 'measure': None,
 'os_release': PosixPath('/etc/os-release'),
 'output': 'linux.efi',
 'pcr_banks': ['sha1', 'sha384'],
 'pcr_private_keys': [PosixPath('PRIV.key'),
                      PosixPath('pcr-private-initrd-key.pem'),
                      PosixPath('pcr-private-system-key.pem')],
 'pcr_public_keys': [PosixPath('PUB.key'),
                     PosixPath('pcr-public-initrd-key.pem'),
                     PosixPath('pcr-public-system-key.pem')],
 'pcrpkey': None,
 'phase_path_groups': [None,
                       ['enter-initrd'],
                       ['enter-initrd:leave-initrd',
                        'enter-initrd:leave-initrd:sysinit',
                        'enter-initrd:leave-initrd:sysinit:ready']],
 'sb_cert': PosixPath('mkosi.secure-boot.crt'),
 'sb_key': PosixPath('mkosi.secure-boot.key'),
 'sections': [],
 'sign_kernel': None,
 'signing_engine': None,
 'splash': None,
 'stub': PosixPath('/usr/lib/systemd/boot/efi/linuxx64.efi.stub'),
 'summary': True,
 'tools': None,
 'uname': None}

With --summary, existence of input paths is not checked. I think we'll
want to show them, instead of throwing an error, but in red, similarly to
'bootctl list'.

This also fixes tests which were failing with e.g.
E       FileNotFoundError: [Errno 2] No such file or directory: '/ARG1'
=========================== short test summary info ============================
FAILED ../src/ukify/test/test_ukify.py::test_parse_args_minimal - FileNotFoun...
FAILED ../src/ukify/test/test_ukify.py::test_parse_args_many - FileNotFoundEr...
FAILED ../src/ukify/test/test_ukify.py::test_parse_sections - FileNotFoundErr...
=================== 3 failed, 10 passed, 3 skipped in 1.51s ====================
2023-05-05 18:42:03 +02:00
Zbigniew Jędrzejewski-Szmek
3f7e77fae1 test_ukify: fix two failing tests
Fixup for 22ad038ac6 and
3fc5eed470. It seems that the tests are
not executed properly in CI. Nevertheless, test-ukify appears in logs:

rpm-build:fedora-rawhide-x86_64:
 409/1191 systemd / test-ukify    OK   0.16s

This is strange.
2023-05-03 12:29:41 +02:00
Zbigniew Jędrzejewski-Szmek
cb3b451e11 test_ukify: fix loop iteration
We'd try to access 'linux' or 'initrd' after failing to set it.
2023-05-03 12:29:41 +02:00
Zbigniew Jędrzejewski-Szmek
d9c8f075af ukify: add missing header
This file is installed, so it should have the long header.
2023-05-03 12:29:41 +02:00
Zbigniew Jędrzejewski-Szmek
50f4add445 ukify: use UPPERCASE for parameter names
We generally nowadays use UPPERCASE for parameters in variuos help text.
Let's be consistent here too, and also drop duplicated 'usage:':
$ ukify -h
usage: ukify [options…] LINUX INITRD…
       ukify -h | --help

Build and sign Unified Kernel Images

positional arguments:
  LINUX                 vmlinuz file [.linux section]
  INITRD…               initrd files [.initrd section]
...
2023-05-03 12:29:41 +02:00
Luca Boccassi
00e5933f57 ukify: allow building PE addon
Make the kernel optional too, so that we can easily build and sign a PE addon,
that can be used to carry extra command line options.
2023-04-26 16:55:26 +02:00
Jan Janssen
7f72dca762 ukify: Strip symbol/string table for old stubs 2023-04-01 14:31:22 +02:00
Daan De Meyer
ac3412c379 ukify: Add workarounds for older stubs
Older stubs are either not stripped, causing their total size to be
unaligned because of an unaligned symbol table at the end, or stripped,
causing the raw data pointers and sizes to be unaligned because strip
does not follow the PE spec correctly when stripping. Let's add
workarounds for both issues, so that we can use ukify with older stubs
as well.
2023-03-28 18:17:59 +02:00
Daan De Meyer
61648c6976 Revert "ukify: Weaken file alignment assertions"
This reverts commit 23428bb19e.
2023-03-28 13:31:38 +02:00
Daan De Meyer
23428bb19e ukify: Weaken file alignment assertions
Older versions of the stub are not aligned to the PE file alignment
size. If we remove the assertions, the UKI still boots without issues,
so let's drop the assertions and print a message about it instead.
2023-03-27 19:33:16 +02:00
Jan Janssen
52abd56915 ukify: Add riscv32 and loongarch support 2023-03-24 13:01:53 +01:00
Jan Janssen
3fc5eed470 ukify: Use pefile to add sections to EFI stub 2023-03-24 13:01:45 +01:00
Tobias Powalowski
bf9f07a629 ukify: allow uncompressed kernel images for UNAME detection on aarch64 and riscv64 (#26929)
Uncompressed aarch64 and riscv64 kernels have a different startpoint than x86.
Example output from ukify:
aarch64: NotImplementedError: unknown file format (starts with b'MZ@\xfa')
riscv64: NotImplementedError: unknown file format (starts with b'MZo\x10')
Add check for (b'MZ') to catch both in one call.

Fix:
https://github.com/systemd/systemd/issues/26923
2023-03-22 12:24:57 +00:00
Jan Janssen
eb81a60c73 tree-wide: Use correct SPDX license identifier 2023-02-24 18:53:17 +00:00
Daan De Meyer
c87ff62210 ukify: Set fast_load option when parsing PE files
Let's skip parsing of some irrelevant information that we don't use
to speed up building UKIs with large initrds from +-15s to less than
1s.
2023-02-20 20:02:22 +01:00
Yu Watanabe
c126c8ac81 ukify: fix padding length 2023-02-15 18:01:02 +00:00
Dmitry V. Levin
30fd9a2dab treewide: fix a few typos in NEWS, docs and comments 2023-02-15 10:41:03 +00:00
Yu Watanabe
0b75493da0 tree-wide: fix typo and comment style update 2023-02-15 10:08:16 +09:00
Zbigniew Jędrzejewski-Szmek
667578bb11 ukify: add explanatory message when import fails 2023-02-08 19:35:42 +00:00
Daan De Meyer
c811aba082 ukify: python 3.9 compat followup 2023-01-27 22:16:07 +01:00
Daan De Meyer
09595fd5f2 ukify: Downgrade required python version to 3.9 2023-01-20 13:55:11 +01:00
Daan De Meyer
67b65ac62d ukify: Fix version string
Let's make sure we mimick the version of our other CLI tooling.
2023-01-15 22:17:37 +00:00
Jan Janssen
c8add4c2e8 ukify: Fix tools detection if --tools was not passed
In 789a642738 llvm-objcopy was given
higher priority over objcopy, but this would only work if --tools was
also passed.
2023-01-11 09:29:41 +01:00
Daan De Meyer
35fdc097f4 Merge pull request #25912 from DaanDeMeyer/ukify
ukify fixes
2023-01-02 16:07:58 +01:00
Jan Janssen
599c930e48 ukify: Fix section offset calculation
objcopy seems to expect that the offset passed to --change-section-vma
is absolute instead of relative to ImageBase. If this is not accounted
for an invalid image is created that cannot be loaded:

  0 .osrel        0000016b  0000000200016000  0000000200016000  00000400  2**2
  …
  6 .text         0000d242  0000000140001000  0000000140001000  00c6e800  2**4

This isn't an issue with gnu-efi based PE images, but natively created
ones will have a non-zero ImageBase.
2023-01-02 15:59:17 +01:00
Daan De Meyer
4f312ba07b ukify: Require specifying --tools for each tools directory
Instead of consuming N arguments as tools directories, let's always
only consume one argument per specification of --tools. This avoids
issues where the linux image and initrd are interpreted as tools
directories.
2023-01-02 14:53:47 +01:00
Daan De Meyer
737dab1a8d ukify: Handle directories in path_is_readable() 2023-01-02 14:52:49 +01:00
Luca Boccassi
21c8d750cd Merge pull request #25814 from DaanDeMeyer/ukify
ukify: Prefer using llvm-objcopy instead of objcopy
2022-12-22 22:19:05 +01:00
Daan De Meyer
3fc1ae89e7 ukify: Validate that there are no overlapping sections
Let's make sure that after calling objcopy we have no overlapping
sections in the UKI
2022-12-22 12:21:11 +01:00
Daan De Meyer
789a642738 ukify: Prefer using llvm-objcopy instead of objcopy
llvm-objcopy works on stubs built for foreign architectures whereas
objcopy doesn't so let's prefer using llvm-objcopy instead of objcopy.

llvm-objcopy automatically sets the virtual address and doesn't provide
an option to set it manually so we only add --change-section-vma when
using objcopy

The default section flags differ between llvm-objcopy and objcopy
so we add a default for the section flags so we make sure all sections
are read-only data unless specified otherwise.
2022-12-22 12:20:24 +01:00
Daan De Meyer
22ad038ac6 ukify: Allow passing multiple directories to --tools 2022-12-22 12:19:55 +01:00
Zbigniew Jędrzejewski-Szmek
095ff238d0 ukify: check early if inputs exist and are readable
It's much nicer for the user if we fail early instead of doing partial
processing if we cannot read some input. We can't do those checks immediately
from argparse.Parser.parse_args(), because we want to fully process the
commandline first. In particular, even with invalid args, if --help is
specified somewhere, we want to handle that. Thus, we need to delay the checks
after argparse.Parser.parse_args() returns.

Ukify didn't have type annotations on functions, but it probably should.
Jörg's suggested correction included them and we might just as well start here.
2022-12-20 16:10:36 +01:00
Zbigniew Jędrzejewski-Szmek
33bdec184f ukify: catch error when loading foreign pe file
The autodetection code is supposed to throw ValueError when it
cannot figure out the version so that we fall back to the next method.
With the patch:
  Kernel version not specified, starting autodetection 😖.
  Real-Mode Kernel Header magic not found
  + readelf --notes vmlinuz/arm64/vmlinuz-6.0.9-300.fc37.aarch64
  readelf: vmlinuz/arm64/vmlinuz-6.0.9-300.fc37.aarch64: Error: Not an ELF file - it has the wrong magic bytes at the start
  Found uname version: 6.0.9-300.fc37.aarch64
2022-12-20 10:39:10 +01:00