Commit Graph
20 Commits
Author SHA1 Message Date
Clayton Craft d52a693164 test: cover both of timesyncd's resolver paths
This checks that we resolve through resolved when it's running, and fall back
to getaddrinfo when it isn't.
2026-08-15 13:12:50 -07:00
Clayton Craft 8027ff6ecd timesyncd: don't ask for AAAA when IPv6 is disabled
ipv6_is_supported only tells us the kernel has IPv6, not that
it's usable. ipv6_is_enabled also checks the sysctl that turns
it off, so we stop resolving addresses we can't use.
2026-08-15 13:12:50 -07:00
Clayton Craft 6279910be7 timesyncd: order after systemd-resolved
Our first resolve happens way before sockets.target, and
systemd-resolved-varlink.socket has no ordering of its own, so there's
nothing making sure the socket is listening by then. Without this the
connect fails and we quietly fall back to getaddrinfo(), which is exactly
the case the varlink path is there for.

Order after both the service and the socket since either one is enough
on its own but neither covers every install situation. For example
resolved binds the socket itself when nothing hands it one, and on
systems where only the socket is enabled the service isn't started at
boot at all.
2026-08-15 13:12:50 -07:00
Clayton Craft 98e7cc36f0 timesyncd: resolve NTP hostnames through resolved's varlink interface
With DNSSEC on, resolved can't validate anything until the clock is
right, and without an RTC the only thing that fixes the clock is talking
to the NTP servers we're trying to resolve. On glibc the timesyncd unit
sets SYSTEMD_NSS_RESOLVE_VALIDATE=0 and nss-resolve turns that into a
per-query flag, which breaks the chicken/egg. musl (and other systems
not using nss-resolve) have no NSS, so nothing reads it there and we're
stuck.

So talk to resolved directly when it's there, and pass
SD_RESOLVED_NO_VALIDATE until we've synced the clock ourselves.
NTP-related lookups after that are validated normally, which is better
than what we get from nss. If resolved isn't around we just keep using
getaddrinfo().

The reply handler duplicates the tail of the getaddrinfo handler, since
the two differ in how they report failures. Varlink discards what the
callback returns, so it has to move on to the next host itself.

This also drops SYSTEMD_NSS_RESOLVE_VALIDATE=0 from the unit because
nss-resolve talks to resolved over the same socket we do, so the
getaddrinfo() path is only ever taken when resolved is unreachable, and
then nothing validates anyway.
2026-08-15 13:12:50 -07:00
Clayton Craft 87fa1c28a5 pam_systemd: document inhibit= and inhibit-why= options 2026-06-20 09:57:42 +01:00
Clayton Craft 179b7c2ab5 pam_systemd: acquire inhibit lock when inhibit= is specified
This adds a new function, acquire_inhibit_lock, that takes a logind
inhibitor lock for the duration of the PAM session. The lock fd is
stored in PAM data with pam_cleanup_close. In pam_sm_close_session, the
lock is released explicitly by passing NULL to pam_set_data, which
triggers the registered cleanup function immediately rather than waiting
for pam_end.

The inhibit type and reason string can also be configured via the
XDG_SESSION_INHIBIT and XDG_SESSION_INHIBIT_WHY environment variables,
which take precedence over the module arguments.

Fixes: #20654
2026-06-20 09:57:42 +01:00
Clayton Craft 58fc96bb71 pam_systemd: add inhibit= and inhibit-why= module arguments to parse_argv
This adds two new module arguments to parse_argv:

1) inhibit= takes a colon-separated list of inhibitor lock types to acquire

2) inhibit-why= takes an optional human-readable reason string.

Both are currently parsed but are unused until a later commit.
2026-06-20 09:57:42 +01:00
Clayton Craft 301b4193a0 bootctl: remove fallback EFI Boot#### variable on uninstall
This cleans up the fallback Boot#### entry that was registered on
install. The logic cleaning up variables was moved from verb_remove into
a new remove_variables function, which mirrors the install side.
2026-05-21 11:30:46 -07:00
Clayton Craft 6e0ebe437b bootctl: register fallback EFI Boot#### entry on install
This adds a second install_boot_option call to register a Boot#### entry
pointing at systemd-boot-fallback{arch}.efi, and place it immediately
after the primary entry in BootOrder.

The fallback file does not exist on the ESP on first install and is
only created on first update when the existing primary binary is
rotated to the fallback path. We register the variable anyway, so
that the entry exists in the BootOrder once the fallback file shows up.
Until then, firmware that reaches the fallback entry will fail to
load it and fall through to the next entry in BootOrder, which is
fine. install_boot_option gains a require_existing parameter so the
existing early return on a missing ESP path can be skipped for the
fallback, where a missing path is expected.

This also does a bit of refactoring by splitting the bottom part of
run_install() into a new install_variables() function that handles
registering both the primary and fallback entries.
2026-05-21 11:30:45 -07:00
Clayton Craft 037b9a0680 bootctl: back up sd-boot binary to fallback path on update
When a primary sd-boot binary already exists on the ESP and is being
updated, it is copied to systemd-boot-fallback{arch}.efi before installing
the new version. This gives firmware a fallback Boot#### entry pointing
to the previous binary in case the new one fails to load.

The fallback is preserved (not overwritten) when its product and version
match the currently booted bootloader (read from the LoaderInfo EFI
variable), since that means it already holds the known good binary that
booted this session. In all other cases it is overwritten with the current
primary, when no fallback exists yet, when LoaderInfo is unavailable, or
when the fallback's product or version differs from what booted.

This also moves the version_check() call up so its result determines
both the rotation decision and the main copy, and avoids a duplicate
check (and duplicate "Skipping..." log) when the binary is already
current.
2026-05-21 11:30:45 -07:00
Clayton Craft 6c356b6a8b bootctl: add after_slot parameter to insert_into_order()
This adds an after_slot parameter that, when not set to UINT16_MAX,
requests that the new slot be placed immediately after the given slot in
BootOrder. When after_slot is set and the new slot already exists in
BootOrder, it will leave its position alone. This is so that if a user
reorders it, we don't stomp on their changes.
2026-05-21 11:26:59 -07:00
Clayton Craft bb520fd6a5 bootctl: add description and ret_slot parameters to install_boot_option()
This moves creation of the EFI boot option description out of
install_boot_option and into the caller, and adds a ret_slot output
parameter for capturing the assigned BootOrder slot. This allows reusing
the function for installing variables with different descriptions.
2026-05-21 11:26:59 -07:00
Clayton Craft 1027cbd8ae bootctl: rename install_variables/remove_variables to install/remove_boot_option
These functions install or remove a single EFI Boot#### entry, not
"all variables," so this renames them to better reflect what they do.
2026-05-21 11:26:59 -07:00
Clayton Craft a2ba538ead bootctl: fix removing variables on uninstall
remove_variables looks up the EFI boot entry by matching both the path
and the partition UUID and it wasn't actually removing any entries
because verb_remove was passing SD_ID128_NULL, so the lookup never
matched and Boot#### entries were left behind on uninstall.

Fixes 38433a6
2026-05-21 11:26:59 -07:00
Clayton Craft 47d408163b loop-util: don't reuse partition fd when partscan needed
Some devices (e.g. android phones running pmOS) cannot have their OEM
partition table altered without breaking the firmware, so the distros's
partitions live inside a nested GPT carved into one of the OEM
partitions. Exposing these subpartitions requires wrapping the outer
partition in a loop device with partscan enabled, since the kernel does
not go into nested partition tables.

systemd already detects this case in udev-builtin-blkid
(ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP) and acts on with
systemd-loop@.service, but this fails towards the end.
loop_device_make_internal has an optimization where if the input is
already a block device with a matching sector size, it skips creating
a loop and just hands back the original fd. That's fine for whole disks
but wrong for partitions, which don't support partscan, so this causes
dissect_image to fail with EPROTONOSUPPORT.

This patch changes the behavior to only take the shortcut when the input
is a whole disk, or when partscan was not requested.

Co-Authored-By: Clayton Craft <clayton@craftyguy.net>
2026-04-28 15:25:07 +01:00
Clayton Craft 9149c75953 ukify: fix default path for hwids
The documentation and commit that added this seem to suggest this should
be under /usr/lib/systemd

fixes 117ec9db7e
2026-04-22 23:05:50 +01:00
Clayton Craft d3d142e5fd updatectl: fix unexpected polkit prompt on update
When running `updatectl update` since f0b2ea63, Install() was called
with the version resolved by Acquire() rather than the originally
requested (empty) version, causing the stricter update-to-version polkit
action to be used instead of the update polkit action.
2026-04-03 14:26:17 +02:00
Clayton Craft bc6380ae07 nsresourced: fix BPF loading when using kernel compiled with Clang
This fixes an issue where nsresourced fails to load BPF on kernels
compiled with Clang (this output was from v259):

    $ sudo env SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-nsresourced
    ; int BPF_PROG(userns_restrict_path_chown, struct path *path, void* uid, void *gid, int ret) { @ userns-restrict.bpf.c:134
    ...
    ; return validate_path(path, ret); @ userns-restrict.bpf.c:135
    ...
    ; static int validate_path(const struct path *path, int ret) { @ userns-restrict.bpf.c:120
    ...
    ; task = (struct task_struct*) bpf_get_current_task_btf(); @ userns-restrict.bpf.c:84
    ...
    ; task_userns = task->cred->user_ns; @ userns-restrict.bpf.c:85
    ...
    R2 invalid mem access 'rcu_ptr_or_null_'

When Clang is used (which sets CONFIG_PAHOLE_HAS_BTF_TAG), btf_type_tag
support is enabled. As a result, an rcu type tag is added to
task_struct::cred:

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep "STRUCT 'task_struct'"
    [459] STRUCT 'task_struct' size=4672 vlen=242

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A200 "^\[459\] STRUCT 'task_struct'" | grep cred
	    'ptracer_cred' type_id=802 bits_offset=14528
	    'real_cred' type_id=802 bits_offset=14592
	    'cred' type_id=802 bits_offset=14656

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[802\]'
    [802] PTR '(anon)' type_id=801

    $ bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[801\]'
    [801] TYPE_TAG 'rcu' type_id=803

Since the struct ptr *could* be null, we have to add a null pointer
check to satisfy the bpf verifier.
2026-03-20 15:27:08 +01:00
Clayton Craft fb7a902aed boot: don't print error if device tree fixup protocol isn't supported
This isn't a failure we care about, and it's somewhat alarming to see a
red error message flash up on the display when booting, so this just
simply returns EFI_SUCCESS and skips printing the "error" altogether.
2024-01-19 11:43:19 +00:00
Clayton CraftandDaniel Thompson 0f781d2aa5 boot: load device tree even if no original config exists
Firmware may not have loaded a devicetree, for example if the device
shipped with windows and exclusively supports ACPI.
We should always load the specified devicetree regardless of firmware
state to enable booting on platforms where Linux only supports DT.

Note: in _cleanup, the orig. config is NULL in this case, and passing
NULL to InstallConfigurationTable is permitted by the EFI spec.
See: https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html

Fixes #24059

Co-authored-by: Daniel Thompson <daniel.thompson@linaro.org>
2023-11-10 22:04:50 +00:00