1
0
mirror of https://github.com/systemd/systemd synced 2025-11-17 15:54:45 +01:00

Compare commits

...

24 Commits

Author SHA1 Message Date
Lennart Poettering
96d03f8e41
importd: port export-tar code to use the one systemd-dissect already uses (#39405)
Split out of #38728.

(Testcase is part of that PR)
2025-10-30 22:15:34 +01:00
Daan De Meyer
056f437487
core: several cleanups/fixes for fd passing (#39491) 2025-10-30 19:57:16 +01:00
Mike Yuan
a274cb0cff
core/exec-invoke: switch keep_fds to heap allocation
Hardcoding total size of the array is error-prone, especially
considering the exeuctable_fd is added far below, so the '4' is
not entirely obvious. Also we seldomly do VLAs.
2025-10-30 17:47:30 +01:00
Mike Yuan
f70346fb87
core/exec-invoke: store all stashed fds in ExecParameters, incl. OpenFile= ones
Keeping a half-detached counter around brings nothing
but confusion, and leads to fd leak in error paths.
2025-10-30 17:47:30 +01:00
Mike Yuan
f4314f2fbc
core/exec-invoke: do not discard stashed fds when stdio is connected to socket
This makes zero sense. Not sure how it got introduced...
2025-10-30 17:47:30 +01:00
Mike Yuan
d93fff5c0c
core/service: also pass sockets to control processes when stdio is named fd 2025-10-30 17:47:30 +01:00
Mike Yuan
c954830fa9
core/service: only pass socket fds to control processes
If socket is used as stdio, we'd currently imply EXEC_PASS_FDS
and dump the whole set of fds to the control processes. This is
pretty much unexpected and unnecessary though, instead let's
pass only the socket fds.

Yes, this is a compat break, but a relatively minor one I'd
argue. And we can always revisit things if users do complain.
2025-10-30 17:47:29 +01:00
Mike Yuan
d85d98f406
core/execute: merge n_storage_fds and n_extra_fds into stashed_fds
The distinction between fdstore and extra fds is only meaningful
to struct Service. As far as executor is concerned they're just
some fds to pass to the service. Let's just merge it hence,
for the sake of simplicity.
2025-10-30 17:47:29 +01:00
Mike Yuan
f78e7ca7da
core/execute: serialize fd_names only if there're fds to pass 2025-10-30 17:47:29 +01:00
Mike Yuan
3299fb4ba7
core/execute: reorder ExecParameters fields 2025-10-30 17:47:28 +01:00
Mike Yuan
75e05a9880
core/exec-invoke: rename process earlier
This is independent of any other setup stages, and should
happen as early as possible to make comm logged by journald
accurate.
2025-10-30 17:45:34 +01:00
Mike Yuan
edb8fcd813
core/exec-invoke: set exit_status on exec_context_named_iofds() failure 2025-10-30 16:12:14 +01:00
Mike Yuan
04072ad9ed
core/exec-invoke: do not attempt to use fdstore/extra fds for stdio
According to systemd.exec(5):

> The fd:name option connects standard input to a specific, named
> file descriptor provided *by a socket unit*. ...

Currently however we're looking at the whole fd array passed,
fix it.
2025-10-30 16:12:14 +01:00
Mike Yuan
234d8f8bc3
core/exec-invoke: drop redundant stdio_fdname checks
exec_context_fdname() would never return NULL if corresponding
stdio mode is set to named fd.
2025-10-30 16:12:14 +01:00
Mike Yuan
e984e2ca26
core/execute: mark exec_context_fdname() as pure 2025-10-30 16:12:13 +01:00
Mike Yuan
2fd2d8d575
core/execute: remove unused ExecParameters.cgroup_supported
Follow-up for 188286eec6b3af2a13c2ccd86038f74e3d5da72f
2025-10-30 16:12:13 +01:00
Daan De Meyer
a79d2e47fc
mkosi: update fedora commit reference to ea1d871ecd6c2fe063523840c1e4cf9bcf200e32 (#39483) 2025-10-30 16:07:22 +01:00
Daan De Meyer
5cabeed80b run0: Add --empower
--empower gives full privileges to a non-root user. Currently this
includes all capabilities but we leave the option open to add more
privileges via this option in the future.

Why is this useful? When running privileged development or debugging
commands from your home directory (think bpftrace, strace and such),
you want any files written by these tools to be owned by your current
user, and not by the root user. run0 --empower will allow you to run
all privileged operations (assuming the tools check for capabilities
and not UIDs), while any files written by the tools will still be owned
by the current user.
2025-10-30 15:28:36 +01:00
Lennart Poettering
19bf12bff3 pcrlock: don't lock PCR 12 by default
This creates a chicken-and-egg problem: we stuff the pcrlock policy into
a credential in the ESP, but credentials get measured into PCR 12, hence
PCR 12 is both input and output of the pcrlock logic, which makes
impossible to calculate.

Let's drop PCR 12 for now.

(We might want to pass the policy some other way one day, to avoid this,
but that's something for another day.)

Note that this still allows locking to PCR12 if people want to (for
example because they don't need this for the rootfs, and hence need no
cred passing via the ESP), this hence only changes the default, nothing
more.

Fixes: #33546
2025-10-30 14:12:41 +00:00
Daan De Meyer
b1856a6c4a analyze: Add shell completion for dlopen-metadata 2025-10-30 14:11:28 +00:00
Daan De Meyer
16f4bc90d2 mkosi: update fedora commit reference to ea1d871ecd6c2fe063523840c1e4cf9bcf200e32
* ea1d871ecd Add missing networkd socket units
* b76b5da2e6 Merge #214 `Drop backwards compat logic from integration tests script`
* 7208fa2b1b Require systemd-rpm-macros for build
* 2e1a6c7474 Require python3-zstandard in ELN
* 79c9db1bc8 Require systemd-libs and systemd-shared to be in the same version
* db38445a7e Drop two patches with workaround (selinux, kernel)
* 593a204189 Version 258.1
* a3e9e27982 Change '%{systemd}' to systemd in Conflicts/Provides/Requires/Recommends
* 88877a4184 Require systemd-networkd and systemd-udev to be in the same version
* 8a446daec7 Version 258 💝
* cceac93491 Pre-create /etc/userdb directory
* b442086d5f Version 258~rc4
* 327e54e421 Add to patch to create userdb root directory with correct label
* 2289d65726 Fix unit name in scriptlet
* 5acde9f1fd Add workaround patch to hopefully pass podman CI tests
* 1f5ed0da1f Version 258~rc3
* 50936458a7 obs: move recipe files in place
* 1bdb4efe40 obs: switch to xz for compression
* be7a4d0863 Version 258~rc2
* 2ace9416e8 obs: also use version with tilde for Source0
* 8d1645af75 Use again %{version} when building in OBS
* 98cc5fd91a Version 258~rc1
* ed7d2f1132 Add "test" that LTO effectively removes unused code from shared lib
* 40b38a04d2 Build docs on 64-bit architectures only
* 5d30fd3b26 Version 257.7
2025-10-30 14:56:41 +01:00
Daan De Meyer
a835537f75 mkosi: Disable lto feature of systemd spec
This makes sure the systemd spec doesn't check if LTO is working as
expected when it is actually disabled.
2025-10-30 14:56:04 +01:00
Lennart Poettering
3780a0b446 export-tar: port to common libarchive tar generation code 2025-10-29 10:09:44 +01:00
Lennart Poettering
faaed501e0 dissect: move tar make code into tar-util.[ch] and make it generic
That way we can later use it for importd's "export" verb
2025-10-29 10:09:44 +01:00
28 changed files with 413 additions and 323 deletions

View File

@ -39,7 +39,7 @@ jobs:
trigger: pull_request
fmf_url: https://src.fedoraproject.org/rpms/systemd
# This is automatically updated by tools/fetch-distro.py --update fedora
fmf_ref: 7de88c66bdc26920db570e67ef74e579f8461d9c
fmf_ref: ea1d871ecd6c2fe063523840c1e4cf9bcf200e32
targets:
- fedora-rawhide-x86_64
# testing-farm in the Fedora repository is explicitly configured to use testing-farm bare metal runners as

View File

@ -139,8 +139,8 @@
<term><option>-g</option></term>
<listitem><para>Switches to the specified user/group. If not specified defaults to
<literal>root</literal>, unless <option>--area=</option> is used (see below), in which case this
defaults to the invoking user.</para>
<literal>root</literal>, unless <option>--area=</option> or <option>--empower</option> are used (see
below), in which case this defaults to the invoking user.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
@ -290,6 +290,17 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--empower</option></term>
<listitem><para>If specified, run0 will elevate the privileges of the selected user (using
<option>--user=</option>) or the current user if no user is explicitly selected. Currently this means
we give the user all available capabilities, but other privileges may be granted in the future as
well when using this option.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>

View File

@ -463,7 +463,7 @@
<para>If used with <command>predict</command> and <command>make-policy</command> this will override
which PCRs to include in the prediction and policy. If unspecified this defaults to PCRs 0-5, 7,
11-15. Note that these commands will not include any PCRs in the prediction/policy (even if specified
11, 13-15. Note that these commands will not include any PCRs in the prediction/policy (even if specified
explicitly) if there are measurements in the event log that do not match the current PCR value, or
there are unrecognized measurements in the event log, or components define measurements not seen in
the event log.</para>

View File

@ -4,5 +4,5 @@
Environment=
GIT_URL=https://src.fedoraproject.org/rpms/systemd.git
GIT_BRANCH=rawhide
GIT_COMMIT=7de88c66bdc26920db570e67ef74e579f8461d9c
GIT_COMMIT=ea1d871ecd6c2fe063523840c1e4cf9bcf200e32
PKG_SUBDIR=fedora

View File

@ -82,6 +82,7 @@ CXX_LD="$( ((LLVM)) && echo lld)" \
--noprep \
--build-in-place \
--with upstream \
--without lto \
$( ((WITH_TESTS)) || echo "--nocheck") \
$( ((WITH_DOCS)) || echo "--without=docs") \
--define "_topdir /var/tmp" \

View File

@ -37,7 +37,7 @@ _run0() {
--machine --unit --property --description --slice -u --user -g --group --nice -D --chdir
--setenv --background
)
local OPTS="${opts_with_values[*]} -h --help -V --version --no-ask-password --slice-inherit"
local OPTS="${opts_with_values[*]} -h --help -V --version --no-ask-password --slice-inherit --empower"
local i
for (( i=1; i <= COMP_CWORD; i++ )); do

View File

@ -78,6 +78,7 @@ _systemd_analyze() {
[SECURITY]='security'
[CONDITION]='condition'
[INSPECT_ELF]='inspect-elf'
[DLOPEN_METADATA]='dlopen-metadata'
[PLOT]='plot'
[ARCHITECTURES]='architectures'
[FDSTORE]='fdstore'
@ -212,6 +213,14 @@ _systemd_analyze() {
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[DLOPEN_METADATA]}; then
if [[ $cur = -* ]]; then
comps='--help --version --json=off --json=pretty --json=short'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[PLOT]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager --json=off --json=pretty --json=short --table --no-legend --scale-svg --detailed'

View File

@ -52,6 +52,7 @@ local -a args=(
'--machine=[Execute the operation on a local container]:machine:_sd_machines'
{-h,--help}'[Show the help text and exit]'
'--version[Print a short version string and exit]'
'--empower[Give privileges to selected or current user]'
)
_arguments -S $args '*:: :{_normal -p $service}'

View File

@ -79,6 +79,7 @@
'timespan:Parse a systemd syntax timespan'
'security:Analyze security settings of a service'
'inspect-elf:Parse and print ELF package metadata'
'dlopen-metadata:Parse and print ELF dlopen metadata'
'has-tpm2:Report whether TPM2 support is available'
'transient-settings:List transient settings for unit types'
# log-level, log-target, service-watchdogs have been deprecated

View File

@ -1277,7 +1277,6 @@ static int setup_pam(
uid_t uid,
gid_t gid,
char ***env, /* updated on success */
const int fds[], size_t n_fds,
bool needs_sandboxing,
int exec_fd) {
@ -1307,7 +1306,6 @@ static int setup_pam(
assert(user);
assert(uid_is_valid(uid));
assert(gid_is_valid(gid));
assert(fds || n_fds == 0);
assert(env);
/* We set up PAM in the parent process, then fork. The child will then stay around until killed via
@ -1393,7 +1391,7 @@ static int setup_pam(
/* Make sure we don't keep open the passed fds in this child. We assume that otherwise only
* those fds are open here that have been opened by PAM. */
(void) close_many(fds, n_fds);
(void) close_many(params->fds, params->n_socket_fds + params->n_stashed_fds);
/* Also close the 'exec_fd' in the child, since the service manager waits for the EOF induced
* by the execve() to wait for completion, and if we'd keep the fd open here in the child
@ -1992,7 +1990,6 @@ static int build_environment(
const ExecContext *c,
const ExecParameters *p,
const CGroupContext *cgroup_context,
size_t n_fds,
const char *home,
const char *username,
const char *shell,
@ -2017,7 +2014,7 @@ static int build_environment(
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
if (p->n_socket_fds + p->n_stashed_fds > 0) {
_cleanup_free_ char *joined = NULL;
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid_cached()) < 0)
@ -2031,7 +2028,7 @@ static int build_environment(
our_env[n_env++] = x;
}
if (asprintf(&x, "LISTEN_FDS=%zu", n_fds) < 0)
if (asprintf(&x, "LISTEN_FDS=%zu", p->n_socket_fds + p->n_stashed_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@ -4327,12 +4324,11 @@ static int exec_context_cpu_affinity_from_numa(const ExecContext *c, CPUSet *ret
return 0;
}
static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int *fd) {
static int add_shifted_fd(int **fds, size_t *n_fds, int *fd) {
int r;
assert(fds);
assert(n_fds);
assert(*n_fds < fds_size);
assert(fd);
if (*fd < 0)
@ -4349,7 +4345,10 @@ static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int *fd) {
close_and_replace(*fd, r);
}
fds[(*n_fds)++] = *fd;
if (!GREEDY_REALLOC(*fds, *n_fds + 1))
return -ENOMEM;
(*fds)[(*n_fds)++] = *fd;
return 1;
}
@ -4426,9 +4425,8 @@ static int get_open_file_fd(const OpenFile *of) {
return TAKE_FD(fd);
}
static int collect_open_file_fds(ExecParameters *p, size_t *n_fds) {
static int collect_open_file_fds(ExecParameters *p) {
assert(p);
assert(n_fds);
LIST_FOREACH(open_files, of, p->open_files) {
_cleanup_close_ int fd = -EBADF;
@ -4446,13 +4444,13 @@ static int collect_open_file_fds(ExecParameters *p, size_t *n_fds) {
return log_error_errno(fd, "Failed to get OpenFile= file descriptor for '%s': %m", of->path);
}
if (!GREEDY_REALLOC(p->fds, *n_fds + 1))
if (!GREEDY_REALLOC(p->fds, p->n_stashed_fds + 1))
return log_oom();
if (strv_extend(&p->fd_names, of->fdname) < 0)
return log_oom();
p->fds[(*n_fds)++] = TAKE_FD(fd);
p->fds[p->n_stashed_fds++] = TAKE_FD(fd);
}
return 0;
@ -4745,9 +4743,8 @@ static int exec_context_named_iofds(
const ExecParameters *p,
int named_iofds[static 3]) {
size_t targets;
const char *stdio_fdname[3];
size_t n_fds;
size_t targets;
assert(c);
assert(p);
@ -4760,12 +4757,11 @@ static int exec_context_named_iofds(
for (size_t i = 0; i < 3; i++)
stdio_fdname[i] = exec_context_fdname(c, i);
n_fds = p->n_storage_fds + p->n_socket_fds + p->n_extra_fds;
for (size_t i = 0; i < n_fds && targets > 0; i++)
/* 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++)
if (named_iofds[STDIN_FILENO] < 0 &&
c->std_input == EXEC_INPUT_NAMED_FD &&
stdio_fdname[STDIN_FILENO] &&
streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) {
named_iofds[STDIN_FILENO] = p->fds[i];
@ -4773,7 +4769,6 @@ static int exec_context_named_iofds(
} else if (named_iofds[STDOUT_FILENO] < 0 &&
c->std_output == EXEC_OUTPUT_NAMED_FD &&
stdio_fdname[STDOUT_FILENO] &&
streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) {
named_iofds[STDOUT_FILENO] = p->fds[i];
@ -4781,7 +4776,6 @@ static int exec_context_named_iofds(
} else if (named_iofds[STDERR_FILENO] < 0 &&
c->std_error == EXEC_OUTPUT_NAMED_FD &&
stdio_fdname[STDERR_FILENO] &&
streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) {
named_iofds[STDERR_FILENO] = p->fds[i];
@ -5032,14 +5026,11 @@ int exec_invoke(
gid_t saved_gid = getgid();
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
size_t n_fds, /* fds to pass to the child */
n_keep_fds; /* total number of fds not to close */
int secure_bits;
_cleanup_free_ gid_t *gids = NULL, *gids_after_pam = NULL;
int ngids = 0, ngids_after_pam = 0;
int socket_fd = -EBADF, named_iofds[3] = EBADF_TRIPLET;
_cleanup_close_ int bpffs_socket_fd = -EBADF, bpffs_errno_pipe = -EBADF;
size_t n_storage_fds, n_socket_fds, n_extra_fds;
int named_iofds[3] = EBADF_TRIPLET;
_cleanup_close_ int socket_fd = -EBADF, bpffs_socket_fd = -EBADF, bpffs_errno_pipe = -EBADF;
_cleanup_(pidref_done_sigkill_wait) PidRef bpffs_pidref = PIDREF_NULL;
assert(command);
@ -5057,30 +5048,30 @@ int exec_invoke(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid command line arguments.");
}
rename_process_from_path(command->path);
if (context->std_input == EXEC_INPUT_SOCKET ||
context->std_output == EXEC_OUTPUT_SOCKET ||
context->std_error == EXEC_OUTPUT_SOCKET) {
if (params->n_socket_fds > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got more than one socket.");
if (params->n_socket_fds != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected exactly one socket, got %zu.",
params->n_socket_fds);
if (params->n_socket_fds == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got no socket.");
socket_fd = TAKE_FD(params->fds[0]);
free(params->fd_names[0]);
params->n_socket_fds = 0;
socket_fd = params->fds[0];
n_storage_fds = n_socket_fds = n_extra_fds = 0;
} else {
n_socket_fds = params->n_socket_fds;
n_storage_fds = params->n_storage_fds;
n_extra_fds = params->n_extra_fds;
memmove(params->fds, params->fds + 1, params->n_stashed_fds * sizeof(int));
memmove(params->fd_names, params->fd_names + 1, params->n_stashed_fds * sizeof(char*));
params->fd_names[params->n_stashed_fds] = NULL;
}
n_fds = n_socket_fds + n_storage_fds + n_extra_fds;
r = exec_context_named_iofds(context, params, named_iofds);
if (r < 0)
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to load a named file descriptor: %m");
rename_process_from_path(command->path);
}
/* We reset exactly these signals, since they are the only ones we set to SIG_IGN in the main
* daemon. All others we leave untouched because we set them to SIG_DFL or a valid handler initially,
@ -5112,30 +5103,33 @@ int exec_invoke(
/* In case anything used libc syslog(), close this here, too */
closelog();
r = collect_open_file_fds(params, &n_fds);
r = collect_open_file_fds(params);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to get OpenFile= file descriptors: %m");
}
int keep_fds[n_fds + 4];
memcpy_safe(keep_fds, params->fds, n_fds * sizeof(int));
n_keep_fds = n_fds;
size_t n_keep_fds = params->n_socket_fds + params->n_stashed_fds;
_cleanup_free_ int *keep_fds = newdup(int, params->fds, n_keep_fds);
if (!keep_fds) {
*exit_status = EXIT_MEMORY;
return log_oom();
}
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &params->exec_fd);
r = add_shifted_fd(&keep_fds, &n_keep_fds, &params->exec_fd);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to collect shifted fd: %m");
}
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &params->handoff_timestamp_fd);
r = add_shifted_fd(&keep_fds, &n_keep_fds, &params->handoff_timestamp_fd);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to collect shifted fd: %m");
}
#if HAVE_LIBBPF
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &params->bpf_restrict_fs_map_fd);
r = add_shifted_fd(&keep_fds, &n_keep_fds, &params->bpf_restrict_fs_map_fd);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to collect shifted fd: %m");
@ -5618,7 +5612,6 @@ int exec_invoke(
context,
params,
cgroup_context,
n_fds,
pwent_home,
username,
shell,
@ -5732,7 +5725,7 @@ int exec_invoke(
/* All fds passed in the fds array will be closed in the pam child process. */
r = setup_pam(context, cgroup_context, params, username, uid, gid, &accum_env,
params->fds, n_fds, needs_sandboxing, params->exec_fd);
needs_sandboxing, params->exec_fd);
if (r < 0) {
*exit_status = EXIT_PAM;
return log_error_errno(r, "Failed to set up PAM session: %m");
@ -5925,7 +5918,7 @@ int exec_invoke(
return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
}
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &executable_fd);
r = add_shifted_fd(&keep_fds, &n_keep_fds, &executable_fd);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to collect shifted fd: %m");
@ -5966,9 +5959,10 @@ int exec_invoke(
r = close_all_fds(keep_fds, n_keep_fds);
if (r >= 0)
r = pack_fds(params->fds, n_fds);
r = pack_fds(params->fds, params->n_socket_fds + params->n_stashed_fds);
if (r >= 0)
r = flag_fds(params->fds, n_socket_fds, n_fds, context->non_blocking);
r = flag_fds(params->fds, params->n_socket_fds, params->n_socket_fds + params->n_stashed_fds,
context->non_blocking);
if (r < 0) {
*exit_status = EXIT_FDS;
return log_error_errno(r, "Failed to adjust passed file descriptors: %m");

View File

@ -1090,26 +1090,20 @@ static int exec_parameters_serialize(const ExecParameters *p, const ExecContext
return r;
}
if (p->n_storage_fds > 0) {
r = serialize_item_format(f, "exec-parameters-n-storage-fds", "%zu", p->n_storage_fds);
if (p->n_stashed_fds > 0) {
r = serialize_item_format(f, "exec-parameters-n-stashed-fds", "%zu", p->n_stashed_fds);
if (r < 0)
return r;
}
if (p->n_extra_fds > 0) {
r = serialize_item_format(f, "exec-parameters-n-extra-fds", "%zu", p->n_extra_fds);
r = serialize_fd_many(f, fds, "exec-parameters-fds", p->fds, p->n_socket_fds + p->n_stashed_fds);
if (r < 0)
return r;
}
r = serialize_fd_many(f, fds, "exec-parameters-fds", p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
if (r < 0)
return r;
}
r = serialize_strv(f, "exec-parameters-fd-names", p->fd_names);
if (r < 0)
return r;
}
if (p->flags != 0) {
r = serialize_item_format(f, "exec-parameters-flags", "%u", (unsigned) p->flags);
@ -1121,12 +1115,6 @@ static int exec_parameters_serialize(const ExecParameters *p, const ExecContext
if (r < 0)
return r;
if (p->cgroup_supported != 0) {
r = serialize_item_format(f, "exec-parameters-cgroup-supported", "%u", (unsigned) p->cgroup_supported);
if (r < 0)
return r;
}
r = serialize_item(f, "exec-parameters-cgroup-path", p->cgroup_path);
if (r < 0)
return r;
@ -1289,47 +1277,37 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
if (p->n_socket_fds > nr_open)
return -EINVAL; /* too many, someone is playing games with us */
} else if ((val = startswith(l, "exec-parameters-n-storage-fds="))) {
} else if ((val = startswith(l, "exec-parameters-n-stashed-fds="))) {
if (p->fds)
return -EINVAL; /* Already received */
r = safe_atozu(val, &p->n_storage_fds);
r = safe_atozu(val, &p->n_stashed_fds);
if (r < 0)
return r;
if (p->n_storage_fds > nr_open)
return -EINVAL; /* too many, someone is playing games with us */
} else if ((val = startswith(l, "exec-parameters-n-extra-fds="))) {
if (p->fds)
return -EINVAL; /* Already received */
r = safe_atozu(val, &p->n_extra_fds);
if (r < 0)
return r;
if (p->n_extra_fds > nr_open)
if (p->n_stashed_fds > nr_open)
return -EINVAL; /* too many, someone is playing games with us */
} else if ((val = startswith(l, "exec-parameters-fds="))) {
if (p->n_socket_fds + p->n_storage_fds + p->n_extra_fds == 0)
if (p->n_socket_fds + p->n_stashed_fds == 0)
return log_warning_errno(
SYNTHETIC_ERRNO(EINVAL),
"Got exec-parameters-fds= without "
"prior exec-parameters-n-socket-fds= or exec-parameters-n-storage-fds= or exec-parameters-n-extra-fds=");
if (p->n_socket_fds + p->n_storage_fds + p->n_extra_fds > nr_open)
"prior exec-parameters-n-socket-fds= or exec-parameters-n-stashed-fds=");
if (p->n_socket_fds + p->n_stashed_fds > nr_open)
return -EINVAL; /* too many, someone is playing games with us */
if (p->fds)
return -EINVAL; /* duplicated */
p->fds = new(int, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
p->fds = new(int, p->n_socket_fds + p->n_stashed_fds);
if (!p->fds)
return log_oom_debug();
/* Ensure we don't leave any FD uninitialized on error, it makes the fuzzer sad */
FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds)
FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_stashed_fds)
*i = -EBADF;
r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds, p->fds);
r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_stashed_fds, p->fds);
if (r < 0)
continue;
@ -1350,13 +1328,6 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
return r;
p->selinux_context_net = r;
} else if ((val = startswith(l, "exec-parameters-cgroup-supported="))) {
unsigned cgroup_supported;
r = safe_atou(val, &cgroup_supported);
if (r < 0)
return r;
p->cgroup_supported = cgroup_supported;
} else if ((val = startswith(l, "exec-parameters-cgroup-path="))) {
r = free_and_strdup(&p->cgroup_path, val);
if (r < 0)
@ -1534,9 +1505,9 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
log_warning("Failed to parse serialized line, ignoring: %s", l);
}
/* Bail out if we got exec-parameters-n-{socket/storage}-fds= but no corresponding
/* Bail out if we got exec-parameters-n-{socket/stashed}-fds= but no corresponding
* exec-parameters-fds= */
if (p->n_socket_fds + p->n_storage_fds > 0 && !p->fds)
if (p->n_socket_fds + p->n_stashed_fds > 0 && !p->fds)
return -EINVAL;
return 0;

View File

@ -486,8 +486,8 @@ int exec_spawn(
assert(command);
assert(context);
assert(params);
assert(!params->fds || FLAGS_SET(params->flags, EXEC_PASS_FDS));
assert(params->fds || (params->n_socket_fds + params->n_storage_fds + params->n_extra_fds == 0));
assert(params->fds || (params->n_socket_fds + params->n_stashed_fds == 0 && !params->fd_names));
assert(params->n_stashed_fds == 0 || FLAGS_SET(params->flags, EXEC_PASS_FDS));
assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
assert(ret);
@ -1048,7 +1048,6 @@ void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
"%sRuntimeScope: %s\n"
"%sExecFlags: %u\n"
"%sSELinuxContextNetwork: %s\n"
"%sCgroupSupportedMask: %u\n"
"%sCgroupPath: %s\n"
"%sCrededentialsDirectory: %s\n"
"%sEncryptedCredentialsDirectory: %s\n"
@ -1061,7 +1060,6 @@ void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
prefix, runtime_scope_to_string(p->runtime_scope),
prefix, p->flags,
prefix, yes_no(p->selinux_context_net),
prefix, p->cgroup_supported,
prefix, p->cgroup_path,
prefix, strempty(p->received_credentials_directory),
prefix, strempty(p->received_encrypted_credentials_directory),
@ -2828,7 +2826,7 @@ void exec_params_deep_clear(ExecParameters *p) {
* to be fully cleaned up to make sanitizers and analyzers happy, as opposed as the shallow clean
* function above. */
close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
p->cgroup_path = mfree(p->cgroup_path);

View File

@ -391,18 +391,16 @@ typedef enum ExecFlags {
typedef struct ExecParameters {
RuntimeScope runtime_scope;
ExecFlags flags;
char **environment;
char **files_env;
int *fds;
char **fd_names;
size_t n_socket_fds;
size_t n_storage_fds;
size_t n_extra_fds;
size_t n_stashed_fds;
ExecFlags flags;
bool selinux_context_net:1;
CGroupMask cgroup_supported;
char *cgroup_path;
uint64_t cgroup_id;
@ -430,7 +428,6 @@ typedef struct ExecParameters {
char *fallback_smack_process_label;
char **files_env;
int user_lookup_fd;
int handoff_timestamp_fd;
int pidref_transport_fd;
@ -443,6 +440,7 @@ typedef struct ExecParameters {
char invocation_id_string[SD_ID128_STRING_MAX];
bool debug_invocation;
bool selinux_context_net;
} ExecParameters;
#define EXEC_PARAMETERS_INIT(_flags) \
@ -519,7 +517,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_root);
int exec_context_destroy_mount_ns_dir(Unit *u);
const char* exec_context_fdname(const ExecContext *c, int fd_index);
const char* exec_context_fdname(const ExecContext *c, int fd_index) _pure_;
bool exec_context_may_touch_console(const ExecContext *c);
bool exec_context_maintains_privileges(const ExecContext *c);
@ -585,8 +583,8 @@ void exec_runtime_clear(ExecRuntime *rt);
int exec_params_needs_control_subcgroup(const ExecParameters *params);
int exec_params_get_cgroup_path(const ExecParameters *params, const CGroupContext *c, const char *prefix, char **ret);
void exec_params_shallow_clear(ExecParameters *p);
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix);
void exec_params_deep_clear(ExecParameters *p);
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix);
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c);

View File

@ -62,8 +62,8 @@ static void exec_fuzz_one(FILE *f, FDSet *fdset) {
params.user_lookup_fd = -EBADF;
params.bpf_restrict_fs_map_fd = -EBADF;
if (!params.fds)
params.n_socket_fds = params.n_storage_fds = params.n_extra_fds = 0;
for (size_t i = 0; params.fds && i < params.n_socket_fds + params.n_storage_fds + params.n_extra_fds; i++)
params.n_socket_fds = params.n_stashed_fds = 0;
for (size_t i = 0; params.fds && i < params.n_socket_fds + params.n_stashed_fds; i++)
params.fds[i] = -EBADF;
exec_command_done_array(&command, /* n= */ 1);

View File

@ -1451,8 +1451,7 @@ static int service_collect_fds(
int **fds,
char ***fd_names,
size_t *n_socket_fds,
size_t *n_storage_fds,
size_t *n_extra_fds) {
size_t *n_stashed_fds) {
_cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
@ -1463,8 +1462,6 @@ static int service_collect_fds(
assert(fds);
assert(fd_names);
assert(n_socket_fds);
assert(n_storage_fds);
assert(n_extra_fds);
if (s->socket_fd >= 0) {
Socket *sock = ASSERT_PTR(SOCKET(UNIT_DEREF(s->accept_socket)));
@ -1511,7 +1508,7 @@ static int service_collect_fds(
}
}
if (s->n_fd_store + s->n_extra_fds > 0) {
if (n_stashed_fds && s->n_fd_store + s->n_extra_fds > 0) {
int *t = reallocarray(rfds, rn_socket_fds + s->n_fd_store + s->n_extra_fds, sizeof(int));
if (!t)
return -ENOMEM;
@ -1548,8 +1545,8 @@ static int service_collect_fds(
*fds = TAKE_PTR(rfds);
*fd_names = TAKE_PTR(rfd_names);
*n_socket_fds = rn_socket_fds;
*n_storage_fds = s->n_fd_store;
*n_extra_fds = s->n_extra_fds;
if (n_stashed_fds)
*n_stashed_fds = s->n_fd_store + s->n_extra_fds;
return 0;
}
@ -1733,25 +1730,32 @@ static int service_spawn_internal(
exec_params.flags &= ~EXEC_APPLY_CHROOT;
}
if (FLAGS_SET(exec_params.flags, EXEC_PASS_FDS) ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
if (FLAGS_SET(exec_params.flags, EXEC_PASS_FDS)) {
r = service_collect_fds(s,
&exec_params.fds,
&exec_params.fd_names,
&exec_params.n_socket_fds,
&exec_params.n_stashed_fds);
if (r < 0)
return r;
log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_stashed_fds);
exec_params.open_files = s->open_files;
} else if (IN_SET(s->exec_context.std_input, EXEC_INPUT_SOCKET, EXEC_INPUT_NAMED_FD) ||
IN_SET(s->exec_context.std_output, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD) ||
IN_SET(s->exec_context.std_error, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD)) {
r = service_collect_fds(s,
&exec_params.fds,
&exec_params.fd_names,
&exec_params.n_socket_fds,
&exec_params.n_storage_fds,
&exec_params.n_extra_fds);
/* n_stashed_fds = */ NULL);
if (r < 0)
return r;
exec_params.open_files = s->open_files;
exec_params.flags |= EXEC_PASS_FDS;
log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds + exec_params.n_extra_fds);
log_unit_debug(UNIT(s), "Passing %zu sockets to service", exec_params.n_socket_fds);
}
if (!FLAGS_SET(exec_params.flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {

View File

@ -2001,7 +2001,6 @@ static int socket_spawn(Socket *s, ExecCommand *c, PidRef *ret_pid) {
if (r < 0)
return r;
exec_params.flags |= EXEC_PASS_FDS;
exec_params.fds = TAKE_PTR(fds);
exec_params.fd_names = TAKE_PTR(fd_names);
exec_params.n_socket_fds = n_fds;

View File

@ -5506,7 +5506,6 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
if (r < 0)
return r;
p->cgroup_supported = u->manager->cgroup_supported;
p->prefix = u->manager->prefix;
SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(u->manager));

View File

@ -53,6 +53,7 @@
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "tar-util.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "uid-classification.h"
@ -1432,109 +1433,6 @@ static int mtree_print_item(
return RECURSE_DIR_CONTINUE;
}
#if HAVE_LIBARCHIVE
static int archive_item(
RecurseDirEvent event,
const char *path,
int dir_fd,
int inode_fd,
const struct dirent *de,
const struct statx *sx,
void *userdata) {
struct archive *a = ASSERT_PTR(userdata);
int r;
assert(path);
if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
return RECURSE_DIR_CONTINUE;
assert(inode_fd >= 0);
assert(sx);
log_debug("Archiving %s\n", path);
_cleanup_(archive_entry_freep) struct archive_entry *entry = NULL;
entry = sym_archive_entry_new();
if (!entry)
return log_oom();
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
sym_archive_entry_set_pathname(entry, path);
sym_archive_entry_set_filetype(entry, sx->stx_mode);
if (!S_ISLNK(sx->stx_mode))
sym_archive_entry_set_perm(entry, sx->stx_mode);
if (FLAGS_SET(sx->stx_mask, STATX_UID))
sym_archive_entry_set_uid(entry, sx->stx_uid);
if (FLAGS_SET(sx->stx_mask, STATX_GID))
sym_archive_entry_set_gid(entry, sx->stx_gid);
if (S_ISREG(sx->stx_mode)) {
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unable to determine file size of '%s'.", path);
sym_archive_entry_set_size(entry, sx->stx_size);
}
if (S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) {
sym_archive_entry_set_rdevmajor(entry, sx->stx_rdev_major);
sym_archive_entry_set_rdevminor(entry, sx->stx_rdev_minor);
}
/* We care about a modicum of reproducibility here, hence we don't save atime/btime here */
if (FLAGS_SET(sx->stx_mask, STATX_MTIME))
sym_archive_entry_set_mtime(entry, sx->stx_mtime.tv_sec, sx->stx_mtime.tv_nsec);
if (FLAGS_SET(sx->stx_mask, STATX_CTIME))
sym_archive_entry_set_ctime(entry, sx->stx_ctime.tv_sec, sx->stx_ctime.tv_nsec);
if (S_ISLNK(sx->stx_mode)) {
_cleanup_free_ char *s = NULL;
assert(dir_fd >= 0);
assert(de);
r = readlinkat_malloc(dir_fd, de->d_name, &s);
if (r < 0)
return log_error_errno(r, "Failed to read symlink target of '%s': %m", path);
sym_archive_entry_set_symlink(entry, s);
}
if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
if (S_ISREG(sx->stx_mode)) {
_cleanup_close_ int data_fd = -EBADF;
/* Convert the O_PATH fd in a proper fd */
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
if (data_fd < 0)
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
for (;;) {
char buffer[64*1024];
ssize_t l;
l = read(data_fd, buffer, sizeof(buffer));
if (l < 0)
return log_error_errno(errno, "Failed to read '%s': %m", path);
if (l == 0)
break;
la_ssize_t k;
k = sym_archive_write_data(a, buffer, l);
if (k < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
}
}
return RECURSE_DIR_CONTINUE;
}
#endif
static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d, int userns_fd) {
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
_cleanup_free_ char *t = NULL;
@ -1736,56 +1634,34 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
case ACTION_MAKE_ARCHIVE: {
#if HAVE_LIBARCHIVE
_cleanup_(unlink_and_freep) char *tar = NULL;
_cleanup_close_ int dfd = -EBADF;
_cleanup_fclose_ FILE *f = NULL;
dfd = open(root, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
if (dfd < 0)
return log_error_errno(errno, "Failed to open mount directory: %m");
_cleanup_(archive_write_freep) struct archive *a = sym_archive_write_new();
if (!a)
return log_oom();
if (arg_target)
r = sym_archive_write_set_format_filter_by_ext(a, arg_target);
else
r = sym_archive_write_set_format_gnutar(a);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
_cleanup_(unlink_and_freep) char *tar = NULL;
_cleanup_close_ int tmp_fd = -EBADF;
int output_fd;
if (arg_target) {
r = fopen_tmpfile_linkable(arg_target, O_WRONLY|O_CLOEXEC, &tar, &f);
if (r < 0)
return log_error_errno(r, "Failed to create target file '%s': %m", arg_target);
tmp_fd = open_tmpfile_linkable(arg_target, O_WRONLY|O_CLOEXEC, &tar);
if (tmp_fd < 0)
return log_error_errno(tmp_fd, "Failed to create target file '%s': %m", arg_target);
r = sym_archive_write_open_FILE(a, f);
output_fd = tmp_fd;
} else {
if (isatty_safe(STDOUT_FILENO))
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Refusing to write archive to TTY.");
r = sym_archive_write_open_fd(a, STDOUT_FILENO);
output_fd = STDOUT_FILENO;
}
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
r = recurse_dir(dfd,
".",
STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
UINT_MAX,
RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
archive_item,
a);
r = tar_c(dfd, output_fd, arg_target, /* flags= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to make archive: %m");
r = sym_archive_write_close(a);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to finish writing archive: %s", sym_archive_error_string(a));
return r;
if (arg_target) {
r = flink_tmpfile(f, tar, arg_target, LINK_TMPFILE_REPLACE);
r = link_tmpfile(tmp_fd, tar, arg_target, LINK_TMPFILE_REPLACE);
if (r < 0)
return log_error_errno(r, "Failed to move archive file into place: %m");

View File

@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "btrfs-util.h"
#include "dissect-image.h"
#include "export-tar.h"
#include "fd-util.h"
#include "format-util.h"
@ -27,11 +28,15 @@ typedef struct TarExport {
TarExportFinished on_finished;
void *userdata;
ImportFlags flags;
char *path;
char *temp_path;
int output_fd;
int tar_fd;
int output_fd; /* compressed tar file in the fs */
int tar_fd; /* uncompressed tar stream coming from child doing the libarchive loop */
int tree_fd; /* directory fd of the tree to set up */
int userns_fd;
ImportCompress compress;
@ -98,6 +103,8 @@ int tar_export_new(
*e = (TarExport) {
.output_fd = -EBADF,
.tar_fd = -EBADF,
.tree_fd = -EBADF,
.userns_fd = -EBADF,
.on_finished = on_finished,
.userdata = userdata,
.quota_referenced = UINT64_MAX,
@ -271,7 +278,13 @@ static int tar_export_on_defer(sd_event_source *s, void *userdata) {
return tar_export_process(i);
}
int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
int tar_export_start(
TarExport *e,
const char *path,
int fd,
ImportCompressType compress,
ImportFlags flags) {
_cleanup_close_ int sfd = -EBADF;
int r;
@ -299,6 +312,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
if (r < 0)
return r;
e->flags = flags;
e->quota_referenced = UINT64_MAX;
if (btrfs_might_be_subvol(&e->st)) {
@ -337,7 +351,33 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
if (r < 0)
return r;
e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
const char *p = e->temp_path ?: e->path;
if (FLAGS_SET(e->flags, IMPORT_FOREIGN_UID)) {
r = import_make_foreign_userns(&e->userns_fd);
if (r < 0)
return r;
_cleanup_close_ int directory_fd = open(p, O_DIRECTORY|O_CLOEXEC|O_PATH);
if (directory_fd < 0)
return log_error_errno(r, "Failed to open '%s': %m", p);
_cleanup_close_ int mapped_fd = -EBADF;
r = mountfsd_mount_directory_fd(directory_fd, e->userns_fd, DISSECT_IMAGE_FOREIGN_UID, &mapped_fd);
if (r < 0)
return r;
/* Drop O_PATH */
e->tree_fd = fd_reopen(mapped_fd, O_DIRECTORY|O_CLOEXEC);
if (e->tree_fd < 0)
return log_error_errno(errno, "Failed to re-open mapped '%s': %m", p);
} else {
e->tree_fd = open(p, O_DIRECTORY|O_CLOEXEC);
if (e->tree_fd < 0)
return log_error_errno(errno, "Failed to open '%s': %m", p);
}
e->tar_fd = import_fork_tar_c(e->tree_fd, e->userns_fd, &e->tar_pid);
if (e->tar_fd < 0) {
e->output_event_source = sd_event_source_unref(e->output_event_source);
return e->tar_fd;

View File

@ -1,8 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
#include "import-common.h"
#include "import-compress.h"
#include "shared-forward.h"
typedef struct TarExport TarExport;
@ -13,4 +14,4 @@ TarExport* tar_export_unref(TarExport *export);
DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref);
int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress);
int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress, ImportFlags flags);

View File

@ -21,6 +21,7 @@
#include "terminal-util.h"
#include "verbs.h"
static ImportFlags arg_import_flags = 0;
static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
static ImageClass arg_class = IMAGE_MACHINE;
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
@ -111,7 +112,12 @@ static int export_tar(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate exporter: %m");
r = tar_export_start(export, local, fd, arg_compress);
r = tar_export_start(
export,
local,
fd,
arg_compress,
arg_import_flags & IMPORT_FLAGS_MASK_TAR);
if (r < 0)
return log_error_errno(r, "Failed to export image: %m");
@ -283,6 +289,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached();
}
if (arg_runtime_scope == RUNTIME_SCOPE_USER)
arg_import_flags |= IMPORT_FOREIGN_UID;
return 1;
}

View File

@ -90,61 +90,60 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
return TAKE_FD(pipefd[1]);
}
int import_fork_tar_c(const char *path, PidRef *ret) {
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
bool use_selinux;
int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
int r;
assert(path);
assert(ret);
assert(tree_fd >= 0);
assert(ret_pid);
r = dlopen_libarchive();
if (r < 0)
return r;
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
if (pipe2(pipefd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pipe for tar: %m");
(void) fcntl(pipefd[0], F_SETPIPE_SZ, IMPORT_BUFFER_SIZE);
use_selinux = mac_selinux_use();
r = pidref_safe_fork_full(
"(tar)",
(int[]) { -EBADF, pipefd[1], STDERR_FILENO },
NULL, 0,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG,
&pid);
"tar-c",
/* stdio_fds= */ NULL,
(int[]) { tree_fd, pipefd[1], userns_fd }, userns_fd >= 0 ? 3 : 2,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG,
ret_pid);
if (r < 0)
return r;
if (r == 0) {
const char *cmdline[] = {
"tar",
"-C", path,
"-c",
"--xattrs",
"--xattrs-include=*",
use_selinux ? "--selinux" : "--no-selinux",
".",
NULL
};
uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
static const uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
/* Child */
if (userns_fd >= 0) {
r = detach_mount_namespace_userns(userns_fd);
if (r < 0) {
log_error_errno(r, "Failed to join user namespace: %m");
_exit(EXIT_FAILURE);
}
}
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
log_debug_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
log_debug_errno(r, "Failed to drop capabilities, ignoring: %m");
execvp("gtar", (char* const*) cmdline);
execvp("tar", (char* const*) cmdline);
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m");
log_error_errno(errno, "Failed to execute tar: %m");
if (tar_c(tree_fd, pipefd[1], /* filename= */ NULL, flags) < 0)
_exit(EXIT_FAILURE);
}
*ret = TAKE_PIDREF(pid);
_exit(EXIT_SUCCESS);
}
return TAKE_FD(pipefd[0]);
}

View File

@ -34,7 +34,7 @@ typedef enum ImportFlags {
_IMPORT_FLAGS_INVALID = -EINVAL,
} ImportFlags;
int import_fork_tar_c(const char *path, PidRef *ret);
int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
int import_mangle_os_tree(const char *path);

View File

@ -118,7 +118,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
(UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG) | \
(UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY) | \
(UINT32_C(1) << TPM2_PCR_KERNEL_BOOT) | \
(UINT32_C(1) << TPM2_PCR_KERNEL_CONFIG) | \
/* Note: we do not add PCR12/TPM2_PCR_KERNEL_CONFIG here, since our pcrlock policy ends up in there, and this would hence result in a conceptual loop */ \
(UINT32_C(1) << TPM2_PCR_SYSEXTS) | \
(UINT32_C(1) << TPM2_PCR_SHIM_POLICY) | \
(UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))

View File

@ -25,6 +25,7 @@
#include "bus-util.h"
#include "bus-wait-for-jobs.h"
#include "calendarspec.h"
#include "capability-util.h"
#include "capsule-util.h"
#include "chase.h"
#include "env-util.h"
@ -117,6 +118,7 @@ static char *arg_shell_prompt_prefix = NULL;
static int arg_lightweight = -1;
static char *arg_area = NULL;
static bool arg_via_shell = false;
static bool arg_empower = false;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -244,6 +246,7 @@ static int help_sudo_mode(void) {
" --lightweight=BOOLEAN Control whether to register a session with service manager\n"
" or without\n"
" --area=AREA Home area to log into\n"
" --empower Give privileges to selected or current user\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -253,11 +256,15 @@ static int help_sudo_mode(void) {
return 0;
}
static bool become_root(void) {
return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0");
}
static bool privileged_execution(void) {
if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
return false;
return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0");
return become_root() || arg_empower;
}
static int add_timer_property(const char *name, const char *val) {
@ -859,6 +866,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_LIGHTWEIGHT,
ARG_AREA,
ARG_VIA_SHELL,
ARG_EMPOWER,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@ -888,6 +896,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{ "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT },
{ "area", required_argument, NULL, ARG_AREA },
{ "empower", no_argument, NULL, ARG_EMPOWER },
{},
};
@ -1027,6 +1036,10 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_via_shell = true;
break;
case ARG_EMPOWER:
arg_empower = true;
break;
case '?':
return -EINVAL;
@ -1034,9 +1047,13 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
assert_not_reached();
}
if (!arg_exec_user && arg_area) {
if (!arg_exec_user && (arg_area || arg_empower)) {
/* If the user specifies --area= but not --user= then consider this an area switch request,
* and default to logging into our own account */
* and default to logging into our own account.
*
* If the user specifies --empower but not --user= then consider this a request to empower
* the current user. */
arg_exec_user = getusername_malloc();
if (!arg_exec_user)
return log_oom();
@ -1211,8 +1228,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
if (arg_lightweight >= 0) {
const char *class =
arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
(arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (become_root() ? "user-early-light" : "user-light") : "background-light") :
(arg_stdio == ARG_STDIO_PTY ? (become_root() ? "user-early" : "user") : "background");
log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
@ -1371,6 +1388,12 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
return bus_log_create_error(r);
}
if (arg_empower) {
r = sd_bus_message_append(m, "(sv)", "AmbientCapabilities", "t", CAP_MASK_ALL);
if (r < 0)
return bus_log_create_error(r);
}
if (arg_nice_set) {
r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
if (r < 0)

View File

@ -14,6 +14,7 @@
#include "iovec-util.h"
#include "libarchive-util.h"
#include "path-util.h"
#include "recurse-dir.h"
#include "stat-util.h"
#include "string-util.h"
#include "tmpfile-util.h"
@ -674,6 +675,145 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
return 0;
}
static int archive_item(
RecurseDirEvent event,
const char *path,
int dir_fd,
int inode_fd,
const struct dirent *de,
const struct statx *sx,
void *userdata) {
struct archive *a = ASSERT_PTR(userdata);
int r;
assert(path);
if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
return RECURSE_DIR_CONTINUE;
assert(inode_fd >= 0);
assert(sx);
log_debug("Archiving %s\n", path);
_cleanup_(archive_entry_freep) struct archive_entry *entry = NULL;
entry = sym_archive_entry_new();
if (!entry)
return log_oom();
assert(FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_MODE));
sym_archive_entry_set_pathname(entry, path);
sym_archive_entry_set_filetype(entry, sx->stx_mode);
if (!S_ISLNK(sx->stx_mode))
sym_archive_entry_set_perm(entry, sx->stx_mode);
if (FLAGS_SET(sx->stx_mask, STATX_UID))
sym_archive_entry_set_uid(entry, sx->stx_uid);
if (FLAGS_SET(sx->stx_mask, STATX_GID))
sym_archive_entry_set_gid(entry, sx->stx_gid);
if (S_ISREG(sx->stx_mode)) {
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Unable to determine file size of '%s'.", path);
sym_archive_entry_set_size(entry, sx->stx_size);
}
if (S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) {
sym_archive_entry_set_rdevmajor(entry, sx->stx_rdev_major);
sym_archive_entry_set_rdevminor(entry, sx->stx_rdev_minor);
}
/* We care about a modicum of reproducibility here, hence we don't save atime/btime here */
if (FLAGS_SET(sx->stx_mask, STATX_MTIME))
sym_archive_entry_set_mtime(entry, sx->stx_mtime.tv_sec, sx->stx_mtime.tv_nsec);
if (FLAGS_SET(sx->stx_mask, STATX_CTIME))
sym_archive_entry_set_ctime(entry, sx->stx_ctime.tv_sec, sx->stx_ctime.tv_nsec);
if (S_ISLNK(sx->stx_mode)) {
_cleanup_free_ char *s = NULL;
assert(dir_fd >= 0);
assert(de);
r = readlinkat_malloc(dir_fd, de->d_name, &s);
if (r < 0)
return log_error_errno(r, "Failed to read symlink target of '%s': %m", path);
sym_archive_entry_set_symlink(entry, s);
}
if (sym_archive_write_header(a, entry) != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(a));
if (S_ISREG(sx->stx_mode)) {
_cleanup_close_ int data_fd = -EBADF;
/* Convert the O_PATH fd in a proper fd */
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
if (data_fd < 0)
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
for (;;) {
char buffer[64*1024];
ssize_t l;
l = read(data_fd, buffer, sizeof(buffer));
if (l < 0)
return log_error_errno(errno, "Failed to read '%s': %m", path);
if (l == 0)
break;
la_ssize_t k;
k = sym_archive_write_data(a, buffer, l);
if (k < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive data: %s", sym_archive_error_string(a));
}
}
return RECURSE_DIR_CONTINUE;
}
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
int r;
assert(tree_fd >= 0);
assert(output_fd >= 0);
_cleanup_(archive_write_freep) struct archive *a = sym_archive_write_new();
if (!a)
return log_oom();
if (filename)
r = sym_archive_write_set_format_filter_by_ext(a, filename);
else
r = sym_archive_write_set_format_gnutar(a);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));
r = sym_archive_write_open_fd(a, output_fd);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output file: %s", sym_archive_error_string(a));
r = recurse_dir(tree_fd,
".",
STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE|STATX_ATIME|STATX_CTIME,
UINT_MAX,
RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL,
archive_item,
a);
if (r < 0)
return log_error_errno(r, "Failed to make archive: %m");
r = sym_archive_write_close(a);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to finish writing archive: %s", sym_archive_error_string(a));
return 0;
}
#else
int tar_x(int input_fd, int tree_fd, TarFlags flags) {
@ -683,4 +823,11 @@ int tar_x(int input_fd, int tree_fd, TarFlags flags) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support not available.");
}
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags) {
assert(tree_fd >= 0);
assert(output_fd >= 0);
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support not available.");
}
#endif

View File

@ -6,3 +6,4 @@ typedef enum TarFlags {
} TarFlags;
int tar_x(int input_fd, int tree_fd, TarFlags flags);
int tar_c(int tree_fd, int output_fd, const char *filename, TarFlags flags);

View File

@ -297,6 +297,14 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
# Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not
assert_neq "$(run0 --pty tty < /dev/null)" "not a tty"
assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"
# Validate that --empower gives all capabilities to a non-root user.
caps="$(run0 -u testuser --empower systemd-analyze capability --mask "$(grep CapEff /proc/self/status | cut -d':' -f2)" --json=pretty | jq -r length)"
assert_neq "$caps" "0"
run0 -u testuser --empower touch /run/empower
assert_eq "$(stat -c "%U" /run/empower)" testuser
rm /run/empower
fi
# Tests whether intermediate disconnects corrupt us (modified testcase from https://github.com/systemd/systemd/issues/27204)