|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  |  | 
|  | #include <linux/fs.h> | 
|  | #include <linux/xattr.h> | 
|  | #include "overlayfs.h" | 
|  |  | 
|  | static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name) | 
|  | { | 
|  | struct ovl_fs *ofs = sb->s_fs_info; | 
|  |  | 
|  | if (ofs->config.userxattr) | 
|  | return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX, | 
|  | OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0; | 
|  | else | 
|  | return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX, | 
|  | OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0; | 
|  | } | 
|  |  | 
|  | static bool ovl_is_own_xattr(struct super_block *sb, const char *name) | 
|  | { | 
|  | struct ovl_fs *ofs = OVL_FS(sb); | 
|  |  | 
|  | if (ofs->config.userxattr) | 
|  | return strncmp(name, OVL_XATTR_USER_PREFIX, | 
|  | OVL_XATTR_USER_PREFIX_LEN) == 0; | 
|  | else | 
|  | return strncmp(name, OVL_XATTR_TRUSTED_PREFIX, | 
|  | OVL_XATTR_TRUSTED_PREFIX_LEN) == 0; | 
|  | } | 
|  |  | 
|  | bool ovl_is_private_xattr(struct super_block *sb, const char *name) | 
|  | { | 
|  | return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name); | 
|  | } | 
|  |  | 
|  | static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, | 
|  | const void *value, size_t size, int flags) | 
|  | { | 
|  | int err; | 
|  | struct ovl_fs *ofs = OVL_FS(dentry->d_sb); | 
|  | struct dentry *upperdentry = ovl_i_dentry_upper(inode); | 
|  | struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); | 
|  | struct path realpath; | 
|  | const struct cred *old_cred; | 
|  |  | 
|  | if (!value && !upperdentry) { | 
|  | ovl_path_lower(dentry, &realpath); | 
|  | old_cred = ovl_override_creds(dentry->d_sb); | 
|  | err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0); | 
|  | revert_creds(old_cred); | 
|  | if (err < 0) | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!upperdentry) { | 
|  | err = ovl_copy_up(dentry); | 
|  | if (err) | 
|  | goto out; | 
|  |  | 
|  | realdentry = ovl_dentry_upper(dentry); | 
|  | } | 
|  |  | 
|  | err = ovl_want_write(dentry); | 
|  | if (err) | 
|  | goto out; | 
|  |  | 
|  | old_cred = ovl_override_creds(dentry->d_sb); | 
|  | if (value) { | 
|  | err = ovl_do_setxattr(ofs, realdentry, name, value, size, | 
|  | flags); | 
|  | } else { | 
|  | WARN_ON(flags != XATTR_REPLACE); | 
|  | err = ovl_do_removexattr(ofs, realdentry, name); | 
|  | } | 
|  | revert_creds(old_cred); | 
|  | ovl_drop_write(dentry); | 
|  |  | 
|  | /* copy c/mtime */ | 
|  | ovl_copyattr(inode); | 
|  | out: | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, | 
|  | void *value, size_t size) | 
|  | { | 
|  | ssize_t res; | 
|  | const struct cred *old_cred; | 
|  | struct path realpath; | 
|  |  | 
|  | ovl_i_path_real(inode, &realpath); | 
|  | old_cred = ovl_override_creds(dentry->d_sb); | 
|  | res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size); | 
|  | revert_creds(old_cred); | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static bool ovl_can_list(struct super_block *sb, const char *s) | 
|  | { | 
|  | /* Never list private (.overlay) */ | 
|  | if (ovl_is_private_xattr(sb, s)) | 
|  | return false; | 
|  |  | 
|  | /* List all non-trusted xattrs */ | 
|  | if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0) | 
|  | return true; | 
|  |  | 
|  | /* list other trusted for superuser only */ | 
|  | return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN); | 
|  | } | 
|  |  | 
|  | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) | 
|  | { | 
|  | struct dentry *realdentry = ovl_dentry_real(dentry); | 
|  | struct ovl_fs *ofs = OVL_FS(dentry->d_sb); | 
|  | ssize_t res; | 
|  | size_t len; | 
|  | char *s; | 
|  | const struct cred *old_cred; | 
|  | size_t prefix_len, name_len; | 
|  |  | 
|  | old_cred = ovl_override_creds(dentry->d_sb); | 
|  | res = vfs_listxattr(realdentry, list, size); | 
|  | revert_creds(old_cred); | 
|  | if (res <= 0 || size == 0) | 
|  | return res; | 
|  |  | 
|  | prefix_len = ofs->config.userxattr ? | 
|  | OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN; | 
|  |  | 
|  | /* filter out private xattrs */ | 
|  | for (s = list, len = res; len;) { | 
|  | size_t slen = strnlen(s, len) + 1; | 
|  |  | 
|  | /* underlying fs providing us with an broken xattr list? */ | 
|  | if (WARN_ON(slen > len)) | 
|  | return -EIO; | 
|  |  | 
|  | len -= slen; | 
|  | if (!ovl_can_list(dentry->d_sb, s)) { | 
|  | res -= slen; | 
|  | memmove(s, s + slen, len); | 
|  | } else if (ovl_is_escaped_xattr(dentry->d_sb, s)) { | 
|  | res -= OVL_XATTR_ESCAPE_PREFIX_LEN; | 
|  | name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN; | 
|  | s += prefix_len; | 
|  | memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len); | 
|  | s += name_len; | 
|  | } else { | 
|  | s += slen; | 
|  | } | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static char *ovl_xattr_escape_name(const char *prefix, const char *name) | 
|  | { | 
|  | size_t prefix_len = strlen(prefix); | 
|  | size_t name_len = strlen(name); | 
|  | size_t escaped_len; | 
|  | char *escaped, *s; | 
|  |  | 
|  | escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len; | 
|  | if (escaped_len > XATTR_NAME_MAX) | 
|  | return ERR_PTR(-EOPNOTSUPP); | 
|  |  | 
|  | escaped = kmalloc(escaped_len + 1, GFP_KERNEL); | 
|  | if (escaped == NULL) | 
|  | return ERR_PTR(-ENOMEM); | 
|  |  | 
|  | s = escaped; | 
|  | memcpy(s, prefix, prefix_len); | 
|  | s += prefix_len; | 
|  | memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN); | 
|  | s += OVL_XATTR_ESCAPE_PREFIX_LEN; | 
|  | memcpy(s, name, name_len + 1); | 
|  |  | 
|  | return escaped; | 
|  | } | 
|  |  | 
|  | static int ovl_own_xattr_get(const struct xattr_handler *handler, | 
|  | struct dentry *dentry, struct inode *inode, | 
|  | const char *name, void *buffer, size_t size) | 
|  | { | 
|  | char *escaped; | 
|  | int r; | 
|  |  | 
|  | escaped = ovl_xattr_escape_name(handler->prefix, name); | 
|  | if (IS_ERR(escaped)) | 
|  | return PTR_ERR(escaped); | 
|  |  | 
|  | r = ovl_xattr_get(dentry, inode, escaped, buffer, size); | 
|  |  | 
|  | kfree(escaped); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static int ovl_own_xattr_set(const struct xattr_handler *handler, | 
|  | struct mnt_idmap *idmap, | 
|  | struct dentry *dentry, struct inode *inode, | 
|  | const char *name, const void *value, | 
|  | size_t size, int flags) | 
|  | { | 
|  | char *escaped; | 
|  | int r; | 
|  |  | 
|  | escaped = ovl_xattr_escape_name(handler->prefix, name); | 
|  | if (IS_ERR(escaped)) | 
|  | return PTR_ERR(escaped); | 
|  |  | 
|  | r = ovl_xattr_set(dentry, inode, escaped, value, size, flags); | 
|  |  | 
|  | kfree(escaped); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static int ovl_other_xattr_get(const struct xattr_handler *handler, | 
|  | struct dentry *dentry, struct inode *inode, | 
|  | const char *name, void *buffer, size_t size) | 
|  | { | 
|  | return ovl_xattr_get(dentry, inode, name, buffer, size); | 
|  | } | 
|  |  | 
|  | static int ovl_other_xattr_set(const struct xattr_handler *handler, | 
|  | struct mnt_idmap *idmap, | 
|  | struct dentry *dentry, struct inode *inode, | 
|  | const char *name, const void *value, | 
|  | size_t size, int flags) | 
|  | { | 
|  | return ovl_xattr_set(dentry, inode, name, value, size, flags); | 
|  | } | 
|  |  | 
|  | static const struct xattr_handler ovl_own_trusted_xattr_handler = { | 
|  | .prefix	= OVL_XATTR_TRUSTED_PREFIX, | 
|  | .get = ovl_own_xattr_get, | 
|  | .set = ovl_own_xattr_set, | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler ovl_own_user_xattr_handler = { | 
|  | .prefix	= OVL_XATTR_USER_PREFIX, | 
|  | .get = ovl_own_xattr_get, | 
|  | .set = ovl_own_xattr_set, | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler ovl_other_xattr_handler = { | 
|  | .prefix	= "", /* catch all */ | 
|  | .get = ovl_other_xattr_get, | 
|  | .set = ovl_other_xattr_set, | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = { | 
|  | &ovl_own_trusted_xattr_handler, | 
|  | &ovl_other_xattr_handler, | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | static const struct xattr_handler * const ovl_user_xattr_handlers[] = { | 
|  | &ovl_own_user_xattr_handler, | 
|  | &ovl_other_xattr_handler, | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs) | 
|  | { | 
|  | return ofs->config.userxattr ? ovl_user_xattr_handlers : | 
|  | ovl_trusted_xattr_handlers; | 
|  | } | 
|  |  |