From 5ff9d8a65ce80efb509ce4e8051394e9ed2cd942 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 29 Mar 2013 21:04:39 -0700 Subject: vfs: Lock in place mounts from more privileged users When creating a less privileged mount namespace or propogating mounts from a more privileged to a less privileged mount namespace lock the submounts so they may not be unmounted individually in the child mount namespace revealing what is under them. This enforces the reasonable expectation that it is not possible to see under a mount point. Most of the time mounts are on empty directories and revealing that does not matter, however I have seen an occassionaly sloppy configuration where there were interesting things concealed under a mount point that probably should not be revealed. Expirable submounts are not locked because they will eventually unmount automatically so whatever is under them already needs to be safe for unprivileged users to access. From a practical standpoint these restrictions do not appear to be significant for unprivileged users of the mount namespace. Recursive bind mounts and pivot_root continues to work, and mounts that are created in a mount namespace may be unmounted there. All of which means that the common idiom of keeping a directory of interesting files and using pivot_root to throw everything else away continues to work just fine. Acked-by: Serge Hallyn Acked-by: Andy Lutomirski Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 7b1ca9ba0b0a..7e16a730559c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; + /* Don't allow unprivileged users to reveal what is under a mount */ + if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) + mnt->mnt.mnt_flags |= MNT_LOCKED; + atomic_inc(&sb->s_active); mnt->mnt.mnt_sb = sb; mnt->mnt.mnt_root = dget(root); @@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags) goto dput_and_out; if (!check_mnt(mnt)) goto dput_and_out; + if (mnt->mnt.mnt_flags & MNT_LOCKED) + goto dput_and_out; retval = do_umount(mnt, flags); dput_and_out: @@ -1381,6 +1387,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, if (IS_ERR(q)) return q; + q->mnt.mnt_flags &= ~MNT_LOCKED; q->mnt_mountpoint = mnt->mnt_mountpoint; p = mnt; @@ -1696,6 +1703,19 @@ static int do_change_type(struct path *path, int flag) return err; } +static bool has_locked_children(struct mount *mnt, struct dentry *dentry) +{ + struct mount *child; + list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { + if (!is_subdir(child->mnt_mountpoint, dentry)) + continue; + + if (child->mnt.mnt_flags & MNT_LOCKED) + return true; + } + return false; +} + /* * do loopback mount. */ @@ -1731,6 +1751,9 @@ static int do_loopback(struct path *path, const char *old_name, if (!check_mnt(parent) || !check_mnt(old)) goto out2; + if (!recurse && has_locked_children(old, old_path.dentry)) + goto out2; + if (recurse) mnt = copy_tree(old, old_path.dentry, 0); else @@ -1741,6 +1764,8 @@ static int do_loopback(struct path *path, const char *old_name, goto out2; } + mnt->mnt.mnt_flags &= ~MNT_LOCKED; + err = graft_tree(mnt, parent, mp); if (err) { br_write_lock(&vfsmount_lock); @@ -1853,6 +1878,9 @@ static int do_move_mount(struct path *path, const char *old_name) if (!check_mnt(p) || !check_mnt(old)) goto out1; + if (old->mnt.mnt_flags & MNT_LOCKED) + goto out1; + err = -EINVAL; if (old_path.dentry != old_path.mnt->mnt_root) goto out1; @@ -2630,6 +2658,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out4; if (!check_mnt(root_mnt) || !check_mnt(new_mnt)) goto out4; + if (new_mnt->mnt.mnt_flags & MNT_LOCKED) + goto out4; error = -ENOENT; if (d_unlinked(new.dentry)) goto out4; @@ -2653,6 +2683,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, br_write_lock(&vfsmount_lock); detach_mnt(new_mnt, &parent_path); detach_mnt(root_mnt, &root_parent); + if (root_mnt->mnt.mnt_flags & MNT_LOCKED) { + new_mnt->mnt.mnt_flags |= MNT_LOCKED; + root_mnt->mnt.mnt_flags &= ~MNT_LOCKED; + } /* mount old root on put_old */ attach_mnt(root_mnt, old_mnt, old_mp); /* mount new_root on / */ -- cgit v1.2.3 From aee1c13dd0f6c2fc56e0e492b349ee8ac655880f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 25 Mar 2013 19:57:10 -0700 Subject: proc: Restrict mounting the proc filesystem Don't allow mounting the proc filesystem unless the caller has CAP_SYS_ADMIN rights over the pid namespace. The principle here is if you create or have capabilities over it you can mount it, otherwise you get to live with what other people have mounted. Andy pointed out that this is needed to prevent users in a user namespace from remounting proc and specifying different hidepid and gid options on already existing proc mounts. Cc: stable@vger.kernel.org Reported-by: Andy Lutomirski Signed-off-by: "Eric W. Biederman" --- fs/proc/root.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/proc/root.c b/fs/proc/root.c index 229e366598da..38bd5d423fcd 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -110,7 +110,8 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, ns = task_active_pid_ns(current); options = data; - if (!current_user_ns()->may_mount_proc) + if (!current_user_ns()->may_mount_proc || + !ns_capable(ns->user_ns, CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); } -- cgit v1.2.3 From 4ce5d2b1a8fde84c0eebe70652cf28b9beda6b4e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 30 Mar 2013 01:35:18 -0700 Subject: vfs: Don't copy mount bind mounts of /proc//ns/mnt between namespaces Don't copy bind mounts of /proc//ns/mnt between namespaces. These files hold references to a mount namespace and copying them between namespaces could result in a reference counting loop. The current mnt_ns_loop test prevents loops on the assumption that mounts don't cross between namespaces. Unfortunately unsharing a mount namespace and shared substrees can both cause mounts to propogate between mount namespaces. Add two flags CL_COPY_UNBINDABLE and CL_COPY_MNT_NS_FILE are added to control this behavior, and CL_COPY_ALL is redefined as both of them. Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 46 ++++++++++++++++++++++++++++++++++------------ fs/pnode.h | 5 ++++- 2 files changed, 38 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 7e16a730559c..64627f883bf2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1355,14 +1355,11 @@ SYSCALL_DEFINE1(oldumount, char __user *, name) #endif -static bool mnt_ns_loop(struct path *path) +static bool is_mnt_ns_file(struct dentry *dentry) { - /* Could bind mounting the mount namespace inode cause a - * mount namespace loop? - */ - struct inode *inode = path->dentry->d_inode; + /* Is this a proxy for a mount namespace? */ + struct inode *inode = dentry->d_inode; struct proc_ns *ei; - struct mnt_namespace *mnt_ns; if (!proc_ns_inode(inode)) return false; @@ -1371,7 +1368,19 @@ static bool mnt_ns_loop(struct path *path) if (ei->ns_ops != &mntns_operations) return false; - mnt_ns = ei->ns; + return true; +} + +static bool mnt_ns_loop(struct dentry *dentry) +{ + /* Could bind mounting the mount namespace inode cause a + * mount namespace loop? + */ + struct mnt_namespace *mnt_ns; + if (!is_mnt_ns_file(dentry)) + return false; + + mnt_ns = get_proc_ns(dentry->d_inode)->ns; return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; } @@ -1380,7 +1389,10 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, { struct mount *res, *p, *q, *r, *parent; - if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) + if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt)) + return ERR_PTR(-EINVAL); + + if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry)) return ERR_PTR(-EINVAL); res = q = clone_mnt(mnt, dentry, flag); @@ -1397,7 +1409,13 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, continue; for (s = r; s; s = next_mnt(s, r)) { - if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) { + if (!(flag & CL_COPY_UNBINDABLE) && + IS_MNT_UNBINDABLE(s)) { + s = skip_mnt_tree(s); + continue; + } + if (!(flag & CL_COPY_MNT_NS_FILE) && + is_mnt_ns_file(s->mnt.mnt_root)) { s = skip_mnt_tree(s); continue; } @@ -1733,7 +1751,7 @@ static int do_loopback(struct path *path, const char *old_name, return err; err = -EINVAL; - if (mnt_ns_loop(&old_path)) + if (mnt_ns_loop(old_path.dentry)) goto out; mp = lock_mount(path); @@ -1755,7 +1773,7 @@ static int do_loopback(struct path *path, const char *old_name, goto out2; if (recurse) - mnt = copy_tree(old, old_path.dentry, 0); + mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE); else mnt = clone_mnt(old, old_path.dentry, 0); @@ -2417,7 +2435,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, namespace_lock(); /* First pass: copy the tree topology */ - copy_flags = CL_COPY_ALL | CL_EXPIRE; + copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != mnt_ns->user_ns) copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; new = copy_tree(old, old->mnt.mnt_root, copy_flags); @@ -2452,6 +2470,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, } p = next_mnt(p, old); q = next_mnt(q, new); + if (!q) + break; + while (p->mnt.mnt_root != q->mnt.mnt_root) + p = next_mnt(p, old); } namespace_unlock(); diff --git a/fs/pnode.h b/fs/pnode.h index b091445c1c4a..59e7eda1851e 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -19,11 +19,14 @@ #define CL_EXPIRE 0x01 #define CL_SLAVE 0x02 -#define CL_COPY_ALL 0x04 +#define CL_COPY_UNBINDABLE 0x04 #define CL_MAKE_SHARED 0x08 #define CL_PRIVATE 0x10 #define CL_SHARED_TO_SLAVE 0x20 #define CL_UNPRIVILEGED 0x40 +#define CL_COPY_MNT_NS_FILE 0x80 + +#define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) static inline void set_mnt_shared(struct mount *mnt) { -- cgit v1.2.3 From e51db73532955dc5eaba4235e62b74b460709d5b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 30 Mar 2013 19:57:41 -0700 Subject: userns: Better restrictions on when proc and sysfs can be mounted Rely on the fact that another flavor of the filesystem is already mounted and do not rely on state in the user namespace. Verify that the mounted filesystem is not covered in any significant way. I would love to verify that the previously mounted filesystem has no mounts on top but there are at least the directories /proc/sys/fs/binfmt_misc and /sys/fs/cgroup/ that exist explicitly for other filesystems to mount on top of. Refactor the test into a function named fs_fully_visible and call that function from the mount routines of proc and sysfs. This makes this test local to the filesystems involved and the results current of when the mounts take place, removing a weird threading of the user namespace, the mount namespace and the filesystems themselves. Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 37 +++++++++++++++++++++++++------------ fs/proc/root.c | 7 +++++-- fs/sysfs/mount.c | 3 ++- include/linux/fs.h | 1 + include/linux/user_namespace.h | 4 ---- kernel/user.c | 2 -- kernel/user_namespace.c | 2 -- 7 files changed, 33 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 64627f883bf2..877e4277f496 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2867,25 +2867,38 @@ bool current_chrooted(void) return chrooted; } -void update_mnt_policy(struct user_namespace *userns) +bool fs_fully_visible(struct file_system_type *type) { struct mnt_namespace *ns = current->nsproxy->mnt_ns; struct mount *mnt; + bool visible = false; - down_read(&namespace_sem); + if (unlikely(!ns)) + return false; + + namespace_lock(); list_for_each_entry(mnt, &ns->list, mnt_list) { - switch (mnt->mnt.mnt_sb->s_magic) { - case SYSFS_MAGIC: - userns->may_mount_sysfs = true; - break; - case PROC_SUPER_MAGIC: - userns->may_mount_proc = true; - break; + struct mount *child; + if (mnt->mnt.mnt_sb->s_type != type) + continue; + + /* This mount is not fully visible if there are any child mounts + * that cover anything except for empty directories. + */ + list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { + struct inode *inode = child->mnt_mountpoint->d_inode; + if (!S_ISDIR(inode->i_mode)) + goto next; + if (inode->i_nlink != 2) + goto next; } - if (userns->may_mount_sysfs && userns->may_mount_proc) - break; + visible = true; + goto found; + next: ; } - up_read(&namespace_sem); +found: + namespace_unlock(); + return visible; } static void *mntns_get(struct task_struct *task) diff --git a/fs/proc/root.c b/fs/proc/root.c index 38bd5d423fcd..45e5fb7da09b 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -110,8 +110,11 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, ns = task_active_pid_ns(current); options = data; - if (!current_user_ns()->may_mount_proc || - !ns_capable(ns->user_ns, CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) + return ERR_PTR(-EPERM); + + /* Does the mounter have privilege over the pid namespace? */ + if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index afd83273e6ce..4a2da3a4b1b1 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -112,7 +112,8 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, struct super_block *sb; int error; - if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs) + if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) && + !fs_fully_visible(fs_type)) return ERR_PTR(-EPERM); info = kzalloc(sizeof(*info), GFP_KERNEL); diff --git a/include/linux/fs.h b/include/linux/fs.h index 981874773e85..3050c620f062 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1897,6 +1897,7 @@ extern int vfs_ustat(dev_t, struct kstatfs *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); extern bool our_mnt(struct vfsmount *mnt); +extern bool fs_fully_visible(struct file_system_type *); extern int current_umask(void); diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index b6b215f13b45..4ce009324933 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -26,8 +26,6 @@ struct user_namespace { kuid_t owner; kgid_t group; unsigned int proc_inum; - bool may_mount_sysfs; - bool may_mount_proc; }; extern struct user_namespace init_user_ns; @@ -84,6 +82,4 @@ static inline void put_user_ns(struct user_namespace *ns) #endif -void update_mnt_policy(struct user_namespace *userns); - #endif /* _LINUX_USER_H */ diff --git a/kernel/user.c b/kernel/user.c index 69b4c3d48cde..5bbb91988e69 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -51,8 +51,6 @@ struct user_namespace init_user_ns = { .owner = GLOBAL_ROOT_UID, .group = GLOBAL_ROOT_GID, .proc_inum = PROC_USER_INIT_INO, - .may_mount_sysfs = true, - .may_mount_proc = true, }; EXPORT_SYMBOL_GPL(init_user_ns); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index d8c30db06c5b..d58ad1e7a794 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -97,8 +97,6 @@ int create_user_ns(struct cred *new) set_cred_user_ns(new, ns); - update_mnt_policy(ns); - return 0; } -- cgit v1.2.3 From 7dc5dbc879bd0779924b5132a48b731a0bc04a1e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 25 Mar 2013 20:07:01 -0700 Subject: sysfs: Restrict mounting sysfs Don't allow mounting sysfs unless the caller has CAP_SYS_ADMIN rights over the net namespace. The principle here is if you create or have capabilities over it you can mount it, otherwise you get to live with what other people have mounted. Instead of testing this with a straight forward ns_capable call, perform this check the long and torturous way with kobject helpers, this keeps direct knowledge of namespaces out of sysfs, and preserves the existing sysfs abstractions. Acked-by: Greg Kroah-Hartman Signed-off-by: "Eric W. Biederman" --- fs/sysfs/mount.c | 12 +++++++++--- include/linux/kobject_ns.h | 2 ++ lib/kobject.c | 15 +++++++++++++++ net/core/net-sysfs.c | 8 ++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 4a2da3a4b1b1..8c69ef49c7f3 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -112,9 +112,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, struct super_block *sb; int error; - if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) && - !fs_fully_visible(fs_type)) - return ERR_PTR(-EPERM); + if (!(flags & MS_KERNMOUNT)) { + if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) + return ERR_PTR(-EPERM); + + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { + if (!kobj_ns_current_may_mount(type)) + return ERR_PTR(-EPERM); + } + } info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) diff --git a/include/linux/kobject_ns.h b/include/linux/kobject_ns.h index f66b065a8b5f..df32d2508290 100644 --- a/include/linux/kobject_ns.h +++ b/include/linux/kobject_ns.h @@ -39,6 +39,7 @@ enum kobj_ns_type { */ struct kobj_ns_type_operations { enum kobj_ns_type type; + bool (*current_may_mount)(void); void *(*grab_current_ns)(void); const void *(*netlink_ns)(struct sock *sk); const void *(*initial_ns)(void); @@ -50,6 +51,7 @@ int kobj_ns_type_registered(enum kobj_ns_type type); const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); +bool kobj_ns_current_may_mount(enum kobj_ns_type type); void *kobj_ns_grab_current(enum kobj_ns_type type); const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); const void *kobj_ns_initial(enum kobj_ns_type type); diff --git a/lib/kobject.c b/lib/kobject.c index 4a1f33d43548..3bbde222c90f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -915,6 +915,21 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) return kobj_child_ns_ops(kobj->parent); } +bool kobj_ns_current_may_mount(enum kobj_ns_type type) +{ + bool may_mount = false; + + if (type == KOBJ_NS_TYPE_NONE) + return true; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type]) + may_mount = kobj_ns_ops_tbl[type]->current_may_mount(); + spin_unlock(&kobj_ns_type_lock); + + return may_mount; +} void *kobj_ns_grab_current(enum kobj_ns_type type) { diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 981fed397d1d..9bd9ae16adf5 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1157,6 +1157,13 @@ static void remove_queue_kobjects(struct net_device *net) #endif } +static bool net_current_may_mount(void) +{ + struct net *net = current->nsproxy->net_ns; + + return ns_capable(net->user_ns, CAP_SYS_ADMIN); +} + static void *net_grab_current_ns(void) { struct net *ns = current->nsproxy->net_ns; @@ -1179,6 +1186,7 @@ static const void *net_netlink_ns(struct sock *sk) struct kobj_ns_type_operations net_ns_type_operations = { .type = KOBJ_NS_TYPE_NET, + .current_may_mount = net_current_may_mount, .grab_current_ns = net_grab_current_ns, .netlink_ns = net_netlink_ns, .initial_ns = net_initial_ns, -- cgit v1.2.3 From c7b96acf1456ef127fef461fcfedb54b81fecfbb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 20 Mar 2013 12:49:49 -0700 Subject: userns: Kill nsown_capable it makes the wrong thing easy nsown_capable is a special case of ns_capable essentially for just CAP_SETUID and CAP_SETGID. For the existing users it doesn't noticably simplify things and from the suggested patches I have seen it encourages people to do the wrong thing. So remove nsown_capable. Acked-by: Serge Hallyn Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 4 ++-- fs/open.c | 2 +- include/linux/capability.h | 1 - ipc/namespace.c | 2 +- kernel/capability.c | 12 ------------ kernel/groups.c | 2 +- kernel/pid_namespace.c | 2 +- kernel/sys.c | 20 ++++++++++---------- kernel/uid16.c | 2 +- kernel/utsname.c | 2 +- net/core/net_namespace.c | 2 +- net/core/scm.c | 4 ++-- 12 files changed, 21 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 877e4277f496..dc519a1437ee 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2929,8 +2929,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns) struct path root; if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || - !nsown_capable(CAP_SYS_CHROOT) || - !nsown_capable(CAP_SYS_ADMIN)) + !ns_capable(current_user_ns(), CAP_SYS_CHROOT) || + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; if (fs->users != 1) diff --git a/fs/open.c b/fs/open.c index 9156cb050d08..1c9d23f7e683 100644 --- a/fs/open.c +++ b/fs/open.c @@ -443,7 +443,7 @@ retry: goto dput_and_out; error = -EPERM; - if (!nsown_capable(CAP_SYS_CHROOT)) + if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) goto dput_and_out; error = security_path_chroot(&path); if (error) diff --git a/include/linux/capability.h b/include/linux/capability.h index d9a4f7f40f32..a6ee1f9a5018 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -210,7 +210,6 @@ extern bool has_ns_capability_noaudit(struct task_struct *t, struct user_namespace *ns, int cap); extern bool capable(int cap); extern bool ns_capable(struct user_namespace *ns, int cap); -extern bool nsown_capable(int cap); extern bool inode_capable(const struct inode *inode, int cap); extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); diff --git a/ipc/namespace.c b/ipc/namespace.c index 7ee61bf44933..4be6581d3b7f 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -171,7 +171,7 @@ static int ipcns_install(struct nsproxy *nsproxy, void *new) { struct ipc_namespace *ns = new; if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || - !nsown_capable(CAP_SYS_ADMIN)) + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; /* Ditch state from the old ipc namespace */ diff --git a/kernel/capability.c b/kernel/capability.c index f6c2ce5701e1..6fc1c8af44df 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -432,18 +432,6 @@ bool capable(int cap) } EXPORT_SYMBOL(capable); -/** - * nsown_capable - Check superior capability to one's own user_ns - * @cap: The capability in question - * - * Return true if the current task has the given superior capability - * targeted at its own user namespace. - */ -bool nsown_capable(int cap) -{ - return ns_capable(current_user_ns(), cap); -} - /** * inode_capable - Check superior capability over inode * @inode: The inode in question diff --git a/kernel/groups.c b/kernel/groups.c index 6b2588dd04ff..90cf1c38c8ea 100644 --- a/kernel/groups.c +++ b/kernel/groups.c @@ -233,7 +233,7 @@ SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist) struct group_info *group_info; int retval; - if (!nsown_capable(CAP_SETGID)) + if (!ns_capable(current_user_ns(), CAP_SETGID)) return -EPERM; if ((unsigned)gidsetsize > NGROUPS_MAX) return -EINVAL; diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index 6917e8edb48e..ee1f6bb83d67 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -329,7 +329,7 @@ static int pidns_install(struct nsproxy *nsproxy, void *ns) struct pid_namespace *ancestor, *new = ns; if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) || - !nsown_capable(CAP_SYS_ADMIN)) + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; /* diff --git a/kernel/sys.c b/kernel/sys.c index 771129b299f8..c18ecca575b4 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -337,7 +337,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid) if (rgid != (gid_t) -1) { if (gid_eq(old->gid, krgid) || gid_eq(old->egid, krgid) || - nsown_capable(CAP_SETGID)) + ns_capable(old->user_ns, CAP_SETGID)) new->gid = krgid; else goto error; @@ -346,7 +346,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid) if (gid_eq(old->gid, kegid) || gid_eq(old->egid, kegid) || gid_eq(old->sgid, kegid) || - nsown_capable(CAP_SETGID)) + ns_capable(old->user_ns, CAP_SETGID)) new->egid = kegid; else goto error; @@ -387,7 +387,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) old = current_cred(); retval = -EPERM; - if (nsown_capable(CAP_SETGID)) + 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; @@ -471,7 +471,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) new->uid = kruid; if (!uid_eq(old->uid, kruid) && !uid_eq(old->euid, kruid) && - !nsown_capable(CAP_SETUID)) + !ns_capable(old->user_ns, CAP_SETUID)) goto error; } @@ -480,7 +480,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) if (!uid_eq(old->uid, keuid) && !uid_eq(old->euid, keuid) && !uid_eq(old->suid, keuid) && - !nsown_capable(CAP_SETUID)) + !ns_capable(old->user_ns, CAP_SETUID)) goto error; } @@ -534,7 +534,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) old = current_cred(); retval = -EPERM; - if (nsown_capable(CAP_SETUID)) { + if (ns_capable(old->user_ns, CAP_SETUID)) { new->suid = new->uid = kuid; if (!uid_eq(kuid, old->uid)) { retval = set_user(new); @@ -591,7 +591,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) old = current_cred(); retval = -EPERM; - if (!nsown_capable(CAP_SETUID)) { + if (!ns_capable(old->user_ns, CAP_SETUID)) { if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) && !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid)) goto error; @@ -673,7 +673,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) old = current_cred(); retval = -EPERM; - if (!nsown_capable(CAP_SETGID)) { + if (!ns_capable(old->user_ns, CAP_SETGID)) { if (rgid != (gid_t) -1 && !gid_eq(krgid, old->gid) && !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid)) goto error; @@ -744,7 +744,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid) if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) || uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) || - nsown_capable(CAP_SETUID)) { + ns_capable(old->user_ns, CAP_SETUID)) { if (!uid_eq(kuid, old->fsuid)) { new->fsuid = kuid; if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0) @@ -783,7 +783,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid) if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->egid) || gid_eq(kgid, old->sgid) || gid_eq(kgid, old->fsgid) || - nsown_capable(CAP_SETGID)) { + ns_capable(old->user_ns, CAP_SETGID)) { if (!gid_eq(kgid, old->fsgid)) { new->fsgid = kgid; goto change_okay; diff --git a/kernel/uid16.c b/kernel/uid16.c index f6c83d7ef000..602e5bbbceff 100644 --- a/kernel/uid16.c +++ b/kernel/uid16.c @@ -176,7 +176,7 @@ SYSCALL_DEFINE2(setgroups16, int, gidsetsize, old_gid_t __user *, grouplist) struct group_info *group_info; int retval; - if (!nsown_capable(CAP_SETGID)) + if (!ns_capable(current_user_ns(), CAP_SETGID)) return -EPERM; if ((unsigned)gidsetsize > NGROUPS_MAX) return -EINVAL; diff --git a/kernel/utsname.c b/kernel/utsname.c index 2fc8576efaa8..fd393124e507 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -114,7 +114,7 @@ static int utsns_install(struct nsproxy *nsproxy, void *new) struct uts_namespace *ns = new; if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || - !nsown_capable(CAP_SYS_ADMIN)) + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; get_uts_ns(ns); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index f97652036754..81d3a9a08453 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -651,7 +651,7 @@ static int netns_install(struct nsproxy *nsproxy, void *ns) struct net *net = ns; if (!ns_capable(net->user_ns, CAP_SYS_ADMIN) || - !nsown_capable(CAP_SYS_ADMIN)) + !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) return -EPERM; put_net(nsproxy->net_ns); diff --git a/net/core/scm.c b/net/core/scm.c index 03795d0147f2..c346f58d97c2 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -56,9 +56,9 @@ static __inline__ int scm_check_creds(struct ucred *creds) if ((creds->pid == task_tgid_vnr(current) || ns_capable(current->nsproxy->pid_ns->user_ns, CAP_SYS_ADMIN)) && ((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || - uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) && + uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && ((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || - gid_eq(gid, cred->sgid)) || nsown_capable(CAP_SETGID))) { + gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) { return 0; } return -EPERM; -- cgit v1.2.3