From patchwork Wed May 4 14:26:49 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Djalal Harouni X-Patchwork-Id: 9014721 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 07CF09F39D for ; Wed, 4 May 2016 14:32:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 02BDC203AB for ; Wed, 4 May 2016 14:32:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5694B203A5 for ; Wed, 4 May 2016 14:32:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753366AbcEDObs (ORCPT ); Wed, 4 May 2016 10:31:48 -0400 Received: from mail-wm0-f43.google.com ([74.125.82.43]:38062 "EHLO mail-wm0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753003AbcEDO3p (ORCPT ); Wed, 4 May 2016 10:29:45 -0400 Received: by mail-wm0-f43.google.com with SMTP id g17so97564845wme.1; Wed, 04 May 2016 07:29:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=eE69hkojHnUPFKNDrM6wYsIpJ8Y9Gf8P9iiNwb/M4Ik=; b=fvwKdz3EiQp4HEniqT1pnffY5FB8vqsE3vd0P+bOZphrta8ei6cg/Si9iubFYUN/xO r38HQkbQrXdg9l8QMRUefzOeigDv7mQNBxBowOMB+A5Efp6Yfzf3oBjmMvrZFpxz3uO3 O1M6qLIvZPoAvsNzME/9s7eAqZn1vUTRHvtBvY4iAbm6mhAuKsSxInbtC7BTPm5Dx2PO 7a4RK1AQyENkdxJB4MMANKHA2PEqKEwEGv8f98+zDlpsjsB+WdxwIsti5I9/zg5Cf/vS 0Uyt5vN4u15bXE734EPE0jam5zNzTFVMzPE6HmvFoIQVqJC/vEF9qyeawg8sTwzJKGDC 0CkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=eE69hkojHnUPFKNDrM6wYsIpJ8Y9Gf8P9iiNwb/M4Ik=; b=cAJHNbb2jvCe9uDT8fzv1NqM6+SIPdwg9bYJTLkTAnBdM8NHwWAVGWFxI3Q1ZkI1TB VcRO89TlwNq7nF0qoKKsXV18Cpt5JlIF3enYpiLfIz3upGPOo3c5GVrgK9QgeWANquzK blERnPKBvLTNK13bGcU4OWhZetgKNBFcrHwEIaBVNKJyVkbyDJbdCdBCnNm80WsBaNjk H212cMsfoPhKnInGrS34S2dYjIeLa4DHBPwXsrmr+1bXRg9otvDDLJ8LsMBQcM8LLbqx xcV7UA2MxDcu/fDtXJljz86K0PxhNKtf5ZUxcdYrijr2jq6Ia0AWUMsEuAHyCGK0AKbe 0IBw== X-Gm-Message-State: AOPr4FUlna5md/L6BlT1DPvcLfjxO26iEKk0oSWQbadgFGZmE0tmPxEfBrK6d8Pxvaxj+A== X-Received: by 10.28.97.9 with SMTP id v9mr9051376wmb.45.1462372183896; Wed, 04 May 2016 07:29:43 -0700 (PDT) Received: from dztty2.localdomain (ip5b42f9c9.dynamic.kabel-deutschland.de. [91.66.249.201]) by smtp.gmail.com with ESMTPSA id a75sm4615505wme.18.2016.05.04.07.29.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 04 May 2016 07:29:42 -0700 (PDT) From: Djalal Harouni To: Alexander Viro , Chris Mason , , Serge Hallyn , Josh Triplett , "Eric W. Biederman" , Andy Lutomirski , Seth Forshee , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, Dongsu Park , David Herrmann , Miklos Szeredi , Alban Crequy Cc: Djalal Harouni , Djalal Harouni Subject: [RFC v2 PATCH 3/8] fs: Treat foreign mounts as nosuid Date: Wed, 4 May 2016 16:26:49 +0200 Message-Id: <1462372014-3786-4-git-send-email-tixxdz@gmail.com> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1462372014-3786-1-git-send-email-tixxdz@gmail.com> References: <1462372014-3786-1-git-send-email-tixxdz@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-8.9 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP If a process gets access to a mount from a different user namespace, that process should not be able to take advantage of setuid files or selinux entrypoints from that filesystem. Prevent this by treating mounts from other mount namespaces and those not owned by current_user_ns() or an ancestor as nosuid. This patch was just adapted from the original one that was written by Andy Lutomirski https://www.redhat.com/archives/dm-devel/2016-April/msg00374.html Signed-off-by: Djalal Harouni --- fs/exec.c | 2 +- fs/namespace.c | 15 +++++++++++++++ include/linux/mount.h | 1 + include/linux/user_namespace.h | 8 ++++++++ kernel/user_namespace.c | 13 +++++++++++++ security/commoncap.c | 2 +- security/selinux/hooks.c | 2 +- 7 files changed, 40 insertions(+), 3 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index c4010b8..706088d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1391,7 +1391,7 @@ static void bprm_fill_uid(struct linux_binprm *bprm) bprm->cred->euid = current_euid(); bprm->cred->egid = current_egid(); - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (!mnt_may_suid(bprm->file->f_path.mnt)) return; if (task_no_new_privs(current)) diff --git a/fs/namespace.c b/fs/namespace.c index de02b39..a8820fb 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3374,6 +3374,21 @@ found: return visible; } +bool mnt_may_suid(struct vfsmount *mnt) +{ + struct mount *m = real_mount(mnt); + + /* + * Foreign mounts (accessed via fchdir or through /proc + * symlinks) are always treated as if they are nosuid. This + * prevents namespaces from trusting potentially unsafe + * suid/sgid bits, file caps, or security labels that originate + * in other namespaces. + */ + return !(mnt->mnt_flags & MNT_NOSUID) && check_mnt(m) && + in_userns(current_user_ns(), m->mnt_ns->user_ns); +} + static struct ns_common *mntns_get(struct task_struct *task) { struct ns_common *ns = NULL; diff --git a/include/linux/mount.h b/include/linux/mount.h index f822c3c..54a594d 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -81,6 +81,7 @@ extern void mntput(struct vfsmount *mnt); extern struct vfsmount *mntget(struct vfsmount *mnt); extern struct vfsmount *mnt_clone_internal(struct path *path); extern int __mnt_is_readonly(struct vfsmount *mnt); +extern bool mnt_may_suid(struct vfsmount *mnt); struct path; extern struct vfsmount *clone_private_mount(struct path *path); diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 8297e5b..a43faa7 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -72,6 +72,8 @@ extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t, loff_t *); extern int proc_setgroups_show(struct seq_file *m, void *v); extern bool userns_may_setgroups(const struct user_namespace *ns); +extern bool in_userns(const struct user_namespace *ns, + const struct user_namespace *target_ns); #else static inline struct user_namespace *get_user_ns(struct user_namespace *ns) @@ -100,6 +102,12 @@ static inline bool userns_may_setgroups(const struct user_namespace *ns) { return true; } + +static inline bool in_userns(const struct user_namespace *ns, + const struct user_namespace *target_ns) +{ + return true; +} #endif #endif /* _LINUX_USER_H */ diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 9bafc21..9a496a8 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -938,6 +938,19 @@ bool userns_may_setgroups(const struct user_namespace *ns) return allowed; } +/* + * Returns true if @ns is the same namespace as or a descendant of + * @target_ns. + */ +bool in_userns(const struct user_namespace *ns, + const struct user_namespace *target_ns) +{ + for (; ns; ns = ns->parent) { + if (ns == target_ns) + return true; + } +} + static inline struct user_namespace *to_user_ns(struct ns_common *ns) { return container_of(ns, struct user_namespace, ns); diff --git a/security/commoncap.c b/security/commoncap.c index 48071ed..6c082d2 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -453,7 +453,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c if (!file_caps_enabled) return 0; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (!mnt_may_suid(bprm->file->f_path.mnt)) return 0; rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 912deee..1350167 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2234,7 +2234,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *new_tsec) { int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); - int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); + int nosuid = !mnt_may_suid(bprm->file->f_path.mnt); int rc; if (!nnp && !nosuid)