xattr: handle idmapped mounts
When interacting with extended attributes the vfs verifies that the caller is privileged over the inode with which the extended attribute is associated. For posix access and posix default extended attributes a uid or gid can be stored on-disk. Let the functions handle posix extended attributes on idmapped mounts. If the inode is accessed through an idmapped mount we need to map it according to the mount's user namespace. Afterwards the checks are identical to non-idmapped mounts. This has no effect for e.g. security xattrs since they don't store uids or gids and don't perform permission checks on them like posix acls do. Link: https://lore.kernel.org/r/20210121131959.646623-10-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> Signed-off-by: Tycho Andersen <tycho@tycho.pizza> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
parent
e65ce2a50c
commit
c7c7a1a18a
@ -39,8 +39,8 @@ int cachefiles_check_object_type(struct cachefiles_object *object)
|
|||||||
_enter("%p{%s}", object, type);
|
_enter("%p{%s}", object, type);
|
||||||
|
|
||||||
/* attempt to install a type label directly */
|
/* attempt to install a type label directly */
|
||||||
ret = vfs_setxattr(dentry, cachefiles_xattr_cache, type, 2,
|
ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, type,
|
||||||
XATTR_CREATE);
|
2, XATTR_CREATE);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
_debug("SET"); /* we succeeded */
|
_debug("SET"); /* we succeeded */
|
||||||
goto error;
|
goto error;
|
||||||
@ -54,7 +54,8 @@ int cachefiles_check_object_type(struct cachefiles_object *object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read the current type label */
|
/* read the current type label */
|
||||||
ret = vfs_getxattr(dentry, cachefiles_xattr_cache, xtype, 3);
|
ret = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, xtype,
|
||||||
|
3);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -ERANGE)
|
if (ret == -ERANGE)
|
||||||
goto bad_type_length;
|
goto bad_type_length;
|
||||||
@ -110,9 +111,8 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object,
|
|||||||
_debug("SET #%u", auxdata->len);
|
_debug("SET #%u", auxdata->len);
|
||||||
|
|
||||||
clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags);
|
clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags);
|
||||||
ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
|
ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
|
||||||
&auxdata->type, auxdata->len,
|
&auxdata->type, auxdata->len, XATTR_CREATE);
|
||||||
XATTR_CREATE);
|
|
||||||
if (ret < 0 && ret != -ENOMEM)
|
if (ret < 0 && ret != -ENOMEM)
|
||||||
cachefiles_io_error_obj(
|
cachefiles_io_error_obj(
|
||||||
object,
|
object,
|
||||||
@ -140,9 +140,8 @@ int cachefiles_update_object_xattr(struct cachefiles_object *object,
|
|||||||
_debug("SET #%u", auxdata->len);
|
_debug("SET #%u", auxdata->len);
|
||||||
|
|
||||||
clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags);
|
clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags);
|
||||||
ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
|
ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
|
||||||
&auxdata->type, auxdata->len,
|
&auxdata->type, auxdata->len, XATTR_REPLACE);
|
||||||
XATTR_REPLACE);
|
|
||||||
if (ret < 0 && ret != -ENOMEM)
|
if (ret < 0 && ret != -ENOMEM)
|
||||||
cachefiles_io_error_obj(
|
cachefiles_io_error_obj(
|
||||||
object,
|
object,
|
||||||
@ -171,7 +170,7 @@ int cachefiles_check_auxdata(struct cachefiles_object *object)
|
|||||||
if (!auxbuf)
|
if (!auxbuf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
xlen = vfs_getxattr(dentry, cachefiles_xattr_cache,
|
xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
|
||||||
&auxbuf->type, 512 + 1);
|
&auxbuf->type, 512 + 1);
|
||||||
ret = -ESTALE;
|
ret = -ESTALE;
|
||||||
if (xlen < 1 ||
|
if (xlen < 1 ||
|
||||||
@ -213,7 +212,7 @@ int cachefiles_check_object_xattr(struct cachefiles_object *object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read the current type label */
|
/* read the current type label */
|
||||||
ret = vfs_getxattr(dentry, cachefiles_xattr_cache,
|
ret = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
|
||||||
&auxbuf->type, 512 + 1);
|
&auxbuf->type, 512 + 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -ENODATA)
|
if (ret == -ENODATA)
|
||||||
@ -270,9 +269,9 @@ int cachefiles_check_object_xattr(struct cachefiles_object *object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* update the current label */
|
/* update the current label */
|
||||||
ret = vfs_setxattr(dentry, cachefiles_xattr_cache,
|
ret = vfs_setxattr(&init_user_ns, dentry,
|
||||||
&auxdata->type, auxdata->len,
|
cachefiles_xattr_cache, &auxdata->type,
|
||||||
XATTR_REPLACE);
|
auxdata->len, XATTR_REPLACE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
cachefiles_io_error_obj(object,
|
cachefiles_io_error_obj(object,
|
||||||
"Can't update xattr on %lu"
|
"Can't update xattr on %lu"
|
||||||
@ -309,7 +308,7 @@ int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = vfs_removexattr(dentry, cachefiles_xattr_cache);
|
ret = vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == -ENOENT || ret == -ENODATA)
|
if (ret == -ENOENT || ret == -ENODATA)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -1110,8 +1110,8 @@ ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inode_lock(lower_inode);
|
inode_lock(lower_inode);
|
||||||
rc = __vfs_setxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME,
|
rc = __vfs_setxattr(&init_user_ns, lower_dentry, lower_inode,
|
||||||
page_virt, size, 0);
|
ECRYPTFS_XATTR_NAME, page_virt, size, 0);
|
||||||
if (!rc && ecryptfs_inode)
|
if (!rc && ecryptfs_inode)
|
||||||
fsstack_copy_attr_all(ecryptfs_inode, lower_inode);
|
fsstack_copy_attr_all(ecryptfs_inode, lower_inode);
|
||||||
inode_unlock(lower_inode);
|
inode_unlock(lower_inode);
|
||||||
|
@ -1024,7 +1024,8 @@ ecryptfs_setxattr(struct dentry *dentry, struct inode *inode,
|
|||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rc = vfs_setxattr(lower_dentry, name, value, size, flags);
|
rc = vfs_setxattr(&init_user_ns, lower_dentry, name, value, size,
|
||||||
|
flags);
|
||||||
if (!rc && inode)
|
if (!rc && inode)
|
||||||
fsstack_copy_attr_all(inode, d_inode(lower_dentry));
|
fsstack_copy_attr_all(inode, d_inode(lower_dentry));
|
||||||
out:
|
out:
|
||||||
@ -1089,7 +1090,7 @@ static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
inode_lock(lower_inode);
|
inode_lock(lower_inode);
|
||||||
rc = __vfs_removexattr(lower_dentry, name);
|
rc = __vfs_removexattr(&init_user_ns, lower_dentry, name);
|
||||||
inode_unlock(lower_inode);
|
inode_unlock(lower_inode);
|
||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -426,8 +426,8 @@ static int ecryptfs_write_inode_size_to_xattr(struct inode *ecryptfs_inode)
|
|||||||
if (size < 0)
|
if (size < 0)
|
||||||
size = 8;
|
size = 8;
|
||||||
put_unaligned_be64(i_size_read(ecryptfs_inode), xattr_virt);
|
put_unaligned_be64(i_size_read(ecryptfs_inode), xattr_virt);
|
||||||
rc = __vfs_setxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME,
|
rc = __vfs_setxattr(&init_user_ns, lower_dentry, lower_inode,
|
||||||
xattr_virt, size, 0);
|
ECRYPTFS_XATTR_NAME, xattr_virt, size, 0);
|
||||||
inode_unlock(lower_inode);
|
inode_unlock(lower_inode);
|
||||||
if (rc)
|
if (rc)
|
||||||
printk(KERN_ERR "Error whilst attempting to write inode size "
|
printk(KERN_ERR "Error whilst attempting to write inode size "
|
||||||
|
@ -499,7 +499,8 @@ int nfsd4_is_junction(struct dentry *dentry)
|
|||||||
return 0;
|
return 0;
|
||||||
if (!(inode->i_mode & S_ISVTX))
|
if (!(inode->i_mode & S_ISVTX))
|
||||||
return 0;
|
return 0;
|
||||||
if (vfs_getxattr(dentry, NFSD_JUNCTION_XATTR_NAME, NULL, 0) <= 0)
|
if (vfs_getxattr(&init_user_ns, dentry, NFSD_JUNCTION_XATTR_NAME,
|
||||||
|
NULL, 0) <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2149,7 +2150,7 @@ nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
|
|||||||
|
|
||||||
inode_lock_shared(inode);
|
inode_lock_shared(inode);
|
||||||
|
|
||||||
len = vfs_getxattr(dentry, name, NULL, 0);
|
len = vfs_getxattr(&init_user_ns, dentry, name, NULL, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Zero-length attribute, just return.
|
* Zero-length attribute, just return.
|
||||||
@ -2176,7 +2177,7 @@ nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = vfs_getxattr(dentry, name, buf, len);
|
len = vfs_getxattr(&init_user_ns, dentry, name, buf, len);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
kvfree(buf);
|
kvfree(buf);
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
@ -2283,7 +2284,8 @@ nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name)
|
|||||||
|
|
||||||
fh_lock(fhp);
|
fh_lock(fhp);
|
||||||
|
|
||||||
ret = __vfs_removexattr_locked(fhp->fh_dentry, name, NULL);
|
ret = __vfs_removexattr_locked(&init_user_ns, fhp->fh_dentry,
|
||||||
|
name, NULL);
|
||||||
|
|
||||||
fh_unlock(fhp);
|
fh_unlock(fhp);
|
||||||
fh_drop_write(fhp);
|
fh_drop_write(fhp);
|
||||||
@ -2307,8 +2309,8 @@ nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name,
|
|||||||
return nfserrno(ret);
|
return nfserrno(ret);
|
||||||
fh_lock(fhp);
|
fh_lock(fhp);
|
||||||
|
|
||||||
ret = __vfs_setxattr_locked(fhp->fh_dentry, name, buf, len, flags,
|
ret = __vfs_setxattr_locked(&init_user_ns, fhp->fh_dentry, name, buf,
|
||||||
NULL);
|
len, flags, NULL);
|
||||||
|
|
||||||
fh_unlock(fhp);
|
fh_unlock(fhp);
|
||||||
fh_drop_write(fhp);
|
fh_drop_write(fhp);
|
||||||
|
@ -85,9 +85,9 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
|
|||||||
if (ovl_is_private_xattr(sb, name))
|
if (ovl_is_private_xattr(sb, name))
|
||||||
continue;
|
continue;
|
||||||
retry:
|
retry:
|
||||||
size = vfs_getxattr(old, name, value, value_size);
|
size = vfs_getxattr(&init_user_ns, old, name, value, value_size);
|
||||||
if (size == -ERANGE)
|
if (size == -ERANGE)
|
||||||
size = vfs_getxattr(old, name, NULL, 0);
|
size = vfs_getxattr(&init_user_ns, old, name, NULL, 0);
|
||||||
|
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
error = size;
|
error = size;
|
||||||
@ -114,7 +114,7 @@ retry:
|
|||||||
error = 0;
|
error = 0;
|
||||||
continue; /* Discard */
|
continue; /* Discard */
|
||||||
}
|
}
|
||||||
error = vfs_setxattr(new, name, value, size, 0);
|
error = vfs_setxattr(&init_user_ns, new, name, value, size, 0);
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error != -EOPNOTSUPP || ovl_must_copy_xattr(name))
|
if (error != -EOPNOTSUPP || ovl_must_copy_xattr(name))
|
||||||
break;
|
break;
|
||||||
@ -795,7 +795,7 @@ static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
|
|||||||
ssize_t res;
|
ssize_t res;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
res = vfs_getxattr(dentry, name, NULL, 0);
|
res = vfs_getxattr(&init_user_ns, dentry, name, NULL, 0);
|
||||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
||||||
@ -804,7 +804,7 @@ static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
|
|||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
res = vfs_getxattr(dentry, name, buf, res);
|
res = vfs_getxattr(&init_user_ns, dentry, name, buf, res);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
else
|
else
|
||||||
@ -846,8 +846,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
|
|||||||
* don't want that to happen for normal copy-up operation.
|
* don't want that to happen for normal copy-up operation.
|
||||||
*/
|
*/
|
||||||
if (capability) {
|
if (capability) {
|
||||||
err = vfs_setxattr(upperpath.dentry, XATTR_NAME_CAPS,
|
err = vfs_setxattr(&init_user_ns, upperpath.dentry,
|
||||||
capability, cap_size, 0);
|
XATTR_NAME_CAPS, capability, cap_size, 0);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
@ -449,7 +449,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
err = vfs_setxattr(upperdentry, name, buffer, size, XATTR_CREATE);
|
err = vfs_setxattr(&init_user_ns, upperdentry, name, buffer, size, XATTR_CREATE);
|
||||||
out_free:
|
out_free:
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return err;
|
return err;
|
||||||
|
@ -352,7 +352,7 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!value && !upperdentry) {
|
if (!value && !upperdentry) {
|
||||||
err = vfs_getxattr(realdentry, name, NULL, 0);
|
err = vfs_getxattr(&init_user_ns, realdentry, name, NULL, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_drop_write;
|
goto out_drop_write;
|
||||||
}
|
}
|
||||||
@ -367,10 +367,11 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
|
|||||||
|
|
||||||
old_cred = ovl_override_creds(dentry->d_sb);
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
if (value)
|
if (value)
|
||||||
err = vfs_setxattr(realdentry, name, value, size, flags);
|
err = vfs_setxattr(&init_user_ns, realdentry, name, value, size,
|
||||||
|
flags);
|
||||||
else {
|
else {
|
||||||
WARN_ON(flags != XATTR_REPLACE);
|
WARN_ON(flags != XATTR_REPLACE);
|
||||||
err = vfs_removexattr(realdentry, name);
|
err = vfs_removexattr(&init_user_ns, realdentry, name);
|
||||||
}
|
}
|
||||||
revert_creds(old_cred);
|
revert_creds(old_cred);
|
||||||
|
|
||||||
@ -392,7 +393,7 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
|||||||
ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
|
ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
|
||||||
|
|
||||||
old_cred = ovl_override_creds(dentry->d_sb);
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
res = vfs_getxattr(realdentry, name, value, size);
|
res = vfs_getxattr(&init_user_ns, realdentry, name, value, size);
|
||||||
revert_creds(old_cred);
|
revert_creds(old_cred);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ static inline ssize_t ovl_do_getxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
|||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
const char *name = ovl_xattr(ofs, ox);
|
const char *name = ovl_xattr(ofs, ox);
|
||||||
return vfs_getxattr(dentry, name, value, size);
|
return vfs_getxattr(&init_user_ns, dentry, name, value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
||||||
@ -194,7 +194,7 @@ static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
|
|||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
const char *name = ovl_xattr(ofs, ox);
|
const char *name = ovl_xattr(ofs, ox);
|
||||||
int err = vfs_setxattr(dentry, name, value, size, 0);
|
int err = vfs_setxattr(&init_user_ns, dentry, name, value, size, 0);
|
||||||
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
|
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
|
||||||
dentry, name, min((int)size, 48), value, size, err);
|
dentry, name, min((int)size, 48), value, size, err);
|
||||||
return err;
|
return err;
|
||||||
@ -204,7 +204,7 @@ static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
|
|||||||
enum ovl_xattr ox)
|
enum ovl_xattr ox)
|
||||||
{
|
{
|
||||||
const char *name = ovl_xattr(ofs, ox);
|
const char *name = ovl_xattr(ofs, ox);
|
||||||
int err = vfs_removexattr(dentry, name);
|
int err = vfs_removexattr(&init_user_ns, dentry, name);
|
||||||
pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
|
pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -794,11 +794,13 @@ retry:
|
|||||||
* allowed as upper are limited to "normal" ones, where checking
|
* allowed as upper are limited to "normal" ones, where checking
|
||||||
* for the above two errors is sufficient.
|
* for the above two errors is sufficient.
|
||||||
*/
|
*/
|
||||||
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
|
err = vfs_removexattr(&init_user_ns, work,
|
||||||
|
XATTR_NAME_POSIX_ACL_DEFAULT);
|
||||||
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
|
|
||||||
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_ACCESS);
|
err = vfs_removexattr(&init_user_ns, work,
|
||||||
|
XATTR_NAME_POSIX_ACL_ACCESS);
|
||||||
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
|
|
||||||
|
120
fs/xattr.c
120
fs/xattr.c
@ -83,7 +83,8 @@ xattr_resolve_name(struct inode *inode, const char **name)
|
|||||||
* because different namespaces have very different rules.
|
* because different namespaces have very different rules.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xattr_permission(struct inode *inode, const char *name, int mask)
|
xattr_permission(struct user_namespace *mnt_userns, struct inode *inode,
|
||||||
|
const char *name, int mask)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We can never set or remove an extended attribute on a read-only
|
* We can never set or remove an extended attribute on a read-only
|
||||||
@ -128,11 +129,11 @@ xattr_permission(struct inode *inode, const char *name, int mask)
|
|||||||
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
|
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
|
||||||
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
|
if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
|
||||||
(mask & MAY_WRITE) &&
|
(mask & MAY_WRITE) &&
|
||||||
!inode_owner_or_capable(&init_user_ns, inode))
|
!inode_owner_or_capable(mnt_userns, inode))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inode_permission(&init_user_ns, inode, mask);
|
return inode_permission(mnt_userns, inode, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -163,8 +164,9 @@ xattr_supported_namespace(struct inode *inode, const char *prefix)
|
|||||||
EXPORT_SYMBOL(xattr_supported_namespace);
|
EXPORT_SYMBOL(xattr_supported_namespace);
|
||||||
|
|
||||||
int
|
int
|
||||||
__vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
|
__vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
const void *value, size_t size, int flags)
|
struct inode *inode, const char *name, const void *value,
|
||||||
|
size_t size, int flags)
|
||||||
{
|
{
|
||||||
const struct xattr_handler *handler;
|
const struct xattr_handler *handler;
|
||||||
|
|
||||||
@ -175,7 +177,7 @@ __vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
value = ""; /* empty EA, do not remove */
|
value = ""; /* empty EA, do not remove */
|
||||||
return handler->set(handler, &init_user_ns, dentry, inode, name, value,
|
return handler->set(handler, mnt_userns, dentry, inode, name, value,
|
||||||
size, flags);
|
size, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__vfs_setxattr);
|
EXPORT_SYMBOL(__vfs_setxattr);
|
||||||
@ -184,6 +186,7 @@ EXPORT_SYMBOL(__vfs_setxattr);
|
|||||||
* __vfs_setxattr_noperm - perform setxattr operation without performing
|
* __vfs_setxattr_noperm - perform setxattr operation without performing
|
||||||
* permission checks.
|
* permission checks.
|
||||||
*
|
*
|
||||||
|
* @mnt_userns - user namespace of the mount the inode was found from
|
||||||
* @dentry - object to perform setxattr on
|
* @dentry - object to perform setxattr on
|
||||||
* @name - xattr name to set
|
* @name - xattr name to set
|
||||||
* @value - value to set @name to
|
* @value - value to set @name to
|
||||||
@ -196,8 +199,9 @@ EXPORT_SYMBOL(__vfs_setxattr);
|
|||||||
* is executed. It also assumes that the caller will make the appropriate
|
* is executed. It also assumes that the caller will make the appropriate
|
||||||
* permission checks.
|
* permission checks.
|
||||||
*/
|
*/
|
||||||
int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
int __vfs_setxattr_noperm(struct user_namespace *mnt_userns,
|
||||||
const void *value, size_t size, int flags)
|
struct dentry *dentry, const char *name,
|
||||||
|
const void *value, size_t size, int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error = -EAGAIN;
|
int error = -EAGAIN;
|
||||||
@ -207,7 +211,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
|||||||
if (issec)
|
if (issec)
|
||||||
inode->i_flags &= ~S_NOSEC;
|
inode->i_flags &= ~S_NOSEC;
|
||||||
if (inode->i_opflags & IOP_XATTR) {
|
if (inode->i_opflags & IOP_XATTR) {
|
||||||
error = __vfs_setxattr(dentry, inode, name, value, size, flags);
|
error = __vfs_setxattr(mnt_userns, dentry, inode, name, value,
|
||||||
|
size, flags);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
fsnotify_xattr(dentry);
|
fsnotify_xattr(dentry);
|
||||||
security_inode_post_setxattr(dentry, name, value,
|
security_inode_post_setxattr(dentry, name, value,
|
||||||
@ -246,14 +251,14 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
|||||||
* a delegation was broken on, NULL if none.
|
* a delegation was broken on, NULL if none.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
__vfs_setxattr_locked(struct dentry *dentry, const char *name,
|
__vfs_setxattr_locked(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
const void *value, size_t size, int flags,
|
const char *name, const void *value, size_t size,
|
||||||
struct inode **delegated_inode)
|
int flags, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = xattr_permission(inode, name, MAY_WRITE);
|
error = xattr_permission(mnt_userns, inode, name, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -265,7 +270,8 @@ __vfs_setxattr_locked(struct dentry *dentry, const char *name,
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
|
error = __vfs_setxattr_noperm(mnt_userns, dentry, name, value,
|
||||||
|
size, flags);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return error;
|
return error;
|
||||||
@ -273,8 +279,8 @@ out:
|
|||||||
EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
|
EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
|
||||||
|
|
||||||
int
|
int
|
||||||
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
size_t size, int flags)
|
const char *name, const void *value, size_t size, int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
struct inode *delegated_inode = NULL;
|
struct inode *delegated_inode = NULL;
|
||||||
@ -282,7 +288,7 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (size && strcmp(name, XATTR_NAME_CAPS) == 0) {
|
if (size && strcmp(name, XATTR_NAME_CAPS) == 0) {
|
||||||
error = cap_convert_nscap(&init_user_ns, dentry, &value, size);
|
error = cap_convert_nscap(mnt_userns, dentry, &value, size);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
return error;
|
return error;
|
||||||
size = error;
|
size = error;
|
||||||
@ -290,8 +296,8 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
|||||||
|
|
||||||
retry_deleg:
|
retry_deleg:
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
error = __vfs_setxattr_locked(dentry, name, value, size, flags,
|
error = __vfs_setxattr_locked(mnt_userns, dentry, name, value, size,
|
||||||
&delegated_inode);
|
flags, &delegated_inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
|
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
@ -341,15 +347,16 @@ out_noalloc:
|
|||||||
* Returns the result of alloc, if failed, or the getxattr operation.
|
* Returns the result of alloc, if failed, or the getxattr operation.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
|
vfs_getxattr_alloc(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
size_t xattr_size, gfp_t flags)
|
const char *name, char **xattr_value, size_t xattr_size,
|
||||||
|
gfp_t flags)
|
||||||
{
|
{
|
||||||
const struct xattr_handler *handler;
|
const struct xattr_handler *handler;
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
char *value = *xattr_value;
|
char *value = *xattr_value;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = xattr_permission(inode, name, MAY_READ);
|
error = xattr_permission(mnt_userns, inode, name, MAY_READ);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -390,12 +397,13 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
|
|||||||
EXPORT_SYMBOL(__vfs_getxattr);
|
EXPORT_SYMBOL(__vfs_getxattr);
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
|
vfs_getxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
|
const char *name, void *value, size_t size)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = xattr_permission(inode, name, MAY_READ);
|
error = xattr_permission(mnt_userns, inode, name, MAY_READ);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -441,7 +449,8 @@ vfs_listxattr(struct dentry *dentry, char *list, size_t size)
|
|||||||
EXPORT_SYMBOL_GPL(vfs_listxattr);
|
EXPORT_SYMBOL_GPL(vfs_listxattr);
|
||||||
|
|
||||||
int
|
int
|
||||||
__vfs_removexattr(struct dentry *dentry, const char *name)
|
__vfs_removexattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
const struct xattr_handler *handler;
|
const struct xattr_handler *handler;
|
||||||
@ -451,8 +460,8 @@ __vfs_removexattr(struct dentry *dentry, const char *name)
|
|||||||
return PTR_ERR(handler);
|
return PTR_ERR(handler);
|
||||||
if (!handler->set)
|
if (!handler->set)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
return handler->set(handler, &init_user_ns, dentry, inode, name, NULL,
|
return handler->set(handler, mnt_userns, dentry, inode, name, NULL, 0,
|
||||||
0, XATTR_REPLACE);
|
XATTR_REPLACE);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__vfs_removexattr);
|
EXPORT_SYMBOL(__vfs_removexattr);
|
||||||
|
|
||||||
@ -466,13 +475,14 @@ EXPORT_SYMBOL(__vfs_removexattr);
|
|||||||
* a delegation was broken on, NULL if none.
|
* a delegation was broken on, NULL if none.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
__vfs_removexattr_locked(struct dentry *dentry, const char *name,
|
__vfs_removexattr_locked(struct user_namespace *mnt_userns,
|
||||||
struct inode **delegated_inode)
|
struct dentry *dentry, const char *name,
|
||||||
|
struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = xattr_permission(inode, name, MAY_WRITE);
|
error = xattr_permission(mnt_userns, inode, name, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -484,7 +494,7 @@ __vfs_removexattr_locked(struct dentry *dentry, const char *name,
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = __vfs_removexattr(dentry, name);
|
error = __vfs_removexattr(mnt_userns, dentry, name);
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
fsnotify_xattr(dentry);
|
fsnotify_xattr(dentry);
|
||||||
@ -497,7 +507,8 @@ out:
|
|||||||
EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
|
EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
|
||||||
|
|
||||||
int
|
int
|
||||||
vfs_removexattr(struct dentry *dentry, const char *name)
|
vfs_removexattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
struct inode *delegated_inode = NULL;
|
struct inode *delegated_inode = NULL;
|
||||||
@ -505,7 +516,8 @@ vfs_removexattr(struct dentry *dentry, const char *name)
|
|||||||
|
|
||||||
retry_deleg:
|
retry_deleg:
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
error = __vfs_removexattr_locked(dentry, name, &delegated_inode);
|
error = __vfs_removexattr_locked(mnt_userns, dentry,
|
||||||
|
name, &delegated_inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
|
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
@ -522,8 +534,9 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
|
|||||||
* Extended attribute SET operations
|
* Extended attribute SET operations
|
||||||
*/
|
*/
|
||||||
static long
|
static long
|
||||||
setxattr(struct dentry *d, const char __user *name, const void __user *value,
|
setxattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||||
size_t size, int flags)
|
const char __user *name, const void __user *value, size_t size,
|
||||||
|
int flags)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
void *kvalue = NULL;
|
void *kvalue = NULL;
|
||||||
@ -550,11 +563,10 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
|
|||||||
}
|
}
|
||||||
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
|
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
|
||||||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
|
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
|
||||||
posix_acl_fix_xattr_from_user(&init_user_ns, kvalue,
|
posix_acl_fix_xattr_from_user(mnt_userns, kvalue, size);
|
||||||
size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error = vfs_setxattr(d, kname, kvalue, size, flags);
|
error = vfs_setxattr(mnt_userns, d, kname, kvalue, size, flags);
|
||||||
out:
|
out:
|
||||||
kvfree(kvalue);
|
kvfree(kvalue);
|
||||||
|
|
||||||
@ -567,13 +579,15 @@ static int path_setxattr(const char __user *pathname,
|
|||||||
{
|
{
|
||||||
struct path path;
|
struct path path;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
error = mnt_want_write(path.mnt);
|
error = mnt_want_write(path.mnt);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = setxattr(path.dentry, name, value, size, flags);
|
error = setxattr(mnt_user_ns(path.mnt), path.dentry, name,
|
||||||
|
value, size, flags);
|
||||||
mnt_drop_write(path.mnt);
|
mnt_drop_write(path.mnt);
|
||||||
}
|
}
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
@ -609,7 +623,9 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
|
|||||||
audit_file(f.file);
|
audit_file(f.file);
|
||||||
error = mnt_want_write_file(f.file);
|
error = mnt_want_write_file(f.file);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = setxattr(f.file->f_path.dentry, name, value, size, flags);
|
error = setxattr(file_mnt_user_ns(f.file),
|
||||||
|
f.file->f_path.dentry, name,
|
||||||
|
value, size, flags);
|
||||||
mnt_drop_write_file(f.file);
|
mnt_drop_write_file(f.file);
|
||||||
}
|
}
|
||||||
fdput(f);
|
fdput(f);
|
||||||
@ -620,8 +636,8 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
|
|||||||
* Extended attribute GET operations
|
* Extended attribute GET operations
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
getxattr(struct dentry *d, const char __user *name, void __user *value,
|
getxattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||||
size_t size)
|
const char __user *name, void __user *value, size_t size)
|
||||||
{
|
{
|
||||||
ssize_t error;
|
ssize_t error;
|
||||||
void *kvalue = NULL;
|
void *kvalue = NULL;
|
||||||
@ -641,12 +657,11 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = vfs_getxattr(d, kname, kvalue, size);
|
error = vfs_getxattr(mnt_userns, d, kname, kvalue, size);
|
||||||
if (error > 0) {
|
if (error > 0) {
|
||||||
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
|
if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
|
||||||
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
|
(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
|
||||||
posix_acl_fix_xattr_to_user(&init_user_ns, kvalue,
|
posix_acl_fix_xattr_to_user(mnt_userns, kvalue, error);
|
||||||
error);
|
|
||||||
if (size && copy_to_user(value, kvalue, error))
|
if (size && copy_to_user(value, kvalue, error))
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
|
} else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
|
||||||
@ -670,7 +685,7 @@ retry:
|
|||||||
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
error = getxattr(path.dentry, name, value, size);
|
error = getxattr(mnt_user_ns(path.mnt), path.dentry, name, value, size);
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
if (retry_estale(error, lookup_flags)) {
|
if (retry_estale(error, lookup_flags)) {
|
||||||
lookup_flags |= LOOKUP_REVAL;
|
lookup_flags |= LOOKUP_REVAL;
|
||||||
@ -700,7 +715,8 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
|
|||||||
if (!f.file)
|
if (!f.file)
|
||||||
return error;
|
return error;
|
||||||
audit_file(f.file);
|
audit_file(f.file);
|
||||||
error = getxattr(f.file->f_path.dentry, name, value, size);
|
error = getxattr(file_mnt_user_ns(f.file), f.file->f_path.dentry,
|
||||||
|
name, value, size);
|
||||||
fdput(f);
|
fdput(f);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -784,7 +800,8 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
|
|||||||
* Extended attribute REMOVE operations
|
* Extended attribute REMOVE operations
|
||||||
*/
|
*/
|
||||||
static long
|
static long
|
||||||
removexattr(struct dentry *d, const char __user *name)
|
removexattr(struct user_namespace *mnt_userns, struct dentry *d,
|
||||||
|
const char __user *name)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
char kname[XATTR_NAME_MAX + 1];
|
char kname[XATTR_NAME_MAX + 1];
|
||||||
@ -795,7 +812,7 @@ removexattr(struct dentry *d, const char __user *name)
|
|||||||
if (error < 0)
|
if (error < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
return vfs_removexattr(d, kname);
|
return vfs_removexattr(mnt_userns, d, kname);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int path_removexattr(const char __user *pathname,
|
static int path_removexattr(const char __user *pathname,
|
||||||
@ -809,7 +826,7 @@ retry:
|
|||||||
return error;
|
return error;
|
||||||
error = mnt_want_write(path.mnt);
|
error = mnt_want_write(path.mnt);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = removexattr(path.dentry, name);
|
error = removexattr(mnt_user_ns(path.mnt), path.dentry, name);
|
||||||
mnt_drop_write(path.mnt);
|
mnt_drop_write(path.mnt);
|
||||||
}
|
}
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
@ -842,7 +859,8 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
|
|||||||
audit_file(f.file);
|
audit_file(f.file);
|
||||||
error = mnt_want_write_file(f.file);
|
error = mnt_want_write_file(f.file);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = removexattr(f.file->f_path.dentry, name);
|
error = removexattr(file_mnt_user_ns(f.file),
|
||||||
|
f.file->f_path.dentry, name);
|
||||||
mnt_drop_write_file(f.file);
|
mnt_drop_write_file(f.file);
|
||||||
}
|
}
|
||||||
fdput(f);
|
fdput(f);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
#include <linux/user_namespace.h>
|
||||||
#include <uapi/linux/xattr.h>
|
#include <uapi/linux/xattr.h>
|
||||||
|
|
||||||
struct inode;
|
struct inode;
|
||||||
@ -49,18 +50,26 @@ struct xattr {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t);
|
ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t);
|
||||||
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
|
ssize_t vfs_getxattr(struct user_namespace *, struct dentry *, const char *,
|
||||||
|
void *, size_t);
|
||||||
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
|
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
|
||||||
int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int);
|
int __vfs_setxattr(struct user_namespace *, struct dentry *, struct inode *,
|
||||||
int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
|
const char *, const void *, size_t, int);
|
||||||
int __vfs_setxattr_locked(struct dentry *, const char *, const void *, size_t, int, struct inode **);
|
int __vfs_setxattr_noperm(struct user_namespace *, struct dentry *,
|
||||||
int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
|
const char *, const void *, size_t, int);
|
||||||
int __vfs_removexattr(struct dentry *, const char *);
|
int __vfs_setxattr_locked(struct user_namespace *, struct dentry *,
|
||||||
int __vfs_removexattr_locked(struct dentry *, const char *, struct inode **);
|
const char *, const void *, size_t, int,
|
||||||
int vfs_removexattr(struct dentry *, const char *);
|
struct inode **);
|
||||||
|
int vfs_setxattr(struct user_namespace *, struct dentry *, const char *,
|
||||||
|
const void *, size_t, int);
|
||||||
|
int __vfs_removexattr(struct user_namespace *, struct dentry *, const char *);
|
||||||
|
int __vfs_removexattr_locked(struct user_namespace *, struct dentry *,
|
||||||
|
const char *, struct inode **);
|
||||||
|
int vfs_removexattr(struct user_namespace *, struct dentry *, const char *);
|
||||||
|
|
||||||
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
|
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
|
||||||
ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
|
ssize_t vfs_getxattr_alloc(struct user_namespace *mnt_userns,
|
||||||
|
struct dentry *dentry, const char *name,
|
||||||
char **xattr_value, size_t size, gfp_t flags);
|
char **xattr_value, size_t size, gfp_t flags);
|
||||||
|
|
||||||
int xattr_supported_namespace(struct inode *inode, const char *prefix);
|
int xattr_supported_namespace(struct inode *inode, const char *prefix);
|
||||||
|
@ -324,8 +324,8 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
|
|||||||
d = bprm->file->f_path.dentry;
|
d = bprm->file->f_path.dentry;
|
||||||
|
|
||||||
for (i = 0; i < profile->xattr_count; i++) {
|
for (i = 0; i < profile->xattr_count; i++) {
|
||||||
size = vfs_getxattr_alloc(d, profile->xattrs[i], &value,
|
size = vfs_getxattr_alloc(&init_user_ns, d, profile->xattrs[i],
|
||||||
value_size, GFP_KERNEL);
|
&value, value_size, GFP_KERNEL);
|
||||||
if (size >= 0) {
|
if (size >= 0) {
|
||||||
u32 perm;
|
u32 perm;
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ int cap_inode_killpriv(struct dentry *dentry)
|
|||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = __vfs_removexattr(dentry, XATTR_NAME_CAPS);
|
error = __vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_CAPS);
|
||||||
if (error == -EOPNOTSUPP)
|
if (error == -EOPNOTSUPP)
|
||||||
error = 0;
|
error = 0;
|
||||||
return error;
|
return error;
|
||||||
@ -386,8 +386,8 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
size = sizeof(struct vfs_ns_cap_data);
|
size = sizeof(struct vfs_ns_cap_data);
|
||||||
ret = (int) vfs_getxattr_alloc(dentry, XATTR_NAME_CAPS,
|
ret = (int)vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_CAPS,
|
||||||
&tmpbuf, size, GFP_NOFS);
|
&tmpbuf, size, GFP_NOFS);
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -222,7 +222,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
|
|||||||
ima_present = true;
|
ima_present = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
size = vfs_getxattr_alloc(dentry, xattr->name,
|
size = vfs_getxattr_alloc(&init_user_ns, dentry, xattr->name,
|
||||||
&xattr_value, xattr_size, GFP_NOFS);
|
&xattr_value, xattr_size, GFP_NOFS);
|
||||||
if (size == -ENOMEM) {
|
if (size == -ENOMEM) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
@ -275,8 +275,8 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Do this the hard way */
|
/* Do this the hard way */
|
||||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
|
rc = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_EVM,
|
||||||
GFP_NOFS);
|
(char **)&xattr_data, 0, GFP_NOFS);
|
||||||
if (rc <= 0) {
|
if (rc <= 0) {
|
||||||
if (rc == -ENODATA)
|
if (rc == -ENODATA)
|
||||||
return 0;
|
return 0;
|
||||||
@ -319,11 +319,12 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
|
|||||||
xattr_value_len, &data);
|
xattr_value_len, &data);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
|
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
|
||||||
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
|
rc = __vfs_setxattr_noperm(&init_user_ns, dentry,
|
||||||
|
XATTR_NAME_EVM,
|
||||||
&data.hdr.xattr.data[1],
|
&data.hdr.xattr.data[1],
|
||||||
SHA1_DIGEST_SIZE + 1, 0);
|
SHA1_DIGEST_SIZE + 1, 0);
|
||||||
} else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
|
} else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
|
||||||
rc = __vfs_removexattr(dentry, XATTR_NAME_EVM);
|
rc = __vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_EVM);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -146,8 +146,8 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
|||||||
/* if status is not PASS, try to check again - against -ENOMEM */
|
/* if status is not PASS, try to check again - against -ENOMEM */
|
||||||
|
|
||||||
/* first need to know the sig type */
|
/* first need to know the sig type */
|
||||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
|
rc = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_EVM,
|
||||||
GFP_NOFS);
|
(char **)&xattr_data, 0, GFP_NOFS);
|
||||||
if (rc <= 0) {
|
if (rc <= 0) {
|
||||||
evm_status = INTEGRITY_FAIL;
|
evm_status = INTEGRITY_FAIL;
|
||||||
if (rc == -ENODATA) {
|
if (rc == -ENODATA) {
|
||||||
|
@ -94,7 +94,7 @@ static int ima_fix_xattr(struct dentry *dentry,
|
|||||||
iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
|
iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
|
||||||
iint->ima_hash->xattr.ng.algo = algo;
|
iint->ima_hash->xattr.ng.algo = algo;
|
||||||
}
|
}
|
||||||
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
rc = __vfs_setxattr_noperm(&init_user_ns, dentry, XATTR_NAME_IMA,
|
||||||
&iint->ima_hash->xattr.data[offset],
|
&iint->ima_hash->xattr.data[offset],
|
||||||
(sizeof(iint->ima_hash->xattr) - offset) +
|
(sizeof(iint->ima_hash->xattr) - offset) +
|
||||||
iint->ima_hash->length, 0);
|
iint->ima_hash->length, 0);
|
||||||
@ -215,8 +215,8 @@ int ima_read_xattr(struct dentry *dentry,
|
|||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
|
ret = vfs_getxattr_alloc(&init_user_ns, dentry, XATTR_NAME_IMA,
|
||||||
0, GFP_NOFS);
|
(char **)xattr_value, 0, GFP_NOFS);
|
||||||
if (ret == -EOPNOTSUPP)
|
if (ret == -EOPNOTSUPP)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
return ret;
|
return ret;
|
||||||
@ -520,7 +520,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
|
|||||||
|
|
||||||
action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||||
if (!action)
|
if (!action)
|
||||||
__vfs_removexattr(dentry, XATTR_NAME_IMA);
|
__vfs_removexattr(&init_user_ns, dentry, XATTR_NAME_IMA);
|
||||||
iint = integrity_iint_find(inode);
|
iint = integrity_iint_find(inode);
|
||||||
if (iint) {
|
if (iint) {
|
||||||
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
|
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
|
||||||
|
@ -6526,7 +6526,8 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
|
|||||||
*/
|
*/
|
||||||
static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||||
{
|
{
|
||||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0);
|
return __vfs_setxattr_noperm(&init_user_ns, dentry, XATTR_NAME_SELINUX,
|
||||||
|
ctx, ctxlen, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||||
|
@ -3425,7 +3425,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
|
|||||||
*/
|
*/
|
||||||
if (isp->smk_flags & SMK_INODE_CHANGED) {
|
if (isp->smk_flags & SMK_INODE_CHANGED) {
|
||||||
isp->smk_flags &= ~SMK_INODE_CHANGED;
|
isp->smk_flags &= ~SMK_INODE_CHANGED;
|
||||||
rc = __vfs_setxattr(dp, inode,
|
rc = __vfs_setxattr(&init_user_ns, dp, inode,
|
||||||
XATTR_NAME_SMACKTRANSMUTE,
|
XATTR_NAME_SMACKTRANSMUTE,
|
||||||
TRANS_TRUE, TRANS_TRUE_SIZE,
|
TRANS_TRUE, TRANS_TRUE_SIZE,
|
||||||
0);
|
0);
|
||||||
@ -4597,12 +4597,14 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
|
|||||||
|
|
||||||
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||||
{
|
{
|
||||||
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx, ctxlen, 0);
|
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx,
|
||||||
|
ctxlen, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||||
{
|
{
|
||||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, ctx, ctxlen, 0);
|
return __vfs_setxattr_noperm(&init_user_ns, dentry, XATTR_NAME_SMACK,
|
||||||
|
ctx, ctxlen, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||||
|
Loading…
Reference in New Issue
Block a user