Skip to content

Commit

Permalink
namei: make permission helpers idmapped mount aware
Browse files Browse the repository at this point in the history
The two helpers inode_permission() and generic_permission() are used by
the vfs to perform basic permission checking by verifying that the
caller is privileged over an inode. In order to handle idmapped mounts
we extend the two helpers with an additional user namespace argument.
On idmapped mounts the two helpers will make sure to map the inode
according to the mount's user namespace and then peform identical
permission checks to inode_permission() and generic_permission(). If the
initial user namespace is passed nothing changes so non-idmapped mounts
will see identical behavior as before.

Link: https://lore.kernel.org/r/[email protected]
Cc: Christoph Hellwig <[email protected]>
Cc: David Howells <[email protected]>
Cc: Al Viro <[email protected]>
Cc: [email protected]
Reviewed-by: Christoph Hellwig <[email protected]>
Reviewed-by: James Morris <[email protected]>
Acked-by: Serge Hallyn <[email protected]>
Signed-off-by: Christian Brauner <[email protected]>
  • Loading branch information
Christian Brauner committed Jan 24, 2021
1 parent 0558c1b commit 47291ba
Showing 36 changed files with 164 additions and 88 deletions.
3 changes: 2 additions & 1 deletion fs/attr.c
Original file line number Diff line number Diff line change
@@ -244,7 +244,8 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
return -EPERM;

if (!inode_owner_or_capable(inode)) {
error = inode_permission(inode, MAY_WRITE);
error = inode_permission(&init_user_ns, inode,
MAY_WRITE);
if (error)
return error;
}
2 changes: 1 addition & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
@@ -9889,7 +9889,7 @@ static int btrfs_permission(struct inode *inode, int mask)
if (BTRFS_I(inode)->flags & BTRFS_INODE_READONLY)
return -EACCES;
}
return generic_permission(inode, mask);
return generic_permission(&init_user_ns, inode, mask);
}

static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
12 changes: 7 additions & 5 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
@@ -922,7 +922,7 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
BUG_ON(d_inode(victim->d_parent) != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);

error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -951,7 +951,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
}

/*
@@ -2538,7 +2538,8 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
ret = PTR_ERR(temp_inode);
goto out_put;
}
ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
ret = inode_permission(&init_user_ns, temp_inode,
MAY_READ | MAY_EXEC);
iput(temp_inode);
if (ret) {
ret = -EACCES;
@@ -3068,7 +3069,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
if (root == dest)
goto out_dput;

err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
err = inode_permission(&init_user_ns, inode,
MAY_WRITE | MAY_EXEC);
if (err)
goto out_dput;
}
@@ -3139,7 +3141,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
* running and allows defrag on files open in read-only mode.
*/
if (!capable(CAP_SYS_ADMIN) &&
inode_permission(inode, MAY_WRITE)) {
inode_permission(&init_user_ns, inode, MAY_WRITE)) {
ret = -EPERM;
goto out;
}
2 changes: 1 addition & 1 deletion fs/ceph/inode.c
Original file line number Diff line number Diff line change
@@ -2331,7 +2331,7 @@ int ceph_permission(struct inode *inode, int mask)
err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED, false);

if (!err)
err = generic_permission(inode, mask);
err = generic_permission(&init_user_ns, inode, mask);
return err;
}

2 changes: 1 addition & 1 deletion fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
@@ -320,7 +320,7 @@ static int cifs_permission(struct inode *inode, int mask)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
return generic_permission(inode, mask);
return generic_permission(&init_user_ns, inode, mask);
}

static struct kmem_cache *cifs_inode_cachep;
3 changes: 2 additions & 1 deletion fs/configfs/symlink.c
Original file line number Diff line number Diff line change
@@ -197,7 +197,8 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna
if (dentry->d_inode || d_unhashed(dentry))
ret = -EEXIST;
else
ret = inode_permission(dir, MAY_WRITE | MAY_EXEC);
ret = inode_permission(&init_user_ns, dir,
MAY_WRITE | MAY_EXEC);
if (!ret)
ret = type->ct_item_ops->allow_link(parent_item, target_item);
if (!ret) {
3 changes: 2 additions & 1 deletion fs/ecryptfs/inode.c
Original file line number Diff line number Diff line change
@@ -864,7 +864,8 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
static int
ecryptfs_permission(struct inode *inode, int mask)
{
return inode_permission(ecryptfs_inode_to_lower(inode), mask);
return inode_permission(&init_user_ns,
ecryptfs_inode_to_lower(inode), mask);
}

/**
2 changes: 1 addition & 1 deletion fs/exec.c
Original file line number Diff line number Diff line change
@@ -1404,7 +1404,7 @@ EXPORT_SYMBOL(begin_new_exec);
void would_dump(struct linux_binprm *bprm, struct file *file)
{
struct inode *inode = file_inode(file);
if (inode_permission(inode, MAY_READ) < 0) {
if (inode_permission(&init_user_ns, inode, MAY_READ) < 0) {
struct user_namespace *old, *user_ns;
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;

5 changes: 3 additions & 2 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
@@ -1280,15 +1280,16 @@ static int fuse_permission(struct inode *inode, int mask)
}

if (fc->default_permissions) {
err = generic_permission(inode, mask);
err = generic_permission(&init_user_ns, inode, mask);

/* If permission is denied, try to refresh file
attributes. This is also needed, because the root
node will at first have no permissions */
if (err == -EACCES && !refreshed) {
err = fuse_perm_getattr(inode, mask);
if (!err)
err = generic_permission(inode, mask);
err = generic_permission(&init_user_ns,
inode, mask);
}

/* Note: the opposite of the above test does not
2 changes: 1 addition & 1 deletion fs/gfs2/inode.c
Original file line number Diff line number Diff line change
@@ -1852,7 +1852,7 @@ int gfs2_permission(struct inode *inode, int mask)
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
error = -EPERM;
else
error = generic_permission(inode, mask);
error = generic_permission(&init_user_ns, inode, mask);
if (gfs2_holder_initialized(&i_gh))
gfs2_glock_dq_uninit(&i_gh);

2 changes: 1 addition & 1 deletion fs/hostfs/hostfs_kern.c
Original file line number Diff line number Diff line change
@@ -779,7 +779,7 @@ static int hostfs_permission(struct inode *ino, int desired)
err = access_file(name, r, w, x);
__putname(name);
if (!err)
err = generic_permission(ino, desired);
err = generic_permission(&init_user_ns, ino, desired);
return err;
}

2 changes: 1 addition & 1 deletion fs/kernfs/inode.c
Original file line number Diff line number Diff line change
@@ -285,7 +285,7 @@ int kernfs_iop_permission(struct inode *inode, int mask)
kernfs_refresh_inode(kn, inode);
mutex_unlock(&kernfs_mutex);

return generic_permission(inode, mask);
return generic_permission(&init_user_ns, inode, mask);
}

int kernfs_xattr_get(struct kernfs_node *kn, const char *name,
7 changes: 6 additions & 1 deletion fs/libfs.c
Original file line number Diff line number Diff line change
@@ -1318,9 +1318,14 @@ static ssize_t empty_dir_listxattr(struct dentry *dentry, char *list, size_t siz
return -EOPNOTSUPP;
}

static int empty_dir_permission(struct inode *inode, int mask)
{
return generic_permission(&init_user_ns, inode, mask);
}

static const struct inode_operations empty_dir_inode_operations = {
.lookup = empty_dir_lookup,
.permission = generic_permission,
.permission = empty_dir_permission,
.setattr = empty_dir_setattr,
.getattr = empty_dir_getattr,
.listxattr = empty_dir_listxattr,
Loading

0 comments on commit 47291ba

Please sign in to comment.