summaryrefslogtreecommitdiffstats
path: root/fs/posix_acl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/posix_acl.c')
-rw-r--r--fs/posix_acl.c79
1 files changed, 58 insertions, 21 deletions
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 9ce8214bfdac..d31b60f5d40d 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -558,8 +558,22 @@ __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
}
EXPORT_SYMBOL(__posix_acl_chmod);
+/**
+ * posix_acl_chmod - chmod a posix acl
+ *
+ * @mnt_userns: user namespace of the mount @inode was found from
+ * @inode: inode to check permissions on
+ * @mode: the new mode of @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.
+ */
int
-posix_acl_chmod(struct inode *inode, umode_t mode)
+ posix_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode,
+ umode_t mode)
{
struct posix_acl *acl;
int ret = 0;
@@ -638,9 +652,10 @@ EXPORT_SYMBOL_GPL(posix_acl_create);
/**
* posix_acl_update_mode - update mode in set_acl
- * @inode: target inode
- * @mode_p: mode (pointer) for update
- * @acl: acl pointer
+ * @mnt_userns: user namespace of the mount @inode was found from
+ * @inode: target inode
+ * @mode_p: mode (pointer) for update
+ * @acl: acl pointer
*
* Update the file mode when setting an ACL: compute the new file permission
* bits based on the ACL. In addition, if the ACL is equivalent to the new
@@ -649,9 +664,16 @@ EXPORT_SYMBOL_GPL(posix_acl_create);
* As with chmod, clear the setgid bit if the caller is not in the owning group
* or capable of CAP_FSETID (see inode_change_ok).
*
+ * 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.
+ *
* Called from set_acl inode operations.
*/
-int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
+int posix_acl_update_mode(struct user_namespace *mnt_userns,
+ struct inode *inode, umode_t *mode_p,
struct posix_acl **acl)
{
umode_t mode = inode->i_mode;
@@ -662,8 +684,8 @@ int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
return error;
if (error == 0)
*acl = NULL;
- if (!in_group_p(inode->i_gid) &&
- !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
+ if (!in_group_p(i_gid_into_mnt(mnt_userns, inode)) &&
+ !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FSETID))
mode &= ~S_ISGID;
*mode_p = mode;
return 0;
@@ -675,7 +697,8 @@ EXPORT_SYMBOL(posix_acl_update_mode);
*/
static void posix_acl_fix_xattr_userns(
struct user_namespace *to, struct user_namespace *from,
- void *value, size_t size)
+ struct user_namespace *mnt_userns,
+ void *value, size_t size, bool from_user)
{
struct posix_acl_xattr_header *header = value;
struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end;
@@ -700,10 +723,18 @@ static void posix_acl_fix_xattr_userns(
switch(le16_to_cpu(entry->e_tag)) {
case ACL_USER:
uid = make_kuid(from, le32_to_cpu(entry->e_id));
+ if (from_user)
+ uid = kuid_from_mnt(mnt_userns, uid);
+ else
+ uid = kuid_into_mnt(mnt_userns, uid);
entry->e_id = cpu_to_le32(from_kuid(to, uid));
break;
case ACL_GROUP:
gid = make_kgid(from, le32_to_cpu(entry->e_id));
+ if (from_user)
+ gid = kgid_from_mnt(mnt_userns, gid);
+ else
+ gid = kgid_into_mnt(mnt_userns, gid);
entry->e_id = cpu_to_le32(from_kgid(to, gid));
break;
default:
@@ -712,20 +743,24 @@ static void posix_acl_fix_xattr_userns(
}
}
-void posix_acl_fix_xattr_from_user(void *value, size_t size)
+void posix_acl_fix_xattr_from_user(struct user_namespace *mnt_userns,
+ void *value, size_t size)
{
struct user_namespace *user_ns = current_user_ns();
- if (user_ns == &init_user_ns)
+ if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))
return;
- posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
+ posix_acl_fix_xattr_userns(&init_user_ns, user_ns, mnt_userns, value,
+ size, true);
}
-void posix_acl_fix_xattr_to_user(void *value, size_t size)
+void posix_acl_fix_xattr_to_user(struct user_namespace *mnt_userns,
+ void *value, size_t size)
{
struct user_namespace *user_ns = current_user_ns();
- if (user_ns == &init_user_ns)
+ if ((user_ns == &init_user_ns) && (mnt_userns == &init_user_ns))
return;
- posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
+ posix_acl_fix_xattr_userns(user_ns, &init_user_ns, mnt_userns, value,
+ size, false);
}
/*
@@ -865,7 +900,8 @@ posix_acl_xattr_get(const struct xattr_handler *handler,
}
int
-set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
+set_posix_acl(struct user_namespace *mnt_userns, struct inode *inode,
+ int type, struct posix_acl *acl)
{
if (!IS_POSIXACL(inode))
return -EOPNOTSUPP;
@@ -874,7 +910,7 @@ set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
- if (!inode_owner_or_capable(&init_user_ns, inode))
+ if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM;
if (acl) {
@@ -888,9 +924,10 @@ EXPORT_SYMBOL(set_posix_acl);
static int
posix_acl_xattr_set(const struct xattr_handler *handler,
- struct dentry *unused, struct inode *inode,
- const char *name, const void *value,
- size_t size, int flags)
+ struct user_namespace *mnt_userns,
+ struct dentry *unused, struct inode *inode,
+ const char *name, const void *value, size_t size,
+ int flags)
{
struct posix_acl *acl = NULL;
int ret;
@@ -900,7 +937,7 @@ posix_acl_xattr_set(const struct xattr_handler *handler,
if (IS_ERR(acl))
return PTR_ERR(acl);
}
- ret = set_posix_acl(inode, handler->flags, acl);
+ ret = set_posix_acl(mnt_userns, inode, handler->flags, acl);
posix_acl_release(acl);
return ret;
}
@@ -934,7 +971,7 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
int error;
if (type == ACL_TYPE_ACCESS) {
- error = posix_acl_update_mode(inode,
+ error = posix_acl_update_mode(&init_user_ns, inode,
&inode->i_mode, &acl);
if (error)
return error;