mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									b0e77598f8
								
							
						
					
					
						commit
						e795b71799
					
				
					 3 changed files with 40 additions and 7 deletions
				
			
		
							
								
								
									
										17
									
								
								fs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								fs/inode.c
									
									
									
									
									
								
							| 
						 | 
					@ -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,
 | 
				
			||||||
	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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								fs/namei.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								fs/namei.c
									
									
									
									
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
			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,
 | 
				
			||||||
	 * 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,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	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)
 | 
				
			||||||
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)
 | 
				
			||||||
	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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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)
 | 
				
			||||||
	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 {
 | 
				
			||||||
#define put_fs_excl() atomic_dec(¤t->fs_excl)
 | 
					#define put_fs_excl() atomic_dec(¤t->fs_excl)
 | 
				
			||||||
#define has_fs_excl() atomic_read(¤t->fs_excl)
 | 
					#define has_fs_excl() atomic_read(¤t->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 *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue