Commit Graph
85 Commits
Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek 0b4cf0a540 Merge pull request #30232 from keszybz/ukify-imports
Use exec() to import ukify
2023-12-13 14:38:03 +01:00
Zbigniew Jędrzejewski-Szmek f3d50fb2c6 ukify: raise error if genkey is called with no output arguments
The idea is that genkey is called with either
--secureboot-private-key= + --secureboot-certificate=, and then it
writes those, or with --pcr-private-key + optionally --pcr-public-key
and then it writes those, or both. But when called with no arguments
whatsover, it did nothing.

There is no implicit value for any of those parameters as input (unlike in
mkosi), so we also don't want to have implicit values when used as output.
But we shouldn't return success if no work was done, this is quite confusing.
2023-12-06 11:46:04 +01:00
Zbigniew Jędrzejewski-Szmek 7b0760ca9c ukify: remove stray line
As requested in review.
2023-12-06 11:44:40 +01:00
Roland Singer bf35f9c835 ukify: fix handling of --secureboot-certificate-validity= (#30315)
Before:
$ python src/ukify/ukify.py genkey --secureboot-private-key=sb2.key --secureboot-certificate=sb2.cert --secureboot-certificate-validity=111
Traceback (most recent call last):
  File "/home/zbyszek/src/systemd-work/src/ukify/ukify.py", line 1660, in <module>
    main()
  File "/home/zbyszek/src/systemd-work/src/ukify/ukify.py", line 1652, in main
    generate_keys(opts)
  File "/home/zbyszek/src/systemd-work/src/ukify/ukify.py", line 943, in generate_keys
    key_pem, cert_pem = generate_key_cert_pair(
                        ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/zbyszek/src/systemd-work/src/ukify/ukify.py", line 891, in generate_key_cert_pair
    now + ONE_DAY * valid_days
          ~~~~~~~~^~~~~~~~~~~~
TypeError: can't multiply sequence by non-int of type 'datetime.timedelta'

Now:
$ python src/ukify/ukify.py genkey --secureboot-private-key=sb2.key --secureboot-certificate=sb2.cert --secureboot-certificate-validity=111
Writing SecureBoot private key to sb2.key
Writing SecureBoot certificate to sb2.cert

The new code is also clearer.
2023-12-06 10:49:47 +01:00
Zbigniew Jędrzejewski-Szmek 24baee3370 ukify: avoid deprecated datetime call
DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for
                    removal in a future version. Use timezone-aware objects to
                    represent datetimes in UTC: datetime.datetime.now(datetime.UTC).

The difference between the two is that .now(datetime.UTC) returns an object with
a timezone attached, "the numbers" are the same.

>>> datetime.datetime.utcnow(), datetime.datetime.now(datetime.UTC)
(datetime.datetime(2023, 12, 1, 9, 37, 53, 891669),
 datetime.datetime(2023, 12, 1, 9, 37, 53, 891688, tzinfo=datetime.timezone.utc))

This value is fed to cryptography's x509.CertificateBuilder object, so as long
as it can accept a datetime object with tzinfo, the result should be identical.
2023-12-01 12:44:08 +00:00
Zbigniew Jędrzejewski-Szmek 34c2fc8505 ukify: simplify import 2023-12-01 10:30:16 +01:00
Zbigniew Jędrzejewski-Szmek f99304fe23 ukify: show .sbom sections as binary
Fixup for e87dec82be:
I misunderstood the format. It's actually CBOR, i.e. some binary format.
When trying to show show text we would first check if it's valid UTF-8,
so we would handle this gracefully, i.e. emit a warning and not print
the contents.
2023-11-13 16:33:07 +00:00
Zbigniew Jędrzejewski-Szmek e87dec82be ukify: show .sbom sections as text
There are draft proposals to embed SBOM metadata in the .sbom section of PE
binaries [1], in the coSWID XML format. Some details of how this is actually
implemented might change, but it seems very likely that both section name and
it being text will stay. Let's show the section as text to make such binaries
easier to inspect. ([1] recommends using 'objcopy -j .sbom' which isn't
particularly readable.)  Once there's more standarization of the actual
format, we can add pretty-printing and/or syntax highlighting.

[1] https://uefi.org/blog/firmware-sbom-proposal
[2] https://www.ietf.org/archive/id/draft-ietf-sacm-coswid-21.html
2023-11-13 10:59:30 +01:00
Zbigniew Jędrzejewski-Szmek 51faf83696 ukify: print a more readable synopsis in --help
The details of formatting are copied from mkosi. This results in the following:

   usage:
     ukify build [--linux=LINUX] [--initrd=INITRD] [options…]
     ukify genkey [options…]
     ukify inspect FILE… [options…]

  Build and sign Unified Kernel Images

  options:
    --version             show program's version number and exit
    ...

I put "[options…]" at the end, because that's what one would generally do
with long options like "--cmdline" and others.
2023-11-13 10:59:30 +01:00
Yu Watanabe d19434fbf8 tree-wide: fix typo 2023-10-31 14:35:33 +09:00
Luca Boccassi 6cd1f6546c Merge pull request #29332 from esposem/ukify_simplify
ukify: automatically infer --signtool from the parameters given
2023-10-27 00:10:28 +01:00
Zbigniew Jędrzejewski-Szmek e3696e6f63 ukify: fix .dtb section name in 'inspect'
Fixup for df4a46733a.
2023-10-11 17:34:43 +02:00
Emanuele Giuseppe Esposito 1277575408 ukify: override default option value with config file
If an option like SecureBootCertificateDir is given, it should override
the default '/etc/pki/pesign'. Until now the config file option were
always ignored if they had a default.

So from now on, every ConfigItem with a config_key and default field
should also give config_push = ConfigItem.config_set.
2023-10-10 07:06:24 -04:00
Emanuele Giuseppe Esposito 5c52078162 ukify: automatically infer --signtool from the parameters given
--signtool is actually useless: it can be inferred depending on if
--secureboot-certificate-name (pesign) is given, or
--secureboot-private-key and --secureboot-certificate (sbsign) is given.

Leave the option just for backwards compatibility.
2023-10-10 07:06:23 -04:00
Valentin Lefebvre 750674da0d ukify: explicitly import attribute
* Explicitly import attributes rsa and serialization from cryptography.hazmat

Signed-off-by: Valentin Lefebvre <valentin.lefebvre@suse.com>
2023-09-29 11:31:12 +02:00
Joerg Behrmann 7c52d5236a treewide: split commandline into command line 2023-09-20 16:37:23 +01:00
Alvin Alvarado a05fa30f88 ukify/man: Look for a config file in systemd folders if not specified
If the user does not specify a config file to use, ukify will try looking for one at {/run,/etc,/usr/local/lib,/usr/lib}/systemd/ukify.conf in order and then use the first one found. Also made sure the --config input is a pathlib.Path by specifying its type in its CONFIG_ITEMS entry.
Big cheers to Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> for helping!
2023-09-08 16:21:28 +02:00
Zbigniew Jędrzejewski-Szmek 594e27b0bc ukify: fail if the config file was not read
Inspired by https://github.com/systemd/systemd/pull/28997.
Noticed by Alvin Alvarado <alvin@striczkof.io>.
2023-08-29 15:16:35 +03:00
Daan De Meyer c1e704626a ukify: Use length= instead of ignore_padding= in inspect
ignore_padding= was only added in a recent version of pefile. Let's
set length= to the virtual size instead which is what ignore_padding
does behind the scenes so we're compatible with older versions of
pefile.
2023-08-10 19:35:21 +01:00
Frantisek Sumsal 79f902eb09 Add .pylintrc to globally suppress warnings we don't really care about
Also, drop the respective disable directives from existing files.
2023-08-10 18:13:29 +02:00
Frantisek Sumsal 0be1de7ffc ukify: don't panic when prepending to an undefined list
Handle the case when all the arguments are passed in through a
configuration file:

$ cat ukify.conf
[UKI]
Linux = /boot/vmlinuz-linux
Initrd = /boot/initramfs-linux.img

Before:
$ src/ukify/ukify.py --config ukify.conf build
Traceback (most recent call last):
  File "/root/systemd/src/ukify/ukify.py", line 1604, in <module>
    main()
  File "/root/systemd/src/ukify/ukify.py", line 1590, in main
    opts = parse_args()
           ^^^^^^^^^^^^
  File "/root/systemd/src/ukify/ukify.py", line 1584, in parse_args
    apply_config(opts)
  File "/root/systemd/src/ukify/ukify.py", line 1431, in apply_config
    item.apply_config(namespace, section_name, group, key, value)
  File "/root/systemd/src/ukify/ukify.py", line 1123, in apply_config
    self.config_push(namespace, group, dest, value)
  File "/root/systemd/src/ukify/ukify.py", line 1019, in config_list_prepend
    setattr(namespace, dest, value + old)
                             ~~~~~~^~~~~
TypeError: can only concatenate list (not "NoneType") to list

After:
$ src/ukify/ukify.py --config ukify.conf build
Kernel version not specified, starting autodetection 😖.
Found uname version: 6.4.7-arch1-3
Wrote unsigned vmlinuz-linux.unsigned.efi

Resolves: #28688
2023-08-05 22:38:46 +01:00
Daan De Meyer d713104abe ukify: Only run systemd-measure after adding all sections
We were running systemd-measure before adding the sbat section,
let's fix that.

Also make sure we only pass --linux to systemd-measure once instead
of twice.
2023-08-01 21:11:15 +01:00
Yu Watanabe f0876c7ab9 ukify: check option length
Follow-up for df4a46733a.
2023-07-29 18:48:32 +09:00
Luca Boccassi 1a1a691ba7 Merge pull request #27621 from esposem/ukify_read
ukify: add an option to read the PE sections
2023-07-28 09:55:03 +01:00
Luca Boccassi 5a945fab7a ukify: fix armv7 architecture mapping
$ uname -m
armv7l

Traceback (most recent call last):
  File "/<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/src/kernel-install/60-ukify.install", line 223, in <module>
    main()
  File "/<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/src/kernel-install/60-ukify.install", line 219, in main
    call_ukify(opts)
  File "/<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/src/kernel-install/60-ukify.install", line 205, in call_ukify
    ukify['finalize_options'](opts2)
  File "/<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/ukify", line 1423, in finalize_options
    opts.efi_arch = guess_efi_arch()
                    ^^^^^^^^^^^^^^^^
  File "/<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/ukify", line 78, in guess_efi_arch
    raise ValueError(f'Unsupported architecture {arch}')
ValueError: Unsupported architecture armv7l
2023-07-24 21:10:48 +02:00
Emanuele Giuseppe EspositoandZbigniew Jędrzejewski-Szmek df4a46733a ukify: add a verb to inspect the PE sections
Since PE can't be read with a normal editor, provide functionality to read PE
sections and output their name and when possible the content too.

ukify inspect <file> describes the sections and print the content of the
well-known sections to stdout.

Co-authored-by: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
2023-07-15 19:41:51 +02:00
Daan De Meyer d7d36252e5 ukify: Derive public key from private key if not specified 2023-07-13 16:31:05 +01:00
Emanuele Giuseppe Esposito b708789dc4 ukify: refactor signing code
When ukify is not signing, it should not check if sbsign/pesign are
installed, nor print if they are missing or not.
2023-07-10 02:41:37 -04:00
Luca Boccassi 635c6ea622 ukify: measure sbat section too 2023-07-05 21:31:08 +01:00
Luca Boccassi a8b645dec8 ukify: enable --sbat for UKIs too
For confidential computing they want to be able to revoke initrds too, so allow
passing a specific --sbat section when building a UKI too, not just an addon.
Merge it with the stub and kernel sections.
2023-07-05 21:31:08 +01:00
Luca Boccassi c3f7501c4d ukify: merge .sbat sections from stub and kernel
If the kernel contains a .sbat section (they should start soon) then merge
it with the stub's so that revocations can apply to either component.

Fixes https://github.com/systemd/systemd/issues/27866
2023-06-30 17:17:56 +01:00
Frantisek Sumsal 9a27ef092e tree-wide: fix a couple of typos
As reported by Fossies.org.
2023-06-15 20:52:45 +02:00
Zbigniew Jędrzejewski-Szmek 814e4d7a67 ukify: make the certficate validity configurable
Requested in https://github.com/systemd/systemd/pull/27946/commits/4cc743319ae378d458050d6e0cfdeaa03addd379#r1228592001
2023-06-14 13:18:00 +02:00
Zbigniew Jędrzejewski-Szmek a1c80efddc ukify: add 'genkey' verb
The idea is to make it easy to generate all the signing key and certs
that can be used for local signing. The verb is the modeled after
'mkosi genkey', but there are some important differences: we generate
the keys to the paths where they will be read from, both pcr signing
keys and the SecureBoot certificate+key.

If any of the outputs exist, operation is refused. Maybe we could add a
--force option in the future, but this operation should be rare, so I think
it's better to refuse to overwrite anything initially.

I'm only doing a token man page change here.
https://github.com/systemd/systemd/pull/27621 reworks the man page,
and the changes done here would conflict heavily with that work. I'll
submit a follow-up patch later.
2023-06-14 13:17:33 +02:00
Zbigniew Jędrzejewski-Szmek b09a5315f5 ukify: move verb mangling to finalize_options()
This simplifies the logic: finalize_options() is the step that does the
checks and the mangling. The checks for consistency are done in more places,
so we need to pass a verb (we only have 'build', but once we add other verbs,
any would do).
2023-06-12 11:12:02 +02:00
Zbigniew Jędrzejewski-Szmek 9a1cb2038e ukify: split out iteration over phase path groups and keys
In preparation for future changes.
2023-06-12 11:12:02 +02:00
Zbigniew Jędrzejewski-Szmek 794385f5df ukify: fix synopsis in --help
Missed in a3b227d28a.
2023-06-12 11:12:02 +02:00
Zbigniew Jędrzejewski-Szmek 6fa7913830 ukify: use pager for --help
The output is now too long to fit on one page, let's use a pager automatically
like in other places.

The implementation is copied from mkosi, but adjusted to follow what other
systemd tools do.
2023-06-12 11:12:02 +02:00
Zbigniew Jędrzejewski-Szmek a3b227d28a ukify: add 'build' verb
The old syntax with linux + initrds as positional arguments is still accepted,
but a warning is emitted. We should remove the support for this after the
next release or so.

Adding a single verb by itself is not very useful, but opens the door to adding
other verbs.
2023-06-06 15:45:57 +02:00
Zbigniew Jędrzejewski-Szmek c4fc25463e ukify: make code pylint clean
The linter is imperfect, but it is useful as a very quick
check for typos and other silly mistakes. Add a few annotations
and do one small change to make it think the code is perfect.
2023-06-05 16:07:52 +02:00
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 PollandOtto Bittner 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
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 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 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