1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/fs.h> |
4 | #include <linux/xattr.h> |
5 | #include "overlayfs.h" |
6 | |
7 | static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name) |
8 | { |
9 | struct ovl_fs *ofs = sb->s_fs_info; |
10 | |
11 | if (ofs->config.userxattr) |
12 | return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX, |
13 | OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0; |
14 | else |
15 | return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX, |
16 | OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0; |
17 | } |
18 | |
19 | static bool ovl_is_own_xattr(struct super_block *sb, const char *name) |
20 | { |
21 | struct ovl_fs *ofs = OVL_FS(sb); |
22 | |
23 | if (ofs->config.userxattr) |
24 | return strncmp(name, OVL_XATTR_USER_PREFIX, |
25 | OVL_XATTR_USER_PREFIX_LEN) == 0; |
26 | else |
27 | return strncmp(name, OVL_XATTR_TRUSTED_PREFIX, |
28 | OVL_XATTR_TRUSTED_PREFIX_LEN) == 0; |
29 | } |
30 | |
31 | bool ovl_is_private_xattr(struct super_block *sb, const char *name) |
32 | { |
33 | return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name); |
34 | } |
35 | |
36 | static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, |
37 | const void *value, size_t size, int flags) |
38 | { |
39 | int err; |
40 | struct ovl_fs *ofs = OVL_FS(sb: dentry->d_sb); |
41 | struct dentry *upperdentry = ovl_i_dentry_upper(inode); |
42 | struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); |
43 | struct path realpath; |
44 | const struct cred *old_cred; |
45 | |
46 | if (!value && !upperdentry) { |
47 | ovl_path_lower(dentry, path: &realpath); |
48 | old_cred = ovl_override_creds(sb: dentry->d_sb); |
49 | err = vfs_getxattr(mnt_idmap(mnt: realpath.mnt), realdentry, name, NULL, 0); |
50 | revert_creds(old_cred); |
51 | if (err < 0) |
52 | goto out; |
53 | } |
54 | |
55 | if (!upperdentry) { |
56 | err = ovl_copy_up(dentry); |
57 | if (err) |
58 | goto out; |
59 | |
60 | realdentry = ovl_dentry_upper(dentry); |
61 | } |
62 | |
63 | err = ovl_want_write(dentry); |
64 | if (err) |
65 | goto out; |
66 | |
67 | old_cred = ovl_override_creds(sb: dentry->d_sb); |
68 | if (value) { |
69 | err = ovl_do_setxattr(ofs, dentry: realdentry, name, value, size, |
70 | flags); |
71 | } else { |
72 | WARN_ON(flags != XATTR_REPLACE); |
73 | err = ovl_do_removexattr(ofs, dentry: realdentry, name); |
74 | } |
75 | revert_creds(old_cred); |
76 | ovl_drop_write(dentry); |
77 | |
78 | /* copy c/mtime */ |
79 | ovl_copyattr(to: inode); |
80 | out: |
81 | return err; |
82 | } |
83 | |
84 | static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, |
85 | void *value, size_t size) |
86 | { |
87 | ssize_t res; |
88 | const struct cred *old_cred; |
89 | struct path realpath; |
90 | |
91 | ovl_i_path_real(inode, path: &realpath); |
92 | old_cred = ovl_override_creds(sb: dentry->d_sb); |
93 | res = vfs_getxattr(mnt_idmap(mnt: realpath.mnt), realpath.dentry, name, value, size); |
94 | revert_creds(old_cred); |
95 | return res; |
96 | } |
97 | |
98 | static bool ovl_can_list(struct super_block *sb, const char *s) |
99 | { |
100 | /* Never list private (.overlay) */ |
101 | if (ovl_is_private_xattr(sb, name: s)) |
102 | return false; |
103 | |
104 | /* List all non-trusted xattrs */ |
105 | if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0) |
106 | return true; |
107 | |
108 | /* list other trusted for superuser only */ |
109 | return ns_capable_noaudit(ns: &init_user_ns, CAP_SYS_ADMIN); |
110 | } |
111 | |
112 | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) |
113 | { |
114 | struct dentry *realdentry = ovl_dentry_real(dentry); |
115 | struct ovl_fs *ofs = OVL_FS(sb: dentry->d_sb); |
116 | ssize_t res; |
117 | size_t len; |
118 | char *s; |
119 | const struct cred *old_cred; |
120 | size_t prefix_len, name_len; |
121 | |
122 | old_cred = ovl_override_creds(sb: dentry->d_sb); |
123 | res = vfs_listxattr(d: realdentry, list, size); |
124 | revert_creds(old_cred); |
125 | if (res <= 0 || size == 0) |
126 | return res; |
127 | |
128 | prefix_len = ofs->config.userxattr ? |
129 | OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN; |
130 | |
131 | /* filter out private xattrs */ |
132 | for (s = list, len = res; len;) { |
133 | size_t slen = strnlen(p: s, maxlen: len) + 1; |
134 | |
135 | /* underlying fs providing us with an broken xattr list? */ |
136 | if (WARN_ON(slen > len)) |
137 | return -EIO; |
138 | |
139 | len -= slen; |
140 | if (!ovl_can_list(sb: dentry->d_sb, s)) { |
141 | res -= slen; |
142 | memmove(s, s + slen, len); |
143 | } else if (ovl_is_escaped_xattr(sb: dentry->d_sb, name: s)) { |
144 | res -= OVL_XATTR_ESCAPE_PREFIX_LEN; |
145 | name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN; |
146 | s += prefix_len; |
147 | memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len); |
148 | s += name_len; |
149 | } else { |
150 | s += slen; |
151 | } |
152 | } |
153 | |
154 | return res; |
155 | } |
156 | |
157 | static char *ovl_xattr_escape_name(const char *prefix, const char *name) |
158 | { |
159 | size_t prefix_len = strlen(prefix); |
160 | size_t name_len = strlen(name); |
161 | size_t escaped_len; |
162 | char *escaped, *s; |
163 | |
164 | escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len; |
165 | if (escaped_len > XATTR_NAME_MAX) |
166 | return ERR_PTR(error: -EOPNOTSUPP); |
167 | |
168 | escaped = kmalloc(size: escaped_len + 1, GFP_KERNEL); |
169 | if (escaped == NULL) |
170 | return ERR_PTR(error: -ENOMEM); |
171 | |
172 | s = escaped; |
173 | memcpy(s, prefix, prefix_len); |
174 | s += prefix_len; |
175 | memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN); |
176 | s += OVL_XATTR_ESCAPE_PREFIX_LEN; |
177 | memcpy(s, name, name_len + 1); |
178 | |
179 | return escaped; |
180 | } |
181 | |
182 | static int ovl_own_xattr_get(const struct xattr_handler *handler, |
183 | struct dentry *dentry, struct inode *inode, |
184 | const char *name, void *buffer, size_t size) |
185 | { |
186 | char *escaped; |
187 | int r; |
188 | |
189 | escaped = ovl_xattr_escape_name(prefix: handler->prefix, name); |
190 | if (IS_ERR(ptr: escaped)) |
191 | return PTR_ERR(ptr: escaped); |
192 | |
193 | r = ovl_xattr_get(dentry, inode, name: escaped, value: buffer, size); |
194 | |
195 | kfree(objp: escaped); |
196 | |
197 | return r; |
198 | } |
199 | |
200 | static int ovl_own_xattr_set(const struct xattr_handler *handler, |
201 | struct mnt_idmap *idmap, |
202 | struct dentry *dentry, struct inode *inode, |
203 | const char *name, const void *value, |
204 | size_t size, int flags) |
205 | { |
206 | char *escaped; |
207 | int r; |
208 | |
209 | escaped = ovl_xattr_escape_name(prefix: handler->prefix, name); |
210 | if (IS_ERR(ptr: escaped)) |
211 | return PTR_ERR(ptr: escaped); |
212 | |
213 | r = ovl_xattr_set(dentry, inode, name: escaped, value, size, flags); |
214 | |
215 | kfree(objp: escaped); |
216 | |
217 | return r; |
218 | } |
219 | |
220 | static int ovl_other_xattr_get(const struct xattr_handler *handler, |
221 | struct dentry *dentry, struct inode *inode, |
222 | const char *name, void *buffer, size_t size) |
223 | { |
224 | return ovl_xattr_get(dentry, inode, name, value: buffer, size); |
225 | } |
226 | |
227 | static int ovl_other_xattr_set(const struct xattr_handler *handler, |
228 | struct mnt_idmap *idmap, |
229 | struct dentry *dentry, struct inode *inode, |
230 | const char *name, const void *value, |
231 | size_t size, int flags) |
232 | { |
233 | return ovl_xattr_set(dentry, inode, name, value, size, flags); |
234 | } |
235 | |
236 | static const struct xattr_handler ovl_own_trusted_xattr_handler = { |
237 | .prefix = OVL_XATTR_TRUSTED_PREFIX, |
238 | .get = ovl_own_xattr_get, |
239 | .set = ovl_own_xattr_set, |
240 | }; |
241 | |
242 | static const struct xattr_handler ovl_own_user_xattr_handler = { |
243 | .prefix = OVL_XATTR_USER_PREFIX, |
244 | .get = ovl_own_xattr_get, |
245 | .set = ovl_own_xattr_set, |
246 | }; |
247 | |
248 | static const struct xattr_handler ovl_other_xattr_handler = { |
249 | .prefix = "" , /* catch all */ |
250 | .get = ovl_other_xattr_get, |
251 | .set = ovl_other_xattr_set, |
252 | }; |
253 | |
254 | static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = { |
255 | &ovl_own_trusted_xattr_handler, |
256 | &ovl_other_xattr_handler, |
257 | NULL |
258 | }; |
259 | |
260 | static const struct xattr_handler * const ovl_user_xattr_handlers[] = { |
261 | &ovl_own_user_xattr_handler, |
262 | &ovl_other_xattr_handler, |
263 | NULL |
264 | }; |
265 | |
266 | const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs) |
267 | { |
268 | return ofs->config.userxattr ? ovl_user_xattr_handlers : |
269 | ovl_trusted_xattr_handlers; |
270 | } |
271 | |
272 | |