Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andy Lutomirski and committed by
Linus Torvalds
23adbe12 5b174fd6

+27 -26
+4 -4
fs/attr.c
··· 50 50 if ((ia_valid & ATTR_UID) && 51 51 (!uid_eq(current_fsuid(), inode->i_uid) || 52 52 !uid_eq(attr->ia_uid, inode->i_uid)) && 53 - !inode_capable(inode, CAP_CHOWN)) 53 + !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) 54 54 return -EPERM; 55 55 56 56 /* Make sure caller can chgrp. */ 57 57 if ((ia_valid & ATTR_GID) && 58 58 (!uid_eq(current_fsuid(), inode->i_uid) || 59 59 (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) && 60 - !inode_capable(inode, CAP_CHOWN)) 60 + !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) 61 61 return -EPERM; 62 62 63 63 /* Make sure a caller can chmod. */ ··· 67 67 /* Also check the setgid bit! */ 68 68 if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : 69 69 inode->i_gid) && 70 - !inode_capable(inode, CAP_FSETID)) 70 + !capable_wrt_inode_uidgid(inode, CAP_FSETID)) 71 71 attr->ia_mode &= ~S_ISGID; 72 72 } 73 73 ··· 160 160 umode_t mode = attr->ia_mode; 161 161 162 162 if (!in_group_p(inode->i_gid) && 163 - !inode_capable(inode, CAP_FSETID)) 163 + !capable_wrt_inode_uidgid(inode, CAP_FSETID)) 164 164 mode &= ~S_ISGID; 165 165 inode->i_mode = mode; 166 166 }
+7 -3
fs/inode.c
··· 1839 1839 * inode_owner_or_capable - check current task permissions to inode 1840 1840 * @inode: inode being checked 1841 1841 * 1842 - * Return true if current either has CAP_FOWNER to the inode, or 1843 - * owns the file. 1842 + * Return true if current either has CAP_FOWNER in a namespace with the 1843 + * inode owner uid mapped, or owns the file. 1844 1844 */ 1845 1845 bool inode_owner_or_capable(const struct inode *inode) 1846 1846 { 1847 + struct user_namespace *ns; 1848 + 1847 1849 if (uid_eq(current_fsuid(), inode->i_uid)) 1848 1850 return true; 1849 - if (inode_capable(inode, CAP_FOWNER)) 1851 + 1852 + ns = current_user_ns(); 1853 + if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid)) 1850 1854 return true; 1851 1855 return false; 1852 1856 }
+6 -5
fs/namei.c
··· 332 332 333 333 if (S_ISDIR(inode->i_mode)) { 334 334 /* DACs are overridable for directories */ 335 - if (inode_capable(inode, CAP_DAC_OVERRIDE)) 335 + if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) 336 336 return 0; 337 337 if (!(mask & MAY_WRITE)) 338 - if (inode_capable(inode, CAP_DAC_READ_SEARCH)) 338 + if (capable_wrt_inode_uidgid(inode, 339 + CAP_DAC_READ_SEARCH)) 339 340 return 0; 340 341 return -EACCES; 341 342 } ··· 346 345 * at least one exec bit set. 347 346 */ 348 347 if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) 349 - if (inode_capable(inode, CAP_DAC_OVERRIDE)) 348 + if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) 350 349 return 0; 351 350 352 351 /* ··· 354 353 */ 355 354 mask &= MAY_READ | MAY_WRITE | MAY_EXEC; 356 355 if (mask == MAY_READ) 357 - if (inode_capable(inode, CAP_DAC_READ_SEARCH)) 356 + if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH)) 358 357 return 0; 359 358 360 359 return -EACCES; ··· 2380 2379 return 0; 2381 2380 if (uid_eq(dir->i_uid, fsuid)) 2382 2381 return 0; 2383 - return !inode_capable(inode, CAP_FOWNER); 2382 + return !capable_wrt_inode_uidgid(inode, CAP_FOWNER); 2384 2383 } 2385 2384 2386 2385 /*
+1 -1
fs/xfs/xfs_ioctl.c
··· 1215 1215 * cleared upon successful return from chown() 1216 1216 */ 1217 1217 if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && 1218 - !inode_capable(VFS_I(ip), CAP_FSETID)) 1218 + !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) 1219 1219 ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); 1220 1220 1221 1221 /*
+1 -1
include/linux/capability.h
··· 210 210 struct user_namespace *ns, int cap); 211 211 extern bool capable(int cap); 212 212 extern bool ns_capable(struct user_namespace *ns, int cap); 213 - extern bool inode_capable(const struct inode *inode, int cap); 213 + extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); 214 214 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); 215 215 216 216 /* audit system wants to get cap info from files as well */
+8 -12
kernel/capability.c
··· 424 424 EXPORT_SYMBOL(capable); 425 425 426 426 /** 427 - * inode_capable - Check superior capability over inode 427 + * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped 428 428 * @inode: The inode in question 429 429 * @cap: The capability in question 430 430 * 431 - * Return true if the current task has the given superior capability 432 - * targeted at it's own user namespace and that the given inode is owned 433 - * by the current user namespace or a child namespace. 434 - * 435 - * Currently we check to see if an inode is owned by the current 436 - * user namespace by seeing if the inode's owner maps into the 437 - * current user namespace. 438 - * 431 + * Return true if the current task has the given capability targeted at 432 + * its own user namespace and that the given inode's uid and gid are 433 + * mapped into the current user namespace. 439 434 */ 440 - bool inode_capable(const struct inode *inode, int cap) 435 + bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) 441 436 { 442 437 struct user_namespace *ns = current_user_ns(); 443 438 444 - return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid); 439 + return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && 440 + kgid_has_mapping(ns, inode->i_gid); 445 441 } 446 - EXPORT_SYMBOL(inode_capable); 442 + EXPORT_SYMBOL(capable_wrt_inode_uidgid);