1
0
mirror of https://github.com/systemd/systemd synced 2026-03-24 15:55:00 +01:00

Compare commits

...

41 Commits

Author SHA1 Message Date
Luca Boccassi
2f363b407f mkosi: update debian commit reference to d9f2aa1704bc98d1aec6519a863a07eaf12b76ad
* d9f2aa1704 Install systemd-tpm2-generator.8 only for UEFI builds
* ac1c7d8048 Drop dependencies on libcap-dev, no longer used since v259
* c36e5871ca Do not install systemd-sysv-generator.8 in upstream build
* bac0cca0e8 Install new files for upstream build
* 2855fb1302 Update changelog for 259-1 release
2025-12-18 22:15:14 +01:00
Daan De Meyer
4c1a2f7d95
Revert qemu debugs (#40138)
Captured some in https://github.com/systemd/systemd/issues/38240 and
they do not look too useful, and add noise, so remove them again
2025-12-18 22:14:21 +01:00
Lennart Poettering
2062b817d4
keyutil: Add extract-certificate (#39962)
Useful to extract a certificate from a hardware token to a file, for
example in mkosi to ship the certificate from a hardware token in
/usr/lib/verity.d in an image
2025-12-18 21:25:57 +01:00
Lennart Poettering
b15cf11327
repart: add basic support for LUKS2 integrity verification (#39295)
Authenticated disk encryption is experimentally supported by cryptsetup
since v2.0.0 and allows for automatic dm-integrity setup for LUKS
devices. Add support for the mode to systemd-repart. The PR adds support
for `cryptsetup luksFormat --integrity` to systemd-repart and
"encryptedwithintegrity" dissection policy.

Limitations:
- No discard, online-only mode for repart.

Closes #39250
2025-12-18 21:19:39 +01:00
Miao Wang
dc3e544104 ssh-proxy: expect OK PORT response from vsock-mux
The unix-domain socket to AF_VSOCK multiplexers in Firecracker and
vhost-device-vsock sends OK PORT response to the client, resulting
ssh clients to abort the connection with the additional response. This
patch addresses this issue by waiting and expecting the possible OK PORT
response from the multiplexer, if any, and then handover the socket fd
to the ssh client. It only checks if the response begins with OK and
consume the response till the first \n, for simplicity.

Signed-off-by: Miao Wang <shankerwangmiao@gmail.com>
2025-12-18 21:15:41 +01:00
Mike Yuan
71aaa744b8
Assorted minor cleanups (#40136) 2025-12-18 20:53:58 +01:00
Lennart Poettering
627e583c64
Use ANSI CSI 18 to query console size (#39833) 2025-12-18 20:49:26 +01:00
Luca Boccassi
84079cd158 Revert "tests: run qemu with cpu_reset,guest_errors debug messages"
This reverts commit b169c7512c9cf3ac8eb2b15141e0b9fab9e8961e.
2025-12-18 17:51:07 +00:00
Luca Boccassi
d6514ebf33 Revert "tests: also add qemu shutdown trace debug logs to integration script"
This reverts commit 9b42cc3b12c17a4b02d713b2ad499a74acecea1f.
2025-12-18 17:51:07 +00:00
Luca Boccassi
293a925e9e Revert "integration-test: check tools distribution rather than image distribution"
This reverts commit fd2cdadc1467c7f57866d45fb579e7cf3830bf54.
2025-12-18 17:51:06 +00:00
Daan De Meyer
30465f789c
test: introduce test-sleep and use it in several integration tests (#40086) 2025-12-18 17:47:57 +01:00
Franck Bui
25393c7c90 man: generate systemd-tpm2-generator man page only when ENABLE_BOOTLOADER is enabled 2025-12-18 17:39:25 +01:00
Mike Yuan
6dacabe534
journalctl-show: drop redundant condition
The combination of --follow and --reverse would have been
rejected in parse_argv() already.
2025-12-18 17:37:26 +01:00
Mike Yuan
58e9239fd7
core/dbus-manager: drop dangling verify_run_space_and_log() prototype
Follow-up for 65d9ef40f222588fcaf55e2932f45b0d4bdaf194
2025-12-18 17:37:26 +01:00
Mike Yuan
f145fc8c7e
core/manager: constify DEFAULT_TASKS_MAX 2025-12-18 17:37:25 +01:00
Mike Yuan
c53b324ecf
socket-label: mark socket_address_bind_ipv6_only_or_bool_from_string as _pure_ 2025-12-18 17:37:25 +01:00
Mike Yuan
4a6e5b0ae9
core: various cleanups for setup_input/output (#39860) 2025-12-18 15:56:42 +01:00
Mike Yuan
6e44160e0f
docs/RELEASE: correct numbering without 7
Follow-up for b1192a01cd83b28ac0c69cd1eab454beee460a72
2025-12-18 15:44:08 +01:00
Daan De Meyer
42b6f87a42 keyutil: Add extract-certificate
Useful to extract a certificate from a hardware token to a file, for
example in mkosi to ship the certificate from a hardware token in
/usr/lib/verity.d in an image
2025-12-18 13:57:32 +01:00
Daan De Meyer
9fc537437b keyutil: Rename public to extract-public 2025-12-18 13:56:38 +01:00
Daan De Meyer
8440492e90 openssl-util: Cast to const type 2025-12-18 13:56:38 +01:00
Daan De Meyer
8ecb7deafa openssl-util: Fix logging 2025-12-18 13:56:37 +01:00
Zbigniew Jędrzejewski-Szmek
d91a184509 core: use terminal_get_size_by_csi18 to query terminal size
This allows us to query the window size without moving the cursor. We have
various reports about the cursor being in an unexpected position and/or state.

Closes https://github.com/systemd/systemd/issues/37526.
2025-12-18 10:47:19 +01:00
Vitaly Kuznetsov
9e78b1e817 TODO: add unimplemented Integrity= systemd-repart options 2025-12-18 09:12:54 +01:00
Vitaly Kuznetsov
bb09cc02bd TEST-50-DISSECT: test 'encrypted' and 'encryptedwithintegrity' policies
Test the newly introduced 'encryptedwithintegrity' image policy and verify
that it's different from 'encrypted' one.
2025-12-18 09:12:54 +01:00
Vitaly Kuznetsov
771085291d dissect-image: introduce 'encryptedwithintegrity' policy
LUKS2 supports built-in integrity checking which may come very handy to
mitigate partial rollback attacks on the storage when only some specific
parts are restored to some old encrypted state. Specific use-cases like
Confidential VMs may want to mandate the usage of feature e.g. on the root
volume. Introduce "encryptedwithintegrity" image policy to support that.

Note, due to the current libcryptsetup limitations, checking whether the
feature is enabled or not for the 'file' case (e.g. DDI image as a raw file)
requires setting up a loop device. To avoid that and keep dissect fully
functional when working unpriviliged, implement a minimal custom LUKS header
parser.
2025-12-18 09:12:54 +01:00
Vitaly Kuznetsov
cb2bc7575a TEST-58-REPART: add testcase for LUKS2 integrity
Create an encrypted partition with LUKS2 integrity checking enabled, verify
that dm-integrity mapping appears.
2025-12-18 09:12:54 +01:00
Vitaly Kuznetsov
1a4a34287f repart: add basic support for LUKS2 integrity verification
Authenticated disk encryption is experimentally supported by cryptsetup since
v2.0.0 and allows for automatic dm-integrity setup for LUKS devices. Add
support for the mode to systemd-repart. Currently, the option can only be used
in 'online' mode as libcryptsetup does not support creating integrity data
without the use of in-kernel dm-integrity infrastructure.

Integrity=/IntegrityAlgorithm= are added in the anticipation of other integrity
protection options, e.g. enabling dm-integrity for a plain unencrypted
partition.
2025-12-18 09:12:49 +01:00
Mike Yuan
7498c391ec core/exec-invoke: remove redundant accmode validation on stderr
Follow-up for 171ceb4a00294c700c0ba6906a3a3abad846699e

In setup_output() we assume stdout has been set up properly
before stderr, hence the stdout we're inheriting from must
be writable (or more precisely, would have been adjusted to be).
Hence no need to duplicate it.
2025-12-17 23:23:22 +00:00
Mike Yuan
a8e1796b09 core/exec-invoke: split out maybe_inherit_stdout_from_stdin(), use exec_input_is_inheritable()
Note that exec_input_is_inheritable() rightfully refuses EXEC_INPUT_FILE,
in which case std_output would have been reset in service_fix_stdio()
already.

While at it, use the generic fallback logic of first trying user manager
stdout when stdin is not writable.
2025-12-17 23:23:22 +00:00
Mike Yuan
e04382384d core/execute: introduce exec_input_is_inheritable() helper 2025-12-17 23:23:22 +00:00
Mike Yuan
b34aee0440 core/exec-invoke: socket_fd must be set if any of stdio requests it
This is checked as one of the very first things in exec_invoke(),
hence remove the condition in fixup_input() and the whole
fixup_output().
2025-12-17 23:23:22 +00:00
Mike Yuan
c8a6c42e53 core/exec-invoke: use path_equal() on for stdio files 2025-12-17 23:23:22 +00:00
Mike Yuan
4dac3c6d76 core/execute-serialize: clean up stdio serialization
* Do not interleave root_directory_as_fd with stdio fields
* Do not use different serialization key for different modes
  pointing to same path
* Escape stdio file paths (as per
  9be46b1da8b01c3f47e6c050185f2b45484d6300)
2025-12-17 23:23:22 +00:00
Mike Yuan
27660b470f core/dbus-execute: path is required if stdio is specified as file
This partially reverts d6ff82d37cae5910a0c62bf830e3cfa70df1e23f.
2025-12-17 23:23:22 +00:00
Mike Yuan
eac3867764 core/exec-invoke: fix stdout and stderr sharing the same named fd
Previously such usage would be refused by exec_context_named_iofds()
with -ENOENT, however setup_output() carried relevant logic.
2025-12-17 23:23:22 +00:00
Yu Watanabe
28826f6ba9 test: use test-sleep executable in softreboot and coredump tests
This partially reverts 355bcc243ca30dd56ab142d5bfb5579c312192ca.
2025-12-15 18:20:53 +09:00
Yu Watanabe
4c98063d6e test: introduce our own sleep command for testing 2025-12-15 18:09:21 +09:00
Yu Watanabe
78385b8e94 time-util: make usleep_safe() support USEC_INFINITY 2025-12-15 18:09:21 +09:00
Yu Watanabe
b8241d3ca5 time-util: continue sleeping when clock_nanosleep() fail with EINTR
The function clock_nanosleep() provides remaining time when failed with
EINTR. Let's continue sleeping in that case.
2025-12-15 17:50:19 +09:00
Zbigniew Jędrzejewski-Szmek
bfb8e9c575 basic/terminal-util: add code to read window size using CSI 18
In my tests, the sequence works on Ptyxis 49.0, gnome-terminal-3.56.3,
xterm-401, rxvt-unicode-9.31, under tmux-3.5a, over ssh and serial console
connected to ptyxis. It did not work on a text console in a VM (TERM=linux) or
under kmscon-9.1.0 (TERM=vt102).
2025-11-20 17:32:50 +01:00
42 changed files with 1069 additions and 373 deletions

6
TODO
View File

@ -137,6 +137,12 @@ Deprecations and removals:
Features:
* systemd-repart: implement Integrity=data/meta and Integrity=inline for non-LUKS
case. Currently, only Integrity=inline combined with Encrypt= is implemented
and uses libcryptsetup features. Add support for plain dm-integrity setups when
integrity tags are stored by the device (inline), interleaved with data (data),
and on a separate device (meta).
* networkd/machined: implement reverse name lookups in the resolved hook
* networkd's resolved hook: optionally map all lease IP addresses handed out to

View File

@ -13,27 +13,27 @@ SPDX-License-Identifier: LGPL-2.1-or-later
4. Update hwdb (`ninja -C build update-hwdb`, `ninja -C build update-hwdb-autosuspend`, commit separately).
5. Update syscall numbers (`ninja -C build update-syscall-tables update-syscall-header`).
6. [RC1] Update library numbers in `meson.build`
8. [RC1] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to RC mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1
9. Update version number in `meson.version` (e.g. from `256~devel` to `256~rc1` or from `256~rc3` to `256`). Note that this uses a tilde (\~) instead of a hyphen (-) because tildes sort lower in version comparisons according to the [UAPI.10 Version Format Specification](https://uapi-group.org/specifications/specs/version_format_specification/), and we want `255~rc1` to sort lower than `255`.
10. Check dbus docs with `ninja -C build update-dbus-docs`
11. Check manpages list with `ninja -C build update-man-rules`
12. Update translation strings (`ninja -C build systemd-pot`, `ninja -C build systemd-update-po`) - drop the header comments from `systemd.pot` + re-add SPDX before committing. If the only change in a file is the 'POT-Creation-Date' field, then ignore that file.
13. Tag the release: `version="v$(sed 's/~/-/g' meson.version)" && git tag -s "${version}" -m "systemd ${version}"` (tildes are replaced with hyphens, because git doesn't accept the former).
14. Do `ninja -C build`
15. Make sure that the version string and package string match: `build/systemctl --version`
16. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
17. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
18. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
19. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released | Online resources https://systemd.io/`)
20. [FINAL] Create an empty -stable branch: `git push systemd origin/main:refs/heads/v${version}-stable`.
21. [FINAL] Build and upload the documentation (on the -stable branch): `ninja -C build doc-sync`
22. [FINAL] Create a new `ci/v${version}-stable` branch for deb package builds on https://salsa.debian.org/systemd-team/systemd
23. [FINAL] Switch `.semaphore/semaphore-runner.sh` and `mkosi/mkosi.pkgenv/mkosi.conf.d/debian-ubuntu.conf`
7. [RC1] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to RC mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1
8. Update version number in `meson.version` (e.g. from `256~devel` to `256~rc1` or from `256~rc3` to `256`). Note that this uses a tilde (\~) instead of a hyphen (-) because tildes sort lower in version comparisons according to the [UAPI.10 Version Format Specification](https://uapi-group.org/specifications/specs/version_format_specification/), and we want `255~rc1` to sort lower than `255`.
9. Check dbus docs with `ninja -C build update-dbus-docs`
10. Check manpages list with `ninja -C build update-man-rules`
11. Update translation strings (`ninja -C build systemd-pot`, `ninja -C build systemd-update-po`) - drop the header comments from `systemd.pot` + re-add SPDX before committing. If the only change in a file is the 'POT-Creation-Date' field, then ignore that file.
12. Tag the release: `version="v$(sed 's/~/-/g' meson.version)" && git tag -s "${version}" -m "systemd ${version}"` (tildes are replaced with hyphens, because git doesn't accept the former).
13. Do `ninja -C build`
14. Make sure that the version string and package string match: `build/systemctl --version`
15. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
16. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
17. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
18. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released | Online resources https://systemd.io/`)
19. [FINAL] Create an empty -stable branch: `git push systemd origin/main:refs/heads/v${version}-stable`.
20. [FINAL] Build and upload the documentation (on the -stable branch): `ninja -C build doc-sync`
21. [FINAL] Create a new `ci/v${version}-stable` branch for deb package builds on https://salsa.debian.org/systemd-team/systemd
22. [FINAL] Switch `.semaphore/semaphore-runner.sh` and `mkosi/mkosi.pkgenv/mkosi.conf.d/debian-ubuntu.conf`
to the new `ci/v${version}-stable` branch on the -stable branch
24. [FINAL] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to release mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1
25. [FINAL] Change the Github Pages branch to the newly created branch (https://github.com/systemd/systemd/settings/pages) and set the 'Custom domain' to 'systemd.io'
26. [FINAL] Update version number in `meson.version` to the devel version of the next release (e.g. from `256` to `257~devel`)
27. [FINAL] Build and upload the documentation (on the main branch): `ninja -C build doc-sync`
23. [FINAL] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to release mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1
24. [FINAL] Change the Github Pages branch to the newly created branch (https://github.com/systemd/systemd/settings/pages) and set the 'Custom domain' to 'systemd.io'
25. [FINAL] Update version number in `meson.version` to the devel version of the next release (e.g. from `256` to `257~devel`)
26. [FINAL] Build and upload the documentation (on the main branch): `ninja -C build doc-sync`
# Steps to a Successful Stable Release

View File

@ -909,6 +909,27 @@
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Integrity=</varname></term>
<listitem><para>Enable integrity checking for the partition. Currently, the only supported option
is <varname>Integrity=inline</varname> which enables authenticated disk encryption for LUKS2 devices.
The option requires <varname>Encrypt=</varname> setting and can only be used in online image build.
Defaults to <literal>off</literal>, i.e. integrity protection is disabled.</para>
<para>Note: authenticated disk encryption is considered EXPERIMENTAL by cryptsetup.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>IntegrityAlgorithm=</varname></term>
<listitem><para>Specify the integrity algorithm to be used for integrity verification. For
<varname>Integrity=inline</varname> the supported values are: <literal>hmac-sha1</literal>,
<literal>hmac-sha256</literal>(default), and <literal>hmac-sha512</literal>.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Compression=</varname></term>

View File

@ -1206,7 +1206,7 @@ manpages = [
'systemd-tmpfiles-setup.service'],
''],
['systemd-tpm2-clear.service', '8', [], 'ENABLE_BOOTLOADER'],
['systemd-tpm2-generator', '8', [], ''],
['systemd-tpm2-generator', '8', [], 'ENABLE_BOOTLOADER'],
['systemd-tpm2-setup.service',
'8',
['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],

View File

@ -56,7 +56,7 @@
</varlistentry>
<varlistentry>
<term><command>public</command></term>
<term><command>extract-public</command></term>
<listitem><para>This commands prints the public key in PEM format extracted from either the
certificate given with <option>--certificate=</option> or the private key given with
@ -65,6 +65,17 @@
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><command>extract-certificate</command></term>
<listitem><para>This command prints the X.509 certificate in PEM format extracted from the
certificate given with <option>--certificate=</option>. This is useful when loading a certificate
from an OpenSSL provider (e.g. a hardware token) and wanting to output a standalone PEM certificate
that can be used without the provider.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry>
<term><command>pkcs7</command></term>

View File

@ -9,5 +9,5 @@ Environment=
GIT_URL=https://salsa.debian.org/systemd-team/systemd.git
GIT_SUBDIR=debian
GIT_BRANCH=debian/master
GIT_COMMIT=6f15bdaae7014c233b662ac4a33d464893b81b36
GIT_COMMIT=d9f2aa1704bc98d1aec6519a863a07eaf12b76ad
PKG_SUBDIR=debian

View File

@ -20,8 +20,8 @@ static int table_add_designator_line(Table *table, PartitionDesignator d, Partit
return log_oom();
color = (f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE ? ansi_grey() :
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
(f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT ? ansi_highlight_red() :
!(f & PARTITION_POLICY_UNPROTECTED) ? ansi_highlight_green() : NULL;

View File

@ -2374,17 +2374,12 @@ int terminal_get_size_by_dsr(
assert(input_fd >= 0);
assert(output_fd >= 0);
/* Tries to determine the terminal dimension by means of ANSI sequences rather than TIOCGWINSZ
* ioctl(). Why bother with this? The ioctl() information is often incorrect on serial terminals
* (since there's no handshake or protocol to determine the right dimensions in RS232), but since the
* ANSI sequences are interpreted by the final terminal instead of an intermediary tty driver they
* should be more accurate.
/* Tries to determine the terminal dimension by means of ANSI sequences.
*
* Unfortunately there's no direct ANSI sequence to query terminal dimensions. But we can hack around
* it: we position the cursor briefly at an absolute location very far down and very far to the
* right, and then read back where we actually ended up. Because cursor locations are capped at the
* terminal width/height we should then see the right values. In order to not risk integer overflows
* in terminal applications we'll use INT16_MAX-1 as location to jump to hopefully a value that is
* We position the cursor briefly at an absolute location very far down and very far to the right,
* and then read back where we actually ended up. Because cursor locations are capped at the terminal
* width/height we should then see the right values. In order to not risk integer overflows in
* terminal applications we'll use INT16_MAX-1 as location to jump to hopefully a value that is
* large enough for any real-life terminals, but small enough to not overflow anything or be
* recognized as a "niche" value. (Note that the dimension fields in "struct winsize" are 16bit only,
* too). */
@ -2518,12 +2513,143 @@ finish:
return r;
}
/*
* See https://terminalguide.namepad.de/seq/csi_st-18/,
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_.
*/
#define CSI18_Q "\x1B[18t" /* Report the size of the text area in characters */
#define CSI18_Rp "\x1B[8;" /* Reply prefix */
#define CSI18_R0 CSI18_Rp "1;1t" /* Shortest reply */
#define CSI18_R1 CSI18_Rp "32766;32766t" /* Longest reply */
static int scan_text_area_size_response(
const char *buf,
size_t size,
unsigned *ret_rows,
unsigned *ret_columns) {
assert(buf);
assert(ret_rows);
assert(ret_columns);
/* Check if we have enough space for the shortest possible answer. */
if (size < STRLEN(CSI18_R0))
return -EAGAIN;
/* Check if the terminating sequence is present */
if (buf[size - 1] != 't')
return -EAGAIN;
unsigned short rows, columns;
if (sscanf(buf, CSI18_Rp "%hu;%hut", &rows, &columns) != 2)
return -EINVAL;
*ret_rows = rows;
*ret_columns = columns;
return 0;
}
int terminal_get_size_by_csi18(
int input_fd,
int output_fd,
unsigned *ret_rows,
unsigned *ret_columns) {
int r;
assert(input_fd >= 0);
assert(output_fd >= 0);
/* Tries to determine the terminal dimension by means of an ANSI sequence CSI 18. */
if (terminal_is_dumb())
return -EOPNOTSUPP;
r = terminal_verify_same(input_fd, output_fd);
if (r < 0)
return log_debug_errno(r, "Called with distinct input/output fds: %m");
/* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read()
* should someone else process the POLLIN. Do all subsequent operations on the new fd. */
_cleanup_close_ int nonblock_input_fd = r = fd_reopen(input_fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (r < 0)
return r;
struct termios old_termios;
if (tcgetattr(nonblock_input_fd, &old_termios) < 0)
return log_debug_errno(errno, "Failed to get terminal settings: %m");
struct termios new_termios = old_termios;
termios_disable_echo(&new_termios);
if (tcsetattr(nonblock_input_fd, TCSANOW, &new_termios) < 0)
return log_debug_errno(errno, "Failed to set new terminal settings: %m");
r = loop_write(output_fd, CSI18_Q, SIZE_MAX);
if (r < 0)
goto finish;
usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC);
char buf[STRLEN(CSI18_R1)];
size_t bytes = 0;
for (;;) {
usec_t n = now(CLOCK_MONOTONIC);
if (n >= end) {
r = -EOPNOTSUPP;
break;
}
r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n));
if (r < 0)
break;
if (r == 0) {
r = -EOPNOTSUPP;
break;
}
/* On the first read, read multiple characters, i.e. the shortest valid reply. Afterwards
* read byte by byte, since we don't want to read too much and drop characters from the input
* queue. */
ssize_t l = read(nonblock_input_fd, buf + bytes, bytes == 0 ? STRLEN(CSI18_R0) : 1);
if (l < 0) {
if (errno == EAGAIN)
continue;
r = -errno;
break;
}
assert((size_t) l <= sizeof(buf) - bytes);
bytes += l;
r = scan_text_area_size_response(buf, bytes, ret_rows, ret_columns);
if (r != -EAGAIN)
break;
if (bytes == sizeof(buf)) {
r = -EOPNOTSUPP; /* The response has the right prefix, but we didn't find a valid
* answer with a terminator in the allotted space. Something is
* wrong, possibly some unrelated bytes got injected into the
* answer. */
break;
}
}
finish:
(void) tcsetattr(nonblock_input_fd, TCSANOW, &old_termios);
return r;
}
int terminal_fix_size(int input_fd, int output_fd) {
unsigned rows, columns;
int r;
/* Tries to update the current terminal dimensions to the ones reported via ANSI sequences */
/* Tries to update the current terminal dimensions to the ones reported via ANSI sequences.
*
* Why bother with this? The ioctl() information is often incorrect on serial terminals (since
* there's no handshake or protocol to determine the right dimensions in RS232), but since the ANSI
* sequences are interpreted by the final terminal instead of an intermediary tty driver they should
* be more accurate.
*/
r = terminal_verify_same(input_fd, output_fd);
if (r < 0)
return r;
@ -2532,7 +2658,12 @@ int terminal_fix_size(int input_fd, int output_fd) {
if (ioctl(output_fd, TIOCGWINSZ, &ws) < 0)
return log_debug_errno(errno, "Failed to query terminal dimensions, ignoring: %m");
r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &columns);
r = terminal_get_size_by_csi18(input_fd, output_fd, &rows, &columns);
if (IN_SET(r, -EOPNOTSUPP, -EINVAL))
/* We get -EOPNOTSUPP if the query fails and -EINVAL when the received answer is invalid.
* Try the fallback method. It is more involved and moves the cursor, but seems to have wider
* support. */
r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &columns);
if (r < 0)
return log_debug_errno(r, "Failed to acquire terminal dimensions via ANSI sequences, not adjusting terminal dimensions: %m");

View File

@ -150,6 +150,7 @@ void termios_disable_echo(struct termios *termios);
int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue);
int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, unsigned *ret_columns);
int terminal_get_size_by_csi18(int input_fd, int output_fd, unsigned *ret_rows, unsigned *ret_columns);
int terminal_fix_size(int input_fd, int output_fd);
int terminal_get_terminfo_by_dcs(int fd, char **ret_name);

View File

@ -1792,6 +1792,39 @@ bool in_utc_timezone(void) {
return timezone == 0 && daylight == 0;
}
int usleep_safe(usec_t usec) {
int r;
/* usleep() takes useconds_t that is (typically?) uint32_t. Also, usleep() may only support the
* range [0, 1000000]. See usleep(3). Let's override usleep() with clock_nanosleep().
*
* Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
* CLOCK_MONOTONIC! */
if (usec == 0)
return 0;
if (usec == USEC_INFINITY)
return RET_NERRNO(pause());
struct timespec t;
timespec_store(&t, usec);
for (;;) {
struct timespec remaining;
/* `clock_nanosleep()` does not use `errno`, but returns positive error codes. */
r = -clock_nanosleep(CLOCK_MONOTONIC, /* flags= */ 0, &t, &remaining);
if (r == -EINTR) {
/* Interrupted. Continue sleeping for the remaining time. */
t = remaining;
continue;
}
return r;
}
}
int time_change_fd(void) {
/* We only care for the cancellation event, hence we set the timeout to the latest possible value. */

View File

@ -213,19 +213,7 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
return usec_sub_unsigned(timestamp, (usec_t) delta);
}
static inline int usleep_safe(usec_t usec) {
/* usleep() takes useconds_t that is (typically?) uint32_t. Also, usleep() may only support the
* range [0, 1000000]. See usleep(3). Let's override usleep() with clock_nanosleep().
*
* Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
* CLOCK_MONOTONIC! */
if (usec == 0)
return 0;
/* `clock_nanosleep()` does not use `errno`, but returns positive error codes. */
return -clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL);
}
int usleep_safe(usec_t usec);
/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
* year territory. However, since we want to stay away from this in all timezones we take one day off. */

View File

@ -3338,17 +3338,15 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
if (!isempty(s)) {
if (!path_is_absolute(s))
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s);
if (!path_is_normalized(s))
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s);
}
if (!path_is_absolute(s))
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s);
if (!path_is_normalized(s))
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (streq(name, "StandardInputFile")) {
r = free_and_strdup(&c->stdio_file[STDIN_FILENO], empty_to_null(s));
r = free_and_strdup(&c->stdio_file[STDIN_FILENO], s);
if (r < 0)
return r;
@ -3356,7 +3354,7 @@ int bus_exec_context_set_transient_property(
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s);
} else if (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend", "StandardOutputFileToTruncate")) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s));
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], s);
if (r < 0)
return r;
@ -3374,7 +3372,7 @@ int bus_exec_context_set_transient_property(
} else {
assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend", "StandardErrorFileToTruncate"));
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s));
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], s);
if (r < 0)
return r;

View File

@ -16,11 +16,10 @@ void bus_manager_send_finished(
usec_t initrd_usec,
usec_t userspace_usec,
usec_t total_usec);
void bus_manager_send_reloading(Manager *m, bool active);
void bus_manager_send_change_signal(Manager *m);
int verify_run_space_and_log(const char *message);
int bus_property_get_oom_policy(
sd_bus *bus,
const char *path,
@ -29,6 +28,7 @@ int bus_property_get_oom_policy(
sd_bus_message *reply,
void *userdata,
sd_bus_error *reterr_error);
int bus_property_get_emergency_action(
sd_bus *bus,
const char *path,

View File

@ -308,7 +308,6 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
static int fixup_input(
const ExecContext *context,
int socket_fd,
bool apply_tty_stdin) {
ExecInput std_input;
@ -320,23 +319,12 @@ static int fixup_input(
if (exec_input_is_terminal(std_input) && !apply_tty_stdin)
return EXEC_INPUT_NULL;
if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
return EXEC_INPUT_NULL;
if (std_input == EXEC_INPUT_DATA && context->stdin_data_size == 0)
return EXEC_INPUT_NULL;
return std_input;
}
static int fixup_output(ExecOutput output, int socket_fd) {
if (output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
return EXEC_OUTPUT_INHERIT;
return output;
}
static int setup_input(
const ExecContext *context,
const ExecParameters *params,
@ -361,7 +349,7 @@ static int setup_input(
return STDIN_FILENO;
}
i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
i = fixup_input(context, params->flags & EXEC_APPLY_TTY_STDIN);
switch (i) {
@ -430,8 +418,8 @@ static int setup_input(
assert(context->stdio_file[STDIN_FILENO]);
rw = (context->std_output == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDOUT_FILENO])) ||
(context->std_error == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDERR_FILENO]));
rw = (context->std_output == EXEC_OUTPUT_FILE && path_equal(context->stdio_file[STDIN_FILENO], context->stdio_file[STDOUT_FILENO])) ||
(context->std_error == EXEC_OUTPUT_FILE && path_equal(context->stdio_file[STDIN_FILENO], context->stdio_file[STDERR_FILENO]));
fd = acquire_path(context->stdio_file[STDIN_FILENO], rw ? O_RDWR : O_RDONLY, 0666 & ~context->umask);
if (fd < 0)
@ -445,30 +433,68 @@ static int setup_input(
}
}
static bool can_inherit_stderr_from_stdout(
const ExecContext *context,
ExecOutput o,
ExecOutput e) {
static bool can_inherit_stderr_from_stdout(const ExecContext *context) {
ExecOutput o, e;
assert(context);
/* Returns true, if given the specified STDERR and STDOUT output we can directly dup() the stdout fd to the
* stderr fd */
o = context->std_output;
e = context->std_error;
if (e == EXEC_OUTPUT_INHERIT)
return true;
if (e != o)
return false;
/* Let's not shortcut named fds here, even though we in theory can by comparing fd names, since
* we have the named_iofds array readily available, and the inherit practice would simply be duplicative. */
if (e == EXEC_OUTPUT_NAMED_FD)
return streq_ptr(context->stdio_fdname[STDOUT_FILENO], context->stdio_fdname[STDERR_FILENO]);
return false;
if (IN_SET(e, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND, EXEC_OUTPUT_FILE_TRUNCATE))
return streq_ptr(context->stdio_file[STDOUT_FILENO], context->stdio_file[STDERR_FILENO]);
return path_equal(context->stdio_file[STDOUT_FILENO], context->stdio_file[STDERR_FILENO]);
return true;
}
static int maybe_inherit_stdout_from_stdin(const ExecContext *context, ExecInput i) {
int r;
assert(context);
if (context->std_output != EXEC_OUTPUT_INHERIT)
return 0;
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && exec_input_is_terminal(context->std_input))
return open_terminal_as(exec_context_tty_path(context), O_WRONLY, STDOUT_FILENO);
if (!exec_input_is_inheritable(i))
goto fallback;
r = fd_is_writable(STDIN_FILENO);
if (r <= 0) {
if (r < 0)
log_warning_errno(r, "Failed to check if inherited stdin is writable for stdout, using fallback: %m");
else
log_warning("Inherited stdin is not writable for stdout, using fallback: %m");
goto fallback;
}
return RET_NERRNO(dup2(STDIN_FILENO, STDOUT_FILENO));
fallback:
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
if (getppid() != 1)
return STDOUT_FILENO;
/* We need to open /dev/null here anew, to get the right access mode. */
return open_null_as(O_WRONLY, STDOUT_FILENO);
}
static int setup_output(
const ExecContext *context,
const ExecParameters *params,
@ -506,65 +532,33 @@ static int setup_output(
return STDERR_FILENO;
}
i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
o = fixup_output(context->std_output, socket_fd);
i = fixup_input(context, params->flags & EXEC_APPLY_TTY_STDIN);
if (fileno == STDERR_FILENO) {
ExecOutput e;
e = fixup_output(context->std_error, socket_fd);
/* This expects the input and output are already set up */
/* Don't change the stderr file descriptor if we inherit all
* the way and are not on a tty */
if (e == EXEC_OUTPUT_INHERIT &&
o == EXEC_OUTPUT_INHERIT &&
i == EXEC_INPUT_NULL &&
!exec_input_is_terminal(context->std_input) &&
if (context->std_error == EXEC_OUTPUT_INHERIT &&
context->std_output == EXEC_OUTPUT_INHERIT &&
i == EXEC_INPUT_NULL && !exec_input_is_terminal(context->std_input) &&
getppid() != 1)
return fileno;
/* Duplicate from stdout if possible */
if (can_inherit_stderr_from_stdout(context, o, e)) {
r = fd_is_writable(STDOUT_FILENO);
if (r <= 0) {
if (r < 0)
log_warning_errno(r, "Failed to check if inherited stdout is writable for stderr, falling back to /dev/null.");
else
log_warning("Inherited stdout is not writable for stderr, falling back to /dev/null.");
return open_null_as(O_WRONLY, fileno);
}
if (can_inherit_stderr_from_stdout(context))
return RET_NERRNO(dup2(STDOUT_FILENO, fileno));
}
o = e;
o = context->std_error;
} else if (o == EXEC_OUTPUT_INHERIT) {
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && exec_input_is_terminal(context->std_input))
return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
} else {
assert(fileno == STDOUT_FILENO);
/* If the input is connected to anything that's not a /dev/null or a data fd, inherit that... */
if (!IN_SET(i, EXEC_INPUT_NULL, EXEC_INPUT_DATA)) {
r = fd_is_writable(STDIN_FILENO);
if (r <= 0) {
if (r < 0)
log_warning_errno(r, "Failed to check if inherited stdin is writable for %s, falling back to /dev/null.",
fileno == STDOUT_FILENO ? "stdout" : "stderr");
else
log_warning("Inherited stdin is not writable for %s, falling back to /dev/null.",
fileno == STDOUT_FILENO ? "stdout" : "stderr");
return open_null_as(O_WRONLY, fileno);
}
return RET_NERRNO(dup2(STDIN_FILENO, fileno));
}
r = maybe_inherit_stdout_from_stdin(context, i);
if (r != 0)
return r;
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
if (getppid() != 1)
return fileno;
/* We need to open /dev/null here anew, to get the right access mode. */
return open_null_as(O_WRONLY, fileno);
o = context->std_output;
}
switch (o) {
@ -630,15 +624,14 @@ static int setup_output(
case EXEC_OUTPUT_FILE:
case EXEC_OUTPUT_FILE_APPEND:
case EXEC_OUTPUT_FILE_TRUNCATE: {
bool rw;
int fd, flags;
assert(context->stdio_file[fileno]);
rw = context->std_input == EXEC_INPUT_FILE &&
streq_ptr(context->stdio_file[fileno], context->stdio_file[STDIN_FILENO]);
if (rw)
/* stdin points to the same file hence setup_input() opened it as rw already?
* Then just duplicate it. */
if (context->std_input == EXEC_INPUT_FILE &&
path_equal(context->stdio_file[fileno], context->stdio_file[STDIN_FILENO]))
return RET_NERRNO(dup2(STDIN_FILENO, fileno));
flags = O_WRONLY;
@ -4794,28 +4787,34 @@ static int exec_context_named_iofds(
/* Note that socket fds are always placed at the beginning of the fds array, no need for extra
* manipulation. */
for (size_t i = 0; i < p->n_socket_fds && targets > 0; i++)
for (size_t i = 0; i < p->n_socket_fds && targets > 0; i++) {
if (named_iofds[STDIN_FILENO] < 0 &&
c->std_input == EXEC_INPUT_NAMED_FD &&
streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
named_iofds[STDIN_FILENO] = p->fds[i];
targets--;
continue;
}
} else if (named_iofds[STDOUT_FILENO] < 0 &&
c->std_output == EXEC_OUTPUT_NAMED_FD &&
streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
/* Allow stdout and stderr to use the same named fd */
if (named_iofds[STDOUT_FILENO] < 0 &&
c->std_output == EXEC_OUTPUT_NAMED_FD &&
streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
named_iofds[STDOUT_FILENO] = p->fds[i];
targets--;
}
} else if (named_iofds[STDERR_FILENO] < 0 &&
c->std_error == EXEC_OUTPUT_NAMED_FD &&
streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
if (named_iofds[STDERR_FILENO] < 0 &&
c->std_error == EXEC_OUTPUT_NAMED_FD &&
streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
named_iofds[STDERR_FILENO] = p->fds[i];
targets--;
}
}
return targets == 0 ? 0 : -ENOENT;
}

View File

@ -1539,48 +1539,6 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
return 0;
}
static int serialize_std_out_err(const ExecContext *c, FILE *f, int fileno) {
char *key, *value;
const char *type;
assert(c);
assert(f);
assert(IN_SET(fileno, STDOUT_FILENO, STDERR_FILENO));
type = fileno == STDOUT_FILENO ? "output" : "error";
switch (fileno == STDOUT_FILENO ? c->std_output : c->std_error) {
case EXEC_OUTPUT_NAMED_FD:
key = strjoina("exec-context-std-", type, "-fd-name");
value = c->stdio_fdname[fileno];
break;
case EXEC_OUTPUT_FILE:
key = strjoina("exec-context-std-", type, "-file");
value = c->stdio_file[fileno];
break;
case EXEC_OUTPUT_FILE_APPEND:
key = strjoina("exec-context-std-", type, "-file-append");
value = c->stdio_file[fileno];
break;
case EXEC_OUTPUT_FILE_TRUNCATE:
key = strjoina("exec-context-std-", type, "-file-truncate");
value = c->stdio_file[fileno];
break;
default:
return 0;
}
return serialize_item(f, key, value);
}
static int exec_context_serialize(const ExecContext *c, FILE *f) {
int r;
@ -2004,6 +1962,10 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
return r;
}
r = serialize_bool_elide(f, "exec-context-root-directory-as-fd", c->root_directory_as_fd);
if (r < 0)
return r;
r = serialize_item(f, "exec-context-std-input", exec_input_to_string(c->std_input));
if (r < 0)
return r;
@ -2020,36 +1982,60 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (r < 0)
return r;
r = serialize_bool_elide(f, "exec-context-root-directory-as-fd", c->root_directory_as_fd);
if (r < 0)
return r;
switch (c->std_input) {
case EXEC_INPUT_NAMED_FD:
r = serialize_item(f, "exec-context-std-input-fd-name", c->stdio_fdname[STDIN_FILENO]);
if (r < 0)
return r;
break;
case EXEC_INPUT_FILE:
r = serialize_item(f, "exec-context-std-input-file", c->stdio_file[STDIN_FILENO]);
if (r < 0)
return r;
r = serialize_item_escaped(f, "exec-context-std-input-file", c->stdio_file[STDIN_FILENO]);
break;
case EXEC_INPUT_DATA:
r = serialize_item_base64mem(f, "exec-context-std-input-data", c->stdin_data, c->stdin_data_size);
break;
default:
;
r = 0;
}
r = serialize_std_out_err(c, f, STDOUT_FILENO);
if (r < 0)
return r;
r = serialize_std_out_err(c, f, STDERR_FILENO);
switch (c->std_output) {
case EXEC_OUTPUT_NAMED_FD:
r = serialize_item(f, "exec-context-std-output-fd-name", c->stdio_fdname[STDOUT_FILENO]);
break;
case EXEC_OUTPUT_FILE:
case EXEC_OUTPUT_FILE_APPEND:
case EXEC_OUTPUT_FILE_TRUNCATE:
r = serialize_item_escaped(f, "exec-context-std-output-file", c->stdio_file[STDOUT_FILENO]);
break;
default:
r = 0;
}
if (r < 0)
return r;
r = serialize_item_base64mem(f, "exec-context-stdin-data", c->stdin_data, c->stdin_data_size);
switch (c->std_error) {
case EXEC_OUTPUT_NAMED_FD:
r = serialize_item(f, "exec-context-std-error-fd-name", c->stdio_fdname[STDERR_FILENO]);
break;
case EXEC_OUTPUT_FILE:
case EXEC_OUTPUT_FILE_APPEND:
case EXEC_OUTPUT_FILE_TRUNCATE:
r = serialize_item_escaped(f, "exec-context-std-error-file", c->stdio_file[STDERR_FILENO]);
break;
default:
r = 0;
}
if (r < 0)
return r;
@ -3017,6 +3003,11 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
r = deserialize_usec(val, (usec_t *)&c->timer_slack_nsec);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-root-directory-as-fd="))) {
r = parse_boolean(val);
if (r < 0)
return r;
c->root_directory_as_fd = r;
} else if ((val = startswith(l, "exec-context-std-input="))) {
c->std_input = exec_input_from_string(val);
if (c->std_input < 0)
@ -3034,11 +3025,13 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
if (r < 0)
return r;
c->stdio_as_fds = r;
} else if ((val = startswith(l, "exec-context-root-directory-as-fd="))) {
r = parse_boolean(val);
} else if ((val = startswith(l, "exec-context-std-input-data="))) {
if (c->stdin_data)
return -EINVAL; /* duplicated */
r = unbase64mem(val, &c->stdin_data, &c->stdin_data_size);
if (r < 0)
return r;
c->root_directory_as_fd = r;
} else if ((val = startswith(l, "exec-context-std-input-fd-name="))) {
r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], val);
if (r < 0)
@ -3052,40 +3045,35 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-input-file="))) {
r = free_and_strdup(&c->stdio_file[STDIN_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-output-file="))) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-output-file-append="))) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-output-file-truncate="))) {
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-error-file="))) {
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-error-file-append="))) {
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-std-error-file-truncate="))) {
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], val);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-stdin-data="))) {
if (c->stdin_data)
return -EINVAL; /* duplicated */
ssize_t k;
char *p;
k = cunescape(val, 0, &p);
if (k < 0)
return k;
free_and_replace(c->stdio_file[STDIN_FILENO], p);
} else if ((val = startswith(l, "exec-context-std-output-file="))) {
ssize_t k;
char *p;
k = cunescape(val, 0, &p);
if (k < 0)
return k;
free_and_replace(c->stdio_file[STDOUT_FILENO], p);
} else if ((val = startswith(l, "exec-context-std-error-file="))) {
ssize_t k;
char *p;
k = cunescape(val, 0, &p);
if (k < 0)
return k;
free_and_replace(c->stdio_file[STDERR_FILENO], p);
r = unbase64mem(val, &c->stdin_data, &c->stdin_data_size);
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-tty-path="))) {
r = free_and_strdup(&c->tty_path, val);
if (r < 0)

View File

@ -480,6 +480,16 @@ static inline bool exec_context_has_tty(const ExecContext *context) {
context->std_error == EXEC_OUTPUT_TTY;
}
static inline bool exec_input_is_inheritable(ExecInput i) {
/* We assume these listed inputs refer to bidirectional streams, and hence duplicating them from
* stdin to stdout/stderr makes sense and hence allowing EXEC_OUTPUT_INHERIT makes sense, too.
* Outputs such as regular files or sealed data memfds otoh don't really make sense to be
* duplicated for both input and output at the same time (since they then would cause a feedback
* loop). */
return exec_input_is_terminal(i) || IN_SET(i, EXEC_INPUT_SOCKET, EXEC_INPUT_NAMED_FD);
}
int exec_spawn(
Unit *unit,
ExecCommand *command,

View File

@ -113,7 +113,7 @@
/* How many units and jobs to process of the bus queue before returning to the event loop. */
#define MANAGER_BUS_MESSAGE_BUDGET 100U
#define DEFAULT_TASKS_MAX ((CGroupTasksMax) { 15U, 100U }) /* 15% */
#define DEFAULT_TASKS_MAX ((const CGroupTasksMax) { 15U, 100U }) /* 15% */
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);

View File

@ -808,19 +808,11 @@ static void service_fix_stdio(Service *s) {
s->exec_context.stdin_data_size > 0)
s->exec_context.std_input = EXEC_INPUT_DATA;
if (IN_SET(s->exec_context.std_input,
EXEC_INPUT_TTY,
EXEC_INPUT_TTY_FORCE,
EXEC_INPUT_TTY_FAIL,
EXEC_INPUT_SOCKET,
EXEC_INPUT_NAMED_FD))
if (exec_input_is_inheritable(s->exec_context.std_input))
return;
/* We assume these listed inputs refer to bidirectional streams, and hence duplicating them from
* stdin to stdout/stderr makes sense and hence leaving EXEC_OUTPUT_INHERIT in place makes sense,
* too. Outputs such as regular files or sealed data memfds otoh don't really make sense to be
* duplicated for both input and output at the same time (since they then would cause a feedback
* loop), hence override EXEC_OUTPUT_INHERIT with the default stderr/stdout setting. */
/* Override EXEC_OUTPUT_INHERIT with the default stderr/stdout setting if not applicable for
* given stdin mode. */
if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
s->exec_context.std_output == EXEC_OUTPUT_INHERIT)

View File

@ -341,7 +341,7 @@ static int on_first_event(sd_event_source *s, void *userdata) {
if (r < 0)
return r;
if (arg_follow && !arg_reverse && !c->has_cursor && !arg_since_set) {
if (arg_follow && !c->has_cursor && !arg_since_set) {
r = sd_journal_get_cursor(c->journal, /* ret_cursor= */ NULL);
if (r == -EADDRNOTAVAIL) {
/* If we shall operate in --follow mode, and we are unable to get a cursor after

View File

@ -48,7 +48,8 @@ static int help(int argc, char *argv[], void *userdata) {
"\n%5$sPerform various operations on private keys and certificates.%6$s\n"
"\n%3$sCommands:%4$s\n"
" validate Load and validate the given certificate and private key\n"
" public Extract a public key\n"
" extract-public Extract a public key\n"
" extract-certificate Extract a certificate\n"
" pkcs7 Generate a PKCS#7 signature\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
@ -247,7 +248,7 @@ static int verb_validate(int argc, char *argv[], void *userdata) {
return 0;
}
static int verb_public(int argc, char *argv[], void *userdata) {
static int verb_extract_public(int argc, char *argv[], void *userdata) {
_cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
int r;
@ -314,6 +315,33 @@ static int verb_public(int argc, char *argv[], void *userdata) {
return 0;
}
static int verb_extract_certificate(int argc, char *argv[], void *userdata) {
_cleanup_(X509_freep) X509 *certificate = NULL;
int r;
if (!arg_certificate)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--certificate= must be specified.");
if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
r = parse_path_argument(arg_certificate, /* suppress_root= */ false, &arg_certificate);
if (r < 0)
return r;
}
r = openssl_load_x509_certificate(
arg_certificate_source_type,
arg_certificate_source,
arg_certificate,
&certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
if (PEM_write_X509(stdout, certificate) == 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write certificate to stdout.");
return 0;
}
static int verb_pkcs7(int argc, char *argv[], void *userdata) {
_cleanup_(X509_freep) X509 *certificate = NULL;
_cleanup_free_ char *pkcs1 = NULL;
@ -399,10 +427,12 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) {
static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "validate", VERB_ANY, 1, 0, verb_validate },
{ "public", VERB_ANY, 1, 0, verb_public },
{ "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 },
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "validate", VERB_ANY, 1, 0, verb_validate },
{ "extract-public", VERB_ANY, 1, 0, verb_extract_public },
{ "public", VERB_ANY, 1, 0, verb_extract_public }, /* Deprecated but kept for backwards compat. */
{ "extract-certificate", VERB_ANY, 1, 0, verb_extract_certificate },
{ "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 },
{}
};
int r;

View File

@ -105,7 +105,7 @@
/* To do LUKS2 offline encryption, we need to keep some extra free space at the end of the partition. */
#define LUKS2_METADATA_KEEP_FREE (LUKS2_METADATA_SIZE*2ULL)
/* LUKS2 volume key size. */
/* LUKS2 default volume key size (no integrity). */
#define VOLUME_KEY_SIZE (512ULL/8ULL)
/* Use 4K as the default filesystem sector size because as long as the partitions are aligned to 4K, the
@ -266,6 +266,21 @@ typedef enum EncryptMode {
_ENCRYPT_MODE_INVALID = -EINVAL,
} EncryptMode;
typedef enum IntegrityMode {
INTEGRITY_OFF,
INTEGRITY_INLINE,
_INTEGRITY_MODE_MAX,
_INTEGRITY_MODE_INVALID = -EINVAL,
} IntegrityMode;
typedef enum IntegrityAlg {
INTEGRITY_ALG_HMAC_SHA1,
INTEGRITY_ALG_HMAC_SHA256,
INTEGRITY_ALG_HMAC_SHA512,
_INTEGRITY_ALG_MAX,
_INTEGRITY_ALG_INVALID = -EINVAL,
} IntegrityAlg;
typedef enum VerityMode {
VERITY_OFF,
VERITY_DATA,
@ -444,6 +459,8 @@ typedef struct Partition {
struct iovec key;
Tpm2PCRValue *tpm2_hash_pcr_values;
size_t tpm2_n_hash_pcr_values;
IntegrityMode integrity;
IntegrityAlg integrity_alg;
VerityMode verity;
char *verity_match_key;
MinimizeMode minimize;
@ -552,6 +569,22 @@ static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
[ENCRYPT_KEY_FILE_TPM2] = "key-file+tpm2",
};
/* Going forward, the plan is to add two more modes:
* [INTEGRITY_DATA] = "data" (interleave data and integrity tags on the same device),
* [INTEGRITY_META] = "meta" (use a separate device for storing integrity tags).
* Also, INTEGRITY_INLINE will be using hardware sector integrity fields when used
* without encryption. */
static const char *integrity_mode_table[_INTEGRITY_MODE_MAX] = {
[INTEGRITY_OFF] = "off", /* no integrity protection */
[INTEGRITY_INLINE] = "inline", /* luks2 storage when encrypted */
};
static const char *integrity_alg_table[_INTEGRITY_ALG_MAX] = {
[INTEGRITY_ALG_HMAC_SHA1] = "hmac-sha1",
[INTEGRITY_ALG_HMAC_SHA256] = "hmac-sha256",
[INTEGRITY_ALG_HMAC_SHA512] = "hmac-sha512",
};
static const char *verity_mode_table[_VERITY_MODE_MAX] = {
[VERITY_OFF] = "off",
[VERITY_DATA] = "data",
@ -584,6 +617,8 @@ static const char *progress_phase_table[_PROGRESS_PHASE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(integrity_mode, IntegrityMode, INTEGRITY_INLINE);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(integrity_alg, IntegrityAlg);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase, ProgressPhase);
@ -2633,6 +2668,9 @@ static int config_parse_key_file(
return parse_key_file(rvalue, &partition->key);
}
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity, integrity_mode, IntegrityMode, INTEGRITY_OFF);
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_integrity_alg, integrity_alg, IntegrityAlg, INTEGRITY_ALG_HMAC_SHA256);
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF);
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF);
@ -2787,6 +2825,8 @@ static int partition_read_definition(
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
{ "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
{ "Partition", "KeyFile", config_parse_key_file, 0, p },
{ "Partition", "Integrity", config_parse_integrity, 0, &p->integrity },
{ "Partition", "IntegrityAlgorithm", config_parse_integrity_alg, 0, &p->integrity_alg },
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
@ -2921,6 +2961,10 @@ static int partition_read_definition(
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
verity_mode_to_string(p->verity));
if (p->integrity == INTEGRITY_INLINE && p->encrypt == ENCRYPT_OFF)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Integrity=inline requires Encrypt=.");
if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
@ -4989,6 +5033,39 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
return 0;
}
/* libcryptsetup uses its own names for integrity algorithms, e.g. 'hmac(sha1)' but systemd
* prefers more standardized 'hmac-sha1', do the conversion here. Default to hmac(sha256). */
static const char* dmcrypt_integrity_alg_name(Partition *p) {
if (p->integrity != INTEGRITY_INLINE)
return NULL;
switch (p->integrity_alg) {
case INTEGRITY_ALG_HMAC_SHA1:
return "hmac(sha1)";
case INTEGRITY_ALG_HMAC_SHA512:
return "hmac(sha512)";
case INTEGRITY_ALG_HMAC_SHA256:
default:
return "hmac(sha256)";
}
}
/* Integrity puts specific limitations on the key size depending on the algorithm */
static size_t dmcrypt_proper_key_size(Partition *p) {
if (p->integrity != INTEGRITY_INLINE)
return VOLUME_KEY_SIZE;
switch (p->integrity_alg) {
case INTEGRITY_ALG_HMAC_SHA1:
return 672/8;
case INTEGRITY_ALG_HMAC_SHA512:
return 1024/8;
case INTEGRITY_ALG_HMAC_SHA256:
default:
return 768/8;
}
}
static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
#if HAVE_LIBCRYPTSETUP
#if HAVE_TPM2
@ -4997,6 +5074,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
_cleanup_fclose_ FILE *h = NULL;
_cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
const char *passphrase = NULL;
const size_t volume_key_size = dmcrypt_proper_key_size(p);
size_t passphrase_size = 0;
const char *vt;
int r;
@ -5021,6 +5099,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
.label = vl,
.sector_size = partition_fs_sector_size(context, p),
.data_device = offline ? node : NULL,
.integrity = dmcrypt_integrity_alg_name(p),
};
struct crypt_params_reencrypt reencrypt_params = {
.mode = CRYPT_REENCRYPT_ENCRYPT,
@ -5032,6 +5111,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
};
if (offline) {
/* libcryptsetup does not currently support reencryption of devices with integrity profiles.*/
if (p->integrity == INTEGRITY_INLINE)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Integrity=inline cannot be enabled in offline mode.");
r = var_tmp_dir(&vt);
if (r < 0)
return log_error_errno(r, "Failed to determine temporary files directory: %m");
@ -5084,7 +5167,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
"xts-plain64",
SD_ID128_TO_UUID_STRING(p->luks_uuid),
NULL,
VOLUME_KEY_SIZE,
/* volume_key_size= */ volume_key_size,
&luks_params);
if (r < 0)
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
@ -5097,7 +5180,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
cd,
CRYPT_ANY_SLOT,
NULL,
VOLUME_KEY_SIZE,
/* volume_key_size= */ volume_key_size,
strempty(iovec_key->iov_base),
iovec_key->iov_len);
if (r < 0)
@ -5272,7 +5355,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
cd,
CRYPT_ANY_SLOT,
/* volume_key= */ NULL,
/* volume_key_size= */ VOLUME_KEY_SIZE,
/* volume_key_size= */ volume_key_size,
base64_encoded,
base64_encoded_size);
if (keyslot < 0)
@ -5372,11 +5455,30 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
cd,
dm_name,
NULL,
VOLUME_KEY_SIZE,
(arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0) | CRYPT_ACTIVATE_PRIVATE);
/* volume_key_size= */ volume_key_size,
(arg_discard && p->integrity != INTEGRITY_INLINE ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0) | CRYPT_ACTIVATE_PRIVATE);
if (r < 0)
return log_error_errno(r, "Failed to activate LUKS superblock: %m");
/* crypt_wipe() the whole device to avoid integrity errors upon mkfs */
if (p->integrity == INTEGRITY_INLINE) {
r = sym_crypt_wipe(
cd,
vol,
CRYPT_WIPE_ZERO,
/* offset= */ 0,
/* length= */ 0,
/* wipe_block_size= */ 1 * U64_MB,
/* flags= */ 0,
/* progress= */ NULL,
/* usrptr= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to wipe LUKS device: %m");
log_info("%s integrity protection for future partition %" PRIu64 " initialized.",
integrity_alg_to_string(p->integrity_alg), p->partno);
}
dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
if (dev_fd < 0)
return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);

View File

@ -70,6 +70,8 @@ DLSYM_PROTOTYPE(crypt_token_set_external_path) = NULL;
DLSYM_PROTOTYPE(crypt_token_status) = NULL;
DLSYM_PROTOTYPE(crypt_volume_key_get) = NULL;
DLSYM_PROTOTYPE(crypt_volume_key_keyring) = NULL;
DLSYM_PROTOTYPE(crypt_wipe) = NULL;
DLSYM_PROTOTYPE(crypt_get_integrity_info) = NULL;
static void cryptsetup_log_glue(int level, const char *msg, void *usrptr) {
@ -274,7 +276,9 @@ int dlopen_cryptsetup(void) {
#endif
DLSYM_ARG(crypt_token_status),
DLSYM_ARG(crypt_volume_key_get),
DLSYM_ARG(crypt_volume_key_keyring));
DLSYM_ARG(crypt_volume_key_keyring),
DLSYM_ARG(crypt_wipe),
DLSYM_ARG(crypt_get_integrity_info));
if (r <= 0)
return r;

View File

@ -68,6 +68,8 @@ extern DLSYM_PROTOTYPE(crypt_token_set_external_path);
extern DLSYM_PROTOTYPE(crypt_token_status);
extern DLSYM_PROTOTYPE(crypt_volume_key_get);
extern DLSYM_PROTOTYPE(crypt_volume_key_keyring);
extern DLSYM_PROTOTYPE(crypt_wipe);
extern DLSYM_PROTOTYPE(crypt_get_integrity_info);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, crypt_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct crypt_device *, sym_crypt_free, NULL);

View File

@ -397,6 +397,114 @@ static int image_policy_check_protection(
return 0;
}
/* internal LUKS2 header defines */
#define LUKS2_FIXED_HDR_SIZE UINT64_C(0x1000)
#define LUKS2_MAGIC "LUKS\xba\xbe"
/* Matches the beginning of 'struct luks2_hdr_disk' from cryptsetup */
struct luks_header_incomplete {
char luks_magic[sizeof(LUKS2_MAGIC) - 1];
be16_t version;
be64_t hdr_len;
};
/* 'integrity' information from LUKS JSON header. Currenly, only 'type' is extracted/checked. */
struct luks_integrity_data {
char *type;
};
static int integrity_information(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
static const sd_json_dispatch_field table[] = {
{ "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct luks_integrity_data, type), SD_JSON_MANDATORY },
{}
};
return sd_json_dispatch(v, table, flags, userdata);
}
/* cryptsetup needs a loop device to work with a partition which has offset/size but
* dissect may be running unpriviliged. Implement a minimal custom LUKS header parser
* checking integrity protection information. */
static int partition_is_luks2_integrity(int part_fd, uint64_t offset, uint64_t size) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_free_ char *json = NULL;
sd_json_variant *w;
const char *key;
struct luks_header_incomplete header;
ssize_t sz, json_len;
int r;
assert(part_fd >= 0);
if (size < LUKS2_FIXED_HDR_SIZE) {
log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Partition is too small to contain a LUKS header.");
return 0;
}
sz = pread(part_fd, &header, sizeof(header), offset);
if (sz < 0)
return log_error_errno(errno, "Failed to read LUKS header.");
if (sz != sizeof(header))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS header.");
if (memcmp(header.luks_magic, LUKS2_MAGIC, sizeof(header.luks_magic)) != 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition's magic is not LUKS.");
if (be16toh(header.version) != 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported LUKS header version: %" PRIu16 ".", be16toh(header.version));
if (be64toh(header.hdr_len) > size)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS header length exceeds partition size.");
if (be64toh(header.hdr_len) <= LUKS2_FIXED_HDR_SIZE || offset > UINT64_MAX - be64toh(header.hdr_len))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid LUKS header length: %" PRIu64 ".", be64toh(header.hdr_len));
json_len = be64toh(header.hdr_len) - LUKS2_FIXED_HDR_SIZE;
json = malloc(json_len + 1);
if (!json)
return -ENOMEM;
sz = pread(part_fd, json, json_len, offset + LUKS2_FIXED_HDR_SIZE);
if (sz < 0)
return log_error_errno(errno, "Failed to read LUKS JSON header.");
if (sz != json_len)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS JSON header.");
json[sz] = '\0';
r = sd_json_parse(json, /* flags = */ 0, &v, /* reterr_line = */ NULL, /* reterr_column = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse LUKS JSON header.");
v = sd_json_variant_by_key(v, "segments");
if (!v || !sd_json_variant_is_object(v)) {
log_debug("LUKS JSON header lacks 'segments' information, assuming no integrity.");
return 0;
}
/* Verify that all segments have integrity protection */
JSON_VARIANT_OBJECT_FOREACH(key, w, v) {
struct luks_integrity_data data = {};
static const sd_json_dispatch_field dispatch_segment[] = {
{ "integrity", SD_JSON_VARIANT_OBJECT, integrity_information, 0, SD_JSON_MANDATORY },
{}
};
r = sd_json_dispatch(w, dispatch_segment, SD_JSON_ALLOW_EXTENSIONS, &data);
if (r < 0) {
log_debug("Failed to get integrity information from LUKS JSON for segment %s, assuming no integrity.", key);
return 0;
}
/* We don't require a particular integrity algorithm, everything but 'none' (which shouldn't
* be there in the first place but is theoretically possible) works. */
if (streq(data.type, "none"))
return 0;
}
return 1;
}
static int image_policy_check_partition_flags(
const ImagePolicy *policy,
PartitionDesignator designator,
@ -457,7 +565,16 @@ static int dissected_image_probe_filesystems(
if (streq_ptr(p->fstype, "crypto_LUKS")) {
m->encrypted = true;
found_flags = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ENCRYPTED; /* found this one, and its definitely encrypted */
if (p->mount_node_fd >= 0)
r = partition_is_luks2_integrity(p->mount_node_fd, /* offset = */ 0, /* size = */ UINT64_MAX);
else
r = partition_is_luks2_integrity(fd, p->offset, p->size);
if (r < 0)
return r;
/* found this one, it's definitely encrypted + with or without integrity checking */
found_flags = PARTITION_POLICY_UNUSED|(r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED);
} else
/* found it, but it's definitely not encrypted, hence mask the encrypted flag, but
* set all other ways that indicate "present". */
@ -948,8 +1065,14 @@ static int dissect_image(
if (verity_settings_data_covers(verity, PARTITION_ROOT))
found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
else
found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
else if (encrypted) {
r = partition_is_luks2_integrity(fd, /* offset = */ 0, /* size = */ UINT64_MAX);
if (r < 0)
return r;
found_flags = r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED;
} else
found_flags = PARTITION_POLICY_UNPROTECTED;
r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
if (r < 0)
@ -1732,7 +1855,7 @@ static int dissect_image(
/* Determine the verity protection level for this partition. */
PartitionPolicyFlags found_flags;
if (m->partitions[di].found) {
found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
PartitionDesignator vi = partition_verity_hash_of(di);
if (vi >= 0 && m->partitions[vi].found) {

View File

@ -78,7 +78,7 @@ static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPol
* all needs no protection, because it *is* the protection */
if (partition_verity_hash_to_data(policy->designator) >= 0 ||
partition_verity_sig_to_data(policy->designator) >= 0)
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED);
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY);
/* if this designator has no verity concept, then mask off verity protection flags */
if (partition_verity_hash_of(policy->designator) < 0)
@ -187,6 +187,8 @@ static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
return PARTITION_POLICY_SIGNED;
if (streq(s, "encrypted"))
return PARTITION_POLICY_ENCRYPTED;
if (streq(s, "encryptedwithintegrity"))
return PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY;
if (streq(s, "unprotected"))
return PARTITION_POLICY_UNPROTECTED;
if (streq(s, "unused"))
@ -423,6 +425,8 @@ int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify,
l[m++] = "signed";
if (flags & PARTITION_POLICY_ENCRYPTED)
l[m++] = "encrypted";
if (flags & PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY)
l[m++] = "encryptedwithintegrity";
if (flags & PARTITION_POLICY_UNPROTECTED)
l[m++] = "unprotected";
if (flags & PARTITION_POLICY_UNUSED)
@ -863,8 +867,8 @@ const ImagePolicy image_policy_sysext = {
* be. */
.n_policies = 2,
.policies = {
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
},
.default_flags = PARTITION_POLICY_IGNORE,
};
@ -884,7 +888,7 @@ const ImagePolicy image_policy_confext = {
* are only interested in the /etc/ tree anyway, and that's really the only place it can be. */
.n_policies = 1,
.policies = {
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
},
.default_flags = PARTITION_POLICY_IGNORE,
};
@ -901,14 +905,14 @@ const ImagePolicy image_policy_container = {
/* For systemd-nspawn containers we use all partitions, with the exception of swap */
.n_policies = 8,
.policies = {
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
},
.default_flags = PARTITION_POLICY_IGNORE,
};
@ -917,15 +921,15 @@ const ImagePolicy image_policy_host = {
/* For the host policy we basically use everything */
.n_policies = 9,
.policies = {
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
},
.default_flags = PARTITION_POLICY_IGNORE,
};
@ -934,12 +938,12 @@ const ImagePolicy image_policy_service = {
/* For RootImage= in services we skip ESP/XBOOTLDR and swap */
.n_policies = 6,
.policies = {
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
},
.default_flags = PARTITION_POLICY_IGNORE,
};

View File

@ -14,29 +14,30 @@ typedef enum PartitionPolicyFlags {
* on. Example: a default policy of "verity+encrypted" certainly makes sense, but for /home/
* partitions this gracefully degrades to "encrypted" (as we do not have a concept of verity for
* /home/), and so on. */
PARTITION_POLICY_VERITY = 1 << 0, /* must exist, activate with verity (only applies to root/usr partitions) */
PARTITION_POLICY_SIGNED = 1 << 1, /* must exist, activate with signed verity (only applies to root/usr partitions) */
PARTITION_POLICY_ENCRYPTED = 1 << 2, /* must exist, activate with LUKS encryption (applies to any data partition, but not to verity/signature partitions */
PARTITION_POLICY_UNPROTECTED = 1 << 3, /* must exist, activate without encryption/verity */
PARTITION_POLICY_UNUSED = 1 << 4, /* must exist, don't use */
PARTITION_POLICY_ABSENT = 1 << 5, /* must not exist */
PARTITION_POLICY_OPEN = PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|
PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
PARTITION_POLICY_IGNORE = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
_PARTITION_POLICY_USE_MASK = PARTITION_POLICY_OPEN,
PARTITION_POLICY_VERITY = 1 << 0, /* must exist, activate with verity (only applies to root/usr partitions) */
PARTITION_POLICY_SIGNED = 1 << 1, /* must exist, activate with signed verity (only applies to root/usr partitions) */
PARTITION_POLICY_ENCRYPTED = 1 << 2, /* must exist, activate with LUKS encryption (applies to any data partition, but not to verity/signature partitions */
PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY = 1 << 3, /* same as PARTITION_POLICY_ENCRYPTED but also requires integrity checking */
PARTITION_POLICY_UNPROTECTED = 1 << 4, /* must exist, activate without encryption/verity */
PARTITION_POLICY_UNUSED = 1 << 5, /* must exist, don't use */
PARTITION_POLICY_ABSENT = 1 << 6, /* must not exist */
PARTITION_POLICY_OPEN = PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|
PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
PARTITION_POLICY_IGNORE = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
_PARTITION_POLICY_USE_MASK = PARTITION_POLICY_OPEN,
PARTITION_POLICY_READ_ONLY_OFF = 1 << 6, /* State of GPT partition flag "read-only" must be on */
PARTITION_POLICY_READ_ONLY_ON = 1 << 7,
_PARTITION_POLICY_READ_ONLY_MASK = PARTITION_POLICY_READ_ONLY_OFF|PARTITION_POLICY_READ_ONLY_ON,
PARTITION_POLICY_GROWFS_OFF = 1 << 8, /* State of GPT partition flag "growfs" must be on */
PARTITION_POLICY_GROWFS_ON = 1 << 9,
_PARTITION_POLICY_GROWFS_MASK = PARTITION_POLICY_GROWFS_OFF|PARTITION_POLICY_GROWFS_ON,
_PARTITION_POLICY_PFLAGS_MASK = _PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
PARTITION_POLICY_READ_ONLY_OFF = 1 << 7, /* State of GPT partition flag "read-only" must be on */
PARTITION_POLICY_READ_ONLY_ON = 1 << 8,
_PARTITION_POLICY_READ_ONLY_MASK = PARTITION_POLICY_READ_ONLY_OFF|PARTITION_POLICY_READ_ONLY_ON,
PARTITION_POLICY_GROWFS_OFF = 1 << 9, /* State of GPT partition flag "growfs" must be on */
PARTITION_POLICY_GROWFS_ON = 1 << 10,
_PARTITION_POLICY_GROWFS_MASK = PARTITION_POLICY_GROWFS_OFF|PARTITION_POLICY_GROWFS_ON,
_PARTITION_POLICY_PFLAGS_MASK = _PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
} PartitionPolicyFlags;
assert_cc((_PARTITION_POLICY_USE_MASK | _PARTITION_POLICY_PFLAGS_MASK) >= 0); /* ensure flags don't collide with errno range */

View File

@ -1763,7 +1763,7 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) {
_cleanup_(memstream_done) MemStream m = {};
FILE *tf = memstream_init(&m);
if (!tf)
return log_oom();
return -ENOMEM;
if (i2d_PUBKEY_fp(tf, private_key) != 1)
return -EIO;
@ -1774,7 +1774,7 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) {
if (r < 0)
return r;
const unsigned char *t = (unsigned char*) buf;
const unsigned char *t = (const unsigned char*) buf;
if (!d2i_PUBKEY(ret, &t, len))
return -EIO;

View File

@ -13,7 +13,7 @@ typedef enum SocketAddressBindIPv6Only {
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s);
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s) _pure_;
int socket_address_listen(
const SocketAddress *a,

View File

@ -14,6 +14,9 @@
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#define HEADER_READ_TIMEOUT_USEC (5 * USEC_PER_SEC)
static int process_vsock_cid(unsigned cid, const char *port) {
int r;
@ -91,6 +94,125 @@ static int process_unix(const char *path) {
return 0;
}
static int skip_ok_port_res(int fd, const char *path, const char *port) {
struct timeval oldtv;
socklen_t oldtv_size = sizeof(oldtv);
if (getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &oldtv, &oldtv_size) < 0)
return log_error_errno(errno, "Failed to get socket receive timeout for %s: %m", path);
if (oldtv_size != sizeof(oldtv))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unexpected size of socket receive timeout for %s", path);
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(HEADER_READ_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
return log_error_errno(errno, "Failed to set socket receive timeout for %s: %m", path);
char recv_buf[STRLEN("OK 65535\n")];
size_t bytes_recv = 0, bytes_avail = 0, pos = 0;
static const char expected_prefix[] = "OK ";
for (;;) {
if (pos >= bytes_avail) {
assert(bytes_recv <= bytes_avail);
if (bytes_avail >= sizeof(recv_buf)) {
/*
Full buffer means that we have peeked as many bytes as possible and not seeing the ending \n .
So the server is believed to not send OK PORT response, and we just pass out the socket to ssh client,
and let it handle the connection.
If we have not received any bytes from the socket buffer, we can safely pass out the socket,
since no change has been made to the socket buffer. Otherwise, if some bytes have been received,
the socket buffer has been changed, the only option is to give up and terminate the connection.
Similar logic applies below when we meet other kinds of unexpected responses.
*/
if (bytes_recv == 0) {
log_debug("Received too many bytes while waiting for OK PORT response from %s\n"
"Assume the multiplexer is not sending OK PORT.",
path);
goto passout_fd;
}
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Received too many bytes while waiting for OK PORT response from %s", path);
}
if (bytes_avail > bytes_recv) {
/*
Discard already peeked bytes before peeking more.
XXX: We cannot use SO_RCVLOWAT to set the minimum number of bytes to be peeked to peek entire
OK PORT response at once to prevent changes to the recving buffer, because SO_RCVLOWAT
does not work on unix sockets with recv(..., MSG_PEEK). Also poll() does not help here,
because poll() returns readable as long as there is any data in the socket buffer for
unix sockets, not respecting SO_RCVLOWAT.
XXX: We could have used SO_PEEK_OFF to continously peek more data without changing the socket
receive buffer, but this fucntion breaks since Linux 4.3 due to a kernel bug, which is fixed
in Linux 6.18 commit 7bf3a476ce43 ("af_unix: Read sk_peek_offset() again after sleeping in
unix_stream_read_generic()."). It is also not possible to detect whether the kernel is
affected by this bug at runtime.
As a result, we have no other choice but to discard already peeked data here.
*/
ssize_t rlen = recv(fd, recv_buf + bytes_recv, bytes_avail - bytes_recv, /* flags= */ 0);
if (rlen < 0)
return log_error_errno(errno, "Failed to discard OK PORT response from %s: %m", path);
if ((size_t) rlen != bytes_avail - bytes_recv)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while discarding OK PORT response from %s", path);
log_debug("Successfully discarded %zi bytes of response: %.*s", rlen, (int) rlen, recv_buf + bytes_recv);
bytes_recv = bytes_avail;
}
ssize_t len = recv(fd, recv_buf + bytes_avail, sizeof(recv_buf) - bytes_avail, MSG_PEEK);
if (len < 0) {
if (errno != EAGAIN)
return log_error_errno(errno, "Failed to receive OK from %s: %m", path);
if (bytes_recv == 0) {
log_debug("Timeout while waiting for OK PORT response from %s\n"
"Assume the multiplexer will not send OK PORT.",
path);
goto passout_fd;
}
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Timed out to receive OK PORT from %s", path);
}
if (len == 0) {
log_debug("Connection closed while waiting for OK PORT response from %s", path);
if (bytes_recv == 0) {
log_debug("No data received, which means the connecting port is not open.");
return log_error_errno(SYNTHETIC_ERRNO(ECONNREFUSED), "Port %s on %s is not open", port, path);
}
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Connection closed before full OK PORT response received from %s.", path);
}
bytes_avail += len;
}
assert(pos < bytes_avail);
if (pos < strlen(expected_prefix) && recv_buf[pos] != expected_prefix[pos]) {
if (bytes_recv == 0) {
log_debug("Received response does not start with expected OK PORT response from %s\n"
"Assume the multiplexer will not send OK PORT.",
path);
goto passout_fd;
}
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Received invalid response while waiting for OK PORT from %s", path);
}
if (recv_buf[pos] == '\n') {
pos += 1;
break;
}
pos += 1;
}
assert(pos <= sizeof(recv_buf));
assert(bytes_recv <= pos);
if (bytes_recv < pos) {
ssize_t len = recv(fd, recv_buf + bytes_recv, pos - bytes_recv, /* flags= */ 0);
if (len < 0)
return log_error_errno(errno, "Failed to discard OK PORT response from %s: %m", path);
if ((size_t) len != pos - bytes_recv)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while discarding OK PORT response from %s", path);
log_debug("Successfully discarded response from %s: %.*s", path, (int) pos, recv_buf);
}
passout_fd:
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &oldtv, sizeof(oldtv)) < 0)
return log_error_errno(errno, "Failed to restore socket receive timeout for %s: %m", path);
return 0;
}
static int process_vsock_mux(const char *path, const char *port) {
int r;
@ -127,6 +249,10 @@ static int process_vsock_mux(const char *path, const char *port) {
if (r < 0)
return log_error_errno(r, "Failed to send CONNECT to %s:%s: %m", path, port);
r = skip_ok_port_res(fd, path, port);
if (r < 0)
return r;
r = send_one_fd_iov(STDOUT_FILENO, fd, &iovec_nul_byte, /* iovlen= */ 1, /* flags= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to send socket via STDOUT: %m");

View File

@ -460,6 +460,10 @@ executables += [
libbasic_static,
],
},
test_template + {
'sources' : files('test-sleep.c'),
'type' : 'manual',
},
test_template + {
'sources' : files('test-time-util.c'),
'timeout' : 120,

View File

@ -99,8 +99,8 @@ TEST_RET(test_image_policy_to_string) {
test_policy_equiv("~", image_policy_equiv_deny);
test_policy_equiv("=absent", image_policy_equiv_deny);
test_policy_equiv("=open", image_policy_equiv_allow);
test_policy_equiv("=verity+signed+encrypted+unprotected+unused+absent", image_policy_equiv_allow);
test_policy_equiv("=signed+verity+encrypted+unused+unprotected+absent", image_policy_equiv_allow);
test_policy_equiv("=verity+signed+encrypted+encryptedwithintegrity+unprotected+unused+absent", image_policy_equiv_allow);
test_policy_equiv("=signed+verity+encrypted+encryptedwithintegrity+unused+unprotected+absent", image_policy_equiv_allow);
test_policy_equiv("=ignore", image_policy_equiv_ignore);
test_policy_equiv("=absent+unused", image_policy_equiv_ignore);
test_policy_equiv("=unused+absent", image_policy_equiv_ignore);

24
src/test/test-sleep.c Normal file
View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "log.h"
#include "main-func.h"
#include "time-util.h"
static int run(int argc, char *argv[]) {
usec_t usec = USEC_INFINITY;
int r;
if (argc > 1) {
r = parse_sec(argv[1], &usec);
if (r < 0)
return log_error_errno(r, "Failed to parse timespan '%s': %m", argv[1]);
}
r = usleep_safe(usec);
if (r < 0)
return log_error_errno(r, "Failed to sleep: %m");
return 0;
}
DEFINE_MAIN_FUNCTION(run);

View File

@ -172,6 +172,27 @@ TEST(get_default_background_color) {
log_notice("R=%g G=%g B=%g", red, green, blue);
}
TEST(terminal_get_size_by_csi18) {
unsigned rows, columns;
int r;
usec_t n = now(CLOCK_MONOTONIC);
r = terminal_get_size_by_csi18(STDIN_FILENO, STDOUT_FILENO, &rows, &columns);
log_info("%s took %s", __func__+5,
FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC));
if (r < 0)
return (void) log_notice_errno(r, "Can't get screen dimensions via CSI 18: %m");
log_notice("terminal size via CSI 18: rows=%u columns=%u", rows, columns);
struct winsize ws = {};
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
log_warning_errno(errno, "Can't get terminal size via ioctl, ignoring: %m");
else
log_notice("terminal size via ioctl: rows=%u columns=%u", ws.ws_row, ws.ws_col);
}
TEST(terminal_get_size_by_dsr) {
unsigned rows, columns;
int r;

View File

@ -1364,7 +1364,7 @@ def make_uki(opts: UkifyConfig) -> None:
pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
if pcrpkey is None:
keyutil_tool = find_tool('systemd-keyutil', '/usr/lib/systemd/systemd-keyutil')
cmd = [keyutil_tool, 'public']
cmd = [keyutil_tool, 'extract-public']
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
# If we're using an engine or provider, the public key will be an X.509 certificate.

View File

@ -63,14 +63,10 @@ exec-context-stdio-as-fds=
exec-context-std-input-fd-name=
exec-context-std-output-fd-name=
exec-context-std-error-fd-name=
exec-context-std-input-data=
exec-context-std-input-file=
exec-context-std-output-file=
exec-context-std-output-file-append=
exec-context-std-output-file-truncate=
exec-context-std-error-file=
exec-context-std-error-file-append=
exec-context-std-error-file-truncate=
exec-context-stdin-data=
exec-context-tty-path=
exec-context-tty-reset=
exec-context-tty-vhangup=

View File

@ -75,18 +75,6 @@ class Summary:
)
def tools_os_release(field: str) -> str:
return subprocess.run(
[
'bash',
'-c',
f'set -eu; . /etc/os-release; echo ${field}',
],
stdout=subprocess.PIPE,
text=True,
).stdout.rstrip()
def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool:
# Collect executable paths of all coredumps and filter out the expected ones.
@ -627,16 +615,6 @@ def main() -> None:
*(['--', '--capability=CAP_BPF'] if not vm else []),
] # fmt: skip
# XXX: debug for https://github.com/systemd/systemd/issues/38240
if vm:
# Tracing is not supported in centos/fedora qemu builds
if tools_os_release('ID') in ('centos', 'fedora'):
cmd += ['--qemu-args=-d cpu_reset,guest_errors -D /dev/stderr']
else:
cmd += [
'--qemu-args=-d cpu_reset,guest_errors,trace:kvm_run_exit_system_event,trace:qemu_system_*_request -D /dev/stderr' # noqa: E501
]
try:
result = subprocess.run(cmd)

View File

@ -0,0 +1,48 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
chmod 0755 "$defs"
echo "*** testcase for systemd-dissect and encrypted partitions ***"
tee "$defs/root.conf" <<EOF
[Partition]
Type=root
Format=ext4
Encrypt=key-file
Integrity=off
EOF
systemd-repart --pretty=yes \
--definitions "$defs" \
--empty=create \
--size=100M \
--dry-run=no \
--offline=no \
"$imgs/encint.img"
systemd-dissect --validate --image-policy "root=encrypted" "$imgs/encint.img"
tee "$defs/root.conf" <<EOF
[Partition]
Type=root
Format=ext4
Encrypt=key-file
Integrity=inline
EOF
systemd-repart --pretty=yes \
--definitions "$defs" \
--empty=create \
--size=100M \
--dry-run=no \
--offline=no \
"$imgs/encint_int.img"
systemd-dissect --validate --image-policy "root=encryptedwithintegrity" "$imgs/encint_int.img"

View File

@ -1838,6 +1838,67 @@ EOF
cmp "$imgs/disk1.img" "$imgs/disk3.img"
}
_test_luks2_integrity() {
local defs imgs output root
if [[ "$OFFLINE" != "no" ]]; then
return 0
fi
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
root="$(mktemp --directory "/var/test-repart.root.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs' '$root'" RETURN
chmod 0755 "$defs"
echo "*** testcase for LUKS2 integrity ***"
tee "$defs/root.conf" <<EOF
[Partition]
Type=root
Format=ext4
Encrypt=key-file
Integrity=inline
EOF
[ -n "$1" ] && echo "IntegrityAlgorithm=$1" >> "$defs/root.conf"
systemd-repart --pretty=yes \
--definitions "$defs" \
--empty=create \
--size=100M \
--seed="$seed" \
--dry-run=no \
--offline=no \
"$imgs/encint.img"
loop="$(losetup -P --show --find "$imgs/encint.img")"
udevadm wait --timeout=60 --settle "${loop:?}p1"
volume="test-repart-luksint-$RANDOM"
dmstatus="$imgs/dmsetup-$RANDOM"
touch "$imgs/empty-password"
# the expectation for hmac-sha256 is 'integrity: hmac(sha256)'
cryptsetup luksDump "${loop}p1" | grep -q "integrity: $(echo "$1" | sed -r 's/^hmac-(.*)$/hmac(\1)/')"
cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p1" "$volume"
dmsetup status > "$dmstatus"
cryptsetup close "$volume"
losetup -d "$loop"
# Check that there's a dm-integrity entry
grep -q "$volume""_dif.* integrity " "$dmstatus"
}
testcase_luks2_integrity() {
_test_luks2_integrity ""
_test_luks2_integrity "hmac-sha1"
_test_luks2_integrity "hmac-sha256"
_test_luks2_integrity "hmac-sha512"
}
OFFLINE="yes"
run_testcases

View File

@ -1083,7 +1083,7 @@ check deny no "$name"
# Let's also test the "image-policy" verb
systemd-analyze image-policy '*' 2>&1 | grep -F "Long form: =verity+signed+encrypted+unprotected+unused+absent" >/dev/null
systemd-analyze image-policy '*' 2>&1 | grep -F "Long form: =verity+signed+encrypted+encryptedwithintegrity+unprotected+unused+absent" >/dev/null
systemd-analyze image-policy '-' 2>&1 | grep -F "Long form: =unused+absent" >/dev/null
systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -F "Long form: usr=verity:home=encrypted:=unused+absent" >/dev/null
systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -e '^home \+encrypted \+' >/dev/null

View File

@ -37,14 +37,21 @@ testcase_validate() {
/usr/lib/systemd/systemd-keyutil validate --certificate /tmp/test.crt --private-key /tmp/test.key
}
testcase_public() {
PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --certificate /tmp/test.crt)"
testcase_extract_public() {
PUBLIC="$(/usr/lib/systemd/systemd-keyutil extract-public --certificate /tmp/test.crt)"
assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)"
PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --private-key /tmp/test.key)"
PUBLIC="$(/usr/lib/systemd/systemd-keyutil extract-public --private-key /tmp/test.key)"
assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)"
(! /usr/lib/systemd/systemd-keyutil public)
(! /usr/lib/systemd/systemd-keyutil extract-public)
}
testcase_extract_certificate() {
CERT="$(/usr/lib/systemd/systemd-keyutil extract-certificate --certificate /tmp/test.crt)"
assert_eq "$CERT" "$(cat /tmp/test.crt)"
(! /usr/lib/systemd/systemd-keyutil extract-certificate)
}
verify_pkcs7() {

View File

@ -90,16 +90,12 @@ if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then
# Check that the surviving services are still around
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
if [[ ! -L $(command -v sleep) ]]; then
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
fi
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
[[ ! -e /run/credentials/TEST-82-SOFTREBOOT-nosurvive.service ]]
if [[ ! -L $(command -v sleep) ]]; then
assert_eq "$(cat /run/credentials/TEST-82-SOFTREBOOT-survive-argv.service/preserve)" "yay"
fi
assert_eq "$(cat /run/credentials/TEST-82-SOFTREBOOT-survive-argv.service/preserve)" "yay"
# There may be huge amount of pending messages in sockets. Processing them may cause journal rotation and
# removal of old archived journal files. If a journal file is removed during journalctl reading it,
@ -134,9 +130,7 @@ elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then
# Check that the surviving services are still around
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
if [[ ! -L $(command -v sleep) ]]; then
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
fi
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
@ -192,9 +186,7 @@ elif [ -f /run/TEST-82-SOFTREBOOT.touch ]; then
# Check that the surviving services are still around
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
if [[ ! -L $(command -v sleep) ]]; then
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
fi
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
@ -261,7 +253,9 @@ EOF
#!/usr/bin/env bash
systemd-notify --ready
rm "$survive_argv"
exec -a @sleep sleep infinity
# Here, we use our own sleep implementation, to support coreutils built with
# --enable-single-binary=symlinks, in that case we cannot rename COMM of sleep command.
exec -a @sleep /usr/lib/systemd/tests/unit-tests/manual/test-sleep infinity
EOF
chmod +x "$survive_argv"
# This sets DefaultDependencies=no so that they remain running until the very end, and
@ -282,17 +276,15 @@ EOF
# soft reboots due to journald being temporarily stopped.
# Note, when coreutils is built with --enable-single-binary=symlinks, unfortunately we cannot freely rename
# sleep command, hence we cannot test the feature.
if [[ ! -L $(command -v sleep) ]]; then
systemd-run --service-type=notify --unit=TEST-82-SOFTREBOOT-survive-argv.service \
--property SurviveFinalKillSignal=no \
--property IgnoreOnIsolate=yes \
--property DefaultDependencies=no \
--property After=basic.target \
--property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
--property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
--property SetCredential=preserve:yay \
"$survive_argv"
fi
systemd-run --service-type=notify --unit=TEST-82-SOFTREBOOT-survive-argv.service \
--property SurviveFinalKillSignal=no \
--property IgnoreOnIsolate=yes \
--property DefaultDependencies=no \
--property After=basic.target \
--property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
--property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
--property SetCredential=preserve:yay \
"$survive_argv"
# shellcheck disable=SC2016
systemd-run --service-type=exec --unit=TEST-82-SOFTREBOOT-survive.service \
--property TemporaryFileSystem="/run /tmp /var" \

View File

@ -6,11 +6,6 @@ set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
if [[ -L $(command -v sleep) ]]; then
# coreutils is built with --enable-single-binary=symlinks, and we cannot rename it.
exit 0
fi
# Make sure the binary name fits into 15 characters
CORE_TEST_BIN="/tmp/test-dump"
CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
@ -35,8 +30,8 @@ sysctl kernel.core_pattern | grep systemd-coredump
# Prepare "fake" binaries for coredumps, so we can properly exercise
# the matching stuff too
cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
cp -vf /usr/lib/systemd/tests/unit-tests/manual/test-sleep "${CORE_TEST_BIN:?}"
cp -vf /usr/lib/systemd/tests/unit-tests/manual/test-sleep "${CORE_TEST_UNPRIV_BIN:?}"
# Simple script that spawns given "fake" binary and then kills it with
# given signal
cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF