From patchwork Sat Aug 14 10:48:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 12436651 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 E292AC4338F for ; Sat, 14 Aug 2021 10:49:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CCFF560F48 for ; Sat, 14 Aug 2021 10:49:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237962AbhHNKtn (ORCPT ); Sat, 14 Aug 2021 06:49:43 -0400 Received: from mail.kernel.org ([198.145.29.99]:42786 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237870AbhHNKtm (ORCPT ); Sat, 14 Aug 2021 06:49:42 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 7055A60F48; Sat, 14 Aug 2021 10:49:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1628938154; bh=vkjdIYHxUnTnjXbNgh0QX5+sfMIVct1R9aWRxGSSaSE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rf2xwxhAIVMtUPUEoht8s2hYaZaQ3tJ0ASHgFVb6ErjqUIacbVR+d4Sw04w3leNdU qZZOPSN55iglxaUVeEzB8oPcY1zT2a89tp2KbjBJTsu36iM6J2jY0Kucn0BZ4fp7hb PCAqGAAr3G2uHDAX/Ikd8P4sWYy9/dA4qvRCHdxf+XmYQHEV+Zd2ivu/LmWaREfob0 evaNJRnWM91LEYddi55TOFxDkd2MEkkmhnJvTuFDJGltuWHvloV8vRS7YiROeAsz61 ikv5yGy7fEogkbxC56AcMbrdTqUbCH1xJwJ+fhAFn3I3lBwBlkDQaOC+fMDXfppVw6 U889WH3TNyePQ== From: Christian Brauner To: fstests@vger.kernel.org, Eryu Guan , Christoph Hellwig Cc: Christian Brauner Subject: [PATCH v4 6/8] idmapped-mounts: add nested userns creation helpers Date: Sat, 14 Aug 2021 12:48:03 +0200 Message-Id: <20210814104805.1124023-7-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=9170; h=from:subject; bh=dFsDzzSne/SIn31F+6Z4wByT3e6B6oAmRERBzLNbiE8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMSSKz49QOXPUIWqemJez3GzN1y7/ruWaO9zxS9DpiG8LDD8Z eexpRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwEQKexgZLp4PV59+Ji2j4tfpSQc8Ug Xe3Nf9XuJ9RHP3cpYzvuIXwhj+2a29USR2MjLq8dsK7bh/2/SmnLv6/1Ot5PF/ZlNf85h+4QAA 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 Add a helper to create a nested userns hierarchy. This will be used in follow-up tests. Cc: fstests@vger.kernel.org Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged /* v4 */ unchanged --- src/idmapped-mounts/idmapped-mounts.c | 14 --- src/idmapped-mounts/mount-idmapped.c | 32 +----- src/idmapped-mounts/utils.c | 160 +++++++++++++++++++++++++- src/idmapped-mounts/utils.h | 25 ++++ 4 files changed, 185 insertions(+), 46 deletions(-) diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index dca03d08..fdbdd827 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -388,20 +388,6 @@ static inline bool switch_fsids(uid_t fsuid, gid_t fsgid) return true; } -static inline bool switch_ids(uid_t uid, gid_t gid) -{ - if (setgroups(0, NULL)) - return log_errno(false, "failure: setgroups"); - - if (setresgid(gid, gid, gid)) - return log_errno(false, "failure: setresgid"); - - if (setresuid(uid, uid, uid)) - return log_errno(false, "failure: setresuid"); - - return true; -} - static inline bool switch_userns(int fd, uid_t uid, gid_t gid, bool drop_caps) { if (setns(fd, CLONE_NEWUSER)) diff --git a/src/idmapped-mounts/mount-idmapped.c b/src/idmapped-mounts/mount-idmapped.c index b1209057..d8490bed 100644 --- a/src/idmapped-mounts/mount-idmapped.c +++ b/src/idmapped-mounts/mount-idmapped.c @@ -29,36 +29,6 @@ static struct list active_map; -static int add_map_entry(__u32 id_host, - __u32 id_ns, - __u32 range, - idmap_type_t map_type) -{ - struct list *new_list = NULL; - struct id_map *newmap = NULL; - - newmap = malloc(sizeof(*newmap)); - if (!newmap) - return -ENOMEM; - - new_list = malloc(sizeof(struct list)); - if (!new_list) { - free(newmap); - return -ENOMEM; - } - - *newmap = (struct id_map){ - .hostid = id_host, - .nsid = id_ns, - .range = range, - .map_type = map_type, - }; - - new_list->elem = newmap; - list_add_tail(&active_map, new_list); - return 0; -} - static int parse_map(char *map) { char types[2] = {'u', 'g'}; @@ -87,7 +57,7 @@ static int parse_map(char *map) else map_type = ID_TYPE_GID; - ret = add_map_entry(id_host, id_ns, range, map_type); + ret = add_map_entry(&active_map, id_host, id_ns, range, map_type); if (ret < 0) return ret; } diff --git a/src/idmapped-mounts/utils.c b/src/idmapped-mounts/utils.c index e54f481d..6ffd6a23 100644 --- a/src/idmapped-mounts/utils.c +++ b/src/idmapped-mounts/utils.c @@ -3,11 +3,15 @@ #define _GNU_SOURCE #endif #include +#include #include +#include #include #include -#include +#include #include +#include +#include #include #include #include @@ -137,6 +141,9 @@ static int map_ids_from_idmap(struct list *idmap, pid_t pid) char mapbuf[4096] = {}; bool had_entry = false; + if (list_empty(idmap)) + return 0; + 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; @@ -225,3 +232,154 @@ int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range) return get_userns_fd_from_idmap(&head); } + +bool switch_ids(uid_t uid, gid_t gid) +{ + if (setgroups(0, NULL)) + return syserror("failure: setgroups"); + + if (setresgid(gid, gid, gid)) + return syserror("failure: setresgid"); + + if (setresuid(uid, uid, uid)) + return syserror("failure: setresuid"); + + return true; +} + +static int userns_fd_cb(void *data) +{ + struct userns_hierarchy *h = data; + char c; + int ret; + + ret = read_nointr(h->fd_event, &c, 1); + if (ret < 0) + return syserror("failure: read from socketpair"); + + /* Only switch ids if someone actually wrote a mapping for us. */ + if (c == '1') { + if (!switch_ids(0, 0)) + return syserror("failure: switch ids to 0"); + + /* Ensure we can access proc files from processes we can ptrace. */ + ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + if (ret < 0) + return syserror("failure: make dumpable"); + } + + ret = write_nointr(h->fd_event, "1", 1); + if (ret < 0) + return syserror("failure: write to socketpair"); + + ret = create_userns_hierarchy(++h); + if (ret < 0) + return syserror("failure: userns level %d", h->level); + + return 0; +} + +int create_userns_hierarchy(struct userns_hierarchy *h) +{ + int fret = -1; + char c; + int fd_socket[2]; + int fd_userns = -EBADF, ret = -1; + ssize_t bytes; + pid_t pid; + char path[256]; + + if (h->level == MAX_USERNS_LEVEL) + return 0; + + ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, fd_socket); + if (ret < 0) + return syserror("failure: create socketpair"); + + /* Note the CLONE_FILES | CLONE_VM when mucking with fds and memory. */ + h->fd_event = fd_socket[1]; + pid = do_clone(userns_fd_cb, h, CLONE_NEWUSER | CLONE_FILES | CLONE_VM); + if (pid < 0) { + syserror("failure: userns level %d", h->level); + goto out_close; + } + + ret = map_ids_from_idmap(&h->id_map, pid); + if (ret < 0) { + kill(pid, SIGKILL); + syserror("failure: writing id mapping for userns level %d for %d", h->level, pid); + goto out_wait; + } + + if (!list_empty(&h->id_map)) + bytes = write_nointr(fd_socket[0], "1", 1); /* Inform the child we wrote a mapping. */ + else + bytes = write_nointr(fd_socket[0], "0", 1); /* Inform the child we didn't write a mapping. */ + if (bytes < 0) { + kill(pid, SIGKILL); + syserror("failure: write to socketpair"); + goto out_wait; + } + + /* Wait for child to set*id() and become dumpable. */ + bytes = read_nointr(fd_socket[0], &c, 1); + if (bytes < 0) { + kill(pid, SIGKILL); + syserror("failure: read from socketpair"); + goto out_wait; + } + + snprintf(path, sizeof(path), "/proc/%d/ns/user", pid); + fd_userns = open(path, O_RDONLY | O_CLOEXEC); + if (fd_userns < 0) { + kill(pid, SIGKILL); + syserror("failure: open userns level %d for %d", h->level, pid); + goto out_wait; + } + + fret = 0; + +out_wait: + if (!wait_for_pid(pid) && !fret) { + h->fd_userns = fd_userns; + fd_userns = -EBADF; + } + +out_close: + if (fd_userns >= 0) + close(fd_userns); + close(fd_socket[0]); + close(fd_socket[1]); + return fret; +} + +int add_map_entry(struct list *head, + __u32 id_host, + __u32 id_ns, + __u32 range, + idmap_type_t map_type) +{ + struct list *new_list = NULL; + struct id_map *newmap = NULL; + + newmap = malloc(sizeof(*newmap)); + if (!newmap) + return -ENOMEM; + + new_list = malloc(sizeof(struct list)); + if (!new_list) { + free(newmap); + return -ENOMEM; + } + + *newmap = (struct id_map){ + .hostid = id_host, + .nsid = id_ns, + .range = range, + .map_type = map_type, + }; + + new_list->elem = newmap; + list_add_tail(head, new_list); + return 0; +} diff --git a/src/idmapped-mounts/utils.h b/src/idmapped-mounts/utils.h index 4f976f9f..9694980e 100644 --- a/src/idmapped-mounts/utils.h +++ b/src/idmapped-mounts/utils.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,9 @@ #include "missing.h" +/* Maximum number of nested user namespaces in the kernel. */ +#define MAX_USERNS_LEVEL 32 + /* A few helpful macros. */ #define STRLITERALLEN(x) (sizeof(""x"") - 1) @@ -63,6 +67,13 @@ struct list { struct list *prev; }; +struct userns_hierarchy { + int fd_userns; + int fd_event; + unsigned int level; + struct list id_map; +}; + #define list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; __iterator != __list; __iterator = __iterator->next) @@ -90,6 +101,16 @@ static inline void list_add_tail(struct list *head, struct list *list) __list_add(list, head->prev, head); } +static inline void list_del(struct list *list) +{ + struct list *next, *prev; + + next = list->next; + prev = list->prev; + next->prev = prev; + prev->next = next; +} + extern pid_t do_clone(int (*fn)(void *), void *arg, int flags); extern int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range); @@ -97,5 +118,9 @@ 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); +extern bool switch_ids(uid_t uid, gid_t gid); +extern int create_userns_hierarchy(struct userns_hierarchy *h); +extern int add_map_entry(struct list *head, __u32 id_host, __u32 id_ns, + __u32 range, idmap_type_t map_type); #endif /* __IDMAP_UTILS_H */