From 8ddefb8eef412f888b230588ebde832fc50dc781 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Oct 2021 13:14:11 +0200 Subject: [PATCH 1/3] basic: move freeze() from shared/exec-util.h to basic/process-util.h That way we can use it in other code from basic/. It fits into both headers equally well or badly, hence let's just move this one function. --- src/basic/process-util.c | 21 +++++++++++++++++++++ src/basic/process-util.h | 2 ++ src/shared/exec-util.c | 23 ----------------------- src/shared/exec-util.h | 2 -- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 5e7ed06ea5..fce96dd5eb 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1618,6 +1618,27 @@ bool invoked_as(char *argv[], const char *token) { return strstr(last_path_component(argv[0]), token); } +_noreturn_ void freeze(void) { + log_close(); + + /* Make sure nobody waits for us on a socket anymore */ + (void) close_all_fds_full(NULL, 0, false); + + sync(); + + /* Let's not freeze right away, but keep reaping zombies. */ + for (;;) { + siginfo_t si = {}; + + if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR) + break; + } + + /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ + for (;;) + pause(); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 7e87f5a17c..451d0a5ff4 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -202,3 +202,5 @@ int pidfd_get_pid(int fd, pid_t *ret); int setpriority_closest(int priority); bool invoked_as(char *argv[], const char *token); + +_noreturn_ void freeze(void); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 042db7db3f..fd0d95c530 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -448,29 +448,6 @@ ExecCommandFlags exec_command_flags_from_string(const char *s) { return 1 << idx; } -_noreturn_ void freeze(void) { - log_close(); - - /* Make sure nobody waits for us on a socket anymore */ - (void) close_all_fds_full(NULL, 0, false); - - sync(); - - /* Let's not freeze right away, but keep reaping zombies. */ - for (;;) { - int r; - siginfo_t si = {}; - - r = waitid(P_ALL, 0, &si, WEXITED); - if (r < 0 && errno != EINTR) - break; - } - - /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ - for (;;) - pause(); -} - int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) { #if ENABLE_FEXECVE execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH); diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h index 05f8e1af83..21d28608f9 100644 --- a/src/shared/exec-util.h +++ b/src/shared/exec-util.h @@ -47,8 +47,6 @@ extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX]; const char* exec_command_flags_to_string(ExecCommandFlags i); ExecCommandFlags exec_command_flags_from_string(const char *s); -_noreturn_ void freeze(void); - int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]); int fork_agent(const char *name, int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) _sentinel_; From 979b0ff2c4dbebb3c04e3bca22be18e82a631098 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Oct 2021 13:15:27 +0200 Subject: [PATCH 2/3] namespace-util: introduce userns_acquire() as helper for allocating new unbound userns This returns a namespace fd, and takes a uidmap/gidmap as string. This is split out out mount-util.c's remount_idmap() logic, so that we can allocate a userns independently. --- src/basic/namespace-util.c | 39 ++++++++++++++++++++++++++++++++++++++ src/basic/namespace-util.h | 2 ++ src/shared/mount-util.c | 27 ++++---------------------- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index d1fee95245..9d2e59e886 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -5,6 +5,7 @@ #include #include "fd-util.h" +#include "fileio.h" #include "missing_fs.h" #include "missing_magic.h" #include "namespace-util.h" @@ -181,3 +182,41 @@ int detach_mount_namespace(void) { return 0; } + +int userns_acquire(const char *uid_map, const char *gid_map) { + char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + _cleanup_(sigkill_waitp) pid_t pid = 0; + _cleanup_close_ int userns_fd = -1; + int r; + + assert(uid_map); + assert(gid_map); + + /* Forks off a process in a new userns, configures the specified uidmap/gidmap, acquires an fd to it, + * and then kills the process again. This way we have a userns fd that is not bound to any + * process. We can use that for file system mounts and similar. */ + + r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NEW_USERNS, &pid); + if (r < 0) + return r; + if (r == 0) + /* Child. We do nothing here, just freeze until somebody kills us. */ + freeze(); + + xsprintf(path, "/proc/" PID_FMT "/uid_map", pid); + r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to write UID map: %m"); + + xsprintf(path, "/proc/" PID_FMT "/gid_map", pid); + r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to write GID map: %m"); + + r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL); + if (r < 0) + return log_error_errno(r, "Failed to open netns fd: %m"); + + return TAKE_FD(userns_fd); + +} diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index 39a6a46d1f..24dce0939e 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -24,3 +24,5 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) { return true; } + +int userns_acquire(const char *uid_map, const char *gid_map); diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index e6b6a7e89a..a7217adfa1 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -995,37 +995,18 @@ int make_mount_point(const char *path) { } static int make_userns(uid_t uid_shift, uid_t uid_range) { - char uid_map[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1]; - _cleanup_(sigkill_waitp) pid_t pid = 0; + char line[DECIMAL_STR_MAX(uid_t)*3+3+1]; _cleanup_close_ int userns_fd = -1; - int r; /* Allocates a userns file descriptor with the mapping we need. For this we'll fork off a child * process whose only purpose is to give us a new user namespace. It's killed when we got it. */ - r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NEW_USERNS, &pid); - if (r < 0) - return r; - if (r == 0) - /* Child. We do nothing here, just freeze until somebody kills us. */ - freeze(); - xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, uid_shift, uid_range); - xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid); - r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER); - if (r < 0) - return log_error_errno(r, "Failed to write UID map: %m"); - /* We always assign the same UID and GID ranges */ - xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid); - r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER); - if (r < 0) - return log_error_errno(r, "Failed to write GID map: %m"); - - r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL); - if (r < 0) - return r; + userns_fd = userns_acquire(line, line); + if (userns_fd < 0) + return log_debug_errno(userns_fd, "Failed to acquire new userns: %m"); return TAKE_FD(userns_fd); } From 43cac49092ec99802d74b1d6279d4d27a4048829 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Oct 2021 16:37:10 +0200 Subject: [PATCH 3/3] process-util: move sync() out of freeze() We are using this for creating userns namespaces, and we really shouldn't try to sync there. Moreover the use of free() in shutdown code doesn't need it anyway, since it just sync()ed right before anyway. Only the third user of freeze() we have actually needs the syc(), hence do it there and nowhere else. --- src/basic/process-util.c | 2 -- src/core/main.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index fce96dd5eb..38df34e339 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1624,8 +1624,6 @@ _noreturn_ void freeze(void) { /* Make sure nobody waits for us on a socket anymore */ (void) close_all_fds_full(NULL, 0, false); - sync(); - /* Let's not freeze right away, but keep reaping zombies. */ for (;;) { siginfo_t si = {}; diff --git a/src/core/main.c b/src/core/main.c index 6e01398523..129f340f6e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -222,6 +222,7 @@ _noreturn_ static void freeze_or_exit_or_reboot(void) { } log_emergency("Freezing execution."); + sync(); freeze(); }