diff options
author | Serge E. Hallyn <serge@hallyn.com> | 2011-03-23 19:43:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-23 22:47:08 -0400 |
commit | e795b71799ff0b27365020c9ddaa25d0d83f99c8 (patch) | |
tree | f3b628c2366f181380a8fbcd490910eb086a7b8e | |
parent | b0e77598f87107001a00b8a4ece9c95e4254ccc4 (diff) |
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>
-rw-r--r-- | fs/inode.c | 17 | ||||
-rw-r--r-- | fs/namei.c | 21 | ||||
-rw-r--r-- | include/linux/fs.h | 9 |
3 files changed, 40 insertions, 7 deletions
diff --git a/fs/inode.c b/fs/inode.c index 16fefd373fc2..a21d5a938a17 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/async.h> | 25 | #include <linux/async.h> |
26 | #include <linux/posix_acl.h> | 26 | #include <linux/posix_acl.h> |
27 | #include <linux/ima.h> | 27 | #include <linux/ima.h> |
28 | #include <linux/cred.h> | ||
28 | 29 | ||
29 | /* | 30 | /* |
30 | * This is needed for the following functions: | 31 | * This is needed for the following functions: |
@@ -1733,3 +1734,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, | |||
1733 | inode->i_mode = mode; | 1734 | inode->i_mode = mode; |
1734 | } | 1735 | } |
1735 | EXPORT_SYMBOL(inode_init_owner); | 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); | ||
diff --git a/fs/namei.c b/fs/namei.c index 5a9a6c3094da..dbb45a652ae3 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -183,6 +183,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag | |||
183 | 183 | ||
184 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; | 184 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; |
185 | 185 | ||
186 | if (current_user_ns() != inode_userns(inode)) | ||
187 | goto other_perms; | ||
188 | |||
186 | if (current_fsuid() == inode->i_uid) | 189 | if (current_fsuid() == inode->i_uid) |
187 | mode >>= 6; | 190 | mode >>= 6; |
188 | else { | 191 | else { |
@@ -196,6 +199,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag | |||
196 | mode >>= 3; | 199 | mode >>= 3; |
197 | } | 200 | } |
198 | 201 | ||
202 | other_perms: | ||
199 | /* | 203 | /* |
200 | * If the DACs are ok we don't need any capability check. | 204 | * If the DACs are ok we don't need any capability check. |
201 | */ | 205 | */ |
@@ -237,7 +241,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags, | |||
237 | * Executable DACs are overridable if at least one exec bit is set. | 241 | * Executable DACs are overridable if at least one exec bit is set. |
238 | */ | 242 | */ |
239 | if (!(mask & MAY_EXEC) || execute_ok(inode)) | 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 | return 0; | 245 | return 0; |
242 | 246 | ||
243 | /* | 247 | /* |
@@ -245,7 +249,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags, | |||
245 | */ | 249 | */ |
246 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; | 250 | mask &= MAY_READ | MAY_WRITE | MAY_EXEC; |
247 | if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) | 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 | return 0; | 253 | return 0; |
250 | 254 | ||
251 | return -EACCES; | 255 | return -EACCES; |
@@ -654,6 +658,7 @@ static inline int handle_reval_path(struct nameidata *nd) | |||
654 | static inline int exec_permission(struct inode *inode, unsigned int flags) | 658 | static inline int exec_permission(struct inode *inode, unsigned int flags) |
655 | { | 659 | { |
656 | int ret; | 660 | int ret; |
661 | struct user_namespace *ns = inode_userns(inode); | ||
657 | 662 | ||
658 | if (inode->i_op->permission) { | 663 | if (inode->i_op->permission) { |
659 | ret = inode->i_op->permission(inode, MAY_EXEC, flags); | 664 | ret = inode->i_op->permission(inode, MAY_EXEC, flags); |
@@ -666,7 +671,8 @@ static inline int exec_permission(struct inode *inode, unsigned int flags) | |||
666 | if (ret == -ECHILD) | 671 | if (ret == -ECHILD) |
667 | return ret; | 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 | goto ok; | 676 | goto ok; |
671 | 677 | ||
672 | return ret; | 678 | return ret; |
@@ -1842,11 +1848,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode) | |||
1842 | 1848 | ||
1843 | if (!(dir->i_mode & S_ISVTX)) | 1849 | if (!(dir->i_mode & S_ISVTX)) |
1844 | return 0; | 1850 | return 0; |
1851 | if (current_user_ns() != inode_userns(inode)) | ||
1852 | goto other_userns; | ||
1845 | if (inode->i_uid == fsuid) | 1853 | if (inode->i_uid == fsuid) |
1846 | return 0; | 1854 | return 0; |
1847 | if (dir->i_uid == fsuid) | 1855 | if (dir->i_uid == fsuid) |
1848 | return 0; | 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 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
2440 | if (error) | 2450 | if (error) |
2441 | return error; | 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 | return -EPERM; | 2455 | return -EPERM; |
2445 | 2456 | ||
2446 | if (!dir->i_op->mknod) | 2457 | if (!dir->i_op->mknod) |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 12529e966350..9eebc646d14a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1457,8 +1457,13 @@ enum { | |||
1457 | #define put_fs_excl() atomic_dec(¤t->fs_excl) | 1457 | #define put_fs_excl() atomic_dec(¤t->fs_excl) |
1458 | #define has_fs_excl() atomic_read(¤t->fs_excl) | 1458 | #define has_fs_excl() atomic_read(¤t->fs_excl) |
1459 | 1459 | ||
1460 | #define is_owner_or_cap(inode) \ | 1460 | /* |
1461 | ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) | 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 | /* not quite ready to be deprecated, but... */ | 1468 | /* not quite ready to be deprecated, but... */ |
1464 | extern void lock_super(struct super_block *); | 1469 | extern void lock_super(struct super_block *); |