CVE-2014-4014 (C, published 2014-06-23T11:21Z)
Description: value: The capabilities implementation in the Linux kernel before 3.14.8 does not properly consider that namespaces are inapplicable to inodes, which allows local users to bypass intended chmod restrictions by first creating a user namespace, as demonstrated by setting the setgid bit on a file with group ownership of root.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-264 \u2014 Permissions, Privileges, and Access Controls
Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
Repository: https://github.com/torvalds/linux
Files changed:
fs/attr.c
fs/inode.c
fs/namei.c
fs/xfs/xfs_ioctl.c
include/linux/capability.h
kernel/capability.c
Commit message: fs,userns: Change inode_capable to capable_wrt_inode_uidgid

The kernel has no concept of capabilities with respect to inodes; inodes
exist independently of namespaces.  For example, inode_capable(inode,
CAP_LINUX_IMMUTABLE) would be nonsense.

This patch changes inode_capable to check for uid and gid mappings and
renames it to capable_wrt_inode_uidgid, which should make it more
obvious what it does.

Fixes CVE-2014-4014.

Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Serge Hallyn <serge.hallyn@ubuntu.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: stable@vger.kernel.org
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>


Vulnerable code:
	    !inode_capable(inode, CAP_CHOWN))
	    !inode_capable(inode, CAP_CHOWN))
		    !inode_capable(inode, CAP_FSETID))
		    !inode_capable(inode, CAP_FSETID))
 * Return true if current either has CAP_FOWNER to the inode, or
 * owns the file.
	if (inode_capable(inode, CAP_FOWNER))
		if (inode_capable(inode, CAP_DAC_OVERRIDE))
			if (inode_capable(inode, CAP_DAC_READ_SEARCH))
		if (inode_capable(inode, CAP_DAC_OVERRIDE))
		if (inode_capable(inode, CAP_DAC_READ_SEARCH))
	return !inode_capable(inode, CAP_FOWNER);
		    !inode_capable(VFS_I(ip), CAP_FSETID))
extern bool inode_capable(const struct inode *inode, int cap);
 * inode_capable - Check superior capability over inode
 * Return true if the current task has the given superior capability
 * targeted at it's own user namespace and that the given inode is owned
 * by the current user namespace or a child namespace.
 *
 * Currently we check to see if an inode is owned by the current
 * user namespace by seeing if the inode's owner maps into the
 * current user namespace.
 *
bool inode_capable(const struct inode *inode, int cap)
	return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid);
EXPORT_SYMBOL(inode_capable);

Fixed code:
	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
	    !capable_wrt_inode_uidgid(inode, CAP_CHOWN))
		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))
		    !capable_wrt_inode_uidgid(inode, CAP_FSETID))
 * Return true if current either has CAP_FOWNER in a namespace with the
 * inode owner uid mapped, or owns the file.
	struct user_namespace *ns;


	ns = current_user_ns();
	if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))
		if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
			if (capable_wrt_inode_uidgid(inode,
						     CAP_DAC_READ_SEARCH))
		if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
		if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
	return !capable_wrt_inode_uidgid(inode, CAP_FOWNER);
		    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
 * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
 * Return true if the current task has the given capability targeted at
 * its own user namespace and that the given inode's uid and gid are
 * mapped into the current user namespace.
bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
	return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) &&
		kgid_has_mapping(ns, inode->i_gid);
EXPORT_SYMBOL(capable_wrt_inode_uidgid);