Commit 4d84a565 authored by David Sterba's avatar David Sterba Committed by Luis Henriques

btrfs: don't accept bare namespace as a valid xattr

commit 3c3b04d1 upstream.

Due to insufficient check in btrfs_is_valid_xattr, this unexpectedly
works:

 $ touch file
 $ setfattr -n user. -v 1 file
 $ getfattr -d file
user.="1"

ie. the missing attribute name after the namespace.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=94291Reported-by: default avatarWilliam Douglas <william.douglas@intel.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.cz>
Signed-off-by: default avatarChris Mason <clm@fb.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 6659c4f1
...@@ -360,22 +360,42 @@ const struct xattr_handler *btrfs_xattr_handlers[] = { ...@@ -360,22 +360,42 @@ const struct xattr_handler *btrfs_xattr_handlers[] = {
/* /*
* Check if the attribute is in a supported namespace. * Check if the attribute is in a supported namespace.
* *
* This applied after the check for the synthetic attributes in the system * This is applied after the check for the synthetic attributes in the system
* namespace. * namespace.
*/ */
static bool btrfs_is_valid_xattr(const char *name) static int btrfs_is_valid_xattr(const char *name)
{ {
return !strncmp(name, XATTR_SECURITY_PREFIX, int len = strlen(name);
XATTR_SECURITY_PREFIX_LEN) || int prefixlen = 0;
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) ||
!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || if (!strncmp(name, XATTR_SECURITY_PREFIX,
!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) || XATTR_SECURITY_PREFIX_LEN))
!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN); prefixlen = XATTR_SECURITY_PREFIX_LEN;
else if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
prefixlen = XATTR_SYSTEM_PREFIX_LEN;
else if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
prefixlen = XATTR_TRUSTED_PREFIX_LEN;
else if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
prefixlen = XATTR_USER_PREFIX_LEN;
else if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
prefixlen = XATTR_BTRFS_PREFIX_LEN;
else
return -EOPNOTSUPP;
/*
* The name cannot consist of just prefix
*/
if (len <= prefixlen)
return -EINVAL;
return 0;
} }
ssize_t btrfs_getxattr(struct dentry *dentry, const char *name, ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
void *buffer, size_t size) void *buffer, size_t size)
{ {
int ret;
/* /*
* If this is a request for a synthetic attribute in the system.* * If this is a request for a synthetic attribute in the system.*
* namespace use the generic infrastructure to resolve a handler * namespace use the generic infrastructure to resolve a handler
...@@ -384,8 +404,9 @@ ssize_t btrfs_getxattr(struct dentry *dentry, const char *name, ...@@ -384,8 +404,9 @@ ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return generic_getxattr(dentry, name, buffer, size); return generic_getxattr(dentry, name, buffer, size);
if (!btrfs_is_valid_xattr(name)) ret = btrfs_is_valid_xattr(name);
return -EOPNOTSUPP; if (ret)
return ret;
return __btrfs_getxattr(dentry->d_inode, name, buffer, size); return __btrfs_getxattr(dentry->d_inode, name, buffer, size);
} }
...@@ -393,6 +414,7 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -393,6 +414,7 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags) size_t size, int flags)
{ {
struct btrfs_root *root = BTRFS_I(dentry->d_inode)->root; struct btrfs_root *root = BTRFS_I(dentry->d_inode)->root;
int ret;
/* /*
* The permission on security.* and system.* is not checked * The permission on security.* and system.* is not checked
...@@ -409,8 +431,9 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -409,8 +431,9 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return generic_setxattr(dentry, name, value, size, flags); return generic_setxattr(dentry, name, value, size, flags);
if (!btrfs_is_valid_xattr(name)) ret = btrfs_is_valid_xattr(name);
return -EOPNOTSUPP; if (ret)
return ret;
if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN)) if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
return btrfs_set_prop(dentry->d_inode, name, return btrfs_set_prop(dentry->d_inode, name,
...@@ -426,6 +449,7 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -426,6 +449,7 @@ int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
int btrfs_removexattr(struct dentry *dentry, const char *name) int btrfs_removexattr(struct dentry *dentry, const char *name)
{ {
struct btrfs_root *root = BTRFS_I(dentry->d_inode)->root; struct btrfs_root *root = BTRFS_I(dentry->d_inode)->root;
int ret;
/* /*
* The permission on security.* and system.* is not checked * The permission on security.* and system.* is not checked
...@@ -442,8 +466,9 @@ int btrfs_removexattr(struct dentry *dentry, const char *name) ...@@ -442,8 +466,9 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return generic_removexattr(dentry, name); return generic_removexattr(dentry, name);
if (!btrfs_is_valid_xattr(name)) ret = btrfs_is_valid_xattr(name);
return -EOPNOTSUPP; if (ret)
return ret;
if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN)) if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
return btrfs_set_prop(dentry->d_inode, name, return btrfs_set_prop(dentry->d_inode, name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment