Commit e795b717 authored by Serge E. Hallyn's avatar Serge E. Hallyn Committed by Linus Torvalds

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: default avatarSerge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@free.fr>
Acked-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b0e77598
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/async.h> #include <linux/async.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/ima.h> #include <linux/ima.h>
#include <linux/cred.h>
/* /*
* This is needed for the following functions: * This is needed for the following functions:
...@@ -1733,3 +1734,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir, ...@@ -1733,3 +1734,19 @@ void inode_init_owner(struct inode *inode, const struct inode *dir,
inode->i_mode = mode; inode->i_mode = mode;
} }
EXPORT_SYMBOL(inode_init_owner); EXPORT_SYMBOL(inode_init_owner);
/*
* return true if current either has CAP_FOWNER to the
* file, or owns the file.
*/
bool is_owner_or_cap(const struct inode *inode)
{
struct user_namespace *ns = inode_userns(inode);
if (current_user_ns() == ns && current_fsuid() == inode->i_uid)
return true;
if (ns_capable(ns, CAP_FOWNER))
return true;
return false;
}
EXPORT_SYMBOL(is_owner_or_cap);
...@@ -183,6 +183,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag ...@@ -183,6 +183,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag
mask &= MAY_READ | MAY_WRITE | MAY_EXEC; mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (current_user_ns() != inode_userns(inode))
goto other_perms;
if (current_fsuid() == inode->i_uid) if (current_fsuid() == inode->i_uid)
mode >>= 6; mode >>= 6;
else { else {
...@@ -196,6 +199,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag ...@@ -196,6 +199,7 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag
mode >>= 3; mode >>= 3;
} }
other_perms:
/* /*
* If the DACs are ok we don't need any capability check. * If the DACs are ok we don't need any capability check.
*/ */
...@@ -237,7 +241,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags, ...@@ -237,7 +241,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
* Executable DACs are overridable if at least one exec bit is set. * Executable DACs are overridable if at least one exec bit is set.
*/ */
if (!(mask & MAY_EXEC) || execute_ok(inode)) if (!(mask & MAY_EXEC) || execute_ok(inode))
if (capable(CAP_DAC_OVERRIDE)) if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
return 0; return 0;
/* /*
...@@ -245,7 +249,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags, ...@@ -245,7 +249,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
*/ */
mask &= MAY_READ | MAY_WRITE | MAY_EXEC; mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
if (capable(CAP_DAC_READ_SEARCH)) if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
return 0; return 0;
return -EACCES; return -EACCES;
...@@ -654,6 +658,7 @@ static inline int handle_reval_path(struct nameidata *nd) ...@@ -654,6 +658,7 @@ static inline int handle_reval_path(struct nameidata *nd)
static inline int exec_permission(struct inode *inode, unsigned int flags) static inline int exec_permission(struct inode *inode, unsigned int flags)
{ {
int ret; int ret;
struct user_namespace *ns = inode_userns(inode);
if (inode->i_op->permission) { if (inode->i_op->permission) {
ret = inode->i_op->permission(inode, MAY_EXEC, flags); 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,7 +671,8 @@ static inline int exec_permission(struct inode *inode, unsigned int flags)
if (ret == -ECHILD) if (ret == -ECHILD)
return ret; return ret;
if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) if (ns_capable(ns, CAP_DAC_OVERRIDE) ||
ns_capable(ns, CAP_DAC_READ_SEARCH))
goto ok; goto ok;
return ret; return ret;
...@@ -1842,11 +1848,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode) ...@@ -1842,11 +1848,15 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
if (!(dir->i_mode & S_ISVTX)) if (!(dir->i_mode & S_ISVTX))
return 0; return 0;
if (current_user_ns() != inode_userns(inode))
goto other_userns;
if (inode->i_uid == fsuid) if (inode->i_uid == fsuid)
return 0; return 0;
if (dir->i_uid == fsuid) if (dir->i_uid == fsuid)
return 0; return 0;
return !capable(CAP_FOWNER);
other_userns:
return !ns_capable(inode_userns(inode), CAP_FOWNER);
} }
/* /*
...@@ -2440,7 +2450,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ...@@ -2440,7 +2450,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
if (error) if (error)
return error; return error;
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) if ((S_ISCHR(mode) || S_ISBLK(mode)) &&
!ns_capable(inode_userns(dir), CAP_MKNOD))
return -EPERM; return -EPERM;
if (!dir->i_op->mknod) if (!dir->i_op->mknod)
......
...@@ -1457,8 +1457,13 @@ enum { ...@@ -1457,8 +1457,13 @@ enum {
#define put_fs_excl() atomic_dec(&current->fs_excl) #define put_fs_excl() atomic_dec(&current->fs_excl)
#define has_fs_excl() atomic_read(&current->fs_excl) #define has_fs_excl() atomic_read(&current->fs_excl)
#define is_owner_or_cap(inode) \ /*
((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER)) * until VFS tracks user namespaces for inodes, just make all files
* belong to init_user_ns
*/
extern struct user_namespace init_user_ns;
#define inode_userns(inode) (&init_user_ns)
extern bool is_owner_or_cap(const struct inode *inode);
/* not quite ready to be deprecated, but... */ /* not quite ready to be deprecated, but... */
extern void lock_super(struct super_block *); extern void lock_super(struct super_block *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment