Commit e795b71799ff0b27365020c9ddaa25d0d83f99c8
Committed by
Linus Torvalds
1 parent
b0e77598f8
Exists in
master
and in
4 other branches
userns: userns: check user namespace for task->file uid equivalence checks
Cheat for now and say all files belong to init_user_ns. Next step will be to let superblocks belong to a user_ns, and derive inode_userns(inode) from inode->i_sb->s_user_ns. Finally we'll introduce more flexible arrangements. Changelog: Feb 15: make is_owner_or_cap take const struct inode Feb 23: make is_owner_or_cap bool [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Acked-by: Daniel Lezcano <daniel.lezcano@free.fr> Acked-by: David Howells <dhowells@redhat.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 3 changed files with 40 additions and 7 deletions Side-by-side Diff
fs/inode.c
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | #include <linux/async.h> |
26 | 26 | #include <linux/posix_acl.h> |
27 | 27 | #include <linux/ima.h> |
28 | +#include <linux/cred.h> | |
28 | 29 | |
29 | 30 | /* |
30 | 31 | * This is needed for the following functions: |
... | ... | @@ -1733,4 +1734,20 @@ |
1733 | 1734 | inode->i_mode = mode; |
1734 | 1735 | } |
1735 | 1736 | EXPORT_SYMBOL(inode_init_owner); |
1737 | + | |
1738 | +/* | |
1739 | + * return true if current either has CAP_FOWNER to the | |
1740 | + * file, or owns the file. | |
1741 | + */ | |
1742 | +bool is_owner_or_cap(const struct inode *inode) | |
1743 | +{ | |
1744 | + struct user_namespace *ns = inode_userns(inode); | |
1745 | + | |
1746 | + if (current_user_ns() == ns && current_fsuid() == inode->i_uid) | |
1747 | + return true; | |
1748 | + if (ns_capable(ns, CAP_FOWNER)) | |
1749 | + return true; | |
1750 | + return false; | |
1751 | +} | |
1752 | +EXPORT_SYMBOL(is_owner_or_cap); |
fs/namei.c
... | ... | @@ -183,6 +183,9 @@ |
183 | 183 | |
184 | 184 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; |
185 | 185 | |
186 | + if (current_user_ns() != inode_userns(inode)) | |
187 | + goto other_perms; | |
188 | + | |
186 | 189 | if (current_fsuid() == inode->i_uid) |
187 | 190 | mode >>= 6; |
188 | 191 | else { |
... | ... | @@ -196,6 +199,7 @@ |
196 | 199 | mode >>= 3; |
197 | 200 | } |
198 | 201 | |
202 | +other_perms: | |
199 | 203 | /* |
200 | 204 | * If the DACs are ok we don't need any capability check. |
201 | 205 | */ |
... | ... | @@ -237,7 +241,7 @@ |
237 | 241 | * Executable DACs are overridable if at least one exec bit is set. |
238 | 242 | */ |
239 | 243 | if (!(mask & MAY_EXEC) || execute_ok(inode)) |
240 | - if (capable(CAP_DAC_OVERRIDE)) | |
244 | + if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE)) | |
241 | 245 | return 0; |
242 | 246 | |
243 | 247 | /* |
... | ... | @@ -245,7 +249,7 @@ |
245 | 249 | */ |
246 | 250 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; |
247 | 251 | if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) |
248 | - if (capable(CAP_DAC_READ_SEARCH)) | |
252 | + if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH)) | |
249 | 253 | return 0; |
250 | 254 | |
251 | 255 | return -EACCES; |
... | ... | @@ -654,6 +658,7 @@ |
654 | 658 | static inline int exec_permission(struct inode *inode, unsigned int flags) |
655 | 659 | { |
656 | 660 | int ret; |
661 | + struct user_namespace *ns = inode_userns(inode); | |
657 | 662 | |
658 | 663 | if (inode->i_op->permission) { |
659 | 664 | ret = inode->i_op->permission(inode, MAY_EXEC, flags); |
... | ... | @@ -666,7 +671,8 @@ |
666 | 671 | if (ret == -ECHILD) |
667 | 672 | return ret; |
668 | 673 | |
669 | - if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) | |
674 | + if (ns_capable(ns, CAP_DAC_OVERRIDE) || | |
675 | + ns_capable(ns, CAP_DAC_READ_SEARCH)) | |
670 | 676 | goto ok; |
671 | 677 | |
672 | 678 | return ret; |
673 | 679 | |
... | ... | @@ -1842,11 +1848,15 @@ |
1842 | 1848 | |
1843 | 1849 | if (!(dir->i_mode & S_ISVTX)) |
1844 | 1850 | return 0; |
1851 | + if (current_user_ns() != inode_userns(inode)) | |
1852 | + goto other_userns; | |
1845 | 1853 | if (inode->i_uid == fsuid) |
1846 | 1854 | return 0; |
1847 | 1855 | if (dir->i_uid == fsuid) |
1848 | 1856 | return 0; |
1849 | - return !capable(CAP_FOWNER); | |
1857 | + | |
1858 | +other_userns: | |
1859 | + return !ns_capable(inode_userns(inode), CAP_FOWNER); | |
1850 | 1860 | } |
1851 | 1861 | |
1852 | 1862 | /* |
... | ... | @@ -2440,7 +2450,8 @@ |
2440 | 2450 | if (error) |
2441 | 2451 | return error; |
2442 | 2452 | |
2443 | - if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) | |
2453 | + if ((S_ISCHR(mode) || S_ISBLK(mode)) && | |
2454 | + !ns_capable(inode_userns(dir), CAP_MKNOD)) | |
2444 | 2455 | return -EPERM; |
2445 | 2456 | |
2446 | 2457 | if (!dir->i_op->mknod) |
include/linux/fs.h
... | ... | @@ -1457,8 +1457,13 @@ |
1457 | 1457 | #define put_fs_excl() atomic_dec(¤t->fs_excl) |
1458 | 1458 | #define has_fs_excl() atomic_read(¤t->fs_excl) |
1459 | 1459 | |
1460 | -#define is_owner_or_cap(inode) \ | |
1461 | - ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) | |
1460 | +/* | |
1461 | + * until VFS tracks user namespaces for inodes, just make all files | |
1462 | + * belong to init_user_ns | |
1463 | + */ | |
1464 | +extern struct user_namespace init_user_ns; | |
1465 | +#define inode_userns(inode) (&init_user_ns) | |
1466 | +extern bool is_owner_or_cap(const struct inode *inode); | |
1462 | 1467 | |
1463 | 1468 | /* not quite ready to be deprecated, but... */ |
1464 | 1469 | extern void lock_super(struct super_block *); |