This adds the following:
1. systemd-report gains a new --sign= option, taking a boolean. If true,
this makes systemd-report generate + systemd-report upload generate a
signed report, instead of a regular one. The signatures are collected
from Varlink-based backends.
2. One such backend is added which does a simple Ed21159 based signing
scheme.
3. this adds a new metrics source which just reports text files
symlinked into a special dir as metrics. This is used to report the
Ed21159 public key as metric, by default, if it exists.
4. finally, systemd-report itself is turned into a varlink service. this
is useful for example for extracting a report from a system coming in
via the varlink/http bridge.
I thought a long time about the format of signing of reports. Initially
i intended to do this like homed's user record signing, i.e. require
normalization of the record, then normalize the record, and write it out
in dense form, since the result. Finally insert the resulting hash into
the user record itself. People have pointed me to the inherent messiness
of signing JSON this way though, as it requires any participant that
wishes to sign/authenticate records this way to implement the exact same
normalization/formatting rules, and in particular in the area of
floating point numbers (of which metrics presumably will have many) this
is quite problematic.
This signing hence goes a different way. instead of expecting
signer+verifier to independently come to the same normalized text form
of the json data, let's instead output a JSON-SEQ sequence, where the
first object is the report, and any subsequent objects are one signature
each. the signatures are supposed to cover the precise binary
representation of the first element in the JSON-SEQ stream. (i.e. from
the RS to the NL).
or in other words: a verifier would receive the JSON-SEQ stream, split
it up before each RS. Then it would leave object 1 unparsed for the
moment, and parse objects 2…n. It would then authenticate object 1's
precise binary representation with objects 2…n. Once that checks out, it
would parse object 1, and use it as report.
Since a1f4fd3876 udev processes
run in an 'udev' subcgroup, to avoid killing control processes
when clearing workers. But the main process is still in the same
cgroup, so the atomic cgroup.kill cannot be used.
If the main subcgroup exists, try to create a sibling 'workers'
cgroup, and use it for workers processes, and use cgroup.kill if
available.
This is especially useful as rules can spawn arbitrary programs/scripts
and TasksMax= is set to unlimited.
Acquiring a LUO session from /dev/liveupdate requires privileges, and
also the device is a single-owner driver so only a single process can
open it at any given time.
Add a LUOSession= service settings that allows units running without
privileges to get a session assigned to them.
The kernel imposes a 64 chars limit on session names, which is too short
to avoid clashes, so derive a hash from joining the unit name with the
parameter name, that way two units using the same setting don't clash.
This fixes 2 issues with `ukify`:
`config_list_prepend` needs a list. The 2 more used entries are
specially treated (parsed into a list) and therefore happen to work, but
trying to set `UKI/Firmware` throws a type error, as it is passed as
just a string.
The `UKI/SBAT` config entry is a list but used the default `config_push`
value which sets it if unset (which also failed since its default of
`[]` is not `None`) - change this to `config_list_prepend`.
I did consider adding a `config_list_append`, but as far as I'm aware,
the order of sbat entries should not change the outcome anyway.
OpenSSL 4 broke ABI, so we need to look for both SONAMEs.
Try libcrypto.so.3 first, and fallback to libcrypto.so.4,
so that the older and more stable version is used if both
are installed, giving distros time to fix regressions.
The binary catalog reader trusted two values straight from a (possibly
hostile) database: open_mmap() summed header_size + n_items *
catalog_item_size in uint64 with no overflow check, and find_id() added
the matched item's offset to the map base with no upper bound. Reachable
through sd_journal_get_catalog() with $SYSTEMD_CATALOG set, this let
catalog_get()/catalog_list() strdup() a string starting outside the
mapping. Guard the size math with MUL_SAFE/INC_SAFE and reject item
offsets that fall outside the file.
A single incorrect password attempt from the user would be tried
twice, because the first failed try would think it was the
try_discover_key method that failed, and would invalidate that instead
of the actual failed password. This resulted in the user being
prompted one fewer time than they should be.
Fixes#38495
journalctl needs gcrypt to set up the journal sealing
keys and other such operations, but gets no dlopen note
with the dependency. Add the dlopen macro with a recommends
level so it can be skipped, but gets pulled in by default
on user/desktop systems.
Follow-up for 590e226437
These signatures can be provided by arbitrary Varlink backends symlinked
into /run/systemd/report/sign.
Also add a proper Varlink IDL for the upload interface, since we are
extended it now.
Acquiring a LUO session from /dev/liveupdate requires privileges,
and also the device is a single-owner driver so only a single
process can open it at any given time.
Add a LUOSession= service settings that allows units running
without privileges to get a session assigned to them.
The kernel imposes a 64 chars limit on session names, which is
too short to avoid clashes, so derive a hash from joining the
unit name with the parameter name, that way two units using
the same setting don't clash.
On hppa '.equ' is overridden, so even this workaround ('.set' is
overridden on alpha) causes a build failure:
cc -Isrc/basic/libbasic.a.p -Isrc/basic -I../src/basic -Isrc/fundamental -I../src/fundamental -Isrc/systemd -I../src/systemd -Isrc/version -I../src/version -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -std=gnu17 -O0 -g -Wno-missing-field-initializers -Wno-unused-parameter -Wno-nonnull-compare -Warray-bounds -Warray-bounds=2 -Wdate-time -Wendif-labels -Werror=bool-compare -Werror=discarded-qualifiers -Werror=flex-array-member-not-at-end -Werror=format=2 -Werror=format-signedness -Werror=implicit-function-declaration -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Werror=missing-declarations -Werror=missing-parameter-name -Werror=missing-prototypes -Werror=overflow -Werror=override-init -Werror=pointer-sign -Werror=return-type -Werror=sequence-point -Werror=shift-count-overflow -Werror=shift-overflow=2 -Werror=strict-flex-arrays -Werror=undef -Wfloat-equal -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op -Wmissing-include-dirs -Wmissing-noreturn -Wnested-externs -Wold-style-definition -Wpointer-arith -Wredundant-decls -Wshadow -Wstrict-aliasing=2 -Wstrict-prototypes -Wsuggest-attribute=noreturn -Wunterminated-string-initialization -Wunused-function -Wwrite-strings -Wzero-as-null-pointer-constant -Wzero-length-bounds -fdiagnostics-show-option -fexcess-precision=standard -fno-common -fstack-protector -fstack-protector-strong -fstrict-flex-arrays=3 -fno-math-errno --param=ssp-buffer-size=4 -Wno-unused-result -Werror=shadow -fPIC -fno-strict-aliasing -fstrict-flex-arrays=1 -fvisibility=hidden -fno-omit-frame-pointer -include config.h -isystem../src/include/glibc -isystem../src/include/override -isystemsrc/include/override -isystem../src/include/uapi -fvisibility=default -MD -MQ src/basic/libbasic.a.p/compress.c.o -MF src/basic/libbasic.a.p/compress.c.o.d -o src/basic/libbasic.a.p/compress.c.o -c ../src/basic/compress.c
/tmp/ccxm7Waj.s: Assembler messages:
/tmp/ccxm7Waj.s:2085: Error: bad or irreducible absolute expression; zero assumed
/tmp/ccxm7Waj.s:2085: Error: junk at end of line, first unrecognized character is `,'
/tmp/ccxm7Waj.s:2268: Error: bad or irreducible absolute expression; zero assumed
/tmp/ccxm7Waj.s:2268: Error: junk at end of line, first unrecognized character is `,'
/tmp/ccxm7Waj.s:2544: Error: bad or irreducible absolute expression; zero assumed
/tmp/ccxm7Waj.s:2544: Error: junk at end of line, first unrecognized character is `,'
/tmp/ccxm7Waj.s:2800: Error: bad or irreducible absolute expression; zero assumed
/tmp/ccxm7Waj.s:2800: Error: junk at end of line, first unrecognized character is `,'
/tmp/ccxm7Waj.s:2956: Error: bad or irreducible absolute expression; zero assumed
/tmp/ccxm7Waj.s:2956: Error: junk at end of line, first unrecognized character is `,'
'.equiv' works on all architecures, but breaks on CentOS 9 due to binutils
2.35. Use an ifdef. Can be dropped and switch to '.equiv' once binutils 2.36
is the baseline.
Follow-up for 7590eb0c74
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
sysext: utimensat() failure was logged with stale r (which is 0 after
the preceding successful write_backing_file call). Pass errno instead
so the actual failure reason is recorded and returned.
sysusers: rename() failure in make_backup() returned the raw positive
errno value. All callers check 'if (r < 0)', so the error was silently
ignored, allowing execution to continue after a failed backup. Return
-errno instead.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The condition on line 1206 checks other->load_state != UNIT_STUB to
decide whether to call the vtable done() callback, but the state was
already overwritten to UNIT_MERGED on line 1198, making the condition
always true.
Save the original load_state before overwriting it, so that units in
UNIT_STUB state (which never went through a load attempt) correctly
skip the done() call.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The only runtime trigger for registry cleanup is the BPF kprobe that fires
on user namespace destruction; when it is missed (ring buffer overflow,
kprobe missing, fdstore entry dropped), the dead namespace's registry entry
survives and keeps its UID/GID ranges blocked until the manager restarts and
its startup sweep runs. The allocation hot path checked whether a candidate
range was already taken but never whether the namespace holding it was still
alive, so a single dead namespace could permanently starve an allocation.
This is most visible when a parent delegates its entire container UID window
to a child that then dies: every subsequent allocation from the parent fails
with NoDynamicRange even though the ranges are reclaimable.
Add userns_registry_reap_if_dead(), which probes a registered namespace's
liveness via the kernel namespace identifier recorded at allocation time and,
if it is authoritatively dead, releases its registry entry — restoring any
ranges it received via delegation to their ancestors. Call it from the
allocation availability check for both transient registrations and delegated
ranges, walking a chain of dead ancestors in the delegation case. This
mirrors the existing inode-slot stale cleanup and makes allocation
self-healing without waiting for a restart.
The startup sweep grew the same load-probe-release logic, so route it through
the new helper too; its errno return distinguishes alive, no-recorded-id, and
unprobeable-environment cases so the sweep keeps its early-out when lookup by
id isn't possible at all.
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
We might want to add more state to the LUO session json payload,
so add a version (to allow clean compat breaks if needed) and nest
the current fdstore contents under a 'units' object, so that more
top-level data can be added in the future without breaking
backward compatibility.
Follow-up for 257c35c1a3
fstab-generator: pass k instead of r to bus_error_message() so the
fallback error string reflects the actual bus call failure, not the
accumulated result that was reset to 0 earlier.
networkd-ndisc: return -ENOMEM when newdup() fails, since r is 0 at
that point and the OOM would otherwise be reported as success.
storagetm: add missing NULL check after strndup() for attr_model,
matching the pattern already used for attr_firmware and attr_serial.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Avoid trying to query for our LUO session on reexec/softreboot/reload/etc.
Currently /dev/liveupdate is only accessible to root so it's not a big
issue, but this might change in the future, so make sure nobody can
play games with us.
Follow-up for 257c35c1a3
When generator_write_initrd_root_device_deps() fails, the error was
swallowed by returning 0 (success) instead of r. The two subsequent
calls in the same block correctly return r on failure.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
control_command and control_command_id were cleared before being passed
to unit_log_process_exit(), so the log always showed an invalid/unknown
command name.
Move both clears after the log call, matching the ordering in
socket_sigchld_event() and service_sigchld_event().
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
663f0bf5cb stopped reusing the original block device fd whenever
partition scanning was requested (LO_FLAGS_PARTSCAN) but couldn't be
enabled on the device, so that nested partition tables on devices the
kernel won't scan (e.g. the pmOS/android case) get exposed via a real
loop device.
However that also forced a pointless loop device for any partition that
carries a file system directly, e.g. a btrfs subvolume mounted via
MountImages=. For multi-device btrfs this is fatal: the kernel rejects
seeing the same member via both the original partition and the loop
device, and the mount fails.
A loop device is only ever needed here to expose a nested partition
table. So only refuse the shortcut when the device actually carries one,
probed via gpt_probe(), instead of whenever partition scanning is
disabled. Devices carrying a file system directly (or nothing) take the
shortcut as before.
Add an integration test to cover the failure scenario of the original
issue.
Fixes: https://github.com/systemd/systemd/issues/42520
Replaces: https://github.com/systemd/systemd/pull/42576
Follow-up for 663f0bf5cb79ecaf6dd71441ecdc9dc401e7eae6
Co-Authored-By: Luca Boccassi <luca.boccassi@gmail.com>
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
let's name the dir "/run/systemd/report.upload/" (rather than
"/run/systemd/metrics-upload/"). After all, these are reports that we
upload, not indiviudual metrics. And it would be particular confusing
since the dir to pick up metrics is called /run/systemd/report/, rather
than /run/systemd/metrics/. Hence the thing that deals with reports is
nmamed metrics, and the thing that deals in metrics is named reports...
We really want to use io.systemd.Report for the interface
provided by systemd-report itself, not by its backend. hence, rename the
interface that uploading plugins shall implement to
io.systemd.Report.Uploader.
Note that we ideally should have a varlink interface definition for that
interface. if we had, we'd have noticed that earlier.
Since 4e0eabd401 ("udev: also trigger loop device for boot disk when
partition scanning is unsupported"), builtin_blkid() bails out entirely as
soon as probe_gpt_boot_disk_needs_loop() reports that a loop device is
needed, skipping all superblock probing. As a result whole-disk properties
such as ID_PART_TABLE_UUID and ID_FS_* are no longer set.
This regresses any whole disk whose partitions the kernel cannot expose
itself but which is otherwise perfectly probeable, most notably
device-mapper multipath disks: kernel partition scanning is disabled on them
(their partitions are managed in userspace by kpartx), so they are now
flagged as needing a loop device and lose their ID_PART_TABLE_UUID.
The early return was never necessary. The original intent was only to skip
root partition discovery on the device, and that already happens on the loop
device instead: find_gpt_root() bails when the kernel can't scan partitions,
blkid probes at the device's own logical sector size so a GPT written for a
different sector size is simply not detected, and PART_ENTRY_* is only
emitted for partitions the kernel actually registered, of which a
loop-needing whole disk has none. So keep probing the device for its
whole-disk properties unconditionally and let partition and root discovery
happen on the loop device.
Co-developed-by: Claude Fable 5 <noreply@anthropic.com>
Now that the fast path performs a deep copy identical to the general
loop (when n_changes_attached==0, found stays false for all entries),
the block is redundant. Remove it and let the general loop handle this
case.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
At line 285, ftruncate() failure was logged using 'r' which is 0
from the preceding successful loop_write() call. log_error_errno(0, ...)
triggers an assertion crash in developer builds (ASSERT_NON_ZERO) and
silently returns success in release builds, swallowing the ftruncate error.
Replace with errno which is set by ftruncate() on failure.
Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
Co-developed-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
```
../src/boot/test-efi-string.c: In function 'test_xvasprintf_status':
../src/boot/test-efi-string.c:744:34: error: format '%zi' expects argument of type 'signed size_t', but argument 4 has type 'long int' [-Werror=format=]
744 | test_printf_one("%i %i %zi", INT_MIN, INT_MAX, SSIZE_MAX);
| ~~^
| |
| int
| %li
cc1: some warnings being treated as errors
ninja: subcommand failed
```
A previous refactoring failed to copy the flag from the command line
argument to the installation context object, so the flag was ignored.
Closes: https://github.com/systemd/systemd/issues/41488
Fixes: 38433a6d06 ("bootctl: rework bootctl-install.c in preparation of varlinkification")
If a partition gets removed due to factory reset, we will recreate it
as blkpg later. So it needs to get removed. So rescan is needed to be
done after we write the partition table for factory reset.
Fixes#42453
This will also allow debugging systemd-boot easily on RISC-V.
Note that the following much simpler variant won't work, as we might be missing
the optional 'zihintpause' extension:
asm volatile("pause");
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@codethink.co.uk>
unhexmem_full() ignores whitespace, so a 64-byte manifest digest field
can decode to fewer than 32 bytes. Reject that while parsing instead.
[ 83.883087] TEST-72-SYSUPDATE.sh[5995]: systemd-sysupdate: ../src/src/sysupdate/sysupdate-resource.c:581: resource_load_from_web: Assertion `h.iov_len == sizeof(instance->metadata.sha256sum)' failed.
Follow-up for 43cc7a3ef4
Assisted-by: kres (claude-opus-4-7)
Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>