diff --git a/src/basic/basic-forward.h b/src/basic/basic-forward.h index 0f8c3ea06c..d26cc5fe9b 100644 --- a/src/basic/basic-forward.h +++ b/src/basic/basic-forward.h @@ -86,6 +86,7 @@ typedef enum CGroupFlags CGroupFlags; typedef enum CGroupMask CGroupMask; typedef enum ChaseFlags ChaseFlags; typedef enum ExtractFlags ExtractFlags; +typedef enum ForkFlags ForkFlags; typedef enum Glyph Glyph; typedef enum ImageClass ImageClass; typedef enum JobMode JobMode; diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index c8ee38b4f1..3a960e19cc 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -8,6 +8,7 @@ #include #include +#include "dlfcn-util.h" #include "errno-util.h" #include "fd-util.h" #include "fileio.h" @@ -217,6 +218,9 @@ int namespace_open( int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { int r; + /* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */ + block_dlopen(); + if (userns_fd >= 0) { /* Can't setns to your own userns, since then you could escalate from non-root to root in * your own namespace, so check if namespaces are equal before attempting to enter. */ diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 9cb3b131fb..31dbdff87a 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1703,7 +1703,8 @@ int pidref_safe_fork_full( * foreign environment. Note that this has no effect on NSS! (i.e. it only has effect on uses of our * dlopen_safe(), which we use comprehensively in our codebase, but glibc NSS doesn't bother, of * course.) */ - block_dlopen(); + if (!FLAGS_SET(flags, FORK_ALLOW_DLOPEN)) + block_dlopen(); if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) { @@ -1906,6 +1907,7 @@ int namespace_fork( /* This is much like safe_fork(), but forks twice, and joins the specified namespaces in the middle * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that * /proc/self/fd works correctly. */ + assert(!FLAGS_SET(flags, FORK_ALLOW_DLOPEN)); /* never allow loading shared library from another ns */ r = safe_fork_full(outer_name, NULL, diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 9217eebc8b..0290978b99 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -187,8 +187,9 @@ typedef enum ForkFlags { FORK_NEW_NETNS = 1 << 20, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_NEW_PIDNS = 1 << 21, /* Run child in its own PID namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_FREEZE = 1 << 22, /* Don't return in child, just call freeze() instead */ + FORK_ALLOW_DLOPEN = 1 << 23, /* Do not block dlopen() in child */ - _FORK_PID_ONLY = 1 << 23, /* Don't open a pidfd referencing the child process */ + _FORK_PID_ONLY = 1 << 24, /* Don't open a pidfd referencing the child process */ } ForkFlags; int pidref_safe_fork_full( diff --git a/src/core/namespace.c b/src/core/namespace.c index a772eea7d8..19bf290336 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -13,6 +13,7 @@ #include "base-filesystem.h" #include "bitfield.h" #include "chase.h" +#include "cryptsetup-util.h" #include "dev-setup.h" #include "devnum-util.h" #include "dissect-image.h" @@ -3852,6 +3853,8 @@ int refresh_extensions_in_namespace( if (r > 0) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Target namespace is not separate, cannot reload extensions"); + (void) dlopen_cryptsetup(); + extension_dir = path_join(p->private_namespace_dir, "unit-extensions"); if (!extension_dir) return log_oom_debug(); diff --git a/src/core/service.c b/src/core/service.c index 99dd7bd0da..dcc4a992cc 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -14,7 +14,6 @@ #include "bus-error.h" #include "bus-util.h" #include "chase.h" -#include "cryptsetup-util.h" #include "dbus-service.h" #include "dbus-unit.h" #include "devnum-util.h" @@ -2882,7 +2881,9 @@ static void service_enter_refresh_extensions(Service *s) { /* Given we are running from PID1, avoid doing potentially heavy I/O operations like opening images * directly, and instead fork a worker process. */ - r = unit_fork_helper_process(UNIT(s), "(sd-refresh-extensions)", /* into_cgroup= */ false, &worker); + r = unit_fork_helper_process_full(UNIT(s), "(sd-refresh-extensions)", /* into_cgroup= */ false, + FORK_ALLOW_DLOPEN, /* permit dlopen() to avoid load of libcryptsetup in pid1 */ + &worker); if (r < 0) { log_unit_error_errno(UNIT(s), r, "Failed to fork process to refresh extensions in unit's namespace: %m"); goto fail; @@ -5562,8 +5563,6 @@ static int service_live_mount( u->id); } - (void) dlopen_cryptsetup(); - service_unwatch_control_pid(s); s->live_mount_result = SERVICE_SUCCESS; s->control_command = NULL; @@ -5584,7 +5583,9 @@ static int service_live_mount( * directly, and instead fork a worker process. We record the D-Bus message, so that we can reply * after the operation has finished. This way callers can wait on the message and know that the new * resource is available (or the operation failed) once they receive the response. */ - r = unit_fork_helper_process(u, "(sd-mount-in-ns)", /* into_cgroup= */ false, &worker); + r = unit_fork_helper_process_full(u, "(sd-mount-in-ns)", /* into_cgroup= */ false, + FORK_ALLOW_DLOPEN, + &worker); if (r < 0) { log_unit_error_errno(u, r, "Failed to fork process to mount '%s' on '%s' in unit's namespace: %m", diff --git a/src/core/unit.c b/src/core/unit.c index f37b12ac62..7161c1cb6c 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5539,12 +5539,13 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) { return 0; } -int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) { +int unit_fork_helper_process_full(Unit *u, const char *name, bool into_cgroup, ForkFlags flags, PidRef *ret) { CGroupRuntime *crt = NULL; pid_t pid; int r; assert(u); + assert((flags & (FORK_RESET_SIGNALS|FORK_DETACH|FORK_WAIT)) == 0); /* these don't really make sense for manager */ assert(ret); /* Forks off a helper process and makes sure it is a member of the unit's cgroup, if configured to @@ -5559,7 +5560,7 @@ int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef crt = unit_get_cgroup_runtime(u); } - r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid); + r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM|flags, &pid); if (r < 0) return r; if (r > 0) { @@ -5592,6 +5593,10 @@ int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef return 0; } +int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret) { + return unit_fork_helper_process_full(u, name, into_cgroup, /* flags = */ 0, ret); +} + int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret_pid) { _cleanup_(pidref_done) PidRef pid = PIDREF_NULL; int r; diff --git a/src/core/unit.h b/src/core/unit.h index b3e0f95e99..dc546116f9 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1005,6 +1005,7 @@ int unit_acquire_invocation_id(Unit *u); int unit_set_exec_params(Unit *u, ExecParameters *p); +int unit_fork_helper_process_full(Unit *u, const char *name, bool into_cgroup, ForkFlags flags, PidRef *ret); int unit_fork_helper_process(Unit *u, const char *name, bool into_cgroup, PidRef *ret); int unit_fork_and_watch_rm_rf(Unit *u, char **paths, PidRef *ret);