1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | /* |
3 | * |
4 | * Copyright (c) International Business Machines Corp., 2003, 2007 |
5 | * Author(s): Steve French (sfrench@us.ibm.com) |
6 | * |
7 | */ |
8 | |
9 | #include <linux/fs.h> |
10 | #include <linux/posix_acl_xattr.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/xattr.h> |
13 | #include "cifsfs.h" |
14 | #include "cifspdu.h" |
15 | #include "cifsglob.h" |
16 | #include "cifsproto.h" |
17 | #include "cifs_debug.h" |
18 | #include "cifs_fs_sb.h" |
19 | #include "cifs_unicode.h" |
20 | #include "cifs_ioctl.h" |
21 | |
22 | #define MAX_EA_VALUE_SIZE CIFSMaxBufSize |
23 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ |
24 | #define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ |
25 | #define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ |
26 | #define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ |
27 | #define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ |
28 | /* |
29 | * Although these three are just aliases for the above, need to move away from |
30 | * confusing users and using the 20+ year old term 'cifs' when it is no longer |
31 | * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago |
32 | */ |
33 | #define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ |
34 | #define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ |
35 | #define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ |
36 | #define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ |
37 | #define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ |
38 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ |
39 | |
40 | enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, |
41 | XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; |
42 | |
43 | static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, |
44 | struct inode *inode, const char *full_path, |
45 | const void *value, size_t size) |
46 | { |
47 | ssize_t rc = -EOPNOTSUPP; |
48 | __u32 *pattrib = (__u32 *)value; |
49 | __u32 attrib; |
50 | FILE_BASIC_INFO info_buf; |
51 | |
52 | if ((value == NULL) || (size != sizeof(__u32))) |
53 | return -ERANGE; |
54 | |
55 | memset(&info_buf, 0, sizeof(info_buf)); |
56 | attrib = *pattrib; |
57 | info_buf.Attributes = cpu_to_le32(attrib); |
58 | if (pTcon->ses->server->ops->set_file_info) |
59 | rc = pTcon->ses->server->ops->set_file_info(inode, full_path, |
60 | &info_buf, xid); |
61 | if (rc == 0) |
62 | CIFS_I(inode)->cifsAttrs = attrib; |
63 | |
64 | return rc; |
65 | } |
66 | |
67 | static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, |
68 | struct inode *inode, const char *full_path, |
69 | const void *value, size_t size) |
70 | { |
71 | ssize_t rc = -EOPNOTSUPP; |
72 | __u64 *pcreation_time = (__u64 *)value; |
73 | __u64 creation_time; |
74 | FILE_BASIC_INFO info_buf; |
75 | |
76 | if ((value == NULL) || (size != sizeof(__u64))) |
77 | return -ERANGE; |
78 | |
79 | memset(&info_buf, 0, sizeof(info_buf)); |
80 | creation_time = *pcreation_time; |
81 | info_buf.CreationTime = cpu_to_le64(creation_time); |
82 | if (pTcon->ses->server->ops->set_file_info) |
83 | rc = pTcon->ses->server->ops->set_file_info(inode, full_path, |
84 | &info_buf, xid); |
85 | if (rc == 0) |
86 | CIFS_I(inode)->createtime = creation_time; |
87 | |
88 | return rc; |
89 | } |
90 | |
91 | static int cifs_xattr_set(const struct xattr_handler *handler, |
92 | struct mnt_idmap *idmap, |
93 | struct dentry *dentry, struct inode *inode, |
94 | const char *name, const void *value, |
95 | size_t size, int flags) |
96 | { |
97 | int rc = -EOPNOTSUPP; |
98 | unsigned int xid; |
99 | struct super_block *sb = dentry->d_sb; |
100 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
101 | struct tcon_link *tlink; |
102 | struct cifs_tcon *pTcon; |
103 | const char *full_path; |
104 | void *page; |
105 | |
106 | tlink = cifs_sb_tlink(cifs_sb); |
107 | if (IS_ERR(ptr: tlink)) |
108 | return PTR_ERR(ptr: tlink); |
109 | pTcon = tlink_tcon(tlink); |
110 | |
111 | xid = get_xid(); |
112 | page = alloc_dentry_path(); |
113 | |
114 | full_path = build_path_from_dentry(dentry, page); |
115 | if (IS_ERR(ptr: full_path)) { |
116 | rc = PTR_ERR(ptr: full_path); |
117 | goto out; |
118 | } |
119 | /* return dos attributes as pseudo xattr */ |
120 | /* return alt name if available as pseudo attr */ |
121 | |
122 | /* if proc/fs/cifs/streamstoxattr is set then |
123 | search server for EAs or streams to |
124 | returns as xattrs */ |
125 | if (size > MAX_EA_VALUE_SIZE) { |
126 | cifs_dbg(FYI, "size of EA value too large\n" ); |
127 | rc = -EOPNOTSUPP; |
128 | goto out; |
129 | } |
130 | |
131 | switch (handler->flags) { |
132 | case XATTR_USER: |
133 | cifs_dbg(FYI, "%s:setting user xattr %s\n" , __func__, name); |
134 | if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || |
135 | (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { |
136 | rc = cifs_attrib_set(xid, pTcon, inode, full_path, |
137 | value, size); |
138 | if (rc == 0) /* force revalidate of the inode */ |
139 | CIFS_I(inode)->time = 0; |
140 | break; |
141 | } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || |
142 | (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { |
143 | rc = cifs_creation_time_set(xid, pTcon, inode, |
144 | full_path, value, size); |
145 | if (rc == 0) /* force revalidate of the inode */ |
146 | CIFS_I(inode)->time = 0; |
147 | break; |
148 | } |
149 | |
150 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
151 | goto out; |
152 | |
153 | if (pTcon->ses->server->ops->set_EA) { |
154 | rc = pTcon->ses->server->ops->set_EA(xid, pTcon, |
155 | full_path, name, value, (__u16)size, |
156 | cifs_sb->local_nls, cifs_sb); |
157 | if (rc == 0) |
158 | inode_set_ctime_current(inode); |
159 | } |
160 | break; |
161 | |
162 | case XATTR_CIFS_ACL: |
163 | case XATTR_CIFS_NTSD: |
164 | case XATTR_CIFS_NTSD_FULL: { |
165 | struct cifs_ntsd *pacl; |
166 | |
167 | if (!value) |
168 | goto out; |
169 | pacl = kmalloc(size, GFP_KERNEL); |
170 | if (!pacl) { |
171 | rc = -ENOMEM; |
172 | } else { |
173 | memcpy(pacl, value, size); |
174 | if (pTcon->ses->server->ops->set_acl) { |
175 | int aclflags = 0; |
176 | rc = 0; |
177 | |
178 | switch (handler->flags) { |
179 | case XATTR_CIFS_NTSD_FULL: |
180 | aclflags = (CIFS_ACL_OWNER | |
181 | CIFS_ACL_GROUP | |
182 | CIFS_ACL_DACL | |
183 | CIFS_ACL_SACL); |
184 | break; |
185 | case XATTR_CIFS_NTSD: |
186 | aclflags = (CIFS_ACL_OWNER | |
187 | CIFS_ACL_GROUP | |
188 | CIFS_ACL_DACL); |
189 | break; |
190 | case XATTR_CIFS_ACL: |
191 | default: |
192 | aclflags = CIFS_ACL_DACL; |
193 | } |
194 | |
195 | rc = pTcon->ses->server->ops->set_acl(pacl, |
196 | size, inode, full_path, aclflags); |
197 | } else { |
198 | rc = -EOPNOTSUPP; |
199 | } |
200 | if (rc == 0) /* force revalidate of the inode */ |
201 | CIFS_I(inode)->time = 0; |
202 | kfree(objp: pacl); |
203 | } |
204 | break; |
205 | } |
206 | } |
207 | |
208 | out: |
209 | free_dentry_path(page); |
210 | free_xid(xid); |
211 | cifs_put_tlink(tlink); |
212 | return rc; |
213 | } |
214 | |
215 | static int cifs_attrib_get(struct dentry *dentry, |
216 | struct inode *inode, void *value, |
217 | size_t size) |
218 | { |
219 | ssize_t rc; |
220 | __u32 *pattribute; |
221 | |
222 | rc = cifs_revalidate_dentry_attr(dentry); |
223 | |
224 | if (rc) |
225 | return rc; |
226 | |
227 | if ((value == NULL) || (size == 0)) |
228 | return sizeof(__u32); |
229 | else if (size < sizeof(__u32)) |
230 | return -ERANGE; |
231 | |
232 | /* return dos attributes as pseudo xattr */ |
233 | pattribute = (__u32 *)value; |
234 | *pattribute = CIFS_I(inode)->cifsAttrs; |
235 | |
236 | return sizeof(__u32); |
237 | } |
238 | |
239 | static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, |
240 | void *value, size_t size) |
241 | { |
242 | ssize_t rc; |
243 | __u64 *pcreatetime; |
244 | |
245 | rc = cifs_revalidate_dentry_attr(dentry); |
246 | if (rc) |
247 | return rc; |
248 | |
249 | if ((value == NULL) || (size == 0)) |
250 | return sizeof(__u64); |
251 | else if (size < sizeof(__u64)) |
252 | return -ERANGE; |
253 | |
254 | /* return dos attributes as pseudo xattr */ |
255 | pcreatetime = (__u64 *)value; |
256 | *pcreatetime = CIFS_I(inode)->createtime; |
257 | return sizeof(__u64); |
258 | } |
259 | |
260 | |
261 | static int cifs_xattr_get(const struct xattr_handler *handler, |
262 | struct dentry *dentry, struct inode *inode, |
263 | const char *name, void *value, size_t size) |
264 | { |
265 | ssize_t rc = -EOPNOTSUPP; |
266 | unsigned int xid; |
267 | struct super_block *sb = dentry->d_sb; |
268 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
269 | struct tcon_link *tlink; |
270 | struct cifs_tcon *pTcon; |
271 | const char *full_path; |
272 | void *page; |
273 | |
274 | tlink = cifs_sb_tlink(cifs_sb); |
275 | if (IS_ERR(ptr: tlink)) |
276 | return PTR_ERR(ptr: tlink); |
277 | pTcon = tlink_tcon(tlink); |
278 | |
279 | xid = get_xid(); |
280 | page = alloc_dentry_path(); |
281 | |
282 | full_path = build_path_from_dentry(dentry, page); |
283 | if (IS_ERR(ptr: full_path)) { |
284 | rc = PTR_ERR(ptr: full_path); |
285 | goto out; |
286 | } |
287 | |
288 | /* return alt name if available as pseudo attr */ |
289 | switch (handler->flags) { |
290 | case XATTR_USER: |
291 | cifs_dbg(FYI, "%s:querying user xattr %s\n" , __func__, name); |
292 | if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || |
293 | (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { |
294 | rc = cifs_attrib_get(dentry, inode, value, size); |
295 | break; |
296 | } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || |
297 | (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { |
298 | rc = cifs_creation_time_get(dentry, inode, value, size); |
299 | break; |
300 | } |
301 | |
302 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
303 | goto out; |
304 | |
305 | if (pTcon->ses->server->ops->query_all_EAs) |
306 | rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, |
307 | full_path, name, value, size, cifs_sb); |
308 | break; |
309 | |
310 | case XATTR_CIFS_ACL: |
311 | case XATTR_CIFS_NTSD: |
312 | case XATTR_CIFS_NTSD_FULL: { |
313 | /* |
314 | * fetch owner, DACL, and SACL if asked for full descriptor, |
315 | * fetch owner and DACL otherwise |
316 | */ |
317 | u32 acllen, ; |
318 | struct cifs_ntsd *pacl; |
319 | |
320 | if (pTcon->ses->server->ops->get_acl == NULL) |
321 | goto out; /* rc already EOPNOTSUPP */ |
322 | |
323 | if (handler->flags == XATTR_CIFS_NTSD_FULL) { |
324 | extra_info = SACL_SECINFO; |
325 | } else { |
326 | extra_info = 0; |
327 | } |
328 | pacl = pTcon->ses->server->ops->get_acl(cifs_sb, |
329 | inode, full_path, &acllen, extra_info); |
330 | if (IS_ERR(ptr: pacl)) { |
331 | rc = PTR_ERR(ptr: pacl); |
332 | cifs_dbg(VFS, "%s: error %zd getting sec desc\n" , |
333 | __func__, rc); |
334 | } else { |
335 | if (value) { |
336 | if (acllen > size) |
337 | acllen = -ERANGE; |
338 | else |
339 | memcpy(value, pacl, acllen); |
340 | } |
341 | rc = acllen; |
342 | kfree(objp: pacl); |
343 | } |
344 | break; |
345 | } |
346 | } |
347 | |
348 | /* We could add an additional check for streams ie |
349 | if proc/fs/cifs/streamstoxattr is set then |
350 | search server for EAs or streams to |
351 | returns as xattrs */ |
352 | |
353 | if (rc == -EINVAL) |
354 | rc = -EOPNOTSUPP; |
355 | |
356 | out: |
357 | free_dentry_path(page); |
358 | free_xid(xid); |
359 | cifs_put_tlink(tlink); |
360 | return rc; |
361 | } |
362 | |
363 | ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) |
364 | { |
365 | ssize_t rc = -EOPNOTSUPP; |
366 | unsigned int xid; |
367 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb: direntry->d_sb); |
368 | struct tcon_link *tlink; |
369 | struct cifs_tcon *pTcon; |
370 | const char *full_path; |
371 | void *page; |
372 | |
373 | if (unlikely(cifs_forced_shutdown(cifs_sb))) |
374 | return -EIO; |
375 | |
376 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
377 | return -EOPNOTSUPP; |
378 | |
379 | tlink = cifs_sb_tlink(cifs_sb); |
380 | if (IS_ERR(ptr: tlink)) |
381 | return PTR_ERR(ptr: tlink); |
382 | pTcon = tlink_tcon(tlink); |
383 | |
384 | xid = get_xid(); |
385 | page = alloc_dentry_path(); |
386 | |
387 | full_path = build_path_from_dentry(direntry, page); |
388 | if (IS_ERR(ptr: full_path)) { |
389 | rc = PTR_ERR(ptr: full_path); |
390 | goto list_ea_exit; |
391 | } |
392 | /* return dos attributes as pseudo xattr */ |
393 | /* return alt name if available as pseudo attr */ |
394 | |
395 | /* if proc/fs/cifs/streamstoxattr is set then |
396 | search server for EAs or streams to |
397 | returns as xattrs */ |
398 | |
399 | if (pTcon->ses->server->ops->query_all_EAs) |
400 | rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, |
401 | full_path, NULL, data, buf_size, cifs_sb); |
402 | list_ea_exit: |
403 | free_dentry_path(page); |
404 | free_xid(xid); |
405 | cifs_put_tlink(tlink); |
406 | return rc; |
407 | } |
408 | |
409 | static const struct xattr_handler cifs_user_xattr_handler = { |
410 | .prefix = XATTR_USER_PREFIX, |
411 | .flags = XATTR_USER, |
412 | .get = cifs_xattr_get, |
413 | .set = cifs_xattr_set, |
414 | }; |
415 | |
416 | /* os2.* attributes are treated like user.* attributes */ |
417 | static const struct xattr_handler cifs_os2_xattr_handler = { |
418 | .prefix = XATTR_OS2_PREFIX, |
419 | .flags = XATTR_USER, |
420 | .get = cifs_xattr_get, |
421 | .set = cifs_xattr_set, |
422 | }; |
423 | |
424 | static const struct xattr_handler cifs_cifs_acl_xattr_handler = { |
425 | .name = CIFS_XATTR_CIFS_ACL, |
426 | .flags = XATTR_CIFS_ACL, |
427 | .get = cifs_xattr_get, |
428 | .set = cifs_xattr_set, |
429 | }; |
430 | |
431 | /* |
432 | * Although this is just an alias for the above, need to move away from |
433 | * confusing users and using the 20 year old term 'cifs' when it is no |
434 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
435 | * SMB3 and later are highly secure. |
436 | */ |
437 | static const struct xattr_handler smb3_acl_xattr_handler = { |
438 | .name = SMB3_XATTR_CIFS_ACL, |
439 | .flags = XATTR_CIFS_ACL, |
440 | .get = cifs_xattr_get, |
441 | .set = cifs_xattr_set, |
442 | }; |
443 | |
444 | static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { |
445 | .name = CIFS_XATTR_CIFS_NTSD, |
446 | .flags = XATTR_CIFS_NTSD, |
447 | .get = cifs_xattr_get, |
448 | .set = cifs_xattr_set, |
449 | }; |
450 | |
451 | /* |
452 | * Although this is just an alias for the above, need to move away from |
453 | * confusing users and using the 20 year old term 'cifs' when it is no |
454 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
455 | * SMB3 and later are highly secure. |
456 | */ |
457 | static const struct xattr_handler smb3_ntsd_xattr_handler = { |
458 | .name = SMB3_XATTR_CIFS_NTSD, |
459 | .flags = XATTR_CIFS_NTSD, |
460 | .get = cifs_xattr_get, |
461 | .set = cifs_xattr_set, |
462 | }; |
463 | |
464 | static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { |
465 | .name = CIFS_XATTR_CIFS_NTSD_FULL, |
466 | .flags = XATTR_CIFS_NTSD_FULL, |
467 | .get = cifs_xattr_get, |
468 | .set = cifs_xattr_set, |
469 | }; |
470 | |
471 | /* |
472 | * Although this is just an alias for the above, need to move away from |
473 | * confusing users and using the 20 year old term 'cifs' when it is no |
474 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
475 | * SMB3 and later are highly secure. |
476 | */ |
477 | static const struct xattr_handler smb3_ntsd_full_xattr_handler = { |
478 | .name = SMB3_XATTR_CIFS_NTSD_FULL, |
479 | .flags = XATTR_CIFS_NTSD_FULL, |
480 | .get = cifs_xattr_get, |
481 | .set = cifs_xattr_set, |
482 | }; |
483 | |
484 | const struct xattr_handler * const cifs_xattr_handlers[] = { |
485 | &cifs_user_xattr_handler, |
486 | &cifs_os2_xattr_handler, |
487 | &cifs_cifs_acl_xattr_handler, |
488 | &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ |
489 | &cifs_cifs_ntsd_xattr_handler, |
490 | &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ |
491 | &cifs_cifs_ntsd_full_xattr_handler, |
492 | &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ |
493 | NULL |
494 | }; |
495 | |