diff options
-rw-r--r-- | src/core/execute.c | 56 | ||||
-rw-r--r-- | src/core/namespace.c | 36 | ||||
-rw-r--r-- | src/core/namespace.h | 1 | ||||
-rw-r--r-- | src/test/test-namespace.c | 1 | ||||
-rw-r--r-- | src/test/test-ns.c | 1 | ||||
-rwxr-xr-x | test/units/testsuite-34.sh | 3 |
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; } |