summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/execute.c56
-rw-r--r--src/core/namespace.c36
-rw-r--r--src/core/namespace.h1
-rw-r--r--src/test/test-namespace.c1
-rw-r--r--src/test/test-ns.c1
-rwxr-xr-xtest/units/testsuite-34.sh3
6 files changed, 95 insertions, 3 deletions
diff --git a/src/core/execute.c b/src/core/execute.c
index 9d10088901..da917e1af4 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -2384,7 +2384,9 @@ static int setup_exec_directory(
goto fail;
}
- /* And link it up from the original place */
+ /* And link it up from the original place. Note that if a mount namespace is going to be
+ * used, then this symlink remains on the host, and a new one for the child namespace will
+ * be created later. */
r = symlink_idempotent(pp, p, true);
if (r < 0)
goto fail;
@@ -3148,6 +3150,49 @@ finish:
return r;
}
+/* ret_symlinks will contain a list of pairs src:dest that describes
+ * the symlinks to create later on. For example, the symlinks needed
+ * to safely give private directories to DynamicUser=1 users. */
+static int compile_symlinks(
+ const ExecContext *context,
+ const ExecParameters *params,
+ char ***ret_symlinks) {
+
+ _cleanup_strv_free_ char **symlinks = NULL;
+ int r;
+
+ assert(context);
+ assert(params);
+ assert(ret_symlinks);
+
+ for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
+ char **src;
+
+ if (!exec_directory_is_private(context, dt))
+ continue;
+
+ STRV_FOREACH(src, context->directories[dt].paths) {
+ _cleanup_free_ char *private_path = NULL, *path = NULL;
+
+ private_path = path_join(params->prefix[dt], "private", *src);
+ if (!private_path)
+ return -ENOMEM;
+
+ path = path_join(params->prefix[dt], *src);
+ if (!path)
+ return -ENOMEM;
+
+ r = strv_consume_pair(&symlinks, TAKE_PTR(private_path), TAKE_PTR(path));
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret_symlinks = TAKE_PTR(symlinks);
+
+ return 0;
+}
+
static bool insist_on_sandboxing(
const ExecContext *context,
const char *root_dir,
@@ -3194,7 +3239,7 @@ static int apply_mount_namespace(
const ExecRuntime *runtime,
char **error_path) {
- _cleanup_strv_free_ char **empty_directories = NULL;
+ _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL;
const char *tmp_dir = NULL, *var_tmp_dir = NULL;
const char *root_dir = NULL, *root_image = NULL;
_cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL;
@@ -3217,6 +3262,12 @@ static int apply_mount_namespace(
if (r < 0)
return r;
+ /* Symlinks for exec dirs are set up after other mounts, before they are
+ * made read-only. */
+ r = compile_symlinks(context, params, &symlinks);
+ if (r < 0)
+ return r;
+
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command_flags & EXEC_COMMAND_FULLY_PRIVILEGED);
if (needs_sandboxing) {
/* The runtime struct only contains the parent of the private /tmp,
@@ -3300,6 +3351,7 @@ static int apply_mount_namespace(
needs_sandboxing ? context->exec_paths : NULL,
needs_sandboxing ? context->no_exec_paths : NULL,
empty_directories,
+ symlinks,
bind_mounts,
n_bind_mounts,
context->temporary_filesystems,
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 48f11f9906..68704dff06 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1586,11 +1586,36 @@ static void normalize_mounts(const char *root_directory, MountEntry *mounts, siz
drop_nop(mounts, n_mounts);
}
+static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
+ char **src, **dst;
+ int r;
+
+ STRV_FOREACH_PAIR(src, dst, strv_symlinks) {
+ _cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
+
+ src_abs = path_join(root, *src);
+ dst_abs = path_join(root, *dst);
+ if (!src_abs || !dst_abs)
+ return -ENOMEM;
+
+ r = mkdir_parents_label(dst_abs, 0755);
+ if (r < 0)
+ return r;
+
+ r = symlink_idempotent(src_abs, dst_abs, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int apply_mounts(
const char *root,
const NamespaceInfo *ns_info,
MountEntry *mounts,
size_t *n_mounts,
+ char **exec_dir_symlinks,
char **error_path) {
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
@@ -1657,6 +1682,14 @@ static int apply_mounts(
normalize_mounts(root, mounts, n_mounts);
}
+ /* Now that all filesystems have been set up, but before the
+ * read-only switches are flipped, create the exec dirs symlinks.
+ * Note that when /var/lib is not empty/tmpfs, these symlinks will already
+ * exist, which means this will be a no-op. */
+ r = create_symlinks_from_tuples(root, exec_dir_symlinks);
+ if (r < 0)
+ return r;
+
/* Create a deny list we can pass to bind_mount_recursive() */
deny_list = new(char*, (*n_mounts)+1);
if (!deny_list)
@@ -1820,6 +1853,7 @@ int setup_namespace(
char** exec_paths,
char** no_exec_paths,
char** empty_directories,
+ char** exec_dir_symlinks,
const BindMount *bind_mounts,
size_t n_bind_mounts,
const TemporaryFileSystem *temporary_filesystems,
@@ -2261,7 +2295,7 @@ int setup_namespace(
(void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
/* Now make the magic happen */
- r = apply_mounts(root, ns_info, mounts, &n_mounts, error_path);
+ r = apply_mounts(root, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
if (r < 0)
goto finish;
diff --git a/src/core/namespace.h b/src/core/namespace.h
index c9373a4adb..62f05d7585 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -121,6 +121,7 @@ int setup_namespace(
char **exec_paths,
char **no_exec_paths,
char **empty_directories,
+ char **exec_dir_symlinks,
const BindMount *bind_mounts,
size_t n_bind_mounts,
const TemporaryFileSystem *temporary_filesystems,
diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c
index 796382f3cf..dd66379b5c 100644
--- a/src/test/test-namespace.c
+++ b/src/test/test-namespace.c
@@ -166,6 +166,7 @@ static void test_protect_kernel_logs(void) {
NULL,
NULL,
NULL,
+ NULL,
NULL, 0,
NULL, 0,
NULL, 0,
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index ae666a3019..b03eabb59b 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -83,6 +83,7 @@ int main(int argc, char *argv[]) {
(char **) writable,
(char **) readonly,
(char **) inaccessible,
+ NULL,
(char **) exec,
(char **) no_exec,
NULL,
diff --git a/test/units/testsuite-34.sh b/test/units/testsuite-34.sh
index 97244ac457..eca16bc78d 100755
--- a/test/units/testsuite-34.sh
+++ b/test/units/testsuite-34.sh
@@ -10,6 +10,7 @@ systemd-analyze log-target console
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
&& { echo 'unexpected success'; exit 1; }
@@ -22,6 +23,7 @@ test ! -f /var/lib/zzz/test-missing
# Convert to DynamicUser=1
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
&& { echo 'unexpected success'; exit 1; }
@@ -34,6 +36,7 @@ test ! -f /var/lib/zzz/test-missing
# Convert back
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
&& { echo 'unexpected success'; exit 1; }