xref: /linux/fs/overlayfs/xattrs.c (revision dad02fad)
1420a62ddSAmir Goldstein // SPDX-License-Identifier: GPL-2.0-only
2420a62ddSAmir Goldstein 
3420a62ddSAmir Goldstein #include <linux/fs.h>
4420a62ddSAmir Goldstein #include <linux/xattr.h>
5420a62ddSAmir Goldstein #include "overlayfs.h"
6420a62ddSAmir Goldstein 
ovl_is_escaped_xattr(struct super_block * sb,const char * name)7*dad02fadSAlexander Larsson static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
8*dad02fadSAlexander Larsson {
9*dad02fadSAlexander Larsson 	struct ovl_fs *ofs = sb->s_fs_info;
10*dad02fadSAlexander Larsson 
11*dad02fadSAlexander Larsson 	if (ofs->config.userxattr)
12*dad02fadSAlexander Larsson 		return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX,
13*dad02fadSAlexander Larsson 			       OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
14*dad02fadSAlexander Larsson 	else
15*dad02fadSAlexander Larsson 		return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
16*dad02fadSAlexander Larsson 			       OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
17*dad02fadSAlexander Larsson }
18*dad02fadSAlexander Larsson 
ovl_is_own_xattr(struct super_block * sb,const char * name)19*dad02fadSAlexander Larsson static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
20420a62ddSAmir Goldstein {
21420a62ddSAmir Goldstein 	struct ovl_fs *ofs = OVL_FS(sb);
22420a62ddSAmir Goldstein 
23420a62ddSAmir Goldstein 	if (ofs->config.userxattr)
24420a62ddSAmir Goldstein 		return strncmp(name, OVL_XATTR_USER_PREFIX,
25d431e652SAlexander Larsson 			       OVL_XATTR_USER_PREFIX_LEN) == 0;
26420a62ddSAmir Goldstein 	else
27420a62ddSAmir Goldstein 		return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
28d431e652SAlexander Larsson 			       OVL_XATTR_TRUSTED_PREFIX_LEN) == 0;
29420a62ddSAmir Goldstein }
30420a62ddSAmir Goldstein 
ovl_is_private_xattr(struct super_block * sb,const char * name)31*dad02fadSAlexander Larsson bool ovl_is_private_xattr(struct super_block *sb, const char *name)
32*dad02fadSAlexander Larsson {
33*dad02fadSAlexander Larsson 	return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name);
34*dad02fadSAlexander Larsson }
35*dad02fadSAlexander Larsson 
ovl_xattr_set(struct dentry * dentry,struct inode * inode,const char * name,const void * value,size_t size,int flags)36420a62ddSAmir Goldstein static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
37420a62ddSAmir Goldstein 			 const void *value, size_t size, int flags)
38420a62ddSAmir Goldstein {
39420a62ddSAmir Goldstein 	int err;
40420a62ddSAmir Goldstein 	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
41420a62ddSAmir Goldstein 	struct dentry *upperdentry = ovl_i_dentry_upper(inode);
42420a62ddSAmir Goldstein 	struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
43420a62ddSAmir Goldstein 	struct path realpath;
44420a62ddSAmir Goldstein 	const struct cred *old_cred;
45420a62ddSAmir Goldstein 
46420a62ddSAmir Goldstein 	if (!value && !upperdentry) {
47420a62ddSAmir Goldstein 		ovl_path_lower(dentry, &realpath);
48420a62ddSAmir Goldstein 		old_cred = ovl_override_creds(dentry->d_sb);
49420a62ddSAmir Goldstein 		err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
50420a62ddSAmir Goldstein 		revert_creds(old_cred);
51420a62ddSAmir Goldstein 		if (err < 0)
52420a62ddSAmir Goldstein 			goto out;
53420a62ddSAmir Goldstein 	}
54420a62ddSAmir Goldstein 
55420a62ddSAmir Goldstein 	if (!upperdentry) {
56420a62ddSAmir Goldstein 		err = ovl_copy_up(dentry);
57420a62ddSAmir Goldstein 		if (err)
58420a62ddSAmir Goldstein 			goto out;
59420a62ddSAmir Goldstein 
60420a62ddSAmir Goldstein 		realdentry = ovl_dentry_upper(dentry);
61420a62ddSAmir Goldstein 	}
62420a62ddSAmir Goldstein 
63420a62ddSAmir Goldstein 	err = ovl_want_write(dentry);
64420a62ddSAmir Goldstein 	if (err)
65420a62ddSAmir Goldstein 		goto out;
66420a62ddSAmir Goldstein 
67420a62ddSAmir Goldstein 	old_cred = ovl_override_creds(dentry->d_sb);
68420a62ddSAmir Goldstein 	if (value) {
69420a62ddSAmir Goldstein 		err = ovl_do_setxattr(ofs, realdentry, name, value, size,
70420a62ddSAmir Goldstein 				      flags);
71420a62ddSAmir Goldstein 	} else {
72420a62ddSAmir Goldstein 		WARN_ON(flags != XATTR_REPLACE);
73420a62ddSAmir Goldstein 		err = ovl_do_removexattr(ofs, realdentry, name);
74420a62ddSAmir Goldstein 	}
75420a62ddSAmir Goldstein 	revert_creds(old_cred);
76420a62ddSAmir Goldstein 	ovl_drop_write(dentry);
77420a62ddSAmir Goldstein 
78420a62ddSAmir Goldstein 	/* copy c/mtime */
79420a62ddSAmir Goldstein 	ovl_copyattr(inode);
80420a62ddSAmir Goldstein out:
81420a62ddSAmir Goldstein 	return err;
82420a62ddSAmir Goldstein }
83420a62ddSAmir Goldstein 
ovl_xattr_get(struct dentry * dentry,struct inode * inode,const char * name,void * value,size_t size)84420a62ddSAmir Goldstein static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
85420a62ddSAmir Goldstein 			 void *value, size_t size)
86420a62ddSAmir Goldstein {
87420a62ddSAmir Goldstein 	ssize_t res;
88420a62ddSAmir Goldstein 	const struct cred *old_cred;
89420a62ddSAmir Goldstein 	struct path realpath;
90420a62ddSAmir Goldstein 
91420a62ddSAmir Goldstein 	ovl_i_path_real(inode, &realpath);
92420a62ddSAmir Goldstein 	old_cred = ovl_override_creds(dentry->d_sb);
93420a62ddSAmir Goldstein 	res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
94420a62ddSAmir Goldstein 	revert_creds(old_cred);
95420a62ddSAmir Goldstein 	return res;
96420a62ddSAmir Goldstein }
97420a62ddSAmir Goldstein 
ovl_can_list(struct super_block * sb,const char * s)98420a62ddSAmir Goldstein static bool ovl_can_list(struct super_block *sb, const char *s)
99420a62ddSAmir Goldstein {
100420a62ddSAmir Goldstein 	/* Never list private (.overlay) */
101420a62ddSAmir Goldstein 	if (ovl_is_private_xattr(sb, s))
102420a62ddSAmir Goldstein 		return false;
103420a62ddSAmir Goldstein 
104420a62ddSAmir Goldstein 	/* List all non-trusted xattrs */
105420a62ddSAmir Goldstein 	if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
106420a62ddSAmir Goldstein 		return true;
107420a62ddSAmir Goldstein 
108420a62ddSAmir Goldstein 	/* list other trusted for superuser only */
109420a62ddSAmir Goldstein 	return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
110420a62ddSAmir Goldstein }
111420a62ddSAmir Goldstein 
ovl_listxattr(struct dentry * dentry,char * list,size_t size)112420a62ddSAmir Goldstein ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
113420a62ddSAmir Goldstein {
114420a62ddSAmir Goldstein 	struct dentry *realdentry = ovl_dentry_real(dentry);
115*dad02fadSAlexander Larsson 	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
116420a62ddSAmir Goldstein 	ssize_t res;
117420a62ddSAmir Goldstein 	size_t len;
118420a62ddSAmir Goldstein 	char *s;
119420a62ddSAmir Goldstein 	const struct cred *old_cred;
120*dad02fadSAlexander Larsson 	size_t prefix_len, name_len;
121420a62ddSAmir Goldstein 
122420a62ddSAmir Goldstein 	old_cred = ovl_override_creds(dentry->d_sb);
123420a62ddSAmir Goldstein 	res = vfs_listxattr(realdentry, list, size);
124420a62ddSAmir Goldstein 	revert_creds(old_cred);
125420a62ddSAmir Goldstein 	if (res <= 0 || size == 0)
126420a62ddSAmir Goldstein 		return res;
127420a62ddSAmir Goldstein 
128*dad02fadSAlexander Larsson 	prefix_len = ofs->config.userxattr ?
129*dad02fadSAlexander Larsson 		OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN;
130*dad02fadSAlexander Larsson 
131420a62ddSAmir Goldstein 	/* filter out private xattrs */
132420a62ddSAmir Goldstein 	for (s = list, len = res; len;) {
133420a62ddSAmir Goldstein 		size_t slen = strnlen(s, len) + 1;
134420a62ddSAmir Goldstein 
135420a62ddSAmir Goldstein 		/* underlying fs providing us with an broken xattr list? */
136420a62ddSAmir Goldstein 		if (WARN_ON(slen > len))
137420a62ddSAmir Goldstein 			return -EIO;
138420a62ddSAmir Goldstein 
139420a62ddSAmir Goldstein 		len -= slen;
140420a62ddSAmir Goldstein 		if (!ovl_can_list(dentry->d_sb, s)) {
141420a62ddSAmir Goldstein 			res -= slen;
142420a62ddSAmir Goldstein 			memmove(s, s + slen, len);
143*dad02fadSAlexander Larsson 		} else if (ovl_is_escaped_xattr(dentry->d_sb, s)) {
144*dad02fadSAlexander Larsson 			res -= OVL_XATTR_ESCAPE_PREFIX_LEN;
145*dad02fadSAlexander Larsson 			name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN;
146*dad02fadSAlexander Larsson 			s += prefix_len;
147*dad02fadSAlexander Larsson 			memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len);
148*dad02fadSAlexander Larsson 			s += name_len;
149420a62ddSAmir Goldstein 		} else {
150420a62ddSAmir Goldstein 			s += slen;
151420a62ddSAmir Goldstein 		}
152420a62ddSAmir Goldstein 	}
153420a62ddSAmir Goldstein 
154420a62ddSAmir Goldstein 	return res;
155420a62ddSAmir Goldstein }
156420a62ddSAmir Goldstein 
ovl_xattr_escape_name(const char * prefix,const char * name)157*dad02fadSAlexander Larsson static char *ovl_xattr_escape_name(const char *prefix, const char *name)
158*dad02fadSAlexander Larsson {
159*dad02fadSAlexander Larsson 	size_t prefix_len = strlen(prefix);
160*dad02fadSAlexander Larsson 	size_t name_len = strlen(name);
161*dad02fadSAlexander Larsson 	size_t escaped_len;
162*dad02fadSAlexander Larsson 	char *escaped, *s;
163*dad02fadSAlexander Larsson 
164*dad02fadSAlexander Larsson 	escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len;
165*dad02fadSAlexander Larsson 	if (escaped_len > XATTR_NAME_MAX)
166*dad02fadSAlexander Larsson 		return ERR_PTR(-EOPNOTSUPP);
167*dad02fadSAlexander Larsson 
168*dad02fadSAlexander Larsson 	escaped = kmalloc(escaped_len + 1, GFP_KERNEL);
169*dad02fadSAlexander Larsson 	if (escaped == NULL)
170*dad02fadSAlexander Larsson 		return ERR_PTR(-ENOMEM);
171*dad02fadSAlexander Larsson 
172*dad02fadSAlexander Larsson 	s = escaped;
173*dad02fadSAlexander Larsson 	memcpy(s, prefix, prefix_len);
174*dad02fadSAlexander Larsson 	s += prefix_len;
175*dad02fadSAlexander Larsson 	memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN);
176*dad02fadSAlexander Larsson 	s += OVL_XATTR_ESCAPE_PREFIX_LEN;
177*dad02fadSAlexander Larsson 	memcpy(s, name, name_len + 1);
178*dad02fadSAlexander Larsson 
179*dad02fadSAlexander Larsson 	return escaped;
180*dad02fadSAlexander Larsson }
181*dad02fadSAlexander Larsson 
ovl_own_xattr_get(const struct xattr_handler * handler,struct dentry * dentry,struct inode * inode,const char * name,void * buffer,size_t size)182420a62ddSAmir Goldstein static int ovl_own_xattr_get(const struct xattr_handler *handler,
183420a62ddSAmir Goldstein 			     struct dentry *dentry, struct inode *inode,
184420a62ddSAmir Goldstein 			     const char *name, void *buffer, size_t size)
185420a62ddSAmir Goldstein {
186*dad02fadSAlexander Larsson 	char *escaped;
187*dad02fadSAlexander Larsson 	int r;
188*dad02fadSAlexander Larsson 
189*dad02fadSAlexander Larsson 	escaped = ovl_xattr_escape_name(handler->prefix, name);
190*dad02fadSAlexander Larsson 	if (IS_ERR(escaped))
191*dad02fadSAlexander Larsson 		return PTR_ERR(escaped);
192*dad02fadSAlexander Larsson 
193*dad02fadSAlexander Larsson 	r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
194*dad02fadSAlexander Larsson 
195*dad02fadSAlexander Larsson 	kfree(escaped);
196*dad02fadSAlexander Larsson 
197*dad02fadSAlexander Larsson 	return r;
198420a62ddSAmir Goldstein }
199420a62ddSAmir Goldstein 
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)200420a62ddSAmir Goldstein static int ovl_own_xattr_set(const struct xattr_handler *handler,
201420a62ddSAmir Goldstein 			     struct mnt_idmap *idmap,
202420a62ddSAmir Goldstein 			     struct dentry *dentry, struct inode *inode,
203420a62ddSAmir Goldstein 			     const char *name, const void *value,
204420a62ddSAmir Goldstein 			     size_t size, int flags)
205420a62ddSAmir Goldstein {
206*dad02fadSAlexander Larsson 	char *escaped;
207*dad02fadSAlexander Larsson 	int r;
208*dad02fadSAlexander Larsson 
209*dad02fadSAlexander Larsson 	escaped = ovl_xattr_escape_name(handler->prefix, name);
210*dad02fadSAlexander Larsson 	if (IS_ERR(escaped))
211*dad02fadSAlexander Larsson 		return PTR_ERR(escaped);
212*dad02fadSAlexander Larsson 
213*dad02fadSAlexander Larsson 	r = ovl_xattr_set(dentry, inode, escaped, value, size, flags);
214*dad02fadSAlexander Larsson 
215*dad02fadSAlexander Larsson 	kfree(escaped);
216*dad02fadSAlexander Larsson 
217*dad02fadSAlexander Larsson 	return r;
218420a62ddSAmir Goldstein }
219420a62ddSAmir Goldstein 
ovl_other_xattr_get(const struct xattr_handler * handler,struct dentry * dentry,struct inode * inode,const char * name,void * buffer,size_t size)220420a62ddSAmir Goldstein static int ovl_other_xattr_get(const struct xattr_handler *handler,
221420a62ddSAmir Goldstein 			       struct dentry *dentry, struct inode *inode,
222420a62ddSAmir Goldstein 			       const char *name, void *buffer, size_t size)
223420a62ddSAmir Goldstein {
224420a62ddSAmir Goldstein 	return ovl_xattr_get(dentry, inode, name, buffer, size);
225420a62ddSAmir Goldstein }
226420a62ddSAmir Goldstein 
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)227420a62ddSAmir Goldstein static int ovl_other_xattr_set(const struct xattr_handler *handler,
228420a62ddSAmir Goldstein 			       struct mnt_idmap *idmap,
229420a62ddSAmir Goldstein 			       struct dentry *dentry, struct inode *inode,
230420a62ddSAmir Goldstein 			       const char *name, const void *value,
231420a62ddSAmir Goldstein 			       size_t size, int flags)
232420a62ddSAmir Goldstein {
233420a62ddSAmir Goldstein 	return ovl_xattr_set(dentry, inode, name, value, size, flags);
234420a62ddSAmir Goldstein }
235420a62ddSAmir Goldstein 
236420a62ddSAmir Goldstein static const struct xattr_handler ovl_own_trusted_xattr_handler = {
237420a62ddSAmir Goldstein 	.prefix	= OVL_XATTR_TRUSTED_PREFIX,
238420a62ddSAmir Goldstein 	.get = ovl_own_xattr_get,
239420a62ddSAmir Goldstein 	.set = ovl_own_xattr_set,
240420a62ddSAmir Goldstein };
241420a62ddSAmir Goldstein 
242420a62ddSAmir Goldstein static const struct xattr_handler ovl_own_user_xattr_handler = {
243420a62ddSAmir Goldstein 	.prefix	= OVL_XATTR_USER_PREFIX,
244420a62ddSAmir Goldstein 	.get = ovl_own_xattr_get,
245420a62ddSAmir Goldstein 	.set = ovl_own_xattr_set,
246420a62ddSAmir Goldstein };
247420a62ddSAmir Goldstein 
248420a62ddSAmir Goldstein static const struct xattr_handler ovl_other_xattr_handler = {
249420a62ddSAmir Goldstein 	.prefix	= "", /* catch all */
250420a62ddSAmir Goldstein 	.get = ovl_other_xattr_get,
251420a62ddSAmir Goldstein 	.set = ovl_other_xattr_set,
252420a62ddSAmir Goldstein };
253420a62ddSAmir Goldstein 
254420a62ddSAmir Goldstein static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
255420a62ddSAmir Goldstein 	&ovl_own_trusted_xattr_handler,
256420a62ddSAmir Goldstein 	&ovl_other_xattr_handler,
257420a62ddSAmir Goldstein 	NULL
258420a62ddSAmir Goldstein };
259420a62ddSAmir Goldstein 
260420a62ddSAmir Goldstein static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
261420a62ddSAmir Goldstein 	&ovl_own_user_xattr_handler,
262420a62ddSAmir Goldstein 	&ovl_other_xattr_handler,
263420a62ddSAmir Goldstein 	NULL
264420a62ddSAmir Goldstein };
265420a62ddSAmir Goldstein 
ovl_xattr_handlers(struct ovl_fs * ofs)266420a62ddSAmir Goldstein const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
267420a62ddSAmir Goldstein {
268420a62ddSAmir Goldstein 	return ofs->config.userxattr ? ovl_user_xattr_handlers :
269420a62ddSAmir Goldstein 		ovl_trusted_xattr_handlers;
270420a62ddSAmir Goldstein }
271420a62ddSAmir Goldstein 
272