From patchwork Tue Feb 18 14:33:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388643 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E174D924 for ; Tue, 18 Feb 2020 14:35:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C3E2F21D56 for ; Tue, 18 Feb 2020 14:35:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726871AbgBROfi (ORCPT ); Tue, 18 Feb 2020 09:35:38 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53017 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726605AbgBROfh (ORCPT ); Tue, 18 Feb 2020 09:35:37 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xp-0000fF-G1; Tue, 18 Feb 2020 14:35:05 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 01/25] user_namespace: introduce fsid mappings infrastructure Date: Tue, 18 Feb 2020 15:33:47 +0100 Message-Id: <20200218143411.2389182-2-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: This introduces the infrastructure to setup fsid mappings which will be used in later patches. All new code depends on CONFIG_USER_NS_FSID=y. It currently defaults to "N". If CONFIG_USER_NS_FSID is not set, no new code is added. In this patch fsuid_m_show() and fsgid_m_show() are introduced. They are identical to uid_m_show() and gid_m_show() until we introduce from_kfsuid() and from_kfsgid() in a follow-up patch. Signed-off-by: Christian Brauner Acked-by: Serge Hallyn --- /* v2 */ - Randy Dunlap : - Fix typo in USER_NS_FSID kconfig documentation. /* v3 */ unchanged --- include/linux/user_namespace.h | 10 +++ init/Kconfig | 11 +++ kernel/user.c | 22 ++++++ kernel/user_namespace.c | 122 +++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+) diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 6ef1c7109fc4..e44742b0cf8a 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -56,6 +56,10 @@ enum ucount_type { struct user_namespace { struct uid_gid_map uid_map; struct uid_gid_map gid_map; +#ifdef CONFIG_USER_NS_FSID + struct uid_gid_map fsuid_map; + struct uid_gid_map fsgid_map; +#endif struct uid_gid_map projid_map; atomic_t count; struct user_namespace *parent; @@ -127,6 +131,12 @@ struct seq_operations; extern const struct seq_operations proc_uid_seq_operations; extern const struct seq_operations proc_gid_seq_operations; extern const struct seq_operations proc_projid_seq_operations; +#ifdef CONFIG_USER_NS_FSID +extern const struct seq_operations proc_fsuid_seq_operations; +extern const struct seq_operations proc_fsgid_seq_operations; +extern ssize_t proc_fsuid_map_write(struct file *, const char __user *, size_t, loff_t *); +extern ssize_t proc_fsgid_map_write(struct file *, const char __user *, size_t, loff_t *); +#endif extern ssize_t proc_uid_map_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_t *); diff --git a/init/Kconfig b/init/Kconfig index cfee56c151f1..d4d0beeba48f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1111,6 +1111,17 @@ config USER_NS If unsure, say N. +config USER_NS_FSID + bool "User namespace fsid mappings" + depends on USER_NS + default n + help + This allows containers to alter their filesystem id mappings. + With this containers with different id mappings can still share + the same filesystem. + + If unsure, say N. + config PID_NS bool "PID Namespaces" default y diff --git a/kernel/user.c b/kernel/user.c index 5235d7f49982..2ccaea9b810b 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -55,6 +55,28 @@ struct user_namespace init_user_ns = { }, }, }, +#ifdef CONFIG_USER_NS_FSID + .fsuid_map = { + .nr_extents = 1, + { + .extent[0] = { + .first = 0, + .lower_first = 0, + .count = 4294967295U, + }, + }, + }, + .fsgid_map = { + .nr_extents = 1, + { + .extent[0] = { + .first = 0, + .lower_first = 0, + .count = 4294967295U, + }, + }, + }, +#endif .count = ATOMIC_INIT(3), .owner = GLOBAL_ROOT_UID, .group = GLOBAL_ROOT_GID, diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 8eadadc478f9..cbdf456f95f0 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -191,6 +191,16 @@ static void free_user_ns(struct work_struct *work) kfree(ns->projid_map.forward); kfree(ns->projid_map.reverse); } +#ifdef CONFIG_USER_NS_FSID + if (ns->fsgid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(ns->fsgid_map.forward); + kfree(ns->fsgid_map.reverse); + } + if (ns->fsuid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(ns->fsuid_map.forward); + kfree(ns->fsuid_map.reverse); + } +#endif retire_userns_sysctls(ns); key_free_user_ns(ns); ns_free_inum(&ns->ns); @@ -637,6 +647,50 @@ static int projid_m_show(struct seq_file *seq, void *v) return 0; } +#ifdef CONFIG_USER_NS_FSID +static int fsuid_m_show(struct seq_file *seq, void *v) +{ + struct user_namespace *ns = seq->private; + struct uid_gid_extent *extent = v; + struct user_namespace *lower_ns; + uid_t lower; + + lower_ns = seq_user_ns(seq); + if ((lower_ns == ns) && lower_ns->parent) + lower_ns = lower_ns->parent; + + lower = from_kuid(lower_ns, KUIDT_INIT(extent->lower_first)); + + seq_printf(seq, "%10u %10u %10u\n", + extent->first, + lower, + extent->count); + + return 0; +} + +static int fsgid_m_show(struct seq_file *seq, void *v) +{ + struct user_namespace *ns = seq->private; + struct uid_gid_extent *extent = v; + struct user_namespace *lower_ns; + gid_t lower; + + lower_ns = seq_user_ns(seq); + if ((lower_ns == ns) && lower_ns->parent) + lower_ns = lower_ns->parent; + + lower = from_kgid(lower_ns, KGIDT_INIT(extent->lower_first)); + + seq_printf(seq, "%10u %10u %10u\n", + extent->first, + lower, + extent->count); + + return 0; +} +#endif + static void *m_start(struct seq_file *seq, loff_t *ppos, struct uid_gid_map *map) { @@ -674,6 +728,22 @@ static void *projid_m_start(struct seq_file *seq, loff_t *ppos) return m_start(seq, ppos, &ns->projid_map); } +#ifdef CONFIG_USER_NS_FSID +static void *fsuid_m_start(struct seq_file *seq, loff_t *ppos) +{ + struct user_namespace *ns = seq->private; + + return m_start(seq, ppos, &ns->fsuid_map); +} + +static void *fsgid_m_start(struct seq_file *seq, loff_t *ppos) +{ + struct user_namespace *ns = seq->private; + + return m_start(seq, ppos, &ns->fsgid_map); +} +#endif + static void *m_next(struct seq_file *seq, void *v, loff_t *pos) { (*pos)++; @@ -706,6 +776,22 @@ const struct seq_operations proc_projid_seq_operations = { .show = projid_m_show, }; +#ifdef CONFIG_USER_NS_FSID +const struct seq_operations proc_fsuid_seq_operations = { + .start = fsuid_m_start, + .stop = m_stop, + .next = m_next, + .show = fsuid_m_show, +}; + +const struct seq_operations proc_fsgid_seq_operations = { + .start = fsgid_m_start, + .stop = m_stop, + .next = m_next, + .show = fsgid_m_show, +}; +#endif + static bool mappings_overlap(struct uid_gid_map *new_map, struct uid_gid_extent *extent) { @@ -1081,6 +1167,42 @@ ssize_t proc_projid_map_write(struct file *file, const char __user *buf, &ns->projid_map, &ns->parent->projid_map); } +#ifdef CONFIG_USER_NS_FSID +ssize_t proc_fsuid_map_write(struct file *file, const char __user *buf, + size_t size, loff_t *ppos) +{ + struct seq_file *seq = file->private_data; + struct user_namespace *ns = seq->private; + struct user_namespace *seq_ns = seq_user_ns(seq); + + if (!ns->parent) + return -EPERM; + + if ((seq_ns != ns) && (seq_ns != ns->parent)) + return -EPERM; + + return map_write(file, buf, size, ppos, CAP_SETUID, &ns->fsuid_map, + &ns->parent->fsuid_map); +} + +ssize_t proc_fsgid_map_write(struct file *file, const char __user *buf, + size_t size, loff_t *ppos) +{ + struct seq_file *seq = file->private_data; + struct user_namespace *ns = seq->private; + struct user_namespace *seq_ns = seq_user_ns(seq); + + if (!ns->parent) + return -EPERM; + + if ((seq_ns != ns) && (seq_ns != ns->parent)) + return -EPERM; + + return map_write(file, buf, size, ppos, CAP_SETGID, &ns->fsgid_map, + &ns->parent->fsgid_map); +} +#endif + static bool new_idmap_permitted(const struct file *file, struct user_namespace *ns, int cap_setid, struct uid_gid_map *new_map) From patchwork Tue Feb 18 14:33:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388641 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 804431395 for ; Tue, 18 Feb 2020 14:35:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6B9C721D56 for ; Tue, 18 Feb 2020 14:35:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726770AbgBROff (ORCPT ); Tue, 18 Feb 2020 09:35:35 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52955 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726567AbgBROfe (ORCPT ); Tue, 18 Feb 2020 09:35:34 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xq-0000fF-Bx; Tue, 18 Feb 2020 14:35:06 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 02/25] proc: add /proc//fsuid_map Date: Tue, 18 Feb 2020 15:33:48 +0100 Message-Id: <20200218143411.2389182-3-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: The /proc//fsuid_map file can be written once to setup an fsuid mapping for a user namespace. Writing to this file has the same restrictions as writing to /proc//fsuid_map: root@e1-vm:/# cat /proc/13023/fsuid_map 0 300000 100000 Fsid mappings have always been around. They are currently always identical to the id mappings for a user namespace. This means, currently whenever an fsid needs to be looked up the kernel will use the id mapping of the user namespace. With the introduction of fsid mappings the kernel will now lookup fsids in the fsid mappings of the user namespace. If no fsid mapping exists the kernel will continue looking up fsids in the id mappings of the user namespace. Hence, if a system supports fsid mappings through /proc//fs*id_map and a container runtime is not aware of fsid mappings it or does not use them it will it will continue to work just as before. Signed-off-by: Christian Brauner Acked-by: Serge Hallyn --- /* v2 */ unchanged /* v3 */ - Christian Brauner : - Fix grammar in commit message. --- fs/proc/base.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs/proc/base.c b/fs/proc/base.c index c7c64272b0fa..5fb28004663e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2970,6 +2970,13 @@ static int proc_projid_map_open(struct inode *inode, struct file *file) return proc_id_map_open(inode, file, &proc_projid_seq_operations); } +#ifdef CONFIG_USER_NS_FSID +static int proc_fsuid_map_open(struct inode *inode, struct file *file) +{ + return proc_id_map_open(inode, file, &proc_fsuid_seq_operations); +} +#endif + static const struct file_operations proc_uid_map_operations = { .open = proc_uid_map_open, .write = proc_uid_map_write, @@ -2994,6 +3001,16 @@ static const struct file_operations proc_projid_map_operations = { .release = proc_id_map_release, }; +#ifdef CONFIG_USER_NS_FSID +static const struct file_operations proc_fsuid_map_operations = { + .open = proc_fsuid_map_open, + .write = proc_fsuid_map_write, + .read = seq_read, + .llseek = seq_lseek, + .release = proc_id_map_release, +}; +#endif + static int proc_setgroups_open(struct inode *inode, struct file *file) { struct user_namespace *ns = NULL; @@ -3176,6 +3193,9 @@ static const struct pid_entry tgid_base_stuff[] = { ONE("io", S_IRUSR, proc_tgid_io_accounting), #endif #ifdef CONFIG_USER_NS +#ifdef CONFIG_USER_NS_FSID + REG("fsuid_map", S_IRUGO|S_IWUSR, proc_fsuid_map_operations), +#endif REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), From patchwork Tue Feb 18 14:33:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388715 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 629C61892 for ; Tue, 18 Feb 2020 14:37:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4DB8724649 for ; Tue, 18 Feb 2020 14:37:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726783AbgBROgu (ORCPT ); Tue, 18 Feb 2020 09:36:50 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52971 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726645AbgBROff (ORCPT ); Tue, 18 Feb 2020 09:35:35 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xr-0000fF-7O; Tue, 18 Feb 2020 14:35:07 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 03/25] proc: add /proc//fsgid_map Date: Tue, 18 Feb 2020 15:33:49 +0100 Message-Id: <20200218143411.2389182-4-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: The /proc//fsgid_map file can be written once to setup an fsgid mapping for a user namespace. Writing to this file has the same restrictions as writing to /proc//fsgid_map. root@e1-vm:/# cat /proc/13023/fsgid_map 0 300000 100000 Fsid mappings have always been around. They are currently always identical to the id mappings for a user namespace. This means, currently whenever an fsid needs to be looked up the kernel will use the id mapping of the user namespace. With the introduction of fsid mappings the kernel will now lookup fsids in the fsid mappings of the user namespace. If no fsid mapping exists the kernel will continue looking up fsids in the id mappings of the user namespace. Hence, if a system supports fsid mappings through /proc//fs*id_map and a container runtime is not aware of fsid mappings it or does not use them it will it will continue to work just as before. Signed-off-by: Christian Brauner Acked-by: Serge Hallyn --- /* v2 */ unchanged /* v3 */ - Christian Brauner : - Fix grammar in commit message. --- fs/proc/base.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/proc/base.c b/fs/proc/base.c index 5fb28004663e..1303cdd2e617 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2975,6 +2975,11 @@ static int proc_fsuid_map_open(struct inode *inode, struct file *file) { return proc_id_map_open(inode, file, &proc_fsuid_seq_operations); } + +static int proc_fsgid_map_open(struct inode *inode, struct file *file) +{ + return proc_id_map_open(inode, file, &proc_fsgid_seq_operations); +} #endif static const struct file_operations proc_uid_map_operations = { @@ -3009,6 +3014,14 @@ static const struct file_operations proc_fsuid_map_operations = { .llseek = seq_lseek, .release = proc_id_map_release, }; + +static const struct file_operations proc_fsgid_map_operations = { + .open = proc_fsgid_map_open, + .write = proc_fsgid_map_write, + .read = seq_read, + .llseek = seq_lseek, + .release = proc_id_map_release, +}; #endif static int proc_setgroups_open(struct inode *inode, struct file *file) @@ -3195,6 +3208,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_USER_NS #ifdef CONFIG_USER_NS_FSID REG("fsuid_map", S_IRUGO|S_IWUSR, proc_fsuid_map_operations), + REG("fsgid_map", S_IRUGO|S_IWUSR, proc_fsgid_map_operations), #endif REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), From patchwork Tue Feb 18 14:33:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388685 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DA054924 for ; Tue, 18 Feb 2020 14:36:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B42A12464E for ; Tue, 18 Feb 2020 14:36:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727778AbgBROgX (ORCPT ); Tue, 18 Feb 2020 09:36:23 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53030 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726830AbgBROfj (ORCPT ); Tue, 18 Feb 2020 09:35:39 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xs-0000fF-2l; Tue, 18 Feb 2020 14:35:08 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 04/25] fsuidgid: add fsid mapping helpers Date: Tue, 18 Feb 2020 15:33:50 +0100 Message-Id: <20200218143411.2389182-5-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: This adds a set of helpers to translate between kfsuid/kfsgid and their userspace fsuid/fsgid counter parts relative to a given user namespace. - kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid) Maps a user-namespace fsuid pair into a kfsuid. If no fsuid mappings have been written it behaves identical to calling make_kuid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid) Maps a user-namespace fsgid pair into a kfsgid. If no fsgid mappings have been written it behaves identical to calling make_kgid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - uid_t from_kfsuid(struct user_namespace *to, kuid_t fsuid) Creates a fsuid from a kfsuid user-namespace pair if possible. If no fsuid mappings have been written it behaves identical to calling from_kuid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - gid_t from_kfsgid(struct user_namespace *to, kgid_t fsgid) Creates a fsgid from a kfsgid user-namespace pair if possible. If no fsgid mappings have been written it behaves identical to calling make_kgid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t fsuid) Always creates a fsuid from a kfsuid user-namespace pair. If no fsuid mappings have been written it behaves identical to calling from_kuid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t fsgid) Always creates a fsgid from a kfsgid user-namespace pair if possible. If no fsgid mappings have been written it behaves identical to calling make_kgid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t uid) Check whether this kfsuid has a mapping in the provided user namespace. If no fsuid mappings have been written it behaves identical to calling from_kuid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t gid) Check whether this kfsgid has a mapping in the provided user namespace. If no fsgid mappings have been written it behaves identical to calling make_kgid(). This ensures backwards compatibility for workloads unaware or not in need of fsid mappings. - kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid) Translate from a kfsuid into a kuid. - kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid) Translate from a kfsgid into a kgid. - kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid) Translate from a kuid into a kfsuid. - kgid_t kgid_to_kfsuid(struct user_namespace *to, kgid_t kgid) Translate from a kgid into a kfsgid. Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ - Jann Horn : - Split changes to map_write() to implement fsid mappings into three separate patches: basic fsid helpers, preparatory changes to map_write(), actual fsid mapping support in map_write(). --- include/linux/fsuidgid.h | 122 +++++++++++++++++++++++++++++++++ kernel/user_namespace.c | 141 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 include/linux/fsuidgid.h diff --git a/include/linux/fsuidgid.h b/include/linux/fsuidgid.h new file mode 100644 index 000000000000..46763591f4e6 --- /dev/null +++ b/include/linux/fsuidgid.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_FSUIDGID_H +#define _LINUX_FSUIDGID_H + +#include + +#ifdef CONFIG_USER_NS_FSID + +extern kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid); +extern kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid); +extern uid_t from_kfsuid(struct user_namespace *to, kuid_t kfsuid); +extern gid_t from_kfsgid(struct user_namespace *to, kgid_t kfsgid); +extern uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t kfsuid); +extern gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t kfsgid); + +static inline bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t kfsuid) +{ + return from_kfsuid(ns, kfsuid) != (uid_t) -1; +} + +static inline bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t kfsgid) +{ + return from_kfsgid(ns, kfsgid) != (gid_t) -1; +} + +static inline kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid) +{ + uid_t fsuid = from_kfsuid(to, kfsuid); + if (fsuid == (uid_t) -1) + return INVALID_UID; + return make_kuid(to, fsuid); +} + +static inline kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid) +{ + gid_t fsgid = from_kfsgid(to, kfsgid); + if (fsgid == (gid_t) -1) + return INVALID_GID; + return make_kgid(to, fsgid); +} + +static inline kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid) +{ + uid_t uid = from_kuid(to, kuid); + if (uid == (uid_t) -1) + return INVALID_UID; + return make_kfsuid(to, uid); +} + +static inline kgid_t kgid_to_kfsgid(struct user_namespace *to, kgid_t kgid) +{ + gid_t gid = from_kgid(to, kgid); + if (gid == (gid_t) -1) + return INVALID_GID; + return make_kfsgid(to, gid); +} + +#else + +static inline kuid_t make_kfsuid(struct user_namespace *from, uid_t fsuid) +{ + return make_kuid(from, fsuid); +} + +static inline kgid_t make_kfsgid(struct user_namespace *from, gid_t fsgid) +{ + return make_kgid(from, fsgid); +} + +static inline uid_t from_kfsuid(struct user_namespace *to, kuid_t kfsuid) +{ + return from_kuid(to, kfsuid); +} + +static inline gid_t from_kfsgid(struct user_namespace *to, kgid_t kfsgid) +{ + return from_kgid(to, kfsgid); +} + +static inline uid_t from_kfsuid_munged(struct user_namespace *to, kuid_t kfsuid) +{ + return from_kuid_munged(to, kfsuid); +} + +static inline gid_t from_kfsgid_munged(struct user_namespace *to, kgid_t kfsgid) +{ + return from_kgid_munged(to, kfsgid); +} + +static inline bool kfsuid_has_mapping(struct user_namespace *ns, kuid_t kfsuid) +{ + return kuid_has_mapping(ns, kfsuid); +} + +static inline bool kfsgid_has_mapping(struct user_namespace *ns, kgid_t kfsgid) +{ + return kgid_has_mapping(ns, kfsgid); +} + +static inline kuid_t kfsuid_to_kuid(struct user_namespace *to, kuid_t kfsuid) +{ + return kfsuid; +} + +static inline kgid_t kfsgid_to_kgid(struct user_namespace *to, kgid_t kfsgid) +{ + return kfsgid; +} + +static inline kuid_t kuid_to_kfsuid(struct user_namespace *to, kuid_t kuid) +{ + return kuid; +} + +static inline kgid_t kgid_to_kfsgid(struct user_namespace *to, kgid_t kgid) +{ + return kgid; +} + +#endif /* CONFIG_USER_NS_FSID */ + +#endif /* _LINUX_FSUIDGID_H */ diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index cbdf456f95f0..2cfd1e519cc4 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex); @@ -583,6 +584,142 @@ projid_t from_kprojid_munged(struct user_namespace *targ, kprojid_t kprojid) } EXPORT_SYMBOL(from_kprojid_munged); +#ifdef CONFIG_USER_NS_FSID +/** + * make_kfsuid - Map a user-namespace fsuid pair into a kuid. + * @ns: User namespace that the fsuid is in + * @fsuid: User identifier + * + * Maps a user-namespace fsuid pair into a kernel internal kfsuid, + * and returns that kfsuid. + * + * When there is no mapping defined for the user-namespace kfsuid + * pair INVALID_UID is returned. Callers are expected to test + * for and handle INVALID_UID being returned. INVALID_UID + * may be tested for using uid_valid(). + */ +kuid_t make_kfsuid(struct user_namespace *ns, uid_t fsuid) +{ + /* Map the fsuid to a global kernel fsuid */ + return KUIDT_INIT(map_id_down(&ns->fsuid_map, fsuid)); +} +EXPORT_SYMBOL(make_kfsuid); + +/** + * from_kfsuid - Create a fsuid from a kfsuid user-namespace pair. + * @targ: The user namespace we want a fsuid in. + * @kfsuid: The kernel internal fsuid to start with. + * + * Map @kfsuid into the user-namespace specified by @targ and + * return the resulting fsuid. + * + * There is always a mapping into the initial user_namespace. + * + * If @kfsuid has no mapping in @targ (uid_t)-1 is returned. + */ +uid_t from_kfsuid(struct user_namespace *targ, kuid_t kfsuid) +{ + /* Map the fsuid from a global kernel fsuid */ + return map_id_up(&targ->fsuid_map, __kuid_val(kfsuid)); +} +EXPORT_SYMBOL(from_kfsuid); + +/** + * from_kfsuid_munged - Create a fsuid from a kfsuid user-namespace pair. + * @targ: The user namespace we want a fsuid in. + * @kfsuid: The kernel internal fsuid to start with. + * + * Map @kfsuid into the user-namespace specified by @targ and + * return the resulting fsuid. + * + * There is always a mapping into the initial user_namespace. + * + * Unlike from_kfsuid from_kfsuid_munged never fails and always + * returns a valid fsuid. This makes from_kfsuid_munged appropriate + * for use in syscalls like stat and getuid where failing the + * system call and failing to provide a valid fsuid are not an + * options. + * + * If @kfsuid has no mapping in @targ overflowuid is returned. + */ +uid_t from_kfsuid_munged(struct user_namespace *targ, kuid_t kfsuid) +{ + uid_t fsuid; + fsuid = from_kfsuid(targ, kfsuid); + + if (fsuid == (uid_t) -1) + fsuid = overflowuid; + return fsuid; +} +EXPORT_SYMBOL(from_kfsuid_munged); + +/** + * make_kfsgid - Map a user-namespace fsgid pair into a kfsgid. + * @ns: User namespace that the fsgid is in + * @fsgid: User identifier + * + * Maps a user-namespace fsgid pair into a kernel internal kfsgid, + * and returns that kfsgid. + * + * When there is no mapping defined for the user-namespace fsgid + * pair INVALID_GID is returned. Callers are expected to test + * for and handle INVALID_GID being returned. INVALID_GID + * may be tested for using gid_valid(). + */ +kgid_t make_kfsgid(struct user_namespace *ns, gid_t fsgid) +{ + /* Map the fsgid to a global kernel fsgid */ + return KGIDT_INIT(map_id_down(&ns->fsgid_map, fsgid)); +} +EXPORT_SYMBOL(make_kfsgid); + +/** + * from_kfsgid - Create a fsgid from a kfsgid user-namespace pair. + * @targ: The user namespace we want a fsgid in. + * @kfsgid: The kernel internal fsgid to start with. + * + * Map @kfsgid into the user-namespace specified by @targ and + * return the resulting fsgid. + * + * There is always a mapping into the initial user_namespace. + * + * If @kfsgid has no mapping in @targ (gid_t)-1 is returned. + */ +gid_t from_kfsgid(struct user_namespace *targ, kgid_t kfsgid) +{ + /* Map the fsgid from a global kernel fsgid */ + return map_id_up(&targ->fsgid_map, __kgid_val(kfsgid)); +} +EXPORT_SYMBOL(from_kfsgid); + +/** + * from_kfsgid_munged - Create a fsgid from a kfsgid user-namespace pair. + * @targ: The user namespace we want a fsgid in. + * @kfsgid: The kernel internal fsgid to start with. + * + * Map @kfsgid into the user-namespace specified by @targ and + * return the resulting fsgid. + * + * There is always a mapping into the initial user_namespace. + * + * Unlike from_kfsgid from_kfsgid_munged never fails and always + * returns a valid fsgid. This makes from_kfsgid_munged appropriate + * for use in syscalls like stat and getgid where failing the + * system call and failing to provide a valid fsgid are not options. + * + * If @kfsgid has no mapping in @targ overflowgid is returned. + */ +gid_t from_kfsgid_munged(struct user_namespace *targ, kgid_t kfsgid) +{ + gid_t fsgid; + fsgid = from_kfsgid(targ, kfsgid); + + if (fsgid == (gid_t) -1) + fsgid = overflowgid; + return fsgid; +} +EXPORT_SYMBOL(from_kfsgid_munged); +#endif /* CONFIG_USER_NS_FSID */ static int uid_m_show(struct seq_file *seq, void *v) { @@ -659,7 +796,7 @@ static int fsuid_m_show(struct seq_file *seq, void *v) if ((lower_ns == ns) && lower_ns->parent) lower_ns = lower_ns->parent; - lower = from_kuid(lower_ns, KUIDT_INIT(extent->lower_first)); + lower = from_kfsuid(lower_ns, KUIDT_INIT(extent->lower_first)); seq_printf(seq, "%10u %10u %10u\n", extent->first, @@ -680,7 +817,7 @@ static int fsgid_m_show(struct seq_file *seq, void *v) if ((lower_ns == ns) && lower_ns->parent) lower_ns = lower_ns->parent; - lower = from_kgid(lower_ns, KGIDT_INIT(extent->lower_first)); + lower = from_kfsgid(lower_ns, KGIDT_INIT(extent->lower_first)); seq_printf(seq, "%10u %10u %10u\n", extent->first, From patchwork Tue Feb 18 14:33:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388689 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 285D81395 for ; Tue, 18 Feb 2020 14:36:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 13DB721D56 for ; Tue, 18 Feb 2020 14:36:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727761AbgBROgW (ORCPT ); Tue, 18 Feb 2020 09:36:22 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53031 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726840AbgBROfj (ORCPT ); Tue, 18 Feb 2020 09:35:39 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xt-0000fF-2o; Tue, 18 Feb 2020 14:35:09 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 05/25] user_namespace: refactor map_write() Date: Tue, 18 Feb 2020 15:33:51 +0100 Message-Id: <20200218143411.2389182-6-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Refactor map_write() to prepare for adding fsid mappings support. This mainly factors out various open-coded parts into helpers that can be reused in the follow up patch. Cc: Jann Horn Signed-off-by: Christian Brauner Acked-by: Serge Hallyn --- /* v2 */ patch not present /* v3 */ patch added - Jann Horn : - Split changes to map_write() to implement fsid mappings into three separate patches: basic fsid helpers, preparatory changes to map_write(), actual fsid mapping support in map_write(). --- kernel/user_namespace.c | 117 +++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 2cfd1e519cc4..e91141262bcc 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -1038,10 +1038,10 @@ static int cmp_extents_reverse(const void *a, const void *b) } /** - * sort_idmaps - Sorts an array of idmap entries. + * sort_map - Sorts an array of idmap entries. * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. */ -static int sort_idmaps(struct uid_gid_map *map) +static int sort_map(struct uid_gid_map *map) { if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) return 0; @@ -1064,6 +1064,71 @@ static int sort_idmaps(struct uid_gid_map *map) return 0; } +static int sort_idmaps(struct uid_gid_map *map) +{ + return sort_map(map); +} + +static int map_from_parent(struct uid_gid_map *new_map, + struct uid_gid_map *parent_map) +{ + unsigned idx; + + /* Map the lower ids from the parent user namespace to the + * kernel global id space. + */ + for (idx = 0; idx < new_map->nr_extents; idx++) { + struct uid_gid_extent *e; + u32 lower_first; + + if (new_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + e = &new_map->extent[idx]; + else + e = &new_map->forward[idx]; + + lower_first = map_id_range_down(parent_map, e->lower_first, e->count); + + /* Fail if we can not map the specified extent to + * the kernel global id space. + */ + if (lower_first == (u32)-1) + return -EPERM; + + e->lower_first = lower_first; + } + + return 0; +} + +static int map_into_kids(struct uid_gid_map *id_map, + struct uid_gid_map *parent_id_map) +{ + return map_from_parent(id_map, parent_id_map); +} + +static void install_idmaps(struct uid_gid_map *id_map, + struct uid_gid_map *new_id_map) +{ + if (new_id_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { + memcpy(id_map->extent, new_id_map->extent, + new_id_map->nr_extents * sizeof(new_id_map->extent[0])); + } else { + id_map->forward = new_id_map->forward; + id_map->reverse = new_id_map->reverse; + } +} + +static void free_idmaps(struct uid_gid_map *new_id_map) +{ + if (new_id_map->nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(new_id_map->forward); + kfree(new_id_map->reverse); + new_id_map->forward = NULL; + new_id_map->reverse = NULL; + new_id_map->nr_extents = 0; + } +} + static ssize_t map_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int cap_setid, @@ -1073,7 +1138,6 @@ static ssize_t map_write(struct file *file, const char __user *buf, struct seq_file *seq = file->private_data; struct user_namespace *ns = seq->private; struct uid_gid_map new_map; - unsigned idx; struct uid_gid_extent extent; char *kbuf = NULL, *pos, *next_line; ssize_t ret; @@ -1191,61 +1255,28 @@ static ssize_t map_write(struct file *file, const char __user *buf, if (!new_idmap_permitted(file, ns, cap_setid, &new_map)) goto out; - ret = -EPERM; - /* Map the lower ids from the parent user namespace to the - * kernel global id space. - */ - for (idx = 0; idx < new_map.nr_extents; idx++) { - struct uid_gid_extent *e; - u32 lower_first; - - if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) - e = &new_map.extent[idx]; - else - e = &new_map.forward[idx]; - - lower_first = map_id_range_down(parent_map, - e->lower_first, - e->count); - - /* Fail if we can not map the specified extent to - * the kernel global id space. - */ - if (lower_first == (u32) -1) - goto out; - - e->lower_first = lower_first; - } + ret = map_into_kids(&new_map, parent_map); + if (ret) + goto out; /* * If we want to use binary search for lookup, this clones the extent * array and sorts both copies. */ ret = sort_idmaps(&new_map); - if (ret < 0) + if (ret) goto out; /* Install the map */ - if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { - memcpy(map->extent, new_map.extent, - new_map.nr_extents * sizeof(new_map.extent[0])); - } else { - map->forward = new_map.forward; - map->reverse = new_map.reverse; - } + install_idmaps(map, &new_map); smp_wmb(); map->nr_extents = new_map.nr_extents; *ppos = count; ret = count; out: - if (ret < 0 && new_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { - kfree(new_map.forward); - kfree(new_map.reverse); - map->forward = NULL; - map->reverse = NULL; - map->nr_extents = 0; - } + if (ret < 0) + free_idmaps(&new_map); mutex_unlock(&userns_state_mutex); kfree(kbuf); From patchwork Tue Feb 18 14:33:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388677 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4C208924 for ; Tue, 18 Feb 2020 14:36:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2EBDD2464E for ; Tue, 18 Feb 2020 14:36:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726811AbgBROgR (ORCPT ); Tue, 18 Feb 2020 09:36:17 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53029 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726829AbgBROfk (ORCPT ); Tue, 18 Feb 2020 09:35:40 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xu-0000fF-4M; Tue, 18 Feb 2020 14:35:10 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 06/25] user_namespace: make map_write() support fsid mappings Date: Tue, 18 Feb 2020 15:33:52 +0100 Message-Id: <20200218143411.2389182-7-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Based on discussions with Jann we decided in order to cleanly handle nested user namespaces that fsid mappings can only be written before the corresponding id mappings have been written. Writing id mappings before writing the corresponding fsid mappings causes fsid mappings to mirror id mappings. Consider creating a user namespace NS1 with the initial user namespace as parent. Assume NS1 receives id mapping 0 100000 100000 and fsid mappings 0 300000 100000. Files that root in NS1 will create will map to kfsuid=300000 and kfsgid=300000 and will hence be owned by uid=300000 and gid 300000 on-disk in the initial user namespace. Now assume user namespace NS2 is created in user namespace NS1. Assume that NS2 receives id mapping 0 10000 65536 and an fsid mapping of 0 10000 65536. Files that root in NS2 will create will map to kfsuid=10000 and kfsgid=10000 in NS1. hence, files created by NS2 will hence be appear to be be owned by uid=10000 and gid=10000 on-disk in NS1. Looking at the initial user namespace, files created by NS2 will map to kfsuid=310000 and kfsgid=310000 and hence will be owned by uid=310000 and gid=310000 on-disk. Suggested-by: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ patch not present /* v3 */ patch added - Jann Horn : - Split changes to map_write() to implement fsid mappings into three separate patches: basic fsid helpers, preparatory changes to map_write(), actual fsid mapping support in map_write(). --- kernel/user_namespace.c | 165 ++++++++++++++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 22 deletions(-) diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index e91141262bcc..7905ca19dfab 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -25,9 +25,18 @@ static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex); +enum idmap_type { + UID_MAP, + GID_MAP, + FSUID_MAP, + FSGID_MAP, + PROJID_MAP, +}; + static bool new_idmap_permitted(const struct file *file, struct user_namespace *ns, int cap_setid, - struct uid_gid_map *map); + struct uid_gid_map *map, + enum idmap_type idmap_type); static void free_user_ns(struct work_struct *work); static struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid) @@ -913,6 +922,16 @@ const struct seq_operations proc_projid_seq_operations = { .show = projid_m_show, }; +static inline bool idmap_exists(const struct uid_gid_map *map) +{ + return map && map->nr_extents != 0; +} + +static inline bool idmap_type_wants_fsidmap(enum idmap_type type) +{ + return type == UID_MAP || type == GID_MAP; +} + #ifdef CONFIG_USER_NS_FSID const struct seq_operations proc_fsuid_seq_operations = { .start = fsuid_m_start, @@ -927,6 +946,31 @@ const struct seq_operations proc_fsgid_seq_operations = { .next = m_next, .show = fsgid_m_show, }; + +static int idmap_to_fsidmap(struct uid_gid_map *id_map, + struct uid_gid_map *fsid_map, + struct uid_gid_map *new_fsid_map, + enum idmap_type type) +{ + if (!idmap_type_wants_fsidmap(type) || idmap_exists(fsid_map)) + return 0; + + /* fsid maps mirror id maps. */ + if (id_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { + memcpy(new_fsid_map, id_map, sizeof(struct uid_gid_map)); + return 0; + } + + memset(new_fsid_map, 0, sizeof(struct uid_gid_map)); + new_fsid_map->forward = kmemdup(id_map->forward, + id_map->nr_extents * sizeof(struct uid_gid_extent), + GFP_KERNEL); + if (!new_fsid_map->forward) + return -ENOMEM; + new_fsid_map->nr_extents = id_map->nr_extents; + + return 0; +} #endif static bool mappings_overlap(struct uid_gid_map *new_map, @@ -1064,9 +1108,17 @@ static int sort_map(struct uid_gid_map *map) return 0; } -static int sort_idmaps(struct uid_gid_map *map) +static int sort_idmaps(struct uid_gid_map *map, + struct uid_gid_map *new_fsid_map) { - return sort_map(map); + int ret; + + ret = sort_map(map); + if (ret) + return ret; + + /* Sort fsid maps in case they mirror id maps. */ + return sort_map(new_fsid_map); } static int map_from_parent(struct uid_gid_map *new_map, @@ -1101,13 +1153,31 @@ static int map_from_parent(struct uid_gid_map *new_map, } static int map_into_kids(struct uid_gid_map *id_map, - struct uid_gid_map *parent_id_map) + struct uid_gid_map *parent_id_map, + struct user_namespace *ns, + struct uid_gid_map *new_fsid_map, enum idmap_type type) { - return map_from_parent(id_map, parent_id_map); + int ret; + + ret = map_from_parent(id_map, parent_id_map); + if (ret) + return ret; + +#ifdef CONFIG_USER_NS_FSID + /* fsid maps mirror id maps. */ + if (idmap_type_wants_fsidmap(type) && idmap_exists(new_fsid_map)) + ret = map_from_parent(new_fsid_map, + type == UID_MAP ? &ns->parent->fsuid_map : + &ns->parent->fsgid_map); +#endif + return ret; } static void install_idmaps(struct uid_gid_map *id_map, - struct uid_gid_map *new_id_map) + struct uid_gid_map *new_id_map, + struct uid_gid_map *fsid_map, + struct uid_gid_map *new_fsid_map, + enum idmap_type type) { if (new_id_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { memcpy(id_map->extent, new_id_map->extent, @@ -1116,9 +1186,21 @@ static void install_idmaps(struct uid_gid_map *id_map, id_map->forward = new_id_map->forward; id_map->reverse = new_id_map->reverse; } + + if (idmap_type_wants_fsidmap(type) && idmap_exists(new_fsid_map)) { + /* fsid maps mirror id maps. */ + if (new_fsid_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { + memcpy(fsid_map->extent, new_fsid_map->extent, + new_fsid_map->nr_extents * sizeof(new_fsid_map->extent[0])); + } else { + fsid_map->forward = new_fsid_map->forward; + fsid_map->reverse = new_fsid_map->reverse; + } + } } -static void free_idmaps(struct uid_gid_map *new_id_map) +static void free_idmaps(struct uid_gid_map *new_id_map, + struct uid_gid_map *new_fsid_map) { if (new_id_map->nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { kfree(new_id_map->forward); @@ -1127,17 +1209,28 @@ static void free_idmaps(struct uid_gid_map *new_id_map) new_id_map->reverse = NULL; new_id_map->nr_extents = 0; } + + /* fsid maps mirror id maps. */ + if (new_fsid_map->nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(new_fsid_map->forward); + kfree(new_fsid_map->reverse); + new_fsid_map->forward = NULL; + new_fsid_map->reverse = NULL; + new_fsid_map->nr_extents = 0; + } } static ssize_t map_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int cap_setid, struct uid_gid_map *map, - struct uid_gid_map *parent_map) + struct uid_gid_map *parent_map, + enum idmap_type type) { struct seq_file *seq = file->private_data; struct user_namespace *ns = seq->private; - struct uid_gid_map new_map; + struct uid_gid_map *fsid_map = NULL; + struct uid_gid_map new_map, new_fsid_map; struct uid_gid_extent extent; char *kbuf = NULL, *pos, *next_line; ssize_t ret; @@ -1173,6 +1266,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, mutex_lock(&userns_state_mutex); memset(&new_map, 0, sizeof(struct uid_gid_map)); + new_fsid_map.nr_extents = 0; ret = -EPERM; /* Only allow one successful write to the map */ @@ -1252,10 +1346,21 @@ static ssize_t map_write(struct file *file, const char __user *buf, ret = -EPERM; /* Validate the user is allowed to use user id's mapped to. */ - if (!new_idmap_permitted(file, ns, cap_setid, &new_map)) + if (!new_idmap_permitted(file, ns, cap_setid, &new_map, type)) + goto out; + +#ifdef CONFIG_USER_NS_FSID + /* Take pointer to fsid maps in case we're mirroring id maps. */ + if (type == UID_MAP) + fsid_map = &ns->fsuid_map; + else if (type == GID_MAP) + fsid_map = &ns->fsgid_map; + ret = idmap_to_fsidmap(&new_map, fsid_map, &new_fsid_map, type); + if (ret) goto out; +#endif - ret = map_into_kids(&new_map, parent_map); + ret = map_into_kids(&new_map, parent_map, ns, &new_fsid_map, type); if (ret) goto out; @@ -1263,20 +1368,22 @@ static ssize_t map_write(struct file *file, const char __user *buf, * If we want to use binary search for lookup, this clones the extent * array and sorts both copies. */ - ret = sort_idmaps(&new_map); + ret = sort_idmaps(&new_map, &new_fsid_map); if (ret) goto out; /* Install the map */ - install_idmaps(map, &new_map); + install_idmaps(map, &new_map, fsid_map, &new_fsid_map, type); smp_wmb(); map->nr_extents = new_map.nr_extents; + if (idmap_exists(&new_fsid_map)) + fsid_map->nr_extents = new_fsid_map.nr_extents; *ppos = count; ret = count; out: if (ret < 0) - free_idmaps(&new_map); + free_idmaps(&new_map, &new_fsid_map); mutex_unlock(&userns_state_mutex); kfree(kbuf); @@ -1297,7 +1404,7 @@ ssize_t proc_uid_map_write(struct file *file, const char __user *buf, return -EPERM; return map_write(file, buf, size, ppos, CAP_SETUID, - &ns->uid_map, &ns->parent->uid_map); + &ns->uid_map, &ns->parent->uid_map, UID_MAP); } ssize_t proc_gid_map_write(struct file *file, const char __user *buf, @@ -1314,7 +1421,7 @@ ssize_t proc_gid_map_write(struct file *file, const char __user *buf, return -EPERM; return map_write(file, buf, size, ppos, CAP_SETGID, - &ns->gid_map, &ns->parent->gid_map); + &ns->gid_map, &ns->parent->gid_map, GID_MAP); } ssize_t proc_projid_map_write(struct file *file, const char __user *buf, @@ -1332,7 +1439,7 @@ ssize_t proc_projid_map_write(struct file *file, const char __user *buf, /* Anyone can set any valid project id no capability needed */ return map_write(file, buf, size, ppos, -1, - &ns->projid_map, &ns->parent->projid_map); + &ns->projid_map, &ns->parent->projid_map, PROJID_MAP); } #ifdef CONFIG_USER_NS_FSID @@ -1350,7 +1457,7 @@ ssize_t proc_fsuid_map_write(struct file *file, const char __user *buf, return -EPERM; return map_write(file, buf, size, ppos, CAP_SETUID, &ns->fsuid_map, - &ns->parent->fsuid_map); + &ns->parent->fsuid_map, FSUID_MAP); } ssize_t proc_fsgid_map_write(struct file *file, const char __user *buf, @@ -1367,15 +1474,25 @@ ssize_t proc_fsgid_map_write(struct file *file, const char __user *buf, return -EPERM; return map_write(file, buf, size, ppos, CAP_SETGID, &ns->fsgid_map, - &ns->parent->fsgid_map); + &ns->parent->fsgid_map, FSGID_MAP); } #endif static bool new_idmap_permitted(const struct file *file, struct user_namespace *ns, int cap_setid, - struct uid_gid_map *new_map) + struct uid_gid_map *new_map, + enum idmap_type idmap_type) { const struct cred *cred = file->f_cred; + + /* Don't allow writing fsuid maps when uid maps have been written. */ + if (idmap_type == FSUID_MAP && idmap_exists(&ns->uid_map)) + return false; + + /* Don't allow writing fsgid maps when gid maps have been written. */ + if (idmap_type == FSGID_MAP && idmap_exists(&ns->gid_map)) + return false; + /* Don't allow mappings that would allow anything that wouldn't * be allowed without the establishment of unprivileged mappings. */ @@ -1383,11 +1500,15 @@ static bool new_idmap_permitted(const struct file *file, uid_eq(ns->owner, cred->euid)) { u32 id = new_map->extent[0].lower_first; if (cap_setid == CAP_SETUID) { - kuid_t uid = make_kuid(ns->parent, id); + kuid_t uid = idmap_type == FSUID_MAP ? + make_kfsuid(ns->parent, id) : + make_kuid(ns->parent, id); if (uid_eq(uid, cred->euid)) return true; } else if (cap_setid == CAP_SETGID) { - kgid_t gid = make_kgid(ns->parent, id); + kgid_t gid = idmap_type == FSGID_MAP ? + make_kfsgid(ns->parent, id) : + make_kgid(ns->parent, id); if (!(ns->flags & USERNS_SETGROUPS_ALLOWED) && gid_eq(gid, cred->egid)) return true; From patchwork Tue Feb 18 14:33:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388699 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 89F94924 for ; Tue, 18 Feb 2020 14:36:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 74F5F20801 for ; Tue, 18 Feb 2020 14:36:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726801AbgBROfg (ORCPT ); Tue, 18 Feb 2020 09:35:36 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52981 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726750AbgBROff (ORCPT ); Tue, 18 Feb 2020 09:35:35 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xv-0000fF-6f; Tue, 18 Feb 2020 14:35:11 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 07/25] proc: task_state(): use from_kfs{g,u}id_munged Date: Tue, 18 Feb 2020 15:33:53 +0100 Message-Id: <20200218143411.2389182-8-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: If fsid mappings have been written, this will cause proc to look at fsid mappings for the user namespace. If no fsid mappings have been written the behavior is as before. Here is part of the output from /proc//status from the initial user namespace for systemd running in an unprivileged container as user namespace root with id mapping 0 100000 100000 and fsid mapping 0 300000 100000: Name: systemd Umask: 0000 State: S (sleeping) Tgid: 13023 Ngid: 0 Pid: 13023 PPid: 13008 TracerPid: 0 Uid: 100000 100000 100000 300000 Gid: 100000 100000 100000 300000 FDSize: 64 Groups: Signed-off-by: Christian Brauner Acked-by: Serge Hallyn --- /* v2 */ unchanged /* v3 */ unchanged --- fs/proc/array.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 5efaf3708ec6..d4a04f85a67e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -91,6 +91,7 @@ #include #include #include +#include #include #include @@ -193,11 +194,11 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, seq_put_decimal_ull(m, "\nUid:\t", from_kuid_munged(user_ns, cred->uid)); seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->euid)); seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->suid)); - seq_put_decimal_ull(m, "\t", from_kuid_munged(user_ns, cred->fsuid)); + seq_put_decimal_ull(m, "\t", from_kfsuid_munged(user_ns, cred->fsuid)); seq_put_decimal_ull(m, "\nGid:\t", from_kgid_munged(user_ns, cred->gid)); seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->egid)); seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->sgid)); - seq_put_decimal_ull(m, "\t", from_kgid_munged(user_ns, cred->fsgid)); + seq_put_decimal_ull(m, "\t", from_kfsgid_munged(user_ns, cred->fsgid)); seq_put_decimal_ull(m, "\nFDSize:\t", max_fds); seq_puts(m, "\nGroups:\t"); From patchwork Tue Feb 18 14:33:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388683 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 11EC0924 for ; Tue, 18 Feb 2020 14:36:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EFF9E21D56 for ; Tue, 18 Feb 2020 14:36:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727786AbgBROgX (ORCPT ); Tue, 18 Feb 2020 09:36:23 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53041 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726856AbgBROfj (ORCPT ); Tue, 18 Feb 2020 09:35:39 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xw-0000fF-2o; Tue, 18 Feb 2020 14:35:12 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 08/25] cred: add kfs{g,u}id Date: Tue, 18 Feb 2020 15:33:54 +0100 Message-Id: <20200218143411.2389182-9-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: After the introduction of fsid mappings we need to carefully handle single-superblock filesystems that are visible in user namespaces. This specifically concerns proc and sysfs. For those filesystems we want to continue looking up fsid in the id mappings of the relevant user namespace. We can either do this by dynamically translating between these fsids or we simply keep them around with the other creds. The latter option is not just simpler but also more performant since we don't need to do the translation from fsid mappings into id mappings on the fly. Link: https://lore.kernel.org/r/20200212145149.zohmc6d3x52bw6j6@wittgenstein Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ patch added /* v3 */ unchanged --- include/linux/cred.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/cred.h b/include/linux/cred.h index 18639c069263..604914d3fd51 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -125,6 +125,8 @@ struct cred { kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ + kuid_t kfsuid; /* UID for VFS ops for userns visible filesystems */ + kgid_t kfsgid; /* GID for VFS ops for userns visible filesystems */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ @@ -384,6 +386,8 @@ static inline void put_cred(const struct cred *_cred) #define current_sgid() (current_cred_xxx(sgid)) #define current_fsuid() (current_cred_xxx(fsuid)) #define current_fsgid() (current_cred_xxx(fsgid)) +#define current_kfsuid() (current_cred_xxx(kfsuid)) +#define current_kfsgid() (current_cred_xxx(kfsgid)) #define current_cap() (current_cred_xxx(cap_effective)) #define current_user() (current_cred_xxx(user)) From patchwork Tue Feb 18 14:33:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388721 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8058192A for ; Tue, 18 Feb 2020 14:37:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6B6FD24654 for ; Tue, 18 Feb 2020 14:37:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727193AbgBROhH (ORCPT ); Tue, 18 Feb 2020 09:37:07 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52967 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726569AbgBROfe (ORCPT ); Tue, 18 Feb 2020 09:35:34 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xx-0000fF-20; Tue, 18 Feb 2020 14:35:13 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 09/25] fs: add is_userns_visible() helper Date: Tue, 18 Feb 2020 15:33:55 +0100 Message-Id: <20200218143411.2389182-10-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Introduce a helper which makes it possible to detect fileystems whose superblock is visible in multiple user namespace. This currently only means proc and sys. Such filesystems usually have special semantics so their behavior will not be changed with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- include/linux/fs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/fs.h b/include/linux/fs.h index 3cd4fe6b845e..fdc8fb2d786b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3651,4 +3651,9 @@ static inline int inode_drain_writes(struct inode *inode) return filemap_write_and_wait(inode->i_mapping); } +static inline bool is_userns_visible(unsigned long iflags) +{ + return (iflags & SB_I_USERNS_VISIBLE); +} + #endif /* _LINUX_FS_H */ From patchwork Tue Feb 18 14:33:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388725 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6B28B924 for ; Tue, 18 Feb 2020 14:37:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 56BE624672 for ; Tue, 18 Feb 2020 14:37:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726703AbgBROfe (ORCPT ); Tue, 18 Feb 2020 09:35:34 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52954 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726442AbgBROfe (ORCPT ); Tue, 18 Feb 2020 09:35:34 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xy-0000fF-1W; Tue, 18 Feb 2020 14:35:14 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 10/25] namei: may_{o_}create(): handle fsid mappings Date: Tue, 18 Feb 2020 15:33:56 +0100 Message-Id: <20200218143411.2389182-11-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch may_{o_}create() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ - Jann Horn : - Ensure that the correct fsid is used when dealing with userns visible filesystems like proc. /* v3 */ unchanged --- fs/namei.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index db6565c99825..c5b014000f13 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "internal.h" #include "mount.h" @@ -287,6 +288,13 @@ static int check_acl(struct inode *inode, int mask) return -EAGAIN; } +static inline kuid_t get_current_fsuid(const struct inode *inode) +{ + if (is_userns_visible(inode->i_sb->s_iflags)) + return current_kfsuid(); + return current_fsuid(); +} + /* * This does the basic permission checking */ @@ -294,7 +302,7 @@ static int acl_permission_check(struct inode *inode, int mask) { unsigned int mode = inode->i_mode; - if (likely(uid_eq(current_fsuid(), inode->i_uid))) + if (likely(uid_eq(get_current_fsuid(inode), inode->i_uid))) mode >>= 6; else { if (IS_POSIXACL(inode) && (mode & S_IRWXG)) { @@ -980,7 +988,7 @@ static inline int may_follow_link(struct nameidata *nd) /* Allowed if owner and follower match. */ inode = nd->link_inode; - if (uid_eq(current_cred()->fsuid, inode->i_uid)) + if (uid_eq(get_current_fsuid(inode), inode->i_uid)) return 0; /* Allowed if parent directory not sticky and world-writable. */ @@ -1097,7 +1105,7 @@ static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid, (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || likely(!(dir_mode & S_ISVTX)) || uid_eq(inode->i_uid, dir_uid) || - uid_eq(current_fsuid(), inode->i_uid)) + uid_eq(get_current_fsuid(inode), inode->i_uid)) return 0; if (likely(dir_mode & 0002) || @@ -2832,7 +2840,7 @@ EXPORT_SYMBOL(kern_path_mountpoint); int __check_sticky(struct inode *dir, struct inode *inode) { - kuid_t fsuid = current_fsuid(); + kuid_t fsuid = get_current_fsuid(inode); if (uid_eq(inode->i_uid, fsuid)) return 0; @@ -2902,6 +2910,20 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) return 0; } +static bool fsid_has_mapping(struct user_namespace *ns, struct super_block *sb) +{ + if (is_userns_visible(sb->s_iflags)) { + if (!kuid_has_mapping(ns, current_kfsuid()) || + !kgid_has_mapping(ns, current_kfsgid())) + return false; + } else if (!kfsuid_has_mapping(ns, current_fsuid()) || + !kfsgid_has_mapping(ns, current_fsgid())) { + return false; + } + + return true; +} + /* Check whether we can create an object with dentry child in directory * dir. * 1. We can't do it if child already exists (open has special treatment for @@ -2920,8 +2942,7 @@ static inline int may_create(struct inode *dir, struct dentry *child) if (IS_DEADDIR(dir)) return -ENOENT; s_user_ns = dir->i_sb->s_user_ns; - if (!kuid_has_mapping(s_user_ns, current_fsuid()) || - !kgid_has_mapping(s_user_ns, current_fsgid())) + if (!fsid_has_mapping(s_user_ns, dir->i_sb)) return -EOVERFLOW; return inode_permission(dir, MAY_WRITE | MAY_EXEC); } @@ -3103,8 +3124,7 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m return error; s_user_ns = dir->dentry->d_sb->s_user_ns; - if (!kuid_has_mapping(s_user_ns, current_fsuid()) || - !kgid_has_mapping(s_user_ns, current_fsgid())) + if (!fsid_has_mapping(s_user_ns, dir->dentry->d_sb)) return -EOVERFLOW; error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); From patchwork Tue Feb 18 14:33:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388705 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8FC191395 for ; Tue, 18 Feb 2020 14:36:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 706C9208C4 for ; Tue, 18 Feb 2020 14:36:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727883AbgBROgu (ORCPT ); Tue, 18 Feb 2020 09:36:50 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:52969 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726620AbgBROff (ORCPT ); Tue, 18 Feb 2020 09:35:35 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43xz-0000fF-2a; Tue, 18 Feb 2020 14:35:15 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 11/25] inode: inode_owner_or_capable(): handle fsid mappings Date: Tue, 18 Feb 2020 15:33:57 +0100 Message-Id: <20200218143411.2389182-12-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch inode_owner_or_capable() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- fs/inode.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 7d57068b6b7a..81d7a30b381d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "internal.h" @@ -2087,8 +2088,12 @@ bool inode_owner_or_capable(const struct inode *inode) return true; ns = current_user_ns(); - if (kuid_has_mapping(ns, inode->i_uid) && ns_capable(ns, CAP_FOWNER)) + if (is_userns_visible(inode->i_sb->s_iflags)) { + if (kuid_has_mapping(ns, inode->i_uid) && ns_capable(ns, CAP_FOWNER)) + return true; + } else if (kfsuid_has_mapping(ns, inode->i_uid) && ns_capable(ns, CAP_FOWNER)) { return true; + } return false; } EXPORT_SYMBOL(inode_owner_or_capable); From patchwork Tue Feb 18 14:33:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388695 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CAC1E92A for ; Tue, 18 Feb 2020 14:36:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id ABE8421D56 for ; Tue, 18 Feb 2020 14:36:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726911AbgBROfi (ORCPT ); Tue, 18 Feb 2020 09:35:38 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53006 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726799AbgBROfi (ORCPT ); Tue, 18 Feb 2020 09:35:38 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y0-0000fF-GX; Tue, 18 Feb 2020 14:35:16 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 12/25] capability: privileged_wrt_inode_uidgid(): handle fsid mappings Date: Tue, 18 Feb 2020 15:33:58 +0100 Message-Id: <20200218143411.2389182-13-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch privileged_wrt_inode_uidgid() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- kernel/capability.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/capability.c b/kernel/capability.c index 1444f3954d75..2b0c1dc992e2 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include /* * Leveraged for setting/resetting capabilities @@ -486,8 +488,12 @@ EXPORT_SYMBOL(file_ns_capable); */ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode) { - return kuid_has_mapping(ns, inode->i_uid) && - kgid_has_mapping(ns, inode->i_gid); + if (is_userns_visible(inode->i_sb->s_iflags)) + return kuid_has_mapping(ns, inode->i_uid) && + kgid_has_mapping(ns, inode->i_gid); + + return kfsuid_has_mapping(ns, inode->i_uid) && + kfsgid_has_mapping(ns, inode->i_gid); } /** From patchwork Tue Feb 18 14:33:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388669 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 60E0D92A for ; Tue, 18 Feb 2020 14:36:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4BDD12464E for ; Tue, 18 Feb 2020 14:36:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727561AbgBROgE (ORCPT ); Tue, 18 Feb 2020 09:36:04 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53040 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726851AbgBROfk (ORCPT ); Tue, 18 Feb 2020 09:35:40 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y1-0000fF-Cw; Tue, 18 Feb 2020 14:35:17 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 13/25] stat: handle fsid mappings Date: Tue, 18 Feb 2020 15:33:59 +0100 Message-Id: <20200218143411.2389182-14-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch attribute functions looking up fsids to them up in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ - Tycho Andersen : - Replace , with = when converting to uid and gid in cp_new_stat64(). --- fs/stat.c | 48 +++++++++++++++++++++++++++++++++++--------- include/linux/stat.h | 1 + 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index 030008796479..612714ebacb4 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,8 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, if (IS_AUTOMOUNT(inode)) stat->attributes |= STATX_ATTR_AUTOMOUNT; + stat->userns_visible = is_userns_visible(inode->i_sb->s_iflags); + if (inode->i_op->getattr) return inode->i_op->getattr(path, stat, request_mask, query_flags); @@ -239,8 +242,13 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; - SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); - SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + if (stat->userns_visible) { + SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + } else { + SET_UID(tmp.st_uid, from_kfsuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kfsgid_munged(current_user_ns(), stat->gid)); + } tmp.st_rdev = old_encode_dev(stat->rdev); #if BITS_PER_LONG == 32 if (stat->size > MAX_NON_LFS) @@ -327,8 +335,13 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; - SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); - SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + if (stat->userns_visible) { + SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + } else { + SET_UID(tmp.st_uid, from_kfsuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kfsgid_munged(current_user_ns(), stat->gid)); + } tmp.st_rdev = encode_dev(stat->rdev); tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; @@ -471,8 +484,13 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf) #endif tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; - tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); - tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); + if (stat->userns_visible) { + tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); + tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); + } else { + tmp.st_uid = from_kfsuid_munged(current_user_ns(), stat->uid); + tmp.st_gid = from_kfsgid_munged(current_user_ns(), stat->gid); + } tmp.st_atime = stat->atime.tv_sec; tmp.st_atime_nsec = stat->atime.tv_nsec; tmp.st_mtime = stat->mtime.tv_sec; @@ -544,8 +562,13 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer) tmp.stx_blksize = stat->blksize; tmp.stx_attributes = stat->attributes; tmp.stx_nlink = stat->nlink; - tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid); - tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid); + if (stat->userns_visible) { + tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid); + tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid); + } else { + tmp.stx_uid = from_kfsuid_munged(current_user_ns(), stat->uid); + tmp.stx_gid = from_kfsgid_munged(current_user_ns(), stat->gid); + } tmp.stx_mode = stat->mode; tmp.stx_ino = stat->ino; tmp.stx_size = stat->size; @@ -615,8 +638,13 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) return -EOVERFLOW; - SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); - SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + if (stat->userns_visible) { + SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + } else { + SET_UID(tmp.st_uid, from_kfsuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kfsgid_munged(current_user_ns(), stat->gid)); + } tmp.st_rdev = old_encode_dev(stat->rdev); if ((u64) stat->size > MAX_NON_LFS) return -EOVERFLOW; diff --git a/include/linux/stat.h b/include/linux/stat.h index 528c4baad091..e6d4ba73a970 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -47,6 +47,7 @@ struct kstat { struct timespec64 ctime; struct timespec64 btime; /* File creation time */ u64 blocks; + bool userns_visible; }; #endif From patchwork Tue Feb 18 14:34:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388663 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4CEF7924 for ; Tue, 18 Feb 2020 14:36:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3852B24649 for ; Tue, 18 Feb 2020 14:36:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727547AbgBROgE (ORCPT ); Tue, 18 Feb 2020 09:36:04 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53047 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726750AbgBROfk (ORCPT ); Tue, 18 Feb 2020 09:35:40 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y2-0000fF-9Z; Tue, 18 Feb 2020 14:35:18 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 14/25] open: handle fsid mappings Date: Tue, 18 Feb 2020 15:34:00 +0100 Message-Id: <20200218143411.2389182-15-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Let chown_common() lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. do_faccessat() just needs to translate from real ids into fsids. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ - Christian Brauner : - handle faccessat() too /* v3 */ unchanged --- fs/open.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/open.c b/fs/open.c index 0788b3715731..4e092845728f 100644 --- a/fs/open.c +++ b/fs/open.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "internal.h" @@ -361,8 +362,10 @@ long do_faccessat(int dfd, const char __user *filename, int mode) if (!override_cred) return -ENOMEM; - override_cred->fsuid = override_cred->uid; - override_cred->fsgid = override_cred->gid; + override_cred->kfsuid = override_cred->uid; + override_cred->kfsgid = override_cred->gid; + override_cred->fsuid = kuid_to_kfsuid(override_cred->user_ns, override_cred->uid); + override_cred->fsgid = kgid_to_kfsgid(override_cred->user_ns, override_cred->gid); if (!issecure(SECURE_NO_SETUID_FIXUP)) { /* Clear the capabilities if we switch to a non-root user */ @@ -626,8 +629,13 @@ static int chown_common(const struct path *path, uid_t user, gid_t group) kuid_t uid; kgid_t gid; - uid = make_kuid(current_user_ns(), user); - gid = make_kgid(current_user_ns(), group); + if (is_userns_visible(inode->i_sb->s_iflags)) { + uid = make_kuid(current_user_ns(), user); + gid = make_kgid(current_user_ns(), group); + } else { + uid = make_kfsuid(current_user_ns(), user); + gid = make_kfsgid(current_user_ns(), group); + } retry_deleg: newattrs.ia_valid = ATTR_CTIME; From patchwork Tue Feb 18 14:34:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388653 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8C73292A for ; Tue, 18 Feb 2020 14:35:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 776F124673 for ; Tue, 18 Feb 2020 14:35:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727161AbgBROfm (ORCPT ); Tue, 18 Feb 2020 09:35:42 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53053 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726963AbgBROfl (ORCPT ); Tue, 18 Feb 2020 09:35:41 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y3-0000fF-7I; Tue, 18 Feb 2020 14:35:19 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 15/25] posix_acl: handle fsid mappings Date: Tue, 18 Feb 2020 15:34:01 +0100 Message-Id: <20200218143411.2389182-16-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch posix_acls() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Afaict, all filesystems that share a superblock in all user namespaces currently do not support acls so this change should be safe to do unconditionally. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- fs/posix_acl.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 249672bf54fe..ed6112c9b804 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -22,6 +22,7 @@ #include #include #include +#include static struct posix_acl **acl_by_type(struct inode *inode, int type) { @@ -692,12 +693,12 @@ static void posix_acl_fix_xattr_userns( for (end = entry + count; entry != end; entry++) { switch(le16_to_cpu(entry->e_tag)) { case ACL_USER: - uid = make_kuid(from, le32_to_cpu(entry->e_id)); - entry->e_id = cpu_to_le32(from_kuid(to, uid)); + uid = make_kfsuid(from, le32_to_cpu(entry->e_id)); + entry->e_id = cpu_to_le32(from_kfsuid(to, uid)); break; case ACL_GROUP: - gid = make_kgid(from, le32_to_cpu(entry->e_id)); - entry->e_id = cpu_to_le32(from_kgid(to, gid)); + gid = make_kfsgid(from, le32_to_cpu(entry->e_id)); + entry->e_id = cpu_to_le32(from_kfsgid(to, gid)); break; default: break; @@ -765,14 +766,14 @@ posix_acl_from_xattr(struct user_namespace *user_ns, case ACL_USER: acl_e->e_uid = - make_kuid(user_ns, + make_kfsuid(user_ns, le32_to_cpu(entry->e_id)); if (!uid_valid(acl_e->e_uid)) goto fail; break; case ACL_GROUP: acl_e->e_gid = - make_kgid(user_ns, + make_kfsgid(user_ns, le32_to_cpu(entry->e_id)); if (!gid_valid(acl_e->e_gid)) goto fail; @@ -817,11 +818,11 @@ posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, switch(acl_e->e_tag) { case ACL_USER: ext_entry->e_id = - cpu_to_le32(from_kuid(user_ns, acl_e->e_uid)); + cpu_to_le32(from_kfsuid(user_ns, acl_e->e_uid)); break; case ACL_GROUP: ext_entry->e_id = - cpu_to_le32(from_kgid(user_ns, acl_e->e_gid)); + cpu_to_le32(from_kfsgid(user_ns, acl_e->e_gid)); break; default: ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); From patchwork Tue Feb 18 14:34:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388649 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BBC02924 for ; Tue, 18 Feb 2020 14:35:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A6EC321D56 for ; Tue, 18 Feb 2020 14:35:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727227AbgBROfp (ORCPT ); Tue, 18 Feb 2020 09:35:45 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53067 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727107AbgBROfn (ORCPT ); Tue, 18 Feb 2020 09:35:43 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y4-0000fF-7s; Tue, 18 Feb 2020 14:35:20 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 16/25] attr: notify_change(): handle fsid mappings Date: Tue, 18 Feb 2020 15:34:02 +0100 Message-Id: <20200218143411.2389182-17-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch notify_change() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Filesystems that share a superblock in all user namespaces they are mounted in will retain their old semantics even with the introduction of fsid mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- fs/attr.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index b4bbdbd4c8ca..b3fe9d9582d2 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include static bool chown_ok(const struct inode *inode, kuid_t uid) { @@ -310,12 +312,21 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de * Verify that uid/gid changes are valid in the target * namespace of the superblock. */ - if (ia_valid & ATTR_UID && - !kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid)) - return -EOVERFLOW; - if (ia_valid & ATTR_GID && - !kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid)) - return -EOVERFLOW; + if (is_userns_visible(inode->i_sb->s_iflags)) { + if (ia_valid & ATTR_UID && + !kuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid)) + return -EOVERFLOW; + if (ia_valid & ATTR_GID && + !kgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid)) + return -EOVERFLOW; + } else { + if (ia_valid & ATTR_UID && + !kfsuid_has_mapping(inode->i_sb->s_user_ns, attr->ia_uid)) + return -EOVERFLOW; + if (ia_valid & ATTR_GID && + !kfsgid_has_mapping(inode->i_sb->s_user_ns, attr->ia_gid)) + return -EOVERFLOW; + } /* Don't allow modifications of files with invalid uids or * gids unless those uids & gids are being made valid. From patchwork Tue Feb 18 14:34:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388655 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 66EA5924 for ; Tue, 18 Feb 2020 14:35:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 524A721D56 for ; Tue, 18 Feb 2020 14:35:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726667AbgBROfz (ORCPT ); Tue, 18 Feb 2020 09:35:55 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53068 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727108AbgBROfm (ORCPT ); Tue, 18 Feb 2020 09:35:42 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y5-0000fF-CD; Tue, 18 Feb 2020 14:35:21 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 17/25] commoncap: cap_bprm_set_creds(): handle fsid mappings Date: Tue, 18 Feb 2020 15:34:03 +0100 Message-Id: <20200218143411.2389182-18-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: During exec the kfsids are currently reset to the effective kids. To retain the same semantics with the introduction of fsid mappings, we lookup the userspace effective id in the id mappings and translate the effective id into the corresponding kfsid in the fsid mapping. This means, the behavior is unchanged when no fsid mappings are setup and the semantics stay the same even when fsid mappings are setup. Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ - Christian Brauner : - Reset kfsids used for userns visible filesystems such as proc too. /* v3 */ unchanged --- security/commoncap.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index f4ee0ae106b2..55e6cc24f887 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -24,6 +24,7 @@ #include #include #include +#include /* * If a non-root user executes a setuid-root binary in @@ -810,7 +811,10 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) struct cred *new = bprm->cred; bool effective = false, has_fcap = false, is_setid; int ret; - kuid_t root_uid; + kuid_t root_uid, kfsuid; + kgid_t kfsgid; + uid_t fsuid; + gid_t fsgid; if (WARN_ON(!cap_ambient_invariant_ok(old))) return -EPERM; @@ -847,8 +851,15 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) old->cap_permitted); } - new->suid = new->fsuid = new->euid; - new->sgid = new->fsgid = new->egid; + fsuid = from_kuid_munged(new->user_ns, new->euid); + kfsuid = make_kfsuid(new->user_ns, fsuid); + new->suid = new->kfsuid = new->euid; + new->fsuid = kfsuid; + + fsgid = from_kgid_munged(new->user_ns, new->egid); + kfsgid = make_kfsgid(new->user_ns, fsgid); + new->sgid = new->kfsgid = new->egid; + new->fsgid = kfsgid; /* File caps or setid cancels ambient. */ if (has_fcap || is_setid) From patchwork Tue Feb 18 14:34:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388661 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9BDF392A for ; Tue, 18 Feb 2020 14:36:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7BFC124670 for ; Tue, 18 Feb 2020 14:36:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727400AbgBROfz (ORCPT ); Tue, 18 Feb 2020 09:35:55 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53073 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726799AbgBROfm (ORCPT ); Tue, 18 Feb 2020 09:35:42 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y6-0000fF-Ax; Tue, 18 Feb 2020 14:35:22 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 18/25] commoncap: cap_task_fix_setuid(): handle fsid mappings Date: Tue, 18 Feb 2020 15:34:04 +0100 Message-Id: <20200218143411.2389182-19-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch cap_task_fix_setuid() to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- security/commoncap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/commoncap.c b/security/commoncap.c index 55e6cc24f887..0581c6aa8bdc 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1062,7 +1062,7 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) * if not, we might be a bit too harsh here. */ if (!issecure(SECURE_NO_SETUID_FIXUP)) { - kuid_t root_uid = make_kuid(old->user_ns, 0); + kuid_t root_uid = make_kfsuid(old->user_ns, 0); if (uid_eq(old->fsuid, root_uid) && !uid_eq(new->fsuid, root_uid)) new->cap_effective = cap_drop_fs_set(new->cap_effective); From patchwork Tue Feb 18 14:34:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388673 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 801C292A for ; Tue, 18 Feb 2020 14:36:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6AD8E21D56 for ; Tue, 18 Feb 2020 14:36:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727506AbgBROgE (ORCPT ); Tue, 18 Feb 2020 09:36:04 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53056 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726996AbgBROfk (ORCPT ); Tue, 18 Feb 2020 09:35:40 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y7-0000fF-DX; Tue, 18 Feb 2020 14:35:23 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 19/25] commoncap: handle fsid mappings with vfs caps Date: Tue, 18 Feb 2020 15:34:05 +0100 Message-Id: <20200218143411.2389182-20-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch vfs cap helpers to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- security/commoncap.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index 0581c6aa8bdc..d2259dc0450b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -328,7 +328,7 @@ static bool rootid_owns_currentns(kuid_t kroot) return false; for (ns = current_user_ns(); ; ns = ns->parent) { - if (from_kuid(ns, kroot) == 0) + if (from_kfsuid(ns, kroot) == 0) return true; if (ns == &init_user_ns) break; @@ -411,11 +411,11 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, nscap = (struct vfs_ns_cap_data *) tmpbuf; root = le32_to_cpu(nscap->rootid); - kroot = make_kuid(fs_ns, root); + kroot = make_kfsuid(fs_ns, root); - /* If the root kuid maps to a valid uid in current ns, then return + /* If the root kfsuid maps to a valid uid in current ns, then return * this as a nscap. */ - mappedroot = from_kuid(current_user_ns(), kroot); + mappedroot = from_kfsuid(current_user_ns(), kroot); if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) { if (alloc) { *buffer = tmpbuf; @@ -460,7 +460,7 @@ static kuid_t rootid_from_xattr(const void *value, size_t size, if (size == XATTR_CAPS_SZ_3) rootid = le32_to_cpu(nscap->rootid); - return make_kuid(task_ns, rootid); + return make_kfsuid(task_ns, rootid); } static bool validheader(size_t size, const struct vfs_cap_data *cap) @@ -501,7 +501,7 @@ int cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size) if (!uid_valid(rootid)) return -EINVAL; - nsrootid = from_kuid(fs_ns, rootid); + nsrootid = from_kfsuid(fs_ns, rootid); if (nsrootid == -1) return -EINVAL; @@ -600,7 +600,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps->magic_etc); - rootkuid = make_kuid(fs_ns, 0); + rootkuid = make_kfsuid(fs_ns, 0); switch (magic_etc & VFS_CAP_REVISION_MASK) { case VFS_CAP_REVISION_1: if (size != XATTR_CAPS_SZ_1) @@ -616,7 +616,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data if (size != XATTR_CAPS_SZ_3) return -EINVAL; tocopy = VFS_CAP_U32_3; - rootkuid = make_kuid(fs_ns, le32_to_cpu(nscaps->rootid)); + rootkuid = make_kfsuid(fs_ns, le32_to_cpu(nscaps->rootid)); break; default: From patchwork Tue Feb 18 14:34:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388741 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9FC1A1395 for ; Tue, 18 Feb 2020 14:38:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8AFDD208C4 for ; Tue, 18 Feb 2020 14:38:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726841AbgBROif (ORCPT ); Tue, 18 Feb 2020 09:38:35 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53250 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726821AbgBROif (ORCPT ); Tue, 18 Feb 2020 09:38:35 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43y8-0000fF-OS; Tue, 18 Feb 2020 14:35:24 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 20/25] exec: bprm_fill_uid(): handle fsid mappings Date: Tue, 18 Feb 2020 15:34:06 +0100 Message-Id: <20200218143411.2389182-21-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Make sure that during suid/sgid binary execution we lookup the fsids in the fsid mappings. If the kernel is compiled without fsid mappings or no fsid mappings are setup the behavior is unchanged. Assuming we have a binary in a given user namespace that is owned by 0:0 in the given user namespace which appears as 300000:300000 on-disk in the initial user namespace. Now assume we write an id mapping of 0 100000 100000 and an fsid mapping for 0 300000 300000 in the user namespace. When we hit bprm_fill_uid() during setid execution we will retrieve inode kuid=300000 and kgid=300000. We first check whether there's an fsid mapping for these kids. In our scenario we find that they map to fsuid=0 and fsgid=0 in the user namespace. Now we translate them into kids in the id mapping. In our example they translate to kuid=100000 and kgid=100000 which means the file will ultimately run as uid=0 and gid=0 in the user namespace and as uid=100000, gid=100000 in the initial user namespace. Let's alter the example and assume that there is an fsid mapping of 0 300000 300000 set up but no id mapping has been setup for the user namespace. In this the last step of translating into a valid kid pair in the id mappings will fail and we will behave as before and ignore the sid bits. Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ patch added - Christian Brauner : - Make sure that bprm_fill_uid() handles fsid mappings. /* v3 */ - Christian Brauner : - Fix commit message. --- fs/exec.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index db17be51b112..9e4a7e757cef 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -1518,8 +1519,8 @@ static void bprm_fill_uid(struct linux_binprm *bprm) { struct inode *inode; unsigned int mode; - kuid_t uid; - kgid_t gid; + kuid_t uid, euid; + kgid_t gid, egid; /* * Since this can be called multiple times (via prepare_binprm), @@ -1551,18 +1552,30 @@ static void bprm_fill_uid(struct linux_binprm *bprm) inode_unlock(inode); /* We ignore suid/sgid if there are no mappings for them in the ns */ - if (!kuid_has_mapping(bprm->cred->user_ns, uid) || - !kgid_has_mapping(bprm->cred->user_ns, gid)) + if (!kfsuid_has_mapping(bprm->cred->user_ns, uid) || + !kfsgid_has_mapping(bprm->cred->user_ns, gid)) return; + if (mode & S_ISUID) { + euid = kfsuid_to_kuid(bprm->cred->user_ns, uid); + if (!uid_valid(euid)) + return; + } + + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + egid = kfsgid_to_kgid(bprm->cred->user_ns, gid); + if (!gid_valid(egid)) + return; + } + if (mode & S_ISUID) { bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->euid = uid; + bprm->cred->euid = euid; } if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->egid = gid; + bprm->cred->egid = egid; } } From patchwork Tue Feb 18 14:34:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388731 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BB7F9924 for ; Tue, 18 Feb 2020 14:37:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9B4DE20801 for ; Tue, 18 Feb 2020 14:37:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726647AbgBROhs (ORCPT ); Tue, 18 Feb 2020 09:37:48 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53197 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726569AbgBROhr (ORCPT ); Tue, 18 Feb 2020 09:37:47 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43yA-0000fF-0F; Tue, 18 Feb 2020 14:35:26 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 21/25] ptrace: adapt ptrace_may_access() to always uses unmapped fsids Date: Tue, 18 Feb 2020 15:34:07 +0100 Message-Id: <20200218143411.2389182-22-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: ptrace_may_access() with PTRACE_MODE_FSCREDS is only used with proc and proc wants to use the unmapped fsids. Suggested-by: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ patch added /* v3 */ unchanged --- kernel/ptrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 43d6179508d6..3734713cc0dd 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -304,8 +304,8 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) return 0; rcu_read_lock(); if (mode & PTRACE_MODE_FSCREDS) { - caller_uid = cred->fsuid; - caller_gid = cred->fsgid; + caller_uid = cred->kfsuid; + caller_gid = cred->kfsgid; } else { /* * Using the euid would make more sense here, but something From patchwork Tue Feb 18 14:34:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388713 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DDB871395 for ; Tue, 18 Feb 2020 14:37:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C8A5420801 for ; Tue, 18 Feb 2020 14:37:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727086AbgBROg6 (ORCPT ); Tue, 18 Feb 2020 09:36:58 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53142 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726620AbgBROg5 (ORCPT ); Tue, 18 Feb 2020 09:36:57 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43yB-0000fF-Br; Tue, 18 Feb 2020 14:35:27 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 22/25] devpts: handle fsid mappings Date: Tue, 18 Feb 2020 15:34:08 +0100 Message-Id: <20200218143411.2389182-23-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: When a uid or gid mount option is specified with devpts have it lookup the corresponding kfsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. Signed-off-by: Christian Brauner --- /* v2 */ unchanged /* v3 */ unchanged --- fs/devpts/inode.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 42e5a766d33c..139958892572 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -24,6 +24,7 @@ #include #include #include +#include #define DEVPTS_DEFAULT_MODE 0600 /* @@ -277,7 +278,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) case Opt_uid: if (match_int(&args[0], &option)) return -EINVAL; - uid = make_kuid(current_user_ns(), option); + uid = make_kfsuid(current_user_ns(), option); if (!uid_valid(uid)) return -EINVAL; opts->uid = uid; @@ -286,7 +287,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) case Opt_gid: if (match_int(&args[0], &option)) return -EINVAL; - gid = make_kgid(current_user_ns(), option); + gid = make_kfsgid(current_user_ns(), option); if (!gid_valid(gid)) return -EINVAL; opts->gid = gid; @@ -410,7 +411,7 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root) from_kuid_munged(&init_user_ns, opts->uid)); if (opts->setgid) seq_printf(seq, ",gid=%u", - from_kgid_munged(&init_user_ns, opts->gid)); + from_kfsgid_munged(&init_user_ns, opts->gid)); seq_printf(seq, ",mode=%03o", opts->mode); seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); if (opts->max < NR_UNIX98_PTY_MAX) From patchwork Tue Feb 18 14:34:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388693 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 256E792A for ; Tue, 18 Feb 2020 14:36:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 07C6F21D56 for ; Tue, 18 Feb 2020 14:36:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726829AbgBROgf (ORCPT ); Tue, 18 Feb 2020 09:36:35 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53123 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726700AbgBROgf (ORCPT ); Tue, 18 Feb 2020 09:36:35 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43yC-0000fF-K3; Tue, 18 Feb 2020 14:35:28 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 23/25] keys: handle fsid mappings Date: Tue, 18 Feb 2020 15:34:09 +0100 Message-Id: <20200218143411.2389182-24-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Similar to proc and sysfs let keys use kfsids which are always mapped according to id mappings. Suggested-by: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ patch not present /* v3 */ patch added - Jann Horn : - Add patch to handle the keyrings. --- security/keys/key.c | 2 +- security/keys/permission.c | 4 ++-- security/keys/process_keys.c | 6 ++++-- security/keys/request_key.c | 10 +++++----- security/keys/request_key_auth.c | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/security/keys/key.c b/security/keys/key.c index 718bf7217420..bfb17e8210d7 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -923,7 +923,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, - cred->fsuid, cred->fsgid, cred, perm, flags, NULL); + cred->kfsuid, cred->kfsgid, cred, perm, flags, NULL); if (IS_ERR(key)) { key_ref = ERR_CAST(key); goto error_link_end; diff --git a/security/keys/permission.c b/security/keys/permission.c index 085f907b64ac..847187ca6b41 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -33,7 +33,7 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, key = key_ref_to_ptr(key_ref); /* use the second 8-bits of permissions for keys the caller owns */ - if (uid_eq(key->uid, cred->fsuid)) { + if (uid_eq(key->uid, cred->kfsuid)) { kperm = key->perm >> 16; goto use_these_perms; } @@ -41,7 +41,7 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, /* use the third 8-bits of permissions for keys the caller has a group * membership in common with */ if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) { - if (gid_eq(key->gid, cred->fsgid)) { + if (gid_eq(key->gid, cred->kfsgid)) { kperm = key->perm >> 8; goto use_these_perms; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 09541de31f2f..32376f0fbb42 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -379,7 +379,7 @@ void key_fsuid_changed(struct cred *new_cred) /* update the ownership of the thread keyring */ if (new_cred->thread_keyring) { down_write(&new_cred->thread_keyring->sem); - new_cred->thread_keyring->uid = new_cred->fsuid; + new_cred->thread_keyring->uid = new_cred->kfsuid; up_write(&new_cred->thread_keyring->sem); } } @@ -392,7 +392,7 @@ void key_fsgid_changed(struct cred *new_cred) /* update the ownership of the thread keyring */ if (new_cred->thread_keyring) { down_write(&new_cred->thread_keyring->sem); - new_cred->thread_keyring->gid = new_cred->fsgid; + new_cred->thread_keyring->gid = new_cred->kfsgid; up_write(&new_cred->thread_keyring->sem); } } @@ -923,10 +923,12 @@ void key_change_session_keyring(struct callback_head *twork) new-> euid = old-> euid; new-> suid = old-> suid; new->fsuid = old->fsuid; + new->kfsuid = old->kfsuid; new-> gid = old-> gid; new-> egid = old-> egid; new-> sgid = old-> sgid; new->fsgid = old->fsgid; + new->kfsgid = old->kfsgid; new->user = get_uid(old->user); new->user_ns = get_user_ns(old->user_ns); new->group_info = get_group_info(old->group_info); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 957b9e3e1492..254a7c2f3fde 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -134,7 +134,7 @@ static int call_sbin_request_key(struct key *authkey, void *aux) sprintf(desc, "_req.%u", key->serial); cred = get_current_cred(); - keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, + keyring = keyring_alloc(desc, cred->kfsuid, cred->kfsgid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL); put_cred(cred); @@ -149,8 +149,8 @@ static int call_sbin_request_key(struct key *authkey, void *aux) goto error_link; /* record the UID and GID */ - sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid)); - sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid)); + sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->kfsuid)); + sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->kfsgid)); /* we say which key is under construction */ sprintf(key_str, "%d", key->serial); @@ -390,7 +390,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, perm |= KEY_POS_WRITE; key = key_alloc(ctx->index_key.type, ctx->index_key.description, - ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, + ctx->cred->kfsuid, ctx->cred->kfsgid, ctx->cred, perm, flags, NULL); if (IS_ERR(key)) goto alloc_failed; @@ -490,7 +490,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, if (ret) goto error; - user = key_user_lookup(current_fsuid()); + user = key_user_lookup(current_kfsuid()); if (!user) { ret = -ENOMEM; goto error_put_dest_keyring; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index ecba39c93fd9..26808146897c 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -215,7 +215,7 @@ struct key *request_key_auth_new(struct key *target, const char *op, sprintf(desc, "%x", target->serial); authkey = key_alloc(&key_type_request_key_auth, desc, - cred->fsuid, cred->fsgid, cred, + cred->kfsuid, cred->kfsgid, cred, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK | KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { From patchwork Tue Feb 18 14:34:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388729 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5F9AD92A for ; Tue, 18 Feb 2020 14:37:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 41BCC20801 for ; Tue, 18 Feb 2020 14:37:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726817AbgBROh0 (ORCPT ); Tue, 18 Feb 2020 09:37:26 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53168 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726569AbgBROhZ (ORCPT ); Tue, 18 Feb 2020 09:37:25 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43yD-0000fF-W8; Tue, 18 Feb 2020 14:35:30 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 24/25] sys: handle fsid mappings in set*id() calls Date: Tue, 18 Feb 2020 15:34:10 +0100 Message-Id: <20200218143411.2389182-25-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: Switch set*id() calls to lookup fsids in the fsid mappings. If no fsid mappings are setup the behavior is unchanged, i.e. fsids are looked up in the id mappings. A caller can only setid() to a given id if the id maps to a valid kid in both the id and fsid maps of the caller's user namespace. This is always the case when no id mappings and fsid mappings have been written. It is also always the case when an id mapping has been written which includes the target id and but no fsid mappings have been written. All non-fsid mapping aware workloads will thus work just as before. During setr*id() calls the kfsid is set to the keid corresponding to the eid that is requested by userspace. If the requested eid is -1 the kfsid is reset to the current keid. For the latter case this means we need to lookup the corresponding userspace eid corresponding to the current keid in the id mappings and translate this eid into the corresponding kfsid in the fsid mappings. We require that a user must have a valid fsid mapping for the target id. This is consistent with how the setid calls work today without fsid mappings. The kfsid to cleanly handle userns visible filesystem is set as before. Cc: Jann Horn Signed-off-by: Christian Brauner --- /* v2 */ - Christian Brauner : - set kfsid which is used when dealing with proc permission checking /* v3 */ - Jann Horn : - Squash all set*id() patches into a single patch and move this to be the last patch so we don't expose a half-done feature in the middle of this series. --- kernel/sys.c | 106 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/kernel/sys.c b/kernel/sys.c index f9bc5c303e3f..78592deee2d8 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -353,7 +354,7 @@ long __sys_setregid(gid_t rgid, gid_t egid) const struct cred *old; struct cred *new; int retval; - kgid_t krgid, kegid; + kgid_t krgid, kegid, kfsgid; krgid = make_kgid(ns, rgid); kegid = make_kgid(ns, egid); @@ -385,12 +386,20 @@ long __sys_setregid(gid_t rgid, gid_t egid) new->egid = kegid; else goto error; + kfsgid = make_kfsgid(ns, egid); + } else { + kfsgid = kgid_to_kfsgid(new->user_ns, new->egid); + } + if (!gid_valid(kfsgid)) { + retval = -EINVAL; + goto error; } if (rgid != (gid_t) -1 || (egid != (gid_t) -1 && !gid_eq(kegid, old->gid))) new->sgid = new->egid; - new->fsgid = new->egid; + new->kfsgid = new->egid; + new->fsgid = kfsgid; return commit_creds(new); @@ -415,24 +424,31 @@ long __sys_setgid(gid_t gid) const struct cred *old; struct cred *new; int retval; - kgid_t kgid; + kgid_t kgid, kfsgid; kgid = make_kgid(ns, gid); if (!gid_valid(kgid)) return -EINVAL; + kfsgid = make_kfsgid(ns, gid); + if (!gid_valid(kfsgid)) + return -EINVAL; + new = prepare_creds(); if (!new) return -ENOMEM; old = current_cred(); retval = -EPERM; - if (ns_capable(old->user_ns, CAP_SETGID)) - new->gid = new->egid = new->sgid = new->fsgid = kgid; - else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid)) - new->egid = new->fsgid = kgid; - else + if (ns_capable(old->user_ns, CAP_SETGID)) { + new->gid = new->egid = new->sgid = new->kfsgid = kgid; + new->fsgid = kfsgid; + } else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid)) { + new->egid = new->kfsgid = kgid; + new->fsgid = kfsgid; + } else { goto error; + } return commit_creds(new); @@ -496,7 +512,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid) const struct cred *old; struct cred *new; int retval; - kuid_t kruid, keuid; + kuid_t kruid, keuid, kfsuid; kruid = make_kuid(ns, ruid); keuid = make_kuid(ns, euid); @@ -527,6 +543,13 @@ long __sys_setreuid(uid_t ruid, uid_t euid) !uid_eq(old->suid, keuid) && !ns_capable_setid(old->user_ns, CAP_SETUID)) goto error; + kfsuid = make_kfsuid(new->user_ns, euid); + } else { + kfsuid = kuid_to_kfsuid(new->user_ns, new->euid); + } + if (!uid_valid(kfsuid)) { + retval = -EINVAL; + goto error; } if (!uid_eq(new->uid, old->uid)) { @@ -537,7 +560,8 @@ long __sys_setreuid(uid_t ruid, uid_t euid) if (ruid != (uid_t) -1 || (euid != (uid_t) -1 && !uid_eq(keuid, old->uid))) new->suid = new->euid; - new->fsuid = new->euid; + new->kfsuid = new->euid; + new->fsuid = kfsuid; retval = security_task_fix_setuid(new, old, LSM_SETID_RE); if (retval < 0) @@ -573,11 +597,16 @@ long __sys_setuid(uid_t uid) struct cred *new; int retval; kuid_t kuid; + kuid_t kfsuid; kuid = make_kuid(ns, uid); if (!uid_valid(kuid)) return -EINVAL; + kfsuid = make_kfsuid(ns, uid); + if (!uid_valid(kfsuid)) + return -EINVAL; + new = prepare_creds(); if (!new) return -ENOMEM; @@ -595,7 +624,8 @@ long __sys_setuid(uid_t uid) goto error; } - new->fsuid = new->euid = kuid; + new->kfsuid = new->euid = kuid; + new->fsuid = kfsuid; retval = security_task_fix_setuid(new, old, LSM_SETID_ID); if (retval < 0) @@ -624,7 +654,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) const struct cred *old; struct cred *new; int retval; - kuid_t kruid, keuid, ksuid; + kuid_t kruid, keuid, ksuid, kfsuid; kruid = make_kuid(ns, ruid); keuid = make_kuid(ns, euid); @@ -666,11 +696,21 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) goto error; } } - if (euid != (uid_t) -1) + if (euid != (uid_t) -1) { new->euid = keuid; + kfsuid = make_kfsuid(ns, euid); + } else { + kfsuid = kuid_to_kfsuid(new->user_ns, new->euid); + } + if (!uid_valid(kfsuid)) { + return -EINVAL; + goto error; + } + if (suid != (uid_t) -1) new->suid = ksuid; - new->fsuid = new->euid; + new->kfsuid = new->euid; + new->fsuid = kfsuid; retval = security_task_fix_setuid(new, old, LSM_SETID_RES); if (retval < 0) @@ -716,7 +756,7 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) const struct cred *old; struct cred *new; int retval; - kgid_t krgid, kegid, ksgid; + kgid_t krgid, kegid, ksgid, kfsgid; krgid = make_kgid(ns, rgid); kegid = make_kgid(ns, egid); @@ -749,11 +789,21 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) if (rgid != (gid_t) -1) new->gid = krgid; - if (egid != (gid_t) -1) + if (egid != (gid_t) -1) { new->egid = kegid; + kfsgid = make_kfsgid(ns, egid); + } else { + kfsgid = kgid_to_kfsgid(new->user_ns, new->egid); + } + if (!gid_valid(kfsgid)) { + retval = -EINVAL; + goto error; + } + if (sgid != (gid_t) -1) new->sgid = ksgid; - new->fsgid = new->egid; + new->kfsgid = new->egid; + new->fsgid = kfsgid; return commit_creds(new); @@ -799,15 +849,19 @@ long __sys_setfsuid(uid_t uid) const struct cred *old; struct cred *new; uid_t old_fsuid; - kuid_t kuid; + kuid_t kuid, kfsuid; old = current_cred(); - old_fsuid = from_kuid_munged(old->user_ns, old->fsuid); + old_fsuid = from_kfsuid_munged(old->user_ns, old->fsuid); - kuid = make_kuid(old->user_ns, uid); + kuid = make_kfsuid(old->user_ns, uid); if (!uid_valid(kuid)) return old_fsuid; + kfsuid = make_kuid(old->user_ns, uid); + if (!uid_valid(kfsuid)) + return old_fsuid; + new = prepare_creds(); if (!new) return old_fsuid; @@ -817,6 +871,7 @@ long __sys_setfsuid(uid_t uid) ns_capable_setid(old->user_ns, CAP_SETUID)) { if (!uid_eq(kuid, old->fsuid)) { new->fsuid = kuid; + new->kfsuid = kfsuid; if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0) goto change_okay; } @@ -843,15 +898,19 @@ long __sys_setfsgid(gid_t gid) const struct cred *old; struct cred *new; gid_t old_fsgid; - kgid_t kgid; + kgid_t kgid, kfsgid; old = current_cred(); - old_fsgid = from_kgid_munged(old->user_ns, old->fsgid); + old_fsgid = from_kfsgid_munged(old->user_ns, old->fsgid); - kgid = make_kgid(old->user_ns, gid); + kgid = make_kfsgid(old->user_ns, gid); if (!gid_valid(kgid)) return old_fsgid; + kfsgid = make_kgid(old->user_ns, gid); + if (!gid_valid(kfsgid)) + return old_fsgid; + new = prepare_creds(); if (!new) return old_fsgid; @@ -861,6 +920,7 @@ long __sys_setfsgid(gid_t gid) ns_capable(old->user_ns, CAP_SETGID)) { if (!gid_eq(kgid, old->fsgid)) { new->fsgid = kgid; + new->kfsgid = kfsgid; goto change_okay; } } From patchwork Tue Feb 18 14:34:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Brauner X-Patchwork-Id: 11388735 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A9F8492A for ; Tue, 18 Feb 2020 14:38:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8C1F820801 for ; Tue, 18 Feb 2020 14:38:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726569AbgBROiL (ORCPT ); Tue, 18 Feb 2020 09:38:11 -0500 Received: from youngberry.canonical.com ([91.189.89.112]:53228 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726666AbgBROiL (ORCPT ); Tue, 18 Feb 2020 09:38:11 -0500 Received: from ip5f5bf7ec.dynamic.kabel-deutschland.de ([95.91.247.236] helo=wittgenstein.fritz.box) by youngberry.canonical.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1j43yF-0000fF-7e; Tue, 18 Feb 2020 14:35:31 +0000 From: Christian Brauner To: =?utf-8?q?St=C3=A9phane_Graber?= , "Eric W. Biederman" , Aleksa Sarai , Jann Horn Cc: smbarber@chromium.org, Seth Forshee , Alexander Viro , Alexey Dobriyan , Serge Hallyn , James Morris , Kees Cook , Jonathan Corbet , Phil Estes , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, containers@lists.linux-foundation.org, linux-security-module@vger.kernel.org, linux-api@vger.kernel.org, Christian Brauner Subject: [PATCH v3 25/25] selftests: add simple fsid mapping selftests Date: Tue, 18 Feb 2020 15:34:11 +0100 Message-Id: <20200218143411.2389182-26-christian.brauner@ubuntu.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200218143411.2389182-1-christian.brauner@ubuntu.com> References: <20200218143411.2389182-1-christian.brauner@ubuntu.com> MIME-Version: 1.0 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: - Verify that fsid mappings cannot be written when if mappings have been written already. - Set up an id mapping and an fsid mapping, create a file and compare ids in child and parent user namespace. Signed-off-by: Christian Brauner --- /* v2 */ patch not present /* v3 */ patch added --- tools/testing/selftests/Makefile | 1 + .../testing/selftests/user_namespace/Makefile | 11 + .../selftests/user_namespace/test_fsid_map.c | 511 ++++++++++++++++++ 3 files changed, 523 insertions(+) create mode 100644 tools/testing/selftests/user_namespace/Makefile create mode 100644 tools/testing/selftests/user_namespace/test_fsid_map.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 63430e2664c2..49dcd21d2be7 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -60,6 +60,7 @@ endif TARGETS += tmpfs TARGETS += tpm2 TARGETS += user +TARGETS += user_namespace TARGETS += vm TARGETS += x86 TARGETS += zram diff --git a/tools/testing/selftests/user_namespace/Makefile b/tools/testing/selftests/user_namespace/Makefile new file mode 100644 index 000000000000..3f89896f3285 --- /dev/null +++ b/tools/testing/selftests/user_namespace/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS += -Wall + +all: + +TEST_GEN_PROGS += test_fsid_map + +include ../lib.mk + +$(OUTPUT)/test_fsid_map: test_fsid_map.c ../clone3/clone3_selftests.h + diff --git a/tools/testing/selftests/user_namespace/test_fsid_map.c b/tools/testing/selftests/user_namespace/test_fsid_map.c new file mode 100644 index 000000000000..e278f137ff55 --- /dev/null +++ b/tools/testing/selftests/user_namespace/test_fsid_map.c @@ -0,0 +1,511 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" +#include "../clone3/clone3_selftests.h" + +static int wait_for_pid(pid_t pid) +{ + 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); +} + +static int setid_userns_root(void) +{ + if (setuid(0)) + return -1; + if (setgid(0)) + return -1; + + setfsuid(0); + setfsgid(0); + + if (setfsuid(0)) + return -1; + + if (setfsgid(0)) + return -1; + + return 0; +} + +enum idmap_type { + UID_MAP, + GID_MAP, + FSUID_MAP, + FSGID_MAP, +}; + +static ssize_t read_nointr(int fd, void *buf, size_t count) +{ + ssize_t ret; +again: + ret = read(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + + return ret; +} + +static ssize_t write_nointr(int fd, const void *buf, size_t count) +{ + ssize_t ret; +again: + ret = write(fd, buf, count); + if (ret < 0 && errno == EINTR) + goto again; + + return ret; +} + +static int write_id_mapping(enum idmap_type type, pid_t pid, const char *buf, + size_t buf_size) +{ + int fd; + int ret; + char path[4096]; + + switch (type) { + case UID_MAP: + ret = snprintf(path, sizeof(path), "/proc/%d/uid_map", pid); + break; + case GID_MAP: + ret = snprintf(path, sizeof(path), "/proc/%d/gid_map", pid); + break; + case FSUID_MAP: + ret = snprintf(path, sizeof(path), "/proc/%d/fsuid_map", pid); + break; + case FSGID_MAP: + ret = snprintf(path, sizeof(path), "/proc/%d/fsgid_map", pid); + break; + default: + return -1; + } + if (ret < 0 || ret >= sizeof(path)) + return -E2BIG; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + ret = write_nointr(fd, buf, buf_size); + close(fd); + if (ret != buf_size) + return -1; + + return 0; +} + +const char id_map[] = "0 100000 100000"; +#define id_map_size (sizeof(id_map) - 1) + +const char fsid_map[] = "0 300000 100000"; +#define fsid_map_size (sizeof(fsid_map) - 1) + +int unix_send_fds_iov(int fd, int *sendfds, int num_sendfds, struct iovec *iov, + size_t iovlen) +{ + char *cmsgbuf = NULL; + int ret; + struct msghdr msg; + struct cmsghdr *cmsg = NULL; + size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); + + memset(&msg, 0, sizeof(msg)); + + cmsgbuf = malloc(cmsgbufsize); + if (!cmsgbuf) { + errno = ENOMEM; + return -1; + } + + msg.msg_control = cmsgbuf; + msg.msg_controllen = cmsgbufsize; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); + + msg.msg_controllen = cmsg->cmsg_len; + + memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); + + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + +again: + ret = sendmsg(fd, &msg, MSG_NOSIGNAL); + if (ret < 0) + if (errno == EINTR) + goto again; + + free(cmsgbuf); + return ret; +} + +static int unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, + size_t size) +{ + char buf[1] = {0}; + struct iovec iov = { + .iov_base = data ? data : buf, + .iov_len = data ? size : sizeof(buf), + }; + return unix_send_fds_iov(fd, sendfds, num_sendfds, &iov, 1); +} + +static int unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds, + struct iovec *iov, size_t iovlen) +{ + char *cmsgbuf = NULL; + int ret; + struct msghdr msg; + struct cmsghdr *cmsg = NULL; + size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(num_recvfds * sizeof(int)); + + memset(&msg, 0, sizeof(msg)); + + cmsgbuf = malloc(cmsgbufsize); + if (!cmsgbuf) { + errno = ENOMEM; + return -1; + } + + msg.msg_control = cmsgbuf; + msg.msg_controllen = cmsgbufsize; + + msg.msg_iov = iov; + msg.msg_iovlen = iovlen; + +again: + ret = recvmsg(fd, &msg, 0); + if (ret < 0) { + if (errno == EINTR) + goto again; + + goto out; + } + if (ret == 0) + goto out; + + /* + * If SO_PASSCRED is set we will always get a ucred message. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_type != SCM_RIGHTS) + continue; + + memset(recvfds, -1, num_recvfds * sizeof(int)); + if (cmsg && + cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && + cmsg->cmsg_level == SOL_SOCKET) + memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); + break; + } + +out: + free(cmsgbuf); + return ret; +} + +static int unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, + size_t size) +{ + char buf[1] = {0}; + struct iovec iov = { + .iov_base = data ? data : buf, + .iov_len = data ? size : sizeof(buf), + }; + return unix_recv_fds_iov(fd, recvfds, num_recvfds, &iov, 1); +} + +static bool has_expected_owner(int fd, uid_t uid, gid_t gid) +{ + int ret; + struct stat s; + ret = fstat(fd, &s); + return !ret && s.st_uid == uid && s.st_gid == gid; +} + +static int make_file_cmp_owner(uid_t uid, gid_t gid) +{ + char template[] = P_tmpdir "/.fsid_map_test_XXXXXX"; + int fd; + + fd = mkstemp(template); + if (fd < 0) + return -1; + unlink(template); + + if (!has_expected_owner(fd, uid, gid)) { + close(fd); + return -1; + } + + return fd; +} + +static void test_id_maps_imply_fsid_maps(void) +{ + int fret = EXIT_FAILURE; + ssize_t ret; + int fd = -EBADF; + pid_t pid; + int ipc[2]; + struct clone_args args = { + .flags = CLONE_NEWUSER, + .exit_signal = SIGCHLD, + }; + + ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc); + if (ret < 0) + ksft_exit_fail_msg("socketpair() failed\n"); + + pid = sys_clone3(&args, sizeof(args)); + if (pid < 0) { + close(ipc[0]); + close(ipc[1]); + ksft_exit_fail_msg("clone3() failed\n"); + } + + if (pid == 0) { + int fd; + char buf; + + close(ipc[1]); + + ret = read_nointr(ipc[0], &buf, 1); + if (ret != 1) + ksft_exit_fail_msg("read_nointr() failed\n"); + + if (setid_userns_root()) + ksft_exit_fail_msg("setid_userns_root() failed\n"); + + fd = make_file_cmp_owner(0, 0); + if (fd < 0) + ksft_exit_fail_msg("make_file_cmp_owner() failed\n"); + + if (unix_send_fds(ipc[0], &fd, 1, NULL, 0) < 0) + ksft_exit_fail_msg("unix_send_fds() failed\n"); + + exit(EXIT_SUCCESS); + } + + close(ipc[0]); + + ret = write_id_mapping(UID_MAP, pid, id_map, id_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + /* Must fail since a uid mapping has already been written. */ + ret = write_id_mapping(FSUID_MAP, pid, fsid_map, fsid_map_size); + if (ret == 0) { + ksft_exit_fail_msg("unix_send_fds() succeeded\n"); + goto kill_child; + } + + ret = write_id_mapping(GID_MAP, pid, id_map, id_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + /* Must fail since a gid mapping has already been written. */ + ret = write_id_mapping(FSGID_MAP, pid, fsid_map, fsid_map_size); + if (ret == 0) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + ret = write_nointr(ipc[1], "1", 1); + if (ret != 1) { + ksft_exit_fail_msg("write_nointr() failed\n"); + goto kill_child; + } + + if (unix_recv_fds(ipc[1], &fd, 1, NULL, 0) < 0) { + ksft_exit_fail_msg("unix_recv_fds() failed\n"); + goto kill_child; + } + + if (!has_expected_owner(fd, 100000, 100000)) { + ksft_exit_fail_msg("has_expected_owner() failed\n"); + goto kill_child; + } + + fret = EXIT_SUCCESS; + +wait_child: + ret = wait_for_pid(pid); + if (ret) + ksft_exit_fail_msg("wait_for_pid() failed\n"); + + if (fret == EXIT_SUCCESS) + return; + exit(fret); + +kill_child: + kill(pid, SIGKILL); + exit(EXIT_FAILURE); + goto wait_child; +} + +static void test_fsid_maps_basic(void) +{ + int fret = EXIT_FAILURE; + ssize_t ret; + int fd = -EBADF; + pid_t pid; + int ipc[2]; + struct clone_args args = { + .flags = CLONE_NEWUSER, + .exit_signal = SIGCHLD, + }; + + ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc); + if (ret < 0) + ksft_exit_fail_msg("socketpair() failed\n"); + + pid = sys_clone3(&args, sizeof(args)); + if (pid < 0) { + close(ipc[0]); + close(ipc[1]); + ksft_exit_fail_msg("clone3() failed\n"); + } + + if (pid == 0) { + int fd; + char buf; + + close(ipc[1]); + + ret = read_nointr(ipc[0], &buf, 1); + if (ret != 1) + ksft_exit_fail_msg("read_nointr() failed\n"); + + if (setid_userns_root()) + ksft_exit_fail_msg("setid_userns_root() failed\n"); + + fd = make_file_cmp_owner(0, 0); + if (fd < 0) + ksft_exit_fail_msg("make_file_cmp_owner() failed\n"); + + if (unix_send_fds(ipc[0], &fd, 1, NULL, 0) < 0) + ksft_exit_fail_msg("unix_send_fds() failed\n"); + + exit(EXIT_SUCCESS); + } + + close(ipc[0]); + + /* Must fail since a uid mapping has already been written. */ + ret = write_id_mapping(FSUID_MAP, pid, fsid_map, fsid_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + ret = write_id_mapping(UID_MAP, pid, id_map, id_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + /* Must fail since a gid mapping has already been written. */ + ret = write_id_mapping(FSGID_MAP, pid, fsid_map, fsid_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + ret = write_id_mapping(GID_MAP, pid, id_map, id_map_size); + if (ret) { + ksft_exit_fail_msg("unix_send_fds() failed\n"); + goto kill_child; + } + + ret = write_nointr(ipc[1], "1", 1); + if (ret != 1) { + ksft_exit_fail_msg("write_nointr() failed\n"); + goto kill_child; + } + + if (unix_recv_fds(ipc[1], &fd, 1, NULL, 0) < 0) { + ksft_exit_fail_msg("unix_recv_fds() failed\n"); + goto kill_child; + } + + if (!has_expected_owner(fd, 300000, 300000)) { + ksft_exit_fail_msg("has_expected_owner() failed\n"); + goto kill_child; + } + + fret = EXIT_SUCCESS; + +wait_child: + ret = wait_for_pid(pid); + if (ret) + ksft_exit_fail_msg("wait_for_pid() failed\n"); + + if (fret == EXIT_SUCCESS) + return; + exit(fret); + +kill_child: + kill(pid, SIGKILL); + exit(EXIT_FAILURE); + goto wait_child; +} + +int main(int argc, char *argv[]) +{ + if (getuid()) + ksft_exit_skip("fsid mapping tests require root\n"); + + if (access("/proc/self/fsuid_map", F_OK)) + ksft_exit_skip("fsid mappings not supported by this kernel\n"); + + test_clone3_supported(); + + test_id_maps_imply_fsid_maps(); + test_fsid_maps_basic(); + + exit(EXIT_SUCCESS); +}