namei: make permission helpers idmapped mount aware
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 21 Jan 2021 13:19:24 +0000 (14:19 +0100)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sun, 24 Jan 2021 13:27:16 +0000 (14:27 +0100)
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/20210121131959.646623-6-christian.brauner@ubuntu.com
Cc: Christoph Hellwig <hch@lst.de>
Cc: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: James Morris <jamorris@linux.microsoft.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
36 files changed:
fs/attr.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/ceph/inode.c
fs/cifs/cifsfs.c
fs/configfs/symlink.c
fs/ecryptfs/inode.c
fs/exec.c
fs/fuse/dir.c
fs/gfs2/inode.c
fs/hostfs/hostfs_kern.c
fs/kernfs/inode.c
fs/libfs.c
fs/namei.c
fs/nfs/dir.c
fs/nfsd/nfsfh.c
fs/nfsd/vfs.c
fs/nilfs2/inode.c
fs/ocfs2/file.c
fs/ocfs2/refcounttree.c
fs/open.c
fs/orangefs/inode.c
fs/overlayfs/file.c
fs/overlayfs/inode.c
fs/overlayfs/util.c
fs/posix_acl.c
fs/proc/base.c
fs/proc/fd.c
fs/reiserfs/xattr.c
fs/remap_range.c
fs/xattr.c
include/linux/fs.h
include/linux/posix_acl.h
ipc/mqueue.c
kernel/bpf/inode.c
kernel/cgroup/cgroup.c

index d270f640a192fbf3d6a05d7e1d646eb8ab98133c..c9e29e589cecca6dff302723f51a815e964394be 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -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;
                }
index a8e0a6b038d3efb2c53a98ae8370262cd4101369..512ee2650bbba15f97c37ca531a7bf0a3177f426 100644 (file)
@@ -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)
index dde49a791f3e236e7ad7279589d4fb94da1917f2..8ced6dfefee4b138c716b5f2dcdad96f6ec5eb08 100644 (file)
@@ -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;
                }
index adc8fc3c5d850d0d08ed84db8ee47c7d2f215146..e8a15ee09bc1052421dd74b30d88238529d724d0 100644 (file)
@@ -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;
 }
 
index ce0d0037fd0afdce968408243ea7bcb4fbebc446..ce14e6f8adb64380be6a6754610590678c9551d7 100644 (file)
@@ -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;
index cb61467478ca81f973ad22a65296c8b41079ed56..8ca36394fa30dc0742ecf8408cba15bb5fdd1ad8 100644 (file)
@@ -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) {
index e23752d9a79f3d255345177db1b66950ae8e6607..0b346baf110d79e7053b3d113879f698ea37545f 100644 (file)
@@ -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);
 }
 
 /**
index 89d4780ff48f970b834c65b58e025013b3484539..a8ec371cd3cd1849a5c4ee45c63d93a9f067fbed 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -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;
 
index 78f9f209078cf03551c5e1d3c9390cd59e519734..7497009a5a453519fb4e07a5eb5dc3d0fb159f90 100644 (file)
@@ -1280,7 +1280,7 @@ 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
@@ -1288,7 +1288,8 @@ static int fuse_permission(struct inode *inode, int mask)
                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
index c1b77e8d6b1c30735c5f2c6a974bc7cd81d6c82f..5b2ff0c74b67b47ae926f7fcaf1b532398404300 100644 (file)
@@ -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);
 
index aea35459d3903159655ee869f144c16acff22fe8..b841a05a2b8c9c552cf3a3f19bba5387ec9fab9a 100644 (file)
@@ -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;
 }
 
index fc2469a20fed078b7ddb777cae7cd2dee65aea88..ff5598cc1de0f9b1427cc856b8733df8a4f7d67a 100644 (file)
@@ -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,
index d1c3bade9f30dd028cfc109d7fc3f8abf2c846cf..f8b3c02b4f0f98c9da74e1f5194cc00873423e16 100644 (file)
@@ -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,
index fd4724bce4f5f11f5b3add79aed0c0c8c80899e4..d78d74f5f5af9eced94db488d063c56f8e369b98 100644 (file)
@@ -259,7 +259,24 @@ void putname(struct filename *name)
                __putname(name);
 }
 
-static int check_acl(struct inode *inode, int mask)
+/**
+ * check_acl - perform ACL permission checking
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @inode:     inode to check permissions on
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
+ *
+ * This function performs the ACL permission checking. Since this function
+ * retrieve POSIX acls it needs to know whether it is called from a blocking or
+ * non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
+ *
+ * If the inode has been found through an idmapped mount the user namespace of
+ * the vfsmount must be passed through @mnt_userns. This function will then take
+ * care to map the inode according to @mnt_userns before checking permissions.
+ * On non-idmapped mounts or if permission checking is to be performed on the
+ * raw inode simply passs init_user_ns.
+ */
+static int check_acl(struct user_namespace *mnt_userns,
+                    struct inode *inode, int mask)
 {
 #ifdef CONFIG_FS_POSIX_ACL
        struct posix_acl *acl;
@@ -271,14 +288,14 @@ static int check_acl(struct inode *inode, int mask)
                /* no ->get_acl() calls in RCU mode... */
                if (is_uncached_acl(acl))
                        return -ECHILD;
-               return posix_acl_permission(inode, acl, mask);
+               return posix_acl_permission(mnt_userns, inode, acl, mask);
        }
 
        acl = get_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
-               int error = posix_acl_permission(inode, acl, mask);
+               int error = posix_acl_permission(mnt_userns, inode, acl, mask);
                posix_acl_release(acl);
                return error;
        }
@@ -287,18 +304,31 @@ static int check_acl(struct inode *inode, int mask)
        return -EAGAIN;
 }
 
-/*
- * This does the basic UNIX permission checking.
+/**
+ * acl_permission_check - perform basic UNIX permission checking
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @inode:     inode to check permissions on
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
+ *
+ * This function performs the basic UNIX permission checking. Since this
+ * function may retrieve POSIX acls it needs to know whether it is called from a
+ * blocking or non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
  *
- * Note that the POSIX ACL check cares about the MAY_NOT_BLOCK bit,
- * for RCU walking.
+ * If the inode has been found through an idmapped mount the user namespace of
+ * the vfsmount must be passed through @mnt_userns. This function will then take
+ * care to map the inode according to @mnt_userns before checking permissions.
+ * On non-idmapped mounts or if permission checking is to be performed on the
+ * raw inode simply passs init_user_ns.
  */
-static int acl_permission_check(struct inode *inode, int mask)
+static int acl_permission_check(struct user_namespace *mnt_userns,
+                               struct inode *inode, int mask)
 {
        unsigned int mode = inode->i_mode;
+       kuid_t i_uid;
 
        /* Are we the owner? If so, ACL's don't matter */
-       if (likely(uid_eq(current_fsuid(), inode->i_uid))) {
+       i_uid = i_uid_into_mnt(mnt_userns, inode);
+       if (likely(uid_eq(current_fsuid(), i_uid))) {
                mask &= 7;
                mode >>= 6;
                return (mask & ~mode) ? -EACCES : 0;
@@ -306,7 +336,7 @@ static int acl_permission_check(struct inode *inode, int mask)
 
        /* Do we have ACL's? */
        if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
-               int error = check_acl(inode, mask);
+               int error = check_acl(mnt_userns, inode, mask);
                if (error != -EAGAIN)
                        return error;
        }
@@ -320,7 +350,8 @@ static int acl_permission_check(struct inode *inode, int mask)
         * about? Need to check group ownership if so.
         */
        if (mask & (mode ^ (mode >> 3))) {
-               if (in_group_p(inode->i_gid))
+               kgid_t kgid = i_gid_into_mnt(mnt_userns, inode);
+               if (in_group_p(kgid))
                        mode >>= 3;
        }
 
@@ -330,6 +361,7 @@ static int acl_permission_check(struct inode *inode, int mask)
 
 /**
  * generic_permission -  check for access rights on a Posix-like filesystem
+ * @mnt_userns:        user namespace of the mount the inode was found from
  * @inode:     inode to check access rights for
  * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC,
  *             %MAY_NOT_BLOCK ...)
@@ -342,25 +374,32 @@ static int acl_permission_check(struct inode *inode, int mask)
  * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk
  * request cannot be satisfied (eg. requires blocking or too much complexity).
  * It would then be called again in ref-walk mode.
+ *
+ * If the inode has been found through an idmapped mount the user namespace of
+ * the vfsmount must be passed through @mnt_userns. This function will then take
+ * care to map the inode according to @mnt_userns before checking permissions.
+ * On non-idmapped mounts or if permission checking is to be performed on the
+ * raw inode simply passs init_user_ns.
  */
-int generic_permission(struct inode *inode, int mask)
+int generic_permission(struct user_namespace *mnt_userns, struct inode *inode,
+                      int mask)
 {
        int ret;
 
        /*
         * Do the basic permission checks.
         */
-       ret = acl_permission_check(inode, mask);
+       ret = acl_permission_check(mnt_userns, inode, mask);
        if (ret != -EACCES)
                return ret;
 
        if (S_ISDIR(inode->i_mode)) {
                /* DACs are overridable for directories */
                if (!(mask & MAY_WRITE))
-                       if (capable_wrt_inode_uidgid(&init_user_ns, inode,
+                       if (capable_wrt_inode_uidgid(mnt_userns, inode,
                                                     CAP_DAC_READ_SEARCH))
                                return 0;
-               if (capable_wrt_inode_uidgid(&init_user_ns, inode,
+               if (capable_wrt_inode_uidgid(mnt_userns, inode,
                                             CAP_DAC_OVERRIDE))
                        return 0;
                return -EACCES;
@@ -371,7 +410,7 @@ int generic_permission(struct inode *inode, int mask)
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
        if (mask == MAY_READ)
-               if (capable_wrt_inode_uidgid(&init_user_ns, inode,
+               if (capable_wrt_inode_uidgid(mnt_userns, inode,
                                             CAP_DAC_READ_SEARCH))
                        return 0;
        /*
@@ -380,7 +419,7 @@ int generic_permission(struct inode *inode, int mask)
         * at least one exec bit set.
         */
        if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
-               if (capable_wrt_inode_uidgid(&init_user_ns, inode,
+               if (capable_wrt_inode_uidgid(mnt_userns, inode,
                                             CAP_DAC_OVERRIDE))
                        return 0;
 
@@ -388,13 +427,19 @@ int generic_permission(struct inode *inode, int mask)
 }
 EXPORT_SYMBOL(generic_permission);
 
-/*
+/**
+ * do_inode_permission - UNIX permission checking
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @inode:     inode to check permissions on
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
+ *
  * We _really_ want to just do "generic_permission()" without
  * even looking at the inode->i_op values. So we keep a cache
  * flag in inode->i_opflags, that says "this has not special
  * permission function, use the fast case".
  */
-static inline int do_inode_permission(struct inode *inode, int mask)
+static inline int do_inode_permission(struct user_namespace *mnt_userns,
+                                     struct inode *inode, int mask)
 {
        if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
                if (likely(inode->i_op->permission))
@@ -405,7 +450,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
                inode->i_opflags |= IOP_FASTPERM;
                spin_unlock(&inode->i_lock);
        }
-       return generic_permission(inode, mask);
+       return generic_permission(mnt_userns, inode, mask);
 }
 
 /**
@@ -430,8 +475,9 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
 
 /**
  * inode_permission - Check for access rights to a given inode
- * @inode: Inode to check permission on
- * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mnt_userns:        User namespace of the mount the inode was found from
+ * @inode:     Inode to check permission on
+ * @mask:      Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
  * Check for read/write/execute permissions on an inode.  We use fs[ug]id for
  * this, letting us set arbitrary permissions for filesystem access without
@@ -439,7 +485,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
  *
  * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
  */
-int inode_permission(struct inode *inode, int mask)
+int inode_permission(struct user_namespace *mnt_userns,
+                    struct inode *inode, int mask)
 {
        int retval;
 
@@ -463,7 +510,7 @@ int inode_permission(struct inode *inode, int mask)
                        return -EACCES;
        }
 
-       retval = do_inode_permission(inode, mask);
+       retval = do_inode_permission(mnt_userns, inode, mask);
        if (retval)
                return retval;
 
@@ -1009,7 +1056,7 @@ static bool safe_hardlink_source(struct inode *inode)
                return false;
 
        /* Hardlinking to unreadable or unwritable sources is dangerous. */
-       if (inode_permission(inode, MAY_READ | MAY_WRITE))
+       if (inode_permission(&init_user_ns, inode, MAY_READ | MAY_WRITE))
                return false;
 
        return true;
@@ -1569,13 +1616,14 @@ static struct dentry *lookup_slow(const struct qstr *name,
 static inline int may_lookup(struct nameidata *nd)
 {
        if (nd->flags & LOOKUP_RCU) {
-               int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+               int err = inode_permission(&init_user_ns, nd->inode,
+                                          MAY_EXEC | MAY_NOT_BLOCK);
                if (err != -ECHILD)
                        return err;
                if (unlazy_walk(nd))
                        return -ECHILD;
        }
-       return inode_permission(nd->inode, MAY_EXEC);
+       return inode_permission(&init_user_ns, nd->inode, MAY_EXEC);
 }
 
 static int reserve_stack(struct nameidata *nd, struct path *link, unsigned seq)
@@ -2509,7 +2557,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
                        return err;
        }
 
-       return inode_permission(base->d_inode, MAY_EXEC);
+       return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC);
 }
 
 /**
@@ -2703,7 +2751,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 
        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))
@@ -2747,7 +2795,7 @@ static inline int may_create(struct inode *dir, struct dentry *child)
        if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
            !kgid_has_mapping(s_user_ns, current_fsgid()))
                return -EOVERFLOW;
-       return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+       return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
 }
 
 /*
@@ -2877,7 +2925,7 @@ static int may_open(const struct path *path, int acc_mode, int flag)
                break;
        }
 
-       error = inode_permission(inode, MAY_OPEN | acc_mode);
+       error = inode_permission(&init_user_ns, inode, MAY_OPEN | acc_mode);
        if (error)
                return error;
 
@@ -2939,7 +2987,8 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
            !kgid_has_mapping(s_user_ns, current_fsgid()))
                return -EOVERFLOW;
 
-       error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
+       error = inode_permission(&init_user_ns, dir->dentry->d_inode,
+                                MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
 
@@ -3276,7 +3325,7 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
        int error;
 
        /* we want directory to be writable */
-       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+       error = inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC);
        if (error)
                goto out_err;
        error = -EOPNOTSUPP;
@@ -4267,12 +4316,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
         */
        if (new_dir != old_dir) {
                if (is_dir) {
-                       error = inode_permission(source, MAY_WRITE);
+                       error = inode_permission(&init_user_ns, source,
+                                                MAY_WRITE);
                        if (error)
                                return error;
                }
                if ((flags & RENAME_EXCHANGE) && new_is_dir) {
-                       error = inode_permission(target, MAY_WRITE);
+                       error = inode_permission(&init_user_ns, target,
+                                                MAY_WRITE);
                        if (error)
                                return error;
                }
index ef827ae193d22266b3bbdcea344714e1f7ea69ca..727e01a8450329bf6c4889916e7c376be7e57286 100644 (file)
@@ -2987,7 +2987,7 @@ out_notsup:
 
        res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
        if (res == 0)
-               res = generic_permission(inode, mask);
+               res = generic_permission(&init_user_ns, inode, mask);
        goto out;
 }
 EXPORT_SYMBOL_GPL(nfs_permission);
index 66f2ef67792a7b3792f92a94d9fb09645590984c..8d90796e236a5a8c27fabb0229fa2307ae066917 100644 (file)
@@ -40,7 +40,8 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
                /* make sure parents give x permission to user */
                int err;
                parent = dget_parent(tdentry);
-               err = inode_permission(d_inode(parent), MAY_EXEC);
+               err = inode_permission(&init_user_ns,
+                                      d_inode(parent), MAY_EXEC);
                if (err < 0) {
                        dput(parent);
                        break;
index 04937e51de56336623436562aa21570d6e38852d..0edf11258aaa7329f637ed93b3b09e3125b56f39 100644 (file)
@@ -2391,13 +2391,14 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
                return 0;
 
        /* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
-       err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+       err = inode_permission(&init_user_ns, inode,
+                              acc & (MAY_READ | MAY_WRITE | MAY_EXEC));
 
        /* Allow read access to binaries even when mode 111 */
        if (err == -EACCES && S_ISREG(inode->i_mode) &&
             (acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
              acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
-               err = inode_permission(inode, MAY_EXEC);
+               err = inode_permission(&init_user_ns, inode, MAY_EXEC);
 
        return err? nfserrno(err) : 0;
 }
index 745d371d6fea6683a02a7e69618228aebcc548c4..b6517220cad54d7a41abedbaeb46b3685e697d5c 100644 (file)
@@ -851,7 +851,7 @@ int nilfs_permission(struct inode *inode, int mask)
            root->cno != NILFS_CPTREE_CURRENT_CNO)
                return -EROFS; /* snapshot is not writable */
 
-       return generic_permission(inode, mask);
+       return generic_permission(&init_user_ns, inode, mask);
 }
 
 int nilfs_load_inode_block(struct inode *inode, struct buffer_head **pbh)
index 85979e2214b39d81209168680885fcd317410237..0c75619adf54676aa1b256774b16a90a013963cb 100644 (file)
@@ -1355,7 +1355,7 @@ int ocfs2_permission(struct inode *inode, int mask)
                dump_stack();
        }
 
-       ret = generic_permission(inode, mask);
+       ret = generic_permission(&init_user_ns, inode, mask);
 
        ocfs2_inode_unlock_tracker(inode, 0, &oh, had_lock);
 out:
index 3b397fa9c9e80434b802714ff8db950a54fdbe50..c26937824be155fd586a7babba339deb1d5a5dda 100644 (file)
@@ -4346,7 +4346,7 @@ static inline int ocfs2_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);
 }
 
 /**
@@ -4400,7 +4400,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir,
         * file.
         */
        if (!preserve) {
-               error = inode_permission(inode, MAY_READ);
+               error = inode_permission(&init_user_ns, inode, MAY_READ);
                if (error)
                        return error;
        }
index cd1efd254cad5f29a3d2ccfedc8f0c8d0624bccf..a6dac6d97988a6abc3116ddaf0c2638c9e5d9751 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -83,7 +83,7 @@ long vfs_truncate(const struct path *path, loff_t length)
        if (error)
                goto out;
 
-       error = inode_permission(inode, MAY_WRITE);
+       error = inode_permission(&init_user_ns, inode, MAY_WRITE);
        if (error)
                goto mnt_drop_write_and_out;
 
@@ -436,7 +436,7 @@ retry:
                        goto out_path_release;
        }
 
-       res = inode_permission(inode, mode | MAY_ACCESS);
+       res = inode_permission(&init_user_ns, inode, mode | MAY_ACCESS);
        /* SuS v2 requires we report a read only fs too */
        if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
                goto out_path_release;
index 48f0547d4850e985411e477dcc61b08721b3f516..4c790cc8042df8590edd4862e6c97d422dd3f48d 100644 (file)
@@ -933,7 +933,7 @@ int orangefs_permission(struct inode *inode, int mask)
        if (ret < 0)
                return ret;
 
-       return generic_permission(inode, mask);
+       return generic_permission(&init_user_ns, inode, mask);
 }
 
 int orangefs_update_time(struct inode *inode, struct timespec64 *time, int flags)
index bd9dd38347aed87ed263d5df56ffcd3e658ea562..b2948e7b3210e323aa7dca5738df7e657182b8a7 100644 (file)
@@ -50,7 +50,7 @@ static struct file *ovl_open_realfile(const struct file *file,
                acc_mode |= MAY_APPEND;
 
        old_cred = ovl_override_creds(inode->i_sb);
-       err = inode_permission(realinode, MAY_OPEN | acc_mode);
+       err = inode_permission(&init_user_ns, realinode, MAY_OPEN | acc_mode);
        if (err) {
                realfile = ERR_PTR(err);
        } else {
index d739e14c6814df6a38c004e96124a749b8948fe1..c101ebbb7a7792efd9faaa1494c0233f7122e10f 100644 (file)
@@ -294,7 +294,7 @@ int ovl_permission(struct inode *inode, int mask)
         * Check overlay inode with the creds of task and underlying inode
         * with creds of mounter
         */
-       err = generic_permission(inode, mask);
+       err = generic_permission(&init_user_ns, inode, mask);
        if (err)
                return err;
 
@@ -305,7 +305,7 @@ int ovl_permission(struct inode *inode, int mask)
                /* Make sure mounter can read file for copy up later */
                mask |= MAY_READ;
        }
-       err = inode_permission(realinode, mask);
+       err = inode_permission(&init_user_ns, realinode, mask);
        revert_creds(old_cred);
 
        return err;
index 6569031af3cdd4ca0fed5cf035161a7f1a33248c..de5c2047a0e9756f25bf932a074121bb43d04e07 100644 (file)
@@ -479,7 +479,7 @@ struct file *ovl_path_open(struct path *path, int flags)
                BUG();
        }
 
-       err = inode_permission(inode, acc_mode | MAY_OPEN);
+       err = inode_permission(&init_user_ns, inode, acc_mode | MAY_OPEN);
        if (err)
                return ERR_PTR(err);
 
index 4ca6d53c6f0af0ac6ce300fdce252ae7d93ba225..5d9fe2fb295314078d740373de31863675408c85 100644 (file)
@@ -345,10 +345,13 @@ EXPORT_SYMBOL(posix_acl_from_mode);
  * by the acl. Returns -E... otherwise.
  */
 int
-posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
+posix_acl_permission(struct user_namespace *mnt_userns, struct inode *inode,
+                    const struct posix_acl *acl, int want)
 {
        const struct posix_acl_entry *pa, *pe, *mask_obj;
        int found = 0;
+       kuid_t uid;
+       kgid_t gid;
 
        want &= MAY_READ | MAY_WRITE | MAY_EXEC;
 
@@ -356,22 +359,26 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
                 switch(pa->e_tag) {
                         case ACL_USER_OBJ:
                                /* (May have been checked already) */
-                               if (uid_eq(inode->i_uid, current_fsuid()))
+                               uid = i_uid_into_mnt(mnt_userns, inode);
+                               if (uid_eq(uid, current_fsuid()))
                                         goto check_perm;
                                 break;
                         case ACL_USER:
-                               if (uid_eq(pa->e_uid, current_fsuid()))
+                               uid = kuid_into_mnt(mnt_userns, pa->e_uid);
+                               if (uid_eq(uid, current_fsuid()))
                                         goto mask;
                                break;
                         case ACL_GROUP_OBJ:
-                                if (in_group_p(inode->i_gid)) {
+                               gid = i_gid_into_mnt(mnt_userns, inode);
+                               if (in_group_p(gid)) {
                                        found = 1;
                                        if ((pa->e_perm & want) == want)
                                                goto mask;
                                 }
                                break;
                         case ACL_GROUP:
-                               if (in_group_p(pa->e_gid)) {
+                               gid = kgid_into_mnt(mnt_userns, pa->e_gid);
+                               if (in_group_p(gid)) {
                                        found = 1;
                                        if ((pa->e_perm & want) == want)
                                                goto mask;
index b3422cda2a91eec22b792a10c90edca77ff49893..b4ec9293625e35592431bdc8bb64a26371325881 100644 (file)
@@ -751,7 +751,7 @@ static int proc_pid_permission(struct inode *inode, int mask)
 
                return -EPERM;
        }
-       return generic_permission(inode, mask);
+       return generic_permission(&init_user_ns, inode, mask);
 }
 
 
@@ -3492,7 +3492,7 @@ static int proc_tid_comm_permission(struct inode *inode, int mask)
                return 0;
        }
 
-       return generic_permission(inode, mask);
+       return generic_permission(&init_user_ns, inode, mask);
 }
 
 static const struct inode_operations proc_tid_comm_inode_operations = {
index cb51763ed554b8d5ec6a41916a44458496d8e02f..d6e76461e135785551c152a46af605bfc4684481 100644 (file)
@@ -281,7 +281,7 @@ int proc_fd_permission(struct inode *inode, int mask)
        struct task_struct *p;
        int rv;
 
-       rv = generic_permission(inode, mask);
+       rv = generic_permission(&init_user_ns, inode, mask);
        if (rv == 0)
                return rv;
 
index fe63a7c3e0da25fdf11ed886e2ab1a80d8e5ae9d..ec440d1957a1b526f79d8ed6682f705256f5bba9 100644 (file)
@@ -957,7 +957,7 @@ int reiserfs_permission(struct inode *inode, int mask)
        if (IS_PRIVATE(inode))
                return 0;
 
-       return generic_permission(inode, mask);
+       return generic_permission(&init_user_ns, inode, mask);
 }
 
 static int xattr_hide_revalidate(struct dentry *dentry, unsigned int flags)
index 77dba3a49e658aa9c9484b9938cc3b81ac2de05f..29a4a4dbfe126a5563a6491c3b1ae4515bc93e2c 100644 (file)
@@ -438,7 +438,7 @@ static bool allow_file_dedupe(struct file *file)
                return true;
        if (uid_eq(current_fsuid(), file_inode(file)->i_uid))
                return true;
-       if (!inode_permission(file_inode(file), MAY_WRITE))
+       if (!inode_permission(&init_user_ns, file_inode(file), MAY_WRITE))
                return true;
        return false;
 }
index fd57153b1f6170f2780736c4d0221afd40be21fa..56151bd9e642b7f16513ddc2a55c508dd6670fc7 100644 (file)
@@ -131,7 +131,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
                        return -EPERM;
        }
 
-       return inode_permission(inode, mask);
+       return inode_permission(&init_user_ns, inode, mask);
 }
 
 /*
index bcd17097d441eed004002cd2c27a09c8f8f00cd6..a85dfe6962dfd1982c406733f8d102216dc77ee0 100644 (file)
@@ -2810,15 +2810,17 @@ static inline int bmap(struct inode *inode,  sector_t *block)
 #endif
 
 extern int notify_change(struct dentry *, struct iattr *, struct inode **);
-extern int inode_permission(struct inode *, int);
-extern int generic_permission(struct inode *, int);
+int inode_permission(struct user_namespace *, struct inode *, int);
+int generic_permission(struct user_namespace *, struct inode *, int);
 static inline int file_permission(struct file *file, int mask)
 {
-       return inode_permission(file_inode(file), mask);
+       return inode_permission(file_mnt_user_ns(file),
+                               file_inode(file), mask);
 }
 static inline int path_permission(const struct path *path, int mask)
 {
-       return inode_permission(d_inode(path->dentry), mask);
+       return inode_permission(mnt_user_ns(path->mnt),
+                               d_inode(path->dentry), mask);
 }
 extern int __check_sticky(struct inode *dir, struct inode *inode);
 
index 90797f1b421d8fec87325268aeae3181fb933978..85fb4c0c650a811be4c8f6ee4819438794c928b3 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/refcount.h>
 #include <uapi/linux/posix_acl.h>
 
+struct user_namespace;
+
 struct posix_acl_entry {
        short                   e_tag;
        unsigned short          e_perm;
@@ -61,8 +63,6 @@ posix_acl_release(struct posix_acl *acl)
 
 extern void posix_acl_init(struct posix_acl *, int);
 extern struct posix_acl *posix_acl_alloc(int, gfp_t);
-extern int posix_acl_valid(struct user_namespace *, const struct posix_acl *);
-extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
 extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
 extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
 extern int __posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
@@ -85,6 +85,9 @@ struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
 void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
 void forget_cached_acl(struct inode *inode, int type);
 void forget_all_cached_acls(struct inode *inode);
+int posix_acl_valid(struct user_namespace *, const struct posix_acl *);
+int posix_acl_permission(struct user_namespace *, struct inode *,
+                        const struct posix_acl *, int);
 
 static inline void cache_no_acl(struct inode *inode)
 {
index beff0cfcd1e874dd3cb03fd23c1a28fb67b373c2..693f01fe1216120d57010153e4d79d85db7bd7f1 100644 (file)
@@ -873,7 +873,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro,
        if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
                return -EINVAL;
        acc = oflag2acc[oflag & O_ACCMODE];
-       return inode_permission(d_inode(dentry), acc);
+       return inode_permission(&init_user_ns, d_inode(dentry), acc);
 }
 
 static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
index 8962f139521ec374ac9d9093d2d3939cebe598e8..e3226b65f5dc8faff9652dff8f191f61ed9c080b 100644 (file)
@@ -558,7 +558,7 @@ int bpf_obj_get_user(const char __user *pathname, int flags)
 static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type type)
 {
        struct bpf_prog *prog;
-       int ret = inode_permission(inode, MAY_READ);
+       int ret = inode_permission(&init_user_ns, inode, MAY_READ);
        if (ret)
                return ERR_PTR(ret);
 
index 613845769103cd36ab9ef9340b9dd90f5c20f5f9..091ffb5d2939e16a7f6facf0f58bb176c2fc2393 100644 (file)
@@ -4670,7 +4670,7 @@ static int cgroup_may_write(const struct cgroup *cgrp, struct super_block *sb)
        if (!inode)
                return -ENOMEM;
 
-       ret = inode_permission(inode, MAY_WRITE);
+       ret = inode_permission(&init_user_ns, inode, MAY_WRITE);
        iput(inode);
        return ret;
 }