From patchwork Sat Aug 14 10:48:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12436649 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT, WEIRD_QUOTING autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4F448C432BE for ; Sat, 14 Aug 2021 10:49:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3492460F51 for ; Sat, 14 Aug 2021 10:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237877AbhHNKtl (ORCPT ); Sat, 14 Aug 2021 06:49:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:42766 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237870AbhHNKtk (ORCPT ); Sat, 14 Aug 2021 06:49:40 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 53D3360F46; Sat, 14 Aug 2021 10:49:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1628938152; bh=GR1VrF+nYVYmtbzVV4vDlaiOs1VF2aT+/5q76D6lBKU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rd9xWqlL995Cr0kiE9kUbGl5WORPn9tZkqoqoTVIGKJUIpd/owPiBZJYlnk4v2a3w /cEL/ml0N8AsYHwp+K7Jhj4syUD2wX/B4od6EA5He5X2ofQ2zOpo2zWPyXEBMRgR/l HD1FdeEYVVofNjBgaXmlNsGPQCxLcCvYULMQAXLuHFKTkGy3bdC2gFjVvHaYrhuAyh Sn8lzTcZNdbRBgWx4OMvtzg43kRqjDidqzTPJPJ2oibAMA46KotCistAUPDm5YOWau 0ttbhV5JtuV9/yrISjzmdYSe2158oBo9qydUbMqtiHel2CgJuKDSQ186ORVfciUlo3 BK8JYAoUP1/Vg== From: Christian Brauner To: fstests@vger.kernel.org, Eryu Guan , Christoph Hellwig Cc: Christian Brauner Subject: [PATCH v4 5/8] idmapped-mounts: refactor helpers Date: Sat, 14 Aug 2021 12:48:02 +0200 Message-Id: <20210814104805.1124023-6-brauner@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210814104805.1124023-1-brauner@kernel.org> References: <20210814104805.1124023-1-brauner@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=15691; h=from:subject; bh=AozbiS4oRf5+cgznjj0sCAND9X8lDMyLKwuTENI8wr8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSSKz494v+585K91eo7np3Xdext+uJjH24jl4uV99el/td88 fqTf0FHKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCR2kJGhqPPPyTHrlJcJtVs8FB49r lp9pH/FYxrfNiD1Drezzu3So7hr8BJ34YjbQ3FQc+7ZS+lvXmSMPOwzN6Vhd2Xzqw/t1XQjB8A X-Developer-Key: i=christian.brauner@ubuntu.com; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Precedence: bulk List-ID: X-Mailing-List: fstests@vger.kernel.org From: Christian Brauner Make all userns creation helpers share a commond codebase and move a bunch of code into utils.{c,h}. This simplifies a bunch of things and makes it easier to create nested user namespaces in follow up patches. Cc: fstests@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ unchanged --- src/idmapped-mounts/mount-idmapped.c | 197 ------------------------- src/idmapped-mounts/utils.c | 209 +++++++++++++++++++-------- src/idmapped-mounts/utils.h | 73 +++++++++- 3 files changed, 223 insertions(+), 256 deletions(-) diff --git a/src/idmapped-mounts/mount-idmapped.c b/src/idmapped-mounts/mount-idmapped.c index 219104e7..b1209057 100644 --- a/src/idmapped-mounts/mount-idmapped.c +++ b/src/idmapped-mounts/mount-idmapped.c @@ -27,77 +27,6 @@ #include "missing.h" #include "utils.h" -/* A few helpful macros. */ -#define STRLITERALLEN(x) (sizeof(""x"") - 1) - -#define INTTYPE_TO_STRLEN(type) \ - (2 + (sizeof(type) <= 1 \ - ? 3 \ - : sizeof(type) <= 2 \ - ? 5 \ - : sizeof(type) <= 4 \ - ? 10 \ - : sizeof(type) <= 8 ? 20 : sizeof(int[-2 * (sizeof(type) > 8)]))) - -#define syserror(format, ...) \ - ({ \ - fprintf(stderr, format, ##__VA_ARGS__); \ - (-errno); \ - }) - -#define syserror_set(__ret__, format, ...) \ - ({ \ - typeof(__ret__) __internal_ret__ = (__ret__); \ - errno = labs(__ret__); \ - fprintf(stderr, format, ##__VA_ARGS__); \ - __internal_ret__; \ - }) - -struct list { - void *elem; - struct list *next; - struct list *prev; -}; - -#define list_for_each(__iterator, __list) \ - for (__iterator = (__list)->next; __iterator != __list; __iterator = __iterator->next) - -static inline void list_init(struct list *list) -{ - list->elem = NULL; - list->next = list->prev = list; -} - -static inline int list_empty(const struct list *list) -{ - return list == list->next; -} - -static inline void __list_add(struct list *new, struct list *prev, struct list *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -static inline void list_add_tail(struct list *head, struct list *list) -{ - __list_add(list, head->prev, head); -} - -typedef enum idmap_type_t { - ID_TYPE_UID, - ID_TYPE_GID -} idmap_type_t; - -struct id_map { - idmap_type_t map_type; - __u32 nsid; - __u32 hostid; - __u32 range; -}; - static struct list active_map; static int add_map_entry(__u32 id_host, @@ -166,132 +95,6 @@ static int parse_map(char *map) return 0; } -static int write_id_mapping(idmap_type_t map_type, pid_t pid, const char *buf, size_t buf_size) -{ - int fd = -EBADF, setgroups_fd = -EBADF; - int fret = -1; - int ret; - char path[STRLITERALLEN("/proc/") + INTTYPE_TO_STRLEN(pid_t) + - STRLITERALLEN("/setgroups") + 1]; - - if (geteuid() != 0 && map_type == ID_TYPE_GID) { - ret = snprintf(path, sizeof(path), "/proc/%d/setgroups", pid); - if (ret < 0 || ret >= sizeof(path)) - goto out; - - setgroups_fd = open(path, O_WRONLY | O_CLOEXEC); - if (setgroups_fd < 0 && errno != ENOENT) { - syserror("Failed to open \"%s\"", path); - goto out; - } - - if (setgroups_fd >= 0) { - ret = write_nointr(setgroups_fd, "deny\n", STRLITERALLEN("deny\n")); - if (ret != STRLITERALLEN("deny\n")) { - syserror("Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid); - goto out; - } - } - } - - ret = snprintf(path, sizeof(path), "/proc/%d/%cid_map", pid, map_type == ID_TYPE_UID ? 'u' : 'g'); - if (ret < 0 || ret >= sizeof(path)) - goto out; - - fd = open(path, O_WRONLY | O_CLOEXEC); - if (fd < 0) { - syserror("Failed to open \"%s\"", path); - goto out; - } - - ret = write_nointr(fd, buf, buf_size); - if (ret != buf_size) { - syserror("Failed to write %cid mapping to \"%s\"", - map_type == ID_TYPE_UID ? 'u' : 'g', path); - goto out; - } - - fret = 0; -out: - if (fd >= 0) - close(fd); - if (setgroups_fd >= 0) - close(setgroups_fd); - - return fret; -} - -static int map_ids_from_idmap(struct list *idmap, pid_t pid) -{ - int fill, left; - char mapbuf[4096] = {}; - bool had_entry = false; - idmap_type_t map_type, u_or_g; - - for (map_type = ID_TYPE_UID, u_or_g = 'u'; - map_type <= ID_TYPE_GID; map_type++, u_or_g = 'g') { - char *pos = mapbuf; - int ret; - struct list *iterator; - - - list_for_each(iterator, idmap) { - struct id_map *map = iterator->elem; - if (map->map_type != map_type) - continue; - - had_entry = true; - - left = 4096 - (pos - mapbuf); - fill = snprintf(pos, left, "%u %u %u\n", map->nsid, map->hostid, map->range); - /* - * The kernel only takes <= 4k for writes to - * /proc//{g,u}id_map - */ - if (fill <= 0 || fill >= left) - return syserror_set(-E2BIG, "Too many %cid mappings defined", u_or_g); - - pos += fill; - } - if (!had_entry) - continue; - - ret = write_id_mapping(map_type, pid, mapbuf, pos - mapbuf); - if (ret < 0) - return syserror("Failed to write mapping: %s", mapbuf); - - memset(mapbuf, 0, sizeof(mapbuf)); - } - - return 0; -} - -static int get_userns_fd_from_idmap(struct list *idmap) -{ - int ret; - pid_t pid; - char path_ns[STRLITERALLEN("/proc/") + INTTYPE_TO_STRLEN(pid_t) + - STRLITERALLEN("/ns/user") + 1]; - - pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER | CLONE_NEWNS); - if (pid < 0) - return -errno; - - ret = map_ids_from_idmap(idmap, pid); - if (ret < 0) - return ret; - - ret = snprintf(path_ns, sizeof(path_ns), "/proc/%d/ns/user", pid); - if (ret < 0 || (size_t)ret >= sizeof(path_ns)) - ret = -EIO; - else - ret = open(path_ns, O_RDONLY | O_CLOEXEC | O_NOCTTY); - - (void)kill(pid, SIGKILL); - (void)wait_for_pid(pid); - return ret; -} - static inline bool strnequal(const char *str, const char *eq, size_t len) { return strncmp(str, eq, len) == 0; diff --git a/src/idmapped-mounts/utils.c b/src/idmapped-mounts/utils.c index 977443f1..e54f481d 100644 --- a/src/idmapped-mounts/utils.c +++ b/src/idmapped-mounts/utils.c @@ -36,99 +36,192 @@ ssize_t write_nointr(int fd, const void *buf, size_t count) return ret; } -static int write_file(const char *path, const void *buf, size_t count) +#define __STACK_SIZE (8 * 1024 * 1024) +pid_t do_clone(int (*fn)(void *), void *arg, int flags) { - int fd; - ssize_t ret; + void *stack; - fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW); - if (fd < 0) - return -1; + stack = malloc(__STACK_SIZE); + if (!stack) + return -ENOMEM; - ret = write_nointr(fd, buf, count); - close(fd); - if (ret < 0 || (size_t)ret != count) - return -1; +#ifdef __ia64__ + return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL); +#else + return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL); +#endif +} +static int get_userns_fd_cb(void *data) +{ return 0; } -static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid, - unsigned long range) +int wait_for_pid(pid_t pid) { - char map[100], procfile[256]; + int status, ret; - snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid); - snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range); - if (write_file(procfile, map, strlen(map))) - return -1; +again: + ret = waitpid(pid, &status, 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + return -1; + } - snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid); - snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range); - if (write_file(procfile, map, strlen(map))) + if (!WIFEXITED(status)) return -1; - return 0; + return WEXITSTATUS(status); } -#define __STACK_SIZE (8 * 1024 * 1024) -pid_t do_clone(int (*fn)(void *), void *arg, int flags) +static int write_id_mapping(idmap_type_t map_type, pid_t pid, const char *buf, size_t buf_size) { - void *stack; + int fd = -EBADF, setgroups_fd = -EBADF; + int fret = -1; + int ret; + char path[STRLITERALLEN("/proc/") + INTTYPE_TO_STRLEN(pid_t) + + STRLITERALLEN("/setgroups") + 1]; + + if (geteuid() != 0 && map_type == ID_TYPE_GID) { + ret = snprintf(path, sizeof(path), "/proc/%d/setgroups", pid); + if (ret < 0 || ret >= sizeof(path)) + goto out; + + setgroups_fd = open(path, O_WRONLY | O_CLOEXEC); + if (setgroups_fd < 0 && errno != ENOENT) { + syserror("Failed to open \"%s\"", path); + goto out; + } + + if (setgroups_fd >= 0) { + ret = write_nointr(setgroups_fd, "deny\n", STRLITERALLEN("deny\n")); + if (ret != STRLITERALLEN("deny\n")) { + syserror("Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid); + goto out; + } + } + } - stack = malloc(__STACK_SIZE); - if (!stack) - return -ENOMEM; + ret = snprintf(path, sizeof(path), "/proc/%d/%cid_map", pid, map_type == ID_TYPE_UID ? 'u' : 'g'); + if (ret < 0 || ret >= sizeof(path)) + goto out; -#ifdef __ia64__ - return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL); -#else - return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL); -#endif + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) { + syserror("Failed to open \"%s\"", path); + goto out; + } + + ret = write_nointr(fd, buf, buf_size); + if (ret != buf_size) { + syserror("Failed to write %cid mapping to \"%s\"", + map_type == ID_TYPE_UID ? 'u' : 'g', path); + goto out; + } + + fret = 0; +out: + if (fd >= 0) + close(fd); + if (setgroups_fd >= 0) + close(setgroups_fd); + + return fret; } -int get_userns_fd_cb(void *data) +static int map_ids_from_idmap(struct list *idmap, pid_t pid) { - return kill(getpid(), SIGSTOP); + int fill, left; + char mapbuf[4096] = {}; + bool had_entry = false; + + for (idmap_type_t map_type = ID_TYPE_UID, u_or_g = 'u'; + map_type <= ID_TYPE_GID; map_type++, u_or_g = 'g') { + char *pos = mapbuf; + int ret; + struct list *iterator; + + + list_for_each(iterator, idmap) { + struct id_map *map = iterator->elem; + if (map->map_type != map_type) + continue; + + had_entry = true; + + left = 4096 - (pos - mapbuf); + fill = snprintf(pos, left, "%u %u %u\n", map->nsid, map->hostid, map->range); + /* + * The kernel only takes <= 4k for writes to + * /proc//{g,u}id_map + */ + if (fill <= 0 || fill >= left) + return syserror_set(-E2BIG, "Too many %cid mappings defined", u_or_g); + + pos += fill; + } + if (!had_entry) + continue; + + ret = write_id_mapping(map_type, pid, mapbuf, pos - mapbuf); + if (ret < 0) + return syserror("Failed to write mapping: %s", mapbuf); + + memset(mapbuf, 0, sizeof(mapbuf)); + } + + return 0; } -int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range) +int get_userns_fd_from_idmap(struct list *idmap) { int ret; pid_t pid; - char path[256]; + char path_ns[STRLITERALLEN("/proc/") + INTTYPE_TO_STRLEN(pid_t) + + STRLITERALLEN("/ns/user") + 1]; - pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER); + pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER | CLONE_NEWNS); if (pid < 0) return -errno; - ret = map_ids(pid, nsid, hostid, range); + ret = map_ids_from_idmap(idmap, pid); if (ret < 0) return ret; - snprintf(path, sizeof(path), "/proc/%d/ns/user", pid); - ret = open(path, O_RDONLY | O_CLOEXEC); - kill(pid, SIGKILL); - wait_for_pid(pid); + ret = snprintf(path_ns, sizeof(path_ns), "/proc/%d/ns/user", pid); + if (ret < 0 || (size_t)ret >= sizeof(path_ns)) + ret = -EIO; + else + ret = open(path_ns, O_RDONLY | O_CLOEXEC | O_NOCTTY); + + (void)kill(pid, SIGKILL); + (void)wait_for_pid(pid); return ret; } -int wait_for_pid(pid_t pid) +int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range) { - int status, ret; - -again: - ret = waitpid(pid, &status, 0); - if (ret == -1) { - if (errno == EINTR) - goto again; - - return -1; - } - - if (!WIFEXITED(status)) - return -1; - - return WEXITSTATUS(status); + struct list head, uid_mapl, gid_mapl; + struct id_map uid_map = { + .map_type = ID_TYPE_UID, + .nsid = nsid, + .hostid = hostid, + .range = range, + }; + struct id_map gid_map = { + .map_type = ID_TYPE_GID, + .nsid = nsid, + .hostid = hostid, + .range = range, + }; + + list_init(&head); + uid_mapl.elem = &uid_map; + gid_mapl.elem = &gid_map; + list_add_tail(&head, &uid_mapl); + list_add_tail(&head, &gid_mapl); + + return get_userns_fd_from_idmap(&head); } diff --git a/src/idmapped-mounts/utils.h b/src/idmapped-mounts/utils.h index efbf3bc3..4f976f9f 100644 --- a/src/idmapped-mounts/utils.h +++ b/src/idmapped-mounts/utils.h @@ -19,10 +19,81 @@ #include "missing.h" +/* A few helpful macros. */ +#define STRLITERALLEN(x) (sizeof(""x"") - 1) + +#define INTTYPE_TO_STRLEN(type) \ + (2 + (sizeof(type) <= 1 \ + ? 3 \ + : sizeof(type) <= 2 \ + ? 5 \ + : sizeof(type) <= 4 \ + ? 10 \ + : sizeof(type) <= 8 ? 20 : sizeof(int[-2 * (sizeof(type) > 8)]))) + +#define syserror(format, ...) \ + ({ \ + fprintf(stderr, "%m - " format "\n", ##__VA_ARGS__); \ + (-errno); \ + }) + +#define syserror_set(__ret__, format, ...) \ + ({ \ + typeof(__ret__) __internal_ret__ = (__ret__); \ + errno = labs(__ret__); \ + fprintf(stderr, "%m - " format "\n", ##__VA_ARGS__); \ + __internal_ret__; \ + }) + +typedef enum idmap_type_t { + ID_TYPE_UID, + ID_TYPE_GID +} idmap_type_t; + +struct id_map { + idmap_type_t map_type; + __u32 nsid; + __u32 hostid; + __u32 range; +}; + +struct list { + void *elem; + struct list *next; + struct list *prev; +}; + +#define list_for_each(__iterator, __list) \ + for (__iterator = (__list)->next; __iterator != __list; __iterator = __iterator->next) + +static inline void list_init(struct list *list) +{ + list->elem = NULL; + list->next = list->prev = list; +} + +static inline int list_empty(const struct list *list) +{ + return list == list->next; +} + +static inline void __list_add(struct list *new, struct list *prev, struct list *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add_tail(struct list *head, struct list *list) +{ + __list_add(list, head->prev, head); +} + extern pid_t do_clone(int (*fn)(void *), void *arg, int flags); -extern int get_userns_fd_cb(void *data); extern int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range); +extern int get_userns_fd_from_idmap(struct list *idmap); extern ssize_t read_nointr(int fd, void *buf, size_t count); extern int wait_for_pid(pid_t pid); extern ssize_t write_nointr(int fd, const void *buf, size_t count);