The integration tests are started with the absolute path of the test
unit as $0, so run_subtests() derives absolute paths for the subtests
and the summary prints them in full. Show just the script name, the
directory is the same for every entry and only adds noise.
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
Not all channels used for credential transport in systemd are trusted
on SNP: while cmdline and initrd are measured and should be used,
smbios and fwcfg are untrusted and the guest should ignore creds from
those delivery paths.
Implement an offensive credential test that tries to inject credentials
via those untrusted channels and check the guest won't consume those
credentials.
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
Check that credentials passed via systemd-vmspawn --set-credential and
on the command line via systemd.set_credential= are successfully
delivered on a SNP coco guest.
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
Grow TEST-94-COCO from a single inline check into the reusable structure
that can be used by multiple future subtests:
- lib.sh: code shared between subtests on the host side.
- guest-test.sh: the testcase_coco_* checks.
- guest-test-runner.sh: runs each testcase_coco_* check, ships guest
side testcase results over vsock.
Helpers live under test/units/TEST-94-COCO/. The vsock records are
best-effort detail, the aggregate exit status stays the authoritative
pass/fail.
Split the test into subtests per platform/boot flow.
Signed-off-by: Paul Meyer <katexochen0@gmail.com>
In preparation for adding a varlink interface for key management, add
support for signing reports with multiple TPM2 keys. During signing,
this iterates over all *.key files in /var/lib/systemd/report.sign.tpm2,
generating a TPM signature with each one. It also looks for the
corresponding *.voucher file, containing some credential that is used to
prove that the signing key is authentic, and which is added to the
signature if it exists.
If no signing keys exist when a signature is requested, a default
signing key is still created as default.key.
Classical DNSSEC algorithms such as RSA and ECDSA are vulnerable to
quantum computers. Add support for post-quantum ML-DSA-44, DNSSEC
algorithm 18.
Verify pure ML-DSA through OpenSSL. Report the algorithm as unsupported
only when it is unavailable. Treat key import and verification failures
as hard errors.
Add the draft test vector and negative tests.
https://datatracker.ietf.org/doc/draft-westerbaan-dnssec-mldsa/
Signed-off-by: Ayoub Zaki <ayoub.zaki@embetrix.com>
When generating a session audit digest, the algorithm isn't included in
the attestation. The algorithm is the auth digest algorithm that the TPM
session is created with, which is currently hardcoded as SHA-256.
This might not always be the case though, and verifiers shouldn't have
to guess what the digest algorithm is, so include the algorithm in the
report signature component.
1. test-fstab-generator: fix inverted /mnt symlink guard
The test-18-options.fstab.input scenario was guarded by a check
that was always true on a normal system, so it was silently
skipped and its fixtures had drifted out of sync with actual
generator output. This commit fixes the guard and repairs the
stale fixtures it exposed.
2. fstab-generator: support for x-systemd.onfailure
Add support for the x-systemd.onfailure= fstab option in
systemd-fstab-generator. This allows configuring OnFailure=
dependencies for generated mount units directly through /etc/fstab.
For example:
/dev/sda1 /data ext4 defaults,x-systemd.onfailure=emergency.target 0 2
results in the generated mount unit containing:
[Unit]
OnFailure=emergency.target
This makes it possible to attach recovery targets or services to
mount failures directly from fstab configuration.
Add documentation and generator tests.
It's nice to use --attach since it validates our images. So far, it
strictly insisted on checking that the image was a DDI however, which
limits its use for certain usecases.
Add "--relax" to address this limitation.
Fixes: #43605
Adds EncryptAddToken=<type>[:<source>:<value>] to repart.d partition
files, registering one or more LUKS2 token objects in the partition
header after the keyslot is created. This allows custom token plugins
(e.g. a TDX KBS plugin) to be wired up at provisioning time without a
separate cryptsetup token import step.
The type name is always explicit and extra token fields can be supplied as
a JSON file (:file:/path) or base64-encoded JSON (:data:<base64>). The
type and keyslots fields are always injected by repart. The option is
repeatable; an empty value clears all tokens for the partition.
Supported with Encrypt=key-file, tpm2, and key-file+tpm2.
If KExecsCount > 0 then do not attempt to mark a UKI, as the stub (and also
any addon and the bootloader) will not have been exercised, so there's no
guaranteed a full reboot with the same image will actually work.
Fixes https://github.com/systemd/systemd/issues/43738
Add support for the x-systemd.onfailure fstab option in fstab-generator.
This allows generated mount units to include the systemd OnFailure=
dependency specified through /etc/fstab.
x-systemd.onfailure= is handled by generalizing the existing
write_dependency() helper (already used for After=/Before=/Requires=/
Wants=) to take an optional unit-name suffix. When no suffix is given,
unit names are mangled without forcing a ".mount" suffix, since
OnFailure= targets are not necessarily mount units. write_on_failure()
is now a thin wrapper around write_dependency().
Example:
/dev/sda1 /data ext4 defaults,x-systemd.onfailure=emergency.target 0 2
generates:
OnFailure=emergency.target
This makes it possible to attach recovery targets or services to mount
failures directly from fstab configuration.
Add documentation and generator tests.
The test-18-options.fstab.input scenario was guarded by
"$(readlink /mnt)" != "/mnt", which is true on any normal system
since readlink prints nothing for a plain directory. This meant the
scenario was always skipped, and its fixtures were never actually
compared against generator output.
Fix the guard to check whether /mnt is a symlink directly, and repair
the pre-existing fixtures that had drifted while the scenario was
dead:
- mnt-wantedby-automount.mount had a stale Where= value and was
missing the escaping the generator emits for the hyphen in
/mnt/wantedby-automount.
- mnt-requires.mount had Requires=/After= in the wrong order.
Also extend the backslash-stripping workaround loop (used because the
.deb packager dislikes files named with a backslash) to cover
.automount units in addition to .mount/.swap, and fix an ordering bug
where -f was checked before -L, which could leave a dangling symlink
when both a unit and its drop-in symlink needed stripping.
It's nice to use --attach since it validates our images. So far, it
strictly insisted on checking that the image was a DDI however, which
limits its use for certain usecases.
Add "--relax" to address this limitation.
Killing the broker is destructive well beyond the units under test: nothing
reconnects to the new one, so logind for instance keeps running with
org.freedesktop.login1 unowned for the rest of the boot. As a subtest of
TEST-23-UNIT-FILE that leaked into everything running after it, and
statedir.sh, the first one to start user@0.service, then spent 25s per logind
property in user-runtime-dir and, where pam_systemd can't use Varlink, blocked
in CreateSession() past user@.service's 90s TimeoutStartSec and failed — with
the failure pointing at a subtest that has nothing to do with the bus.
In its own test the damage dies with the machine.
Similar as the previous commit: the field was renamed by accident, and
we should revert to the old name to not break compat needlessly, and
because the old name actually made sense, given the underlying .transfer
file knob is AppStream=
The "isEnabled" name was changed from "enabled" post v261 by accident
(because a6f7ded859 replaced json
formatting via table formatter to a native json generator which was
introduced for varlink, which changed the names). THis was later made
official in #43603, but it should rather have changed things back to the
original name, in particular as the underling .feature file field
actually is called "Enabled=" and not "IsEnabled=".
We really should take care to generate the same names for Varlink JSON
data as for the data returned by the systemd-sysupdate cli.
a6f7ded859 ensured that, so that's great.
But at the same time I think the varlink names should be adjusted to the
old names, since varlink support for this was just added in this cycle.
Given that v262 is not tagged yet, let's now revert to the old name.
This adds a fallback to `blockdev_get_root`, to handle the case where
`/usr` is a sysext overlay. Additinal changes are:
- move `extension_overlay_block` to `extension-util.h`, and make it
generic to work in both confext and sysext cases (though only sysext is
actually used currently)
- add a usr-only integration test to exercise the failure
Closes#43565.
When an automount goes away, the tokens we answer are only those we have
read from the pipe. Requests the kernel queued after our last read stayed
blocked: closing the pipe does not wake them, the kernel only notices the
closed pipe and turns the autofs catatonic when yet another request fails
to reach us, and after the lazy unmount no request reaches it anymore. In
the normal stop path that window is one event loop iteration; for an
automount torn down at coldplug it spans the whole reload.
Set the autofs to catatonic state before letting go of it, which fails
everything still queued with ENOENT. Answer the known tokens first:
closing our end of the pipe before that made a racing request flip the
kernel into catatonic mode on its own, which discards the queue and
turns our answers into "token not found" errors.
Signed-off-by: Martin Pitt <martin@amutable.com>
When an automount unit ceases to exist across daemon-reload (observed:
because a generator no longer emits it), its deserialized state still
makes `automount_coldplug()` pick up the autofs pipe and resume the unit
as waiting/running. But nothing can serve that mount point anymore. The
kernel keeps blocking every process that touches the mount point until
PID 1 answers (which it won't), forever.
To fix this, fail pending requests and unmount the autofs when
coldplugging an automount unit that is not loaded anymore.
Fixes#43700
Signed-off-by: Martin Pitt <martin@amutable.com>
The conversion to the OPTION macros in c3f4c1b8d6 ("journalctl:
convert to OPTION macros") replaced strv_copy_unless_empty() with
strv_copy().
This is problematic because the positional arguments are now an empty
strv rather than NULL when none are given, but `--{list,dump}-catalog`
rely on that.
Instead let's hand NULL back to them when there is nothing on the
command line.
Component definitions may contain multiple Documentation= URLs, but the
components command previously displayed only the first URL in text output.
Use TABLE_STRV_WRAPPED to display all component documentation URLs in the
table, making the full documentation list visible to users.
Add integration test coverage for multiple Documentation= assignments.
The feature JSON payload switched to `id`, `isEnabled`, and `appstream`,
and made `documentation` an array of URLs.
`updatectl` still parsed legacy table-derived names and a single URL.
This broke feature JSON parsing and made output inconsistent.
Features without documentation could also cause a null dereference.
Align `updatectl` with the current schema and preserve all
documentation URLs across sysupdate, D-Bus, and Varlink payloads.
Propagate condition evaluation errors through `condition_test_list()`
and `feature_is_suggested()`, with consistent JSON error handling and
tests.
The `suggested` field is now optional and always a boolean when
present; it is omitted rather than set to an error string when
SuggestOn*= conditions cannot be evaluated.
Add `suggested` metadata and document the field transition.
Follow-up for 3e34ccf6df.
Follow-up for 75a247d4de.
Follow-up for 40e727c93b.
Fixes#43603
The json mode was returning the string `none` when unmerged and
an array for merged extensions. Return an empty array instead.
Instead of branching on the output format, always add an empty strv
for an unmerged hierarchy: the table code renders an empty strv as
the ersatz string ("-") in text mode and as an empty array in json
mode. This also changes the human-readable output from "none" to "-".
It also updated the TEST-50-DISSECT.sysext.sh test. Technically
this is a compatbility break but its also arguably a bugfix.
The PolicyPCR digest is not always the complete input to an offline
signature. PolicyAuthorize also includes the hashed policy reference.
This is now required for NVPCRs to work in the initrd.
Expose the complete data as "tbs" (to-be-signed) and use it when ukify
matches offline signatures. Keep "pol" as the compatibility fallback.
Fixes: https://github.com/systemd/systemd/issues/43635
Follow-up for f0f3687700
Fixes#43645.
`FailureAction=exit` propagates the exit status of the unit's main
process. When there is none, `emergency_action()` left `m->return_value`
at its initial 0, so the manager exited 0 for a unit that failed —
indistinguishable from an orderly shutdown to whoever supervises it.
machinectl clone --read-only ignored the requested read-only mode when
cloning a foreign-UID-owned directory because the unprivileged clone path
did not receive the read_only argument.
Pass the flag through and apply the immutable bit via the already-open
destination directory fd after copying. Keep this best-effort, matching
the ordinary directory clone fallback, so failure to set the attribute
does not turn a completed copy into a reported clone failure.
Add a regression test using foreign-0 rather than a fixed UID. Verify
the cloned image's ReadOnly property, copied contents and ownership, and
that the immutable directory rejects new entries. Run this subtest only
when file attributes and the existing rootless nspawn prerequisites are
available, leaving the other machined tests unchanged.
unprivileged_clone() returns success when mountfsd_make_directory() fails.
Consequently, callers can report a successful clone even though the
destination directory was never created.
Return the original error instead of zero so the failed clone is
reported to the caller.
Add a regression test that places a regular file without an image suffix
at the destination path. Such a file is ignored by image discovery but
prevents directory creation. Check that cloning reports failure without
changing the file, then succeeds after the conflicting file is removed.
network-generator writes generated command line configuration below
the alternate root passed with --root=, but network.* credentials
were copied to fixed host /run paths.
Build the credential target directories with arg_root before passing
them to pick_up_credentials(). This keeps normal boot behavior
unchanged while making credential output land in the requested root.
Fixes#43442.
A `.swap` unit can end up **active but unloadable, with no device**, and
stopping such a unit aborts PID 1.
### What it looks like
```
$ systemctl show 'dev-disk-byx2dpartlabel-primary.swap' -p LoadState,ActiveState,What
LoadState=not-found
ActiveState=active
What= # <-- no device
$ systemctl stop 'dev-disk-byx2dpartlabel-primary.swap'
systemd[1]: About to execute: /usr/sbin/swapoff # <-- no argument
systemd[1]: Caught <ABRT>, from our own process.
systemd[1]: Freezing execution.
```
PID 1 stays alive but frozen, so the machine stops answering `systemctl`
and never reboots.
### How it happens
1. systemd creates one `.swap` unit per udev device link of every device
in `/proc/swaps`.
2. A device link gets repointed at another device while the swap stays
on. `/proc/swaps` doesn't change, so nothing re-examines the unit — it
stays active.
3. The next `daemon-reload` drops all units and rebuilds them.
Enumeration from `/proc/swaps` no longer produces this name, so only the
deserialization brings the unit back: by name, with no fragment on disk.
4. `swap_load()` only calls `swap_add_extras()` for units that loaded a
fragment or are backed by `/proc/swaps`, so this one gets neither a
`What=` nor a patched `ExecContext`.
5. `swap_coldplug()` restores its active state anyway.
6. Shutdown stops it. `unit_stop()` deliberately permits stopping units
whose load failed, so it reaches `exec_spawn()` and trips the
`PrivateVarTmp=` assertion in `exec_context_serialize()`.
Since the D-Bus DNS-SD API was introduced in c3036641f0ed (v236),
`RegisterService()` has created an `sd_bus_track` object to destroy the
registered service when the registering client disconnects from the bus.
However, the track object is a `_cleanup_(sd_bus_track_unrefp)` local in
`bus_method_register_service()` that is never stored anywhere, so it is
destroyed again as soon as the method handler returns. The vanish
handler (`dnssd_registered_service_on_bus_track()`) has thus been dead
code for the past ~8 years.
The observable effect: services registered via D-Bus silently persist
after the client disconnects — unowned, advertised until resolved exits,
and impossible to re-register since the identifier stays taken
(`DNSSD_SERVICE_EXISTS`). At the same time, the registration doesn't
survive a resolved restart, so the current behavior is neither reliably
ephemeral nor reliably persistent. Noticed while reviewing a project
that (unknowingly relying on this bug) registers a service with a
one-shot `gdbus call` and expects it to persist.
This PR:
1. splits the goodbye/zone-removal logic out of
`bus_dnssd_method_unregister()` into a helper — the vanish path needs
the full teardown, not just `dnssd_registered_service_free()`, which
would leave the service's RRs published in the per-link mDNS zones;
2. stores the track object in `DnssdRegisteredService`, making client
tracking work as originally intended;
3. documents the lifetime semantics in `org.freedesktop.resolve1(5)`
(matching the equivalent Avahi D-Bus API, whose entry groups are
likewise bound to the client connection) and points to `*.dnssd` files
for registrations independent of a client's lifetime;
4. adds a TEST-75 case covering the connection-bound lifetime.
Note this is a behavioral change for fire-and-forget clients (one-shot
`busctl`/`gdbus` invocations). Arguably those were never working
reliably (registrations silently vanished on any resolved restart, and
stale entries blocked re-registration), but if this is deemed too risky,
the alternative would be to drop the dead tracking code and codify the
persist-until-exit behavior instead — I'd argue tracking as originally
intended is the better contract.
Verification: code compiles; the new integration test is included.
shellcheck 0.10+ reports SC2329 ("This function is never invoked") for
the per-testcase cleanup() functions in TEST-75-RESOLVED.sh, which are
only invoked indirectly via trap. The existing SC2317 markers don't
cover it, and inserting a new testcase shuffles which definitions the
heuristic fires on, making pre-existing findings show up as new in
differential CI runs. Extend the markers to SC2317,SC2329, matching
what testcase_dot_strict_per_link_verify() in this file and other test
units already do.
Verify that a DNS-SD service registered via the D-Bus RegisterService()
call is bound to the lifetime of the client's bus connection: since
busctl disconnects right after the call returns, the service must be
unregistered again shortly after. Wait for resolved's vanish handler
log message as proof that the client tracking triggered the
unregistration, check that a subsequent UnregisterService() fails
specifically because the service is not known anymore, and verify that
the identifier can be registered again — previously it stayed taken by
the vanished client's service forever, failing with
DNSSD_SERVICE_EXISTS.
A dummy link with mDNS enabled makes sure the per-scope goodbye/zone
teardown path actually runs, and since resolved would be restarted
automatically if it crashed in it, letting the D-Bus level checks pass
vacuously, verify the InvocationID stayed the same throughout.