Merge tag 'idmapped-mounts-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / security / commoncap.c
index 26c1cb7..28f4d25 100644 (file)
@@ -303,17 +303,25 @@ int cap_inode_need_killpriv(struct dentry *dentry)
 
 /**
  * cap_inode_killpriv - Erase the security markings on an inode
- * @dentry: The inode/dentry to alter
+ *
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @dentry:    The inode/dentry to alter
  *
  * Erase the privilege-enhancing security markings on an inode.
  *
+ * 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.
+ *
  * Returns 0 if successful, -ve on error.
  */
-int cap_inode_killpriv(struct dentry *dentry)
+int cap_inode_killpriv(struct user_namespace *mnt_userns, struct dentry *dentry)
 {
        int error;
 
-       error = __vfs_removexattr(dentry, XATTR_NAME_CAPS);
+       error = __vfs_removexattr(mnt_userns, dentry, XATTR_NAME_CAPS);
        if (error == -EOPNOTSUPP)
                error = 0;
        return error;
@@ -366,7 +374,8 @@ static bool is_v3header(size_t size, const struct vfs_cap_data *cap)
  * by the integrity subsystem, which really wants the unconverted values -
  * so that's good.
  */
-int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
+int cap_inode_getsecurity(struct user_namespace *mnt_userns,
+                         struct inode *inode, const char *name, void **buffer,
                          bool alloc)
 {
        int size, ret;
@@ -387,8 +396,8 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
                return -EINVAL;
 
        size = sizeof(struct vfs_ns_cap_data);
-       ret = (int) vfs_getxattr_alloc(dentry, XATTR_NAME_CAPS,
-                                &tmpbuf, size, GFP_NOFS);
+       ret = (int)vfs_getxattr_alloc(mnt_userns, dentry, XATTR_NAME_CAPS,
+                                     &tmpbuf, size, GFP_NOFS);
        dput(dentry);
 
        if (ret < 0)
@@ -408,6 +417,9 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
 
        kroot = make_kuid(fs_ns, root);
 
+       /* If this is an idmapped mount shift the kuid. */
+       kroot = kuid_into_mnt(mnt_userns, kroot);
+
        /* If the root kuid maps to a valid uid in current ns, then return
         * this as a nscap. */
        mappedroot = from_kuid(current_user_ns(), kroot);
@@ -469,16 +481,33 @@ out_free:
        return size;
 }
 
+/**
+ * rootid_from_xattr - translate root uid of vfs caps
+ *
+ * @value:     vfs caps value which may be modified by this function
+ * @size:      size of @ivalue
+ * @task_ns:   user namespace of the caller
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ *
+ * 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 kuid_t rootid_from_xattr(const void *value, size_t size,
-                               struct user_namespace *task_ns)
+                               struct user_namespace *task_ns,
+                               struct user_namespace *mnt_userns)
 {
        const struct vfs_ns_cap_data *nscap = value;
+       kuid_t rootkid;
        uid_t rootid = 0;
 
        if (size == XATTR_CAPS_SZ_3)
                rootid = le32_to_cpu(nscap->rootid);
 
-       return make_kuid(task_ns, rootid);
+       rootkid = make_kuid(task_ns, rootid);
+       return kuid_from_mnt(mnt_userns, rootkid);
 }
 
 static bool validheader(size_t size, const struct vfs_cap_data *cap)
@@ -486,13 +515,27 @@ static bool validheader(size_t size, const struct vfs_cap_data *cap)
        return is_v2header(size, cap) || is_v3header(size, cap);
 }
 
-/*
+/**
+ * cap_convert_nscap - check vfs caps
+ *
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @dentry:    used to retrieve inode to check permissions on
+ * @ivalue:    vfs caps value which may be modified by this function
+ * @size:      size of @ivalue
+ *
  * User requested a write of security.capability.  If needed, update the
  * xattr to change from v2 to v3, or to fixup the v3 rootid.
  *
+ * 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.
+ *
  * If all is ok, we return the new size, on error return < 0.
  */
-int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size)
+int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry,
+                     const void **ivalue, size_t size)
 {
        struct vfs_ns_cap_data *nscap;
        uid_t nsrootid;
@@ -500,7 +543,8 @@ int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size)
        __u32 magic, nsmagic;
        struct inode *inode = d_backing_inode(dentry);
        struct user_namespace *task_ns = current_user_ns(),
-               *fs_ns = inode->i_sb->s_user_ns;
+               *fs_ns = inode->i_sb->s_user_ns,
+               *ancestor;
        kuid_t rootid;
        size_t newsize;
 
@@ -508,14 +552,14 @@ int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size)
                return -EINVAL;
        if (!validheader(size, cap))
                return -EINVAL;
-       if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP))
+       if (!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_SETFCAP))
                return -EPERM;
-       if (size == XATTR_CAPS_SZ_2)
+       if (size == XATTR_CAPS_SZ_2 && (mnt_userns == &init_user_ns))
                if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP))
                        /* user is privileged, just write the v2 */
                        return size;
 
-       rootid = rootid_from_xattr(*ivalue, size, task_ns);
+       rootid = rootid_from_xattr(*ivalue, size, task_ns, mnt_userns);
        if (!uid_valid(rootid))
                return -EINVAL;
 
@@ -523,6 +567,15 @@ int cap_convert_nscap(struct dentry *dentry, const void **ivalue, size_t size)
        if (nsrootid == -1)
                return -EINVAL;
 
+       /*
+        * Do not allow allow adding a v3 filesystem capability xattr
+        * if the rootid field is ambiguous.
+        */
+       for (ancestor = task_ns->parent; ancestor; ancestor = ancestor->parent) {
+               if (from_kuid(ancestor, rootid) == 0)
+                       return -EINVAL;
+       }
+
        newsize = sizeof(struct vfs_ns_cap_data);
        nscap = kmalloc(newsize, GFP_ATOMIC);
        if (!nscap)
@@ -583,10 +636,24 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
        return *effective ? ret : 0;
 }
 
-/*
+/**
+ * get_vfs_caps_from_disk - retrieve vfs caps from disk
+ *
+ * @mnt_userns:        user namespace of the mount the inode was found from
+ * @dentry:    dentry from which @inode is retrieved
+ * @cpu_caps:  vfs capabilities
+ *
  * Extract the on-exec-apply capability sets for an executable file.
+ *
+ * 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 get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
+int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
+                          const struct dentry *dentry,
+                          struct cpu_vfs_cap_data *cpu_caps)
 {
        struct inode *inode = d_backing_inode(dentry);
        __u32 magic_etc;
@@ -642,6 +709,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
        /* Limit the caps to the mounter of the filesystem
         * or the more limited uid specified in the xattr.
         */
+       rootkuid = kuid_into_mnt(mnt_userns, rootkuid);
        if (!rootid_owns_currentns(rootkuid))
                return -ENODATA;
 
@@ -687,7 +755,8 @@ static int get_file_caps(struct linux_binprm *bprm, struct file *file,
        if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns))
                return 0;
 
-       rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps);
+       rc = get_vfs_caps_from_disk(file_mnt_user_ns(file),
+                                   file->f_path.dentry, &vcaps);
        if (rc < 0) {
                if (rc == -EINVAL)
                        printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
@@ -952,16 +1021,25 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
 
 /**
  * cap_inode_removexattr - Determine whether an xattr may be removed
- * @dentry: The inode/dentry being altered
- * @name: The name of the xattr to be changed
+ *
+ * @mnt_userns:        User namespace of the mount the inode was found from
+ * @dentry:    The inode/dentry being altered
+ * @name:      The name of the xattr to be changed
  *
  * Determine whether an xattr may be removed from an inode, returning 0 if
  * permission is granted, -ve if denied.
  *
+ * 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.
+ *
  * This is used to make sure security xattrs don't get removed by those who
  * aren't privileged to remove them.
  */
-int cap_inode_removexattr(struct dentry *dentry, const char *name)
+int cap_inode_removexattr(struct user_namespace *mnt_userns,
+                         struct dentry *dentry, const char *name)
 {
        struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
 
@@ -975,7 +1053,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
                struct inode *inode = d_backing_inode(dentry);
                if (!inode)
                        return -EINVAL;
-               if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP))
+               if (!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_SETFCAP))
                        return -EPERM;
                return 0;
        }