For some reason claude refuses to review
https://github.com/systemd/systemd/pull/42651.
Let's see if we can make it work by splitting up the big PR into smaller
ones. So here's the first one with 5 commits that should be relatively
easy to review and get merged early.
Luca Boccassi [Fri, 26 Jun 2026 22:12:54 +0000 (23:12 +0100)]
boot: require a minimum PE optional header size in verify_pe()
verify_pe() only checked SizeOfOptionalHeader against a SIZE_MAX wrap (a
clause that, given SizeOfOptionalHeader is a uint16_t, can never reject
anything) and never read NumberOfRvaAndSizes. But pe_kernel_info(),
pe_kernel_check_nx_compat() and pe_kernel_check_no_relocation() then read
SizeOfImage, AddressOfEntryPoint, DllCharacteristics and the base
relocation data directory entry from the optional header.
Require SizeOfOptionalHeader to be large enough to contain everything down
to the base relocation data directory entry, and require the image to
declare that many data directory entries.
Luca Boccassi [Fri, 26 Jun 2026 21:17:28 +0000 (22:17 +0100)]
boot: restore parent loaded image when initrd registration fails
linux_exec() patches the stub's own EFI_LOADED_IMAGE_PROTOCOL to point
at the loaded inner kernel, and restores the saved original only after
the entry point returns. The initrd_register() failure path returns
without restoring, leaving the firmware's protocol pointing to freed
data.
Luca Boccassi [Fri, 26 Jun 2026 21:10:20 +0000 (22:10 +0100)]
boot: restore RW/RO memory attributes on every error
linux_exec() marks code sections RO+X for W^X and reverts them to RW+NX
in a loop just before returning, because EDK2 requires freed buffers to
be writable and non-executable or FreePages() crashes. Not every error
path is currently covered. Switch to a _cleanup_ helper so that every
return path is covered.
Luca Boccassi [Fri, 26 Jun 2026 20:58:57 +0000 (21:58 +0100)]
boot: check PE section against SizeOfImage
pe_locate_sections_internal() stores each matching section's VirtualSize
and VirtualAddress into PeSectionVector.memory_size/memory_offset with
only SIZE_MAX overflow guards, never checking them against the image's
SizeOfImage.
Wire up the image's SizeOfImage down to pe_locate_sections_internal()
and skip any section whose in-memory section does not fit within it.
Luca Boccassi [Fri, 26 Jun 2026 19:12:36 +0000 (20:12 +0100)]
boot: bound PE section VirtualSize before zeroing the inner kernel
The inner-kernel section loader checks VirtualAddress + SizeOfRawData
against kernel_size_in_memory (for the memcpy), but the memzero right
after it clears up to VirtualAddress + VirtualSize, and VirtualSize is
only constrained to be >= SizeOfRawData.
Reject a VirtualAddress + VirtualSize that overflows or exceeds
kernel_size_in_memory, mirroring the existing SizeOfRawData checks.
Luca Boccassi [Fri, 26 Jun 2026 18:57:23 +0000 (19:57 +0100)]
boot: reject inner kernel entry point outside the image
pe_kernel_info() returned AddressOfEntryPoint (and the .compat section
entry_point) straight from the PE header with no check against
SizeOfImage. Since cab9c7b5a4 the stub calls the inner kernel directly
as ImageBase + entry_point, and only EFI_SIZE_TO_PAGES(SizeOfImage) pages
are allocated for it.
Luca Boccassi [Fri, 26 Jun 2026 18:41:31 +0000 (19:41 +0100)]
boot: don't unquote an empty value in line_get_key_value()
de0da85d41b switched the unquote check to strchr8(QUOTES, value[0]),
which is not equivalent to the old explicit comparison for an empty
value: strchr8(), like strchr(3), returns a pointer to the haystack's
terminating NUL when the needle is '\0', so strchr8(QUOTES, '\0') is
non-NULL. For a line whose separator is the last byte (e.g. "ID=") the
split leaves value[0] == '\0' and line[linelen - 1] == '\0' too, so both
conjuncts hold and value++ steps one byte past the value's terminator.
Luca Boccassi [Fri, 26 Jun 2026 16:29:31 +0000 (17:29 +0100)]
boot: initialize return parameters on zero-length EFI variable read
When a variable exists but is empty, the initial size-query
GetVariable() in efivar_get_raw_full() returns EFI_SUCCESS instead of
EFI_BUFFER_TOO_SMALL: the zero-length payload already "fits" the
zero-length query buffer. The helper returns success, but does not
initialize the return parameters.
Handle a couple of corner cases by checking the return size.
Luca Boccassi [Fri, 26 Jun 2026 16:21:52 +0000 (17:21 +0100)]
boot: make device_path_next_node() robust against malformed zero-length nodes
device_path_next_node() advances by the current node's Length field,
which per the EFI device path protocol includes the 4-byte node header;
a well-formed node is therefore at least sizeof(EFI_DEVICE_PATH) bytes
long. A malformed node with Length < sizeof(EFI_DEVICE_PATH), in
particular Length == 0, makes the helper return its input pointer
unchanged.
Allow graphical installers to use system sysinstall. This will be used
by gnome-setup to install GNOME OS and ideally installers of other
distributions talk to the same varlink interface.
tree-wide: check if necessary cflags dependencies are set
This makes each `foo_cflags` dependency define a `SYSTEMD_CFLAGS_MARKER_FOO`
macro, and checks if the macro is set when headers provided by external
libraries are included.
With this, we can fail fast at compile time if necessary `_cflags`
dependencies are omitted in meson.build. Missing dependencies found by this
mechanism have been added across the tree.
Julian Sparber [Fri, 26 Jun 2026 16:48:23 +0000 (18:48 +0200)]
sysinstall: Add varlink interface
Allow graphical installers to use system sysinstall.
This will be used by gnome-setup to install GNOME OS and ideally installers
of other distributions talk to the same varlink interface.
Julian Sparber [Thu, 25 Jun 2026 16:33:49 +0000 (18:33 +0200)]
sysinstall: Look for valid kernel image before installing
Instead of failing the installation after creating the partitions and
doing almost the entire installation, and then failing if we don't have
a kernel image. Look for it before we start doing modifications to the disk.
This way we tell the user as soon as possible that we can't install the
OS because of the missing kernel image.
include: do not override kernel headers with libaudit headers
Overriding <linux/audit.h> to implicitly include <libaudit.h> turns out to
be problematic. Because <linux/audit.h> is pulled in by various other kernel
headers, any source file including those kernel headers indirectly ends up
depending on <libaudit.h>, even if the executable or library being built
does not require libaudit at all.
Let's drop the override header and include <libaudit.h> only where explicitly
needed. This also moves the fallback definitions of AUDIT_SERVICE_* and
MAX_AUDIT_MESSAGE_LENGTH to libaudit-util.h.
Luca Boccassi [Fri, 26 Jun 2026 16:15:38 +0000 (17:15 +0100)]
boot: reject GPT headers with SizeOfPartitionEntry below the minimum
Commit 0cf5f816f22c replaced the original lower-bound check
if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
return false;
with a multiple-of check
if ((h->SizeOfPartitionEntry % sizeof(EFI_PARTITION_ENTRY)) != 0)
return false;
to additionally require the entry size to be a multiple of 128. The
modulo test is however also satisfied by SizeOfPartitionEntry == 0, so a
GPT header advertising a zero entry size now passes verify_gpt().
Restore the lower bound in addition to the multiple-of check, so the
entry size must be at least sizeof(EFI_PARTITION_ENTRY) and a multiple of
it (128 bytes).
File names containing backslashes cannot be checked out on Windows,
are not handled properly by build systems such as buck, and are awkward
to work with in general, so store the \x2d slice units under sanitized
file names, and rename them to the real unit name on install via a new
optional "name" key in the units list.
The fuzz-unit-file corpus sample is renamed as well; its file name is
not meaningful to the fuzzer, and dropping the backslash means it is no
longer skipped by the meson workaround for
https://github.com/mesonbuild/meson/issues/1564, so it runs as a
regression test again.
calendarspec: warn on weekday/date conflict in systemd-analyze and systemd-run
When a fixed date (e.g. 2027-01-01) is paired with a weekday constraint
(e.g. Thu) that does not match, the timer silently never elapses.
Add calendar_spec_from_string_full(..., warn_on_weekday_mismatch) so
user-facing tools can opt in to a log_warning() at parse time:
- systemd-analyze calendar: uses _full(true)
- systemd-run --on-calendar: uses _full(true)
- .timer OnCalendar=: uses log_syntax() with file/line context
Add test_calendar_spec_weekday_conflict(): forks a child with stderr
captured in a memfd via pidref_safe_fork_full(), verifies the warning
is emitted for conflicting specs and suppressed for valid ones.
test: ignore fails when the formatted timezone differs from the current one
When formatting a timestamp the C API takes into account historical data
from tzdata, so it returns a date strings with a historically-correct
timezone abbreviation. However, tzname[] doesn't do this and it returns
the most recent abbreviation for the given zone.
For example, according to tzdata America/Cancun switched from EST/EDT to
CST/CDT on 1998-08-02:
Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 6:00u
-6:00 - CST 1981 Dec 26 2:00
-5:00 - EST 1983 Jan 4 0:00
-6:00 Mexico C%sT 1997 Oct 26 2:00
-5:00 Mexico E%sT 1998 Aug 2 2:00
-6:00 Mexico C%sT 2015 Feb 1 2:00
-5:00 - EST
So, formatting a timestamp from this time will yield a string with the
EDT timezone:
$ TZ=America/Cancun date -d "@902035565"
Sun Aug 2 01:26:05 EDT 1998
But using tzname[] (or strptime %z) shows the most recent data, where
America/Cancun uses EST (and doesn't use DST anymore, hence
tzname[1]=CDT that glibc remembers from the previous zone epoch):
$ TZ=America/Cancun ./tz
{EST, CDT}
This means that when we parse the formatted timestamp back we don't use
the historical timezone data, so we might end up with a different
offset:
TZ=America/Cancun, tzname[0]=EST, tzname[1]=CDT
@902035565603993 → Sun 1998-08-02 01:26:05 EDT → @902039165000000 → Sun 1998-08-02 01:26:05 CDT
src/test/test-time-util.c:452: Assertion failed: Expected "ignore" to be true
Aborted (core dumped) build-local/test-time-util
Instead of adding exceptions for every single timezone that switched
between different offsets in the past, let's address this a bit more
generally and skip the check if the parsed timezone doesn't match any of
the current timezones - this still keeps the check that the time
difference in such case is exactly one hour, so its effect should be
limited mostly to DST-related changes.
bootctl: expose --esp-path/--boot-path/--make-entry-directory via Varlink (#42838)
The Install/Unlink/Link/LinkAuto Varlink methods always auto-discover
the ESP and XBOOTLDR partitions and Install always runs
make-entry-directory in auto mode, so IPC callers cannot match what the
CLI verbs do with `--esp-path`, `--boot-path` and
`--make-entry-directory`. Add optional `espPath`, `xbootldrPath` and
`makeEntryDirectory` parameters that feed into the same code paths the
CLI already uses.
Allow systemd to be built as as single statically-linked binary (#42820)
The idea is that we can build a container by building a single-binary
systemd:
```console
$ meson setup build-static --default-library=static --prefer-static --auto-features=disabled -Dbuild-static=true -Dsystemd-multicall-binary=true && ninja -C build-static systemd
$ mkdir /var/tmp/container/usr/lib -p
$ cp build-static/systemd /var/tmp/container/usr/lib/
$ echo 'ID=quick' >/var/tmp/container/usr/lib/os-release
$ systemd-nspawn --restrict-address-families=af_unix --register=no --private-users=managed -D /var/tmp/container/ /usr/lib/systemd
░ Spawning container container on /var/tmp/container.
░ Press Ctrl-] three times within 1s to kill container; two times followed by r
░ to reboot container; two times followed by p to poweroff container.
Selected user namespace base 1855193088 and range 65536.
systemd 262~devel running in system mode (-PAM -AUDIT +SELINUX -APPARMOR +IMA +IPE +SMACK -SECCOMP -GCRYPT +GNUTLS +OPENSSL -ACL +BLKID +CURL -ELFUTILS -FIDO2 +IDN2 +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TPM2 -BZIP2 -LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -BTF -XKBCOMMON +UTMP -LIBARCHIVE)
Detected virtualization systemd-nspawn.
Detected architecture x86-64.
Detected first boot.
Welcome to Linux!
Initializing machine ID from container UUID.
Failed to open netlink, ignoring: Address family not supported by protocol
Applying preset policy.
Populated /etc with preset unit settings.
Unit default.target not found.
Falling back to graphical.target.
Mount unit not supported, skipping *MountsFor= dependencies.
Queued start job for default target graphical.target.
[ OK ] Reached target sysinit.target.
[ OK ] Reached target basic.target.
System is tainted: unmerged-bin:var-run-bad
[ OK ] Reached target multi-user.target.
[ OK ] Reached target graphical.target.
Startup finished in 61ms.
```
The container can be reloaded with SIGTERM, powered off with SIGRTMIN+4,
etc. SIGRTMIN+5 should cause a reboot but it currently fails:
```
...
Rebooting.
Container container is being rebooted.
Failed to attach root directory: Invalid argument
Failed to receive mount namespace fd from outer child: Input/output error
```
It's a bug … somewhere, but probably not caused by the linking changes
being done here.
tpm2: cache NvPCR NV space exhaustion via flag files in /run/
When we run out of NV index space while allocating an NvPCR, the
situation will unlikely improve until (at least) reboot. Retrying the
(doomed) Esys_NV_DefineSpace call on every subsequent allocation attempt
is wasteful (and very slow), so remember the exhaustion in a flag file
under /run/ and fail early next time.
We use two separate flag files, one for orderly and one for non-orderly
NvPCRs, since the two draw on different TPM resources (RAM-backed vs.
NVRAM-backed): exhaustion of one doesn't imply exhaustion of the other.
The files live in /run/, hence are cleared on reboot, which is
potentially is when NV space might become available again.
tpm2: optionally disable TPMA_NV_ORDERLY for NvPCRs
NVIndexes in TPMs can operate in two modes:
1. Backed by TPM RAM. In this case they are only written to NVRAM on an
orderly TPM shutdown when the system goes down. (TPMA_NV_ORDERLY flag
is on)
2. Backed by TPM NVRAM. In this case the nvindex value is written to NVRAM
on every write, and things are not delayed until orderly shutdown.
Normally mode 1 sounds like the obvious choice for NvPCRs, which reset
to zero anyway at boot. However, things are more complicated since
real-life TPMs tend to have a lot less RAM than NVRAM (both are
constrained but RAM even more than NVRAM). Hence there's value in using
NVRAM right-away. However, writing to NVRAM all the time means wearing
it out (since NVRAM is more vulnerable to that).
So far we unconditionally went for mode 1, but ran into space
constraints of RAM due to that.
Let's improve things a bit, and use orderly mode for NvPCRs we expect to
write many times, and non-orderly mode for those we expect to write only
a small, fixed number of times at boot, and not anymore during runtime.
Right now, this is only the "hardware" NvPCR, which measures hw identity
at boot.
Hopefully, this stretches available resources a bit further.
This also makes sure if the flag was set differently on allocation as
we'd set now, we accept it and won't complain, to make upgrades safe.
dongshengyuan [Wed, 24 Jun 2026 11:57:04 +0000 (19:57 +0800)]
pcrextend,tpm2-util,tpm2-setup: gracefully skip NvPCR when TPM NV space is exhausted
Map TPM2_RC_NV_SPACE to -ENOBUFS in tpm2_define_nvpcr_nv_index() rather
than -ENOSPC, giving it a dedicated errno distinct from the -ENOSPC that
write_string_file_at() can return when /run is full — a different failure
that occurs after the NV index is already allocated on the TPM.
In extend_nvpcr_now(), propagate -ENOBUFS as-is so callers can handle it
in a nicer fashion. In vl_method_extend(), map it to the new varlink error
io.systemd.PCRExtend.NvPCRSpaceExhausted. In run(), handle -ENOBUFS
explicitly under --graceful and print an appropriate message. Update
tpm2-setup.c accordingly.
import: Trust subkeys included in signature (#41860)
- import: Trust subkeys included in signature
With gpg sub keys one can rotate signing keys while having a stable
trust anchor. So far one still had to ship the sub key out of band but
a newer gpg has the option to include the sub key in the signature and
import it automatically. This is safe if we only allow importing a sub
key signed by the top key we already have in the key ring.
Add the --auto-key-import argument to gpg to import subkeys but also
set --import-options=merge-only,import-clean to restrict what we import
to only be sub keys signed by the top key we have in the keyring and
discard any irrelevant parts.
- import: Support env var to override gpg keyring
By default there is a fixed keyring in /usr or /etc. But when running
systemd-pull unprivileged in the user context or with a custom transfer
definition as in systemd-sysupdate --definitions=./... (e.g., for local
ParticleOS updates) it is limiting to require that all keys have to be
part of the OS keyring or otherwise no verification can be used. Also,
for testing it is valuable to point it at a different keyring.
Add a SYSTEMD_OPENPGP_KEYRING env var where the omission or empty
assignment sticks to the current behavior of the global OS keyrings but
a keyring path given will take precedence. While an env var can leak
down the process tree and is more difficult to secure for being the
trust anchor the advantage is that one can directly specify it in the
service unit as drop-in instead of having to patch the command
invocation. Anyway it's a niche use case and thus not part of the man
page.
Shihao Ren [Wed, 24 Jun 2026 03:46:56 +0000 (11:46 +0800)]
run: default run0 to root explicitly
When neither --user=, --area= nor --empower is given, run0 already
behaves as if root was requested, but only implicitly. That can be
misleading downstream.
Set arg_exec_user to "root" up front in parse_argv_sudo_mode(), so the
intent is visible and the rest of the code can rely on it.
Chris Coulson [Thu, 4 Jun 2026 14:04:29 +0000 (15:04 +0100)]
measure: Support binding signed policies to individual phases
systemd-measure can produce multiple signed policies for different
phases. However, a policy for a TPM resource that includes these signed
policies can currently be satisfied by any policy that is signed
with the same key.
It can be desirable to bind a resource's policy to one or more
specific phases. One way to do this could be to sign policies for
different phases or phase combinations with different keys. Another
approach is to limit the scope of signed policies using a policy
reference.
Using a policy reference works because:
- The reference is included along with the approved policy digest in the
digest that is signed.
- The reference argument is included in the authorization policy for a
resource via the TPM2_PolicyAuthorize assertion.
- During execution of the TPM2_PolicyAuthorize assertion, the TPM checks
that the session's current policy digest is the approved policy digest,
computes a digest from the approved policy digest and the supplied policy
reference, and checks that the resulting digest is the one that was
verified by TPM2_VerifySignature (via the returned ticket).
This adds a new --policyref argument to systemd-measure which binds all
of the signed policies to the specified policy reference. I did consider
making this more intelligent by auto-generating policy references for each
phase, but this approach provides the most flexibility for now. By making
use of the existing --append argument, a signer can produce multiple
signed policies with the same key that are bound to any individual phases
or combinations of phases.
The policy reference is a string without the NULL terminator. It is
supplied to the TPM via the TPM2B_NONCE type, which has a maximum size
equivalent to the size of the largest digest supported by the TPM. As
the signer doesn't know the capabilities of the target TPM,
systemd-measure limits the size of the policy reference to 32 bytes, to
fit within the size of a SHA256 digest.
This also includes the corresponding changes to systemd-cryptenroll and
systemd-repart (to add an equivalent --tpm2-public-key-policyref argument
so that the policy can be bound to the desired corresponding phase, and to
ensure that the policy reference is included in the LUKS2 token metadata),
and systemd-cryptsetup (to handle the policy reference stored in the LUKS2
token metadata).
This doesn't include policy reference support for credentials yet
because it requires a change to the credential headers.
Obserbed with GCC-11 on Ubuntu.
```
In file included from ../src/shared/format-table.h:7,
from ../src/bless-boot/bless-boot.c:11:
../src/bless-boot/bless-boot.c: In function ‘verb_set’:
../src/basic/log.h:187:27: error: ‘source2’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
| ^~~~~~~~~~~~
../src/bless-boot/bless-boot.c:458:40: note: ‘source2’ was declared here
458 | const char *target, *source1, *source2;
| ^~~~~~~
In file included from ../src/shared/format-table.h:7,
from ../src/bless-boot/bless-boot.c:11:
../src/basic/log.h:187:27: error: ‘source1’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
| ^~~~~~~~~~~~
../src/bless-boot/bless-boot.c:458:30: note: ‘source1’ was declared here
458 | const char *target, *source1, *source2;
| ^~~~~~~
In file included from ../src/shared/format-table.h:7,
from ../src/bless-boot/bless-boot.c:11:
../src/basic/log.h:187:27: error: ‘target’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
| ^~~~~~~~~~~~
../src/bless-boot/bless-boot.c:458:21: note: ‘target’ was declared here
458 | const char *target, *source1, *source2;
| ^~~~~~
cc1: all warnings being treated as errors
```
crypto-util: make OpenSSL UI API symbols optional during dlopen
Previously, if systemd was built with OpenSSL UI support, it would fail
to load libcrypto at runtime if the library lacked UI support, requiring
a recompilation of systemd to fix.
Let's relax this strict requirement by making the UI methods optional
during dlopen(). openssl_ui_supported() is added to dynamically check
if all required UI symbols were successfully loaded.
crypto-util: allow loading private keys from engine/provider without UI support
OpenSSL UI is not a mandatory feature to load private keys from an engine
or a provider. Let's allow loading private keys even if OpenSSL UI is not
supported.
Note that even if OPENSSL_NO_UI_CONSOLE is set, the type UI_METHOD is
always defined. Hence, the `#ifndef` condition in the definition of
struct OpenSSLAskPasswordUI is unnecessary and can be dropped.
crypto-util: make OpenSSL ENGINE API symbols optional during dlopen
If systemd is compiled with OpenSSL 3 headers but executed in an environment
where OpenSSL 4 (libcrypto.so.4) is loaded, dlopen_many_sym_or_warn() will
fail because OpenSSL 4 completely removes the deprecated ENGINE API. This
breaks the ability to dynamically fallback and seamlessly upgrade OpenSSL
without recompiling systemd.
To fix this, drop the ENGINE API symbols from the mandatory DLSYM_ARG() list.
Instead, try to load them via DLSYM_OPTIONAL() after the library is opened.
load_key_from_engine() is updated to check for their presence and return
-EOPNOTSUPP if the loaded OpenSSL version does not provide them.
Yu Watanabe [Tue, 30 Jun 2026 15:39:53 +0000 (00:39 +0900)]
crypto-util: drop manual endianness handling in rsa_pkey_from_n_e()
Currently, rsa_pkey_from_n_e() uses architecture-specific `#if` branches
and memdup_reverse() to handle big-endian RSA components (n and e)
before passing them directly to OSSL_PARAM_construct_BN().
We can simplify this by parsing the raw big-endian bytes into BIGNUMs
first using BN_bin2bn(), which natively expects big-endian data. We
can then push these BIGNUMs into OSSL_PARAM_BLD. This delegates the
data format handling entirely to OpenSSL and successfully removes the
platform-specific code.
Drop memstream and i2d_PUBKEY_fp(). We can simply use i2d_PUBKEY()
which automatically allocates the necessary buffer for us.
Note that dropping the secure erase (erase_and_freep()) in favor of
OPENSSL_free() is intentional and safe, as the buffer only holds
public key material which does not need to be securely wiped.
Yu Watanabe [Tue, 30 Jun 2026 15:00:11 +0000 (00:00 +0900)]
crypto-util: use correct cleanup function for OpenSSL buffers
Buffers allocated by OpenSSL must be freed with OPENSSL_free().
Fortunately, we do not enable the secure heap, so OPENSSL_free()
is currently equivalent to free(), but let's fix this for correctness.
Yu Watanabe [Tue, 30 Jun 2026 12:05:50 +0000 (21:05 +0900)]
resolved: migrate ECDSA verification to OpenSSL 3 EVP API
OpenSSL 3.0 deprecated low-level Elliptic Curve (EC) key manipulation
functions (EC_KEY, EC_POINT, EC_GROUP) and direct signature verification
functions like ECDSA_do_verify().
This commit modernizes dnssec_ecdsa_verify_raw() by transitioning to the
provider-aware EVP API:
* Uses OSSL_PARAM arrays and EVP_PKEY_fromdata() to construct the EC
public key directly from the raw octet string and curve name, avoiding
deprecated EC_POINT parsing.
* Converts the raw R and S signature components into a DER-encoded ASN.1
signature using i2d_ECDSA_SIG(), as required by the modern EVP API.
* Uses EVP_PKEY_verify() for the actual signature validation.
Additionally, this drops an outdated TODO comment waiting for raw ECDSA
support in the EVP API, as well as the deprecated warning suppression
macros and fallback code blocks. Unit tests for ECDSA are now run
unconditionally.
Yu Watanabe [Tue, 30 Jun 2026 08:00:27 +0000 (17:00 +0900)]
resolved: migrate RSA key construction to OpenSSL 3 EVP API
OpenSSL 3.0 deprecated low-level key manipulation functions and direct
access to RSA structures (such as RSA_new(), RSA_set0_key(), and
RSA_size()).
This commit modernizes dnssec_rsa_verify_raw() by replacing these
deprecated functions with the provider-aware EVP API:
* Uses OSSL_PARAM_BLD and EVP_PKEY_fromdata() to construct the RSA
public key directly from the modulus and exponent BIGNUMs.
* Replaces RSA_size() with EVP_PKEY_get_size().
Consequently, the workaround macros suppressing deprecated warnings
(DISABLE_WARNING_DEPRECATED_DECLARATIONS) and the conditional fallback
blocks (#if !defined(OPENSSL_NO_DEPRECATED_3_0)) are no longer needed
and have been dropped. Unit tests are also updated to run unconditionally.
dongshengyuan [Tue, 30 Jun 2026 10:10:49 +0000 (18:10 +0800)]
coredumpctl: use break instead of continue for time bound checks
When iterating journal entries with --until (forward scan) or --since
(reverse scan), the code used continue instead of break after crossing
the time boundary.
Since sd_journal_seek_realtime_usec() is called before the loop to
position at the start of the range, sd_journal_next()/previous()
returns entries in monotonically increasing/decreasing time order.
Once an entry's timestamp exceeds arg_until (or falls below arg_since
in reverse), all subsequent entries will also be out of range.
Using continue caused the entire remaining journal to be scanned
unnecessarily. journalctl uses break for the identical pattern in
src/journal/journalctl-show.c.
report: drop redundant .generate = NULL metric initializers
The .generate field of MetricFamily defaults to NULL when omitted from a
designated initializer, so spelling it out explicitly on the metric-family
entries (and the field-generating macros) that have no generator of their
own is just noise. Drop it.
bootctl: accept makeEntryDirectory in Install Varlink method
The CLI defaults --make-entry-directory to off and lets callers opt in
or request auto mode. The Varlink Install method always ran in auto
mode with no way to override it. Expose the tri-state so IPC callers
can match the CLI behaviour.
bootctl: accept espPath/xbootldrPath in Varlink methods
The CLI verbs have --esp-path/--boot-path but the Varlink methods
always auto-discover the partitions, so callers that mount the ESP
or XBOOTLDR at a non-standard location have to fall back to the
SYSTEMD_ESP_PATH/SYSTEMD_XBOOTLDR_PATH environment variables.
Allow to specify the paths when calling Install/Unlink/Link/LinkAuto
so the Varlink API is on par with the CLI.
Kai Lüke [Mon, 15 Jun 2026 13:11:41 +0000 (15:11 +0200)]
vmspawn/nspawn: Always use a per-machine runtime subdirectory
Some state files were prefixed with the machine name which protected
against collisions when running concurrent machines in a systemd unit
with RuntimeDirectory= set. However, the socket files tpm.sock and
control were not, which caused a race. Prefixing these socket files
does not work because the path gets too long when both the subdirectory
and the socket file within it get unique identifiers.
Instead of prefixing all files, we can rather always create a
subdirectory and use simple names within in. This makes paths shorter
in the normal case and protects against races with RuntimeDirectory=
were instead of directly reusing RUNTIME_DIRECTORY we also create the
normal per-machine subdirectory. Since this here is about runtime state
it should not impact any running VMs/containers. The ssh-proxy looks
only at the normal case and does not support RUNTIME_DIRECTORY, so no
impact there as well.
manager: fall back to direct reboot() if shutdown binary is missing
When PID 1 reaches the shutdown phase, if systemd-shutdown was missing,
it'd complain loudly and freeze. Implement an internal fallback to
handle this case gracefully. The fallback is always in place, since it
makes things generally more robust and is only a little bit of code.
This shutdown is simplified, systemd issues the reboot() matching the
requested objective directly, mirroring what systemd-shutdown would do
as its final step. This skips all the cleanup systemd-shutdown normally
performs (unmounting file systems, detaching storage, killing remaining
processes, switching into the exitrd).
With SYSTEMD_LOG_LEVEL=debug:
...
[ OK ] Reached target poweroff.target.
Notify message sent to '/run/host/notify': "X_SYSTEMD_UNIT_ACTIVE=poweroff.target"
Shutting down.
OSC sequence for shutdown successfully written: ...
Failed to execute shutdown binary: No such file or directory
Powering off.
Container container has been shut down.
manager: use reboot.target as the default for ctrl-alt-del.target
systemd tries to start ctrl-alt-del.target after receiving a request
to reboot in a container (or when ctrl-alt-del is pressed on a real
machine). If the alias is missing, this will fail. Let's provide a
fallback for this too. I don't think it makes sense to make it
configurable: it seems almost everybody is fine with the default
mapping. If not, people can always just create the symlink in the
filesystem.
core: rename and extend manager_add_job_by_name_and_warn
The "and" in the name was a misnomer. We do one *or* the other.
So rename 's/_and_/_or_/', and extend with the ability to load a second
unit. No functional change.
If we try to reexecute, and the binary was started from a non-standard
path, this would fail:
src/core/main.c:2396: Reexecuting.
src/core/main.c:2240: Failed to execute our own binary /usr/lib/systemd/systemd: No such file or directory
Let's make things more robust by trying /proc/self/exe too.
core: let manager_load_startable_unit_or_warn take a log level
Add a log_level parameter so callers can control the severity of load
failures. During boot, when no unit was explicitly requested on the
kernel command line (arg_default_unit is unset), failing to load the
default unit is not fatal since we fall back to other targets, so log
those attempts at LOG_INFO instead of LOG_ERR. All other callers pass
LOG_ERR to keep their existing behaviour.
Compile the contents of a few essential target unit files
(graphical.target, multi-user.target, rescue.target, etc.) into the
manager binary, with comments stripped, and fall back to those built-in
copies when no fragment is found on disk. This lets the manager reach a
usable state even on a system that ships none of these unit files, e.g.
in minimal environments or one-off chroots.
On-disk files always take precedence (masks included), as the fallback is
only consulted when nothing is found in the lookup paths. The unit file
list is defined in units/meson.build and embedded via a small generator
script; 'units' is now processed before 'src/core' so the list is
available there.
The set of units and services is selected that should allow for a normal
operation of starting/stopping/restarting, of the machine. The services
all use SuccessAction=…, so they don't require any binaries to be
installed.
sigpwr.target is included, even though it doesn't do anything useful.
The same is true in normal installations. We might want to change it
do something useful there too.
Add a 'fallback-default-target' meson option that configures which unit
to activate when default.target is not installed. This makes things more
resilient in general and is also useful for minimal/statically-linked
installations that may not ship a default.target symlink.
'graphical.target' is used as the default value of the setting, because
that's what we symlink as default.target in units/meson.build.
In the initrd, we had a fallback to start default.target if initrd.target
cannot be started. This fallback is changed to only do that if it is
not found, not on other errors. This seems more correct (and makes
the two fallbacks symmetrical.)
After stripping, in an unoptimized build with glibc, we get a 8.7 MB binary:
$ file build-static/systemd
build-static/systemd: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=1ec004bf61b775d284d909a818a3c4f63f83f055, for GNU/Linux 3.2.0, stripped
$ ls -lh build-static/systemd
-rwxr-xr-x 1 zbyszek zbyszek 8.7M Jun 30 14:07 build-static/systemd
core: convert exec-invoke to use the new static-friendly functions
The libc API is rather ugly: it requires the caller to pass an
array of the maximum possible size (or to repeatedly query the group
list to discover the actual size). With our "static-friendly" helpers,
we reallocate the array on the fly.
Static linking, i.e. without dlopen, sometimes fails when getpwuid
is used. I doesn't fail in all cases (glibc version?), so I didn't
do this initially.
various: add -Dbuild-static and alternatives to libc functions
When we try to build with -static, the linker refuses dlopen and
various functions related to nss. This commit adds the basic support
to compile with -static and allow the link to pass.