smbd: reject FILE_ATTRIBUTE_TEMPORARY on directories
[Samba.git] / source3 / modules / vfs_vxfs.c
blobddd34ba812a0401d1133eec543297a893c093c1e
1 /*
2 Unix SMB/CIFS implementation.
3 Wrap VxFS calls in vfs functions.
4 This module is for ACL and XATTR handling.
6 Copyright (C) Symantec Corporation <www.symantec.com> 2014
7 Copyright (C) Veritas Technologies LLC <www.veritas.com> 2016
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "smbd/smbd.h"
25 #include "librpc/gen_ndr/ndr_xattr.h"
26 #include "../libcli/security/security.h"
27 #include "../librpc/gen_ndr/ndr_security.h"
28 #include "system/filesys.h"
29 #include "vfs_vxfs.h"
31 #undef strcasecmp
33 #undef DBGC_CLASS
34 #define DBGC_CLASS DBGC_VFS
36 #define MODULE_NAME "vxfs"
39 * WARNING !! WARNING !!
41 * DO NOT CHANGE THIS FROM "system." space to
42 * "user." space unless you are shipping a product
43 * that RESTRICTS access to extended attributes
44 * to smbd-only. "system." space is restricted
45 * to root access only, "user." space is available
46 * to ANY USER.
48 * If this is changed to "user." and access
49 * to extended attributes is available via
50 * local processes or other remote file system
51 * (e.g. NFS) then the security of the system
52 * WILL BE COMPROMISED. i.e. non-root users
53 * WILL be able to overwrite Samba ACLs on
54 * the file system.
56 * If you need to modify this define, do
57 * so using CFLAGS on your build command
58 * line.
59 * e.g. CFLAGS=-DXATTR_USER_NTACL="user.NTACL"
61 * Added by: <jra@samba.org> 17 Sept. 2014.
65 #ifndef XATTR_USER_NTACL
66 #define XATTR_USER_NTACL "system.NTACL"
67 #endif
69 /* type values */
70 #define VXFS_ACL_UNDEFINED_TYPE 0
71 #define VXFS_ACL_USER_OBJ 1
72 #define VXFS_ACL_GROUP_OBJ 2
73 #define VXFS_ACL_USER 3
74 #define VXFS_ACL_GROUP 4
75 #define VXFS_ACL_OTHER 5
76 #define VXFS_ACL_MASK 6
80 * Compare aces
81 * This will compare two ace entries for sorting
82 * each entry contains: type, perms and id
83 * Sort by type first, if type is same sort by id.
85 static int vxfs_ace_cmp(const void *ace1, const void *ace2)
87 int ret = 0;
88 uint16_t type_a1, type_a2;
89 uint32_t id_a1, id_a2;
91 /* Type must be compared first */
92 type_a1 = SVAL(ace1, 0);
93 type_a2 = SVAL(ace2, 0);
95 ret = (type_a1 - type_a2);
96 if (!ret) {
97 /* Compare ID under type */
98 /* skip perm thus take offset as 4*/
99 id_a1 = IVAL(ace1, 4);
100 id_a2 = IVAL(ace2, 4);
101 ret = id_a1 - id_a2;
104 return ret;
107 static void vxfs_print_ace_buf(char *buf, int count) {
109 int i, offset = 0;
110 uint16_t type, perm;
111 uint32_t id;
113 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
114 for (i = 0; i < count; i++) {
115 type = SVAL(buf, offset);
116 offset += 2;
117 perm = SVAL(buf, offset);
118 offset += 2;
119 id = IVAL(buf, offset);
120 offset += 4;
122 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
123 (unsigned int)type, (unsigned int)perm,
124 (unsigned int)id));
129 * Sort aces so that comparing 2 ACLs will be straight forward.
130 * This function will fill buffer as follows:
131 * For each ace:
132 * 1. ace->a_type will be filled as first 2 bytes in buf.
133 * 2. ace->a_perm will be filled as next 2 bytes.
134 * 3. ace->xid will be filled as next 4 bytes.
135 * Thus each ace entry in buf is equal to 8 bytes.
136 * Also a_type is mapped to VXFS_ACL_* so that ordering aces
137 * becomes easy.
139 static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx,
140 uint32_t o_uid,
141 uint32_t o_gid) {
143 struct smb_acl_entry *smb_ace;
144 int i, count;
145 uint16_t type, perm;
146 uint32_t id;
147 int offset = 0;
148 char *buf = NULL;
150 count = theacl->count;
152 buf = talloc_zero_size(mem_ctx, count * 8);
153 if (!buf) {
154 return NULL;
157 smb_ace = theacl->acl;
159 for (i = 0; i < count; i++) {
160 /* Calculate type */
161 /* Map type to SMB_ACL_* to VXFS_ACL_* */
162 switch(smb_ace->a_type) {
163 case SMB_ACL_USER:
164 type = VXFS_ACL_USER;
165 break;
166 case SMB_ACL_USER_OBJ:
167 type = VXFS_ACL_USER_OBJ;
168 break;
169 case SMB_ACL_GROUP:
170 type = VXFS_ACL_GROUP;
171 break;
172 case SMB_ACL_GROUP_OBJ:
173 type = VXFS_ACL_GROUP_OBJ;
174 break;
175 case SMB_ACL_OTHER:
176 type = VXFS_ACL_OTHER;
177 break;
178 case SMB_ACL_MASK:
179 type = VXFS_ACL_MASK;
180 break;
181 default:
182 type = -1;
183 talloc_free(buf);
184 return NULL;
187 type = type & 0xff;
189 /* Calculate id:
190 * We get owner uid and owner group gid in o_uid and o_gid
191 * Put these ids instead of -1
193 switch(smb_ace->a_type) {
194 case SMB_ACL_USER:
195 id = smb_ace->info.user.uid;
196 break;
197 case SMB_ACL_GROUP:
198 id = smb_ace->info.group.gid;
199 break;
200 case SMB_ACL_USER_OBJ:
201 id = o_uid;
202 break;
203 case SMB_ACL_GROUP_OBJ:
204 id = o_gid;
205 break;
206 case SMB_ACL_MASK:
207 case SMB_ACL_OTHER:
208 id = -1;
209 break;
210 default:
211 /* Can't happen.. */
212 id = -1;
213 break;
216 /* Calculate perm */
217 perm = smb_ace->a_perm & 0xff;
219 /* TYPE is the first 2 bytes of an entry */
220 SSVAL(buf, offset, type);
221 offset += 2;
223 /* PERM is the next 2 bytes of an entry */
224 SSVAL(buf, offset, perm);
225 offset += 2;
227 /* ID is the last 4 bytes of an entry */
228 SIVAL(buf, offset, id);
229 offset += 4;
231 smb_ace++;
234 qsort(buf, count, 8, vxfs_ace_cmp);
236 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
237 vxfs_print_ace_buf(buf, count);
239 return buf;
242 /* This function gets e_buf as an arg which is sorted and created out of
243 * existing ACL. This function will compact this e_buf to c_buf where USER
244 * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged
245 * respectively.
246 * This is similar to what posix_acls.c does. This will make sure existing
247 * acls are converted much similar to what posix_acls calculates.
250 static char * vxfs_compact_buf(char *e_buf, int *new_count, int count,
251 TALLOC_CTX *mem_ctx)
253 int i, e_offset = 0, c_offset = 0;
254 uint16_t type, perm, o_perm;
255 uint32_t id, owner_id, group_id;
256 char *c_buf = NULL;
259 if (count < 2) {
260 return NULL;
263 c_buf = talloc_zero_size(mem_ctx, count * 8);
264 if (!c_buf) {
265 return NULL;
268 /*Copy first two enries from e_buf to c_buf
269 *These are USER_OBJ and GROUP_OBJ
272 memcpy(c_buf, e_buf, 16);
274 (*new_count) = 2;
276 owner_id = IVAL(e_buf, 4);
277 group_id = IVAL(e_buf, 12);
279 c_offset = e_offset = 16;
281 /* Start comparing other entries */
282 for (i = 2; i < count; i++) {
284 type = SVAL(e_buf, e_offset);
285 e_offset += 2;
286 perm = SVAL(e_buf, e_offset);
287 e_offset += 2;
288 id = IVAL(e_buf, e_offset);
289 e_offset += 4;
291 switch(type) {
292 case VXFS_ACL_USER:
293 if (id == owner_id) {
294 o_perm = SVAL(c_buf, 2);
295 o_perm |= perm;
296 SSVAL(c_buf, 2, o_perm);
297 DEBUG(10, ("vfs_vxfs: merging with owner"
298 "e_type = %u,"
299 "e_perm = %u,"
300 "e_id = %u\n", (unsigned int)type,
301 (unsigned int)perm,
302 (unsigned int)id));
303 continue;
305 break;
306 case VXFS_ACL_GROUP:
307 if (id == group_id) {
308 o_perm = SVAL(c_buf, 10);
309 o_perm |= perm;
310 SSVAL(c_buf, 10, o_perm);
311 DEBUG(10, ("vfs_vxfs: merging with owner group"
312 "e_type = %u,"
313 "e_perm = %u,"
314 "e_id = %u\n", (unsigned int)type,
315 (unsigned int)perm,
316 (unsigned int)id));
317 continue;
319 break;
322 SSVAL(c_buf, c_offset, type);
323 c_offset += 2;
325 SSVAL(c_buf, c_offset, perm);
326 c_offset += 2;
328 SIVAL(c_buf, c_offset, id);
329 c_offset += 4;
331 (*new_count)++;
333 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count));
334 return c_buf;
337 /* Actually compare New ACL and existing ACL buf */
338 static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count,
339 int e_count) {
341 uint16_t e_type, n_type;
342 int offset = 0;
344 if (!e_buf && !n_buf) {
345 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
346 return false;
349 if ((e_count < 2) || (n_count < 2)) {
350 return false;
352 /*Get type from last entry from both buffers.
353 * It may or may not be ACL_MASK
355 n_type = SVAL(n_buf, offset + (8 * (n_count-1)));
356 e_type = SVAL(e_buf, offset + (8 * (e_count-1)));
358 /* Check for ACL_MASK entry properly. Handle all 4 cases*/
360 /* If ACL_MASK entry is present in any of the buffers,
361 * it will be always the last one. Calculate count to compare
362 * based on if ACL_MASK is present on new and existing ACL
364 if ((n_type != VXFS_ACL_MASK) && (e_type == VXFS_ACL_MASK)){
365 DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry,"
366 "reduce count by 1 and compare\n"));
367 e_count = e_count -1;
369 if ((n_type == VXFS_ACL_MASK) && (e_type != VXFS_ACL_MASK)){
370 DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask"
371 "existing ACL does not have mask entry\n"
372 "Need to set New ACL\n"));
373 return false;
376 if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) {
377 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
378 "buffers not same!\n"));
379 return false;
382 return true;
385 /* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir.
386 * However, files/dir share same POSIX ACL inode if ACLs are inherited
387 * from parent.
388 * To retain this behaviour, below function avoids ACL set call if
389 * underlying ACLs are already same and thus saves creating extra inode.
391 * This function will execute following steps:
392 * 1. Get existing ACL
393 * 2. Sort New ACL and existing ACL into buffers
394 * 3. Compact existing ACL buf
395 * 4. Finally compare New ACL buf and Compact buf
396 * 5. If same, return true
397 * 6. Else need to set New ACL
400 static bool vxfs_compare(struct files_struct *fsp,
401 SMB_ACL_T the_acl,
402 SMB_ACL_TYPE_T the_acl_type)
404 SMB_ACL_T existing_acl = NULL;
405 bool ret = false;
406 int count = 0;
407 TALLOC_CTX *mem_ctx = talloc_tos();
408 char *existing_buf = NULL, *new_buf = NULL, *compact_buf = NULL;
409 int status;
410 NTSTATUS ntstatus;
412 DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", fsp_str_dbg(fsp)));
414 existing_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, the_acl_type, mem_ctx);
415 if (existing_acl == NULL) {
416 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
417 goto out;
420 DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl->count));
421 DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl->count));
423 if (existing_acl->count == 0) {
424 DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n"));
425 goto out;
428 ntstatus = vfs_stat_fsp(fsp);
429 if (!NT_STATUS_IS_OK(ntstatus)) {
430 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
431 errno = map_errno_from_nt_status(ntstatus);
432 goto out;
435 DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n"));
436 existing_buf = vxfs_sort_acl(existing_acl, mem_ctx,
437 fsp->fsp_name->st.st_ex_uid,
438 fsp->fsp_name->st.st_ex_gid);
439 if (!existing_buf)
440 goto out;
442 DEBUG(10, ("vfs_vxfs: Sorting new ACL\n"));
443 new_buf = vxfs_sort_acl(the_acl, mem_ctx, fsp->fsp_name->st.st_ex_uid,
444 fsp->fsp_name->st.st_ex_gid);
445 if (!new_buf) {
446 goto out;
449 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
450 compact_buf = vxfs_compact_buf(existing_buf, &count,
451 existing_acl->count,
452 mem_ctx);
453 if (!compact_buf) {
454 goto out;
457 vxfs_print_ace_buf(compact_buf, count);
459 /* COmpare ACLs only if count is same or mismatch by 1 */
460 if ((count == the_acl->count) ||
461 (count == the_acl->count + 1) ||
462 (count+1 == the_acl->count)) {
464 if (vxfs_compare_acls(compact_buf, new_buf, the_acl->count,
465 count)) {
466 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
467 ret = true;
468 goto out;
469 } else
470 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
471 } else {
472 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
475 out:
477 TALLOC_FREE(existing_acl);
478 TALLOC_FREE(existing_buf);
479 TALLOC_FREE(compact_buf);
480 TALLOC_FREE(new_buf);
482 return ret;
485 #ifdef VXFS_ACL_SHARE
486 static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle,
487 struct files_struct *fsp,
488 SMB_ACL_TYPE_T type,
489 SMB_ACL_T theacl)
492 if (vxfs_compare(fsp, theacl, type)) {
493 return 0;
496 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
498 #endif
500 static int vxfs_fset_xattr(struct vfs_handle_struct *handle,
501 struct files_struct *fsp, const char *name,
502 const void *value, size_t size, int flags){
503 int ret = 0;
505 DEBUG(10, ("In vxfs_fset_xattr\n"));
507 ret = vxfs_setxattr_fd(fsp_get_io_fd(fsp), name, value, size, flags);
508 if ((ret == 0) ||
509 ((ret == -1) && (errno != ENOTSUP) && (errno != ENOSYS))) {
510 SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
511 return ret;
514 DEBUG(10, ("Fallback to xattr"));
515 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
516 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, XATTR_USER_NTACL,
517 value, size, flags);
520 /* Clients can't set XATTR_USER_NTACL directly. */
521 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
522 errno = EACCES;
523 return -1;
526 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags);
529 static ssize_t vxfs_fget_xattr(struct vfs_handle_struct *handle,
530 struct files_struct *fsp, const char *name,
531 void *value, size_t size){
532 int ret;
534 DEBUG(10, ("In vxfs_fget_xattr\n"));
536 ret = vxfs_getxattr_fd(fsp_get_io_fd(fsp), name, value, size);
537 if ((ret != -1) || ((errno != ENOTSUP) &&
538 (errno != ENOSYS) && (errno != ENODATA))) {
539 return ret;
542 DEBUG(10, ("Fallback to xattr\n"));
543 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
544 return SMB_VFS_NEXT_FGETXATTR(handle, fsp, XATTR_USER_NTACL,
545 value, size);
548 /* Clients can't see XATTR_USER_NTACL directly. */
549 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
550 errno = ENOATTR;
551 return -1;
554 return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size);
557 static int vxfs_fremove_xattr(struct vfs_handle_struct *handle,
558 struct files_struct *fsp, const char *name){
559 int ret = 0, ret_new = 0, old_errno;
561 DEBUG(10, ("In vxfs_fremove_xattr\n"));
563 /* Remove with old way */
564 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
565 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp,
566 XATTR_USER_NTACL);
567 } else {
568 /* Clients can't remove XATTR_USER_NTACL directly. */
569 if (strcasecmp(name, XATTR_USER_NTACL) != 0) {
570 ret = SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp,
571 name);
574 old_errno = errno;
576 /* Remove with new way */
577 ret_new = vxfs_removexattr_fd(fsp_get_io_fd(fsp), name);
579 * If both fail, return failuer else return whichever succeeded
581 if (errno == ENOTSUP || errno == ENOSYS) {
582 errno = old_errno;
584 if ((ret_new != -1) && (ret == -1)) {
585 ret = ret_new;
588 return ret;
592 static size_t vxfs_filter_list(char *list, size_t size)
594 char *str = list;
596 while (str - list < size) {
597 size_t element_len = strlen(str) + 1;
598 if (strcasecmp(str, XATTR_USER_NTACL) == 0) {
599 memmove(str,
600 str + element_len,
601 size - (str - list) - element_len);
602 size -= element_len;
603 continue;
605 str += element_len;
607 return size;
610 static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle,
611 struct files_struct *fsp, char *list,
612 size_t size)
614 ssize_t result;
616 result = vxfs_listxattr_fd(fsp_get_io_fd(fsp), list, size);
617 if (result >= 0 || ((errno != ENOTSUP) && (errno != ENOSYS))) {
618 return result;
621 result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
623 if (result <= 0) {
624 return result;
627 /* Remove any XATTR_USER_NTACL elements from the returned list. */
628 result = vxfs_filter_list(list, result);
630 return result;
633 static NTSTATUS vxfs_fset_ea_dos_attributes(struct vfs_handle_struct *handle,
634 struct files_struct *fsp,
635 uint32_t dosmode)
637 NTSTATUS err;
638 int ret = 0;
639 bool attrset = false;
641 DBG_DEBUG("Entered function\n");
643 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
644 ret = vxfs_checkwxattr_fd(fsp_get_io_fd(fsp));
645 if (ret == -1) {
646 DBG_DEBUG("ret:%d\n", ret);
647 if ((errno != EOPNOTSUPP) && (errno != ENOENT)) {
648 return map_nt_error_from_unix(errno);
652 if (dosmode & FILE_ATTRIBUTE_READONLY) {
653 ret = vxfs_setwxattr_fd(fsp_get_io_fd(fsp));
654 DBG_DEBUG("ret:%d\n", ret);
655 if (ret == -1) {
656 if ((errno != EOPNOTSUPP) && (errno != EINVAL)) {
657 return map_nt_error_from_unix(errno);
659 } else {
660 attrset = true;
663 err = SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
664 if (!NT_STATUS_IS_OK(err)) {
665 if (attrset) {
666 ret = vxfs_clearwxattr_fd(fsp_get_io_fd(fsp));
667 DBG_DEBUG("ret:%d\n", ret);
668 if ((ret == -1) && (errno != ENOENT)) {
669 return map_nt_error_from_unix(errno);
673 return err;
676 static int vfs_vxfs_connect(struct vfs_handle_struct *handle,
677 const char *service, const char *user)
680 int ret;
682 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
683 if (ret < 0) {
684 return ret;
687 vxfs_init();
689 return 0;
692 static struct vfs_fn_pointers vfs_vxfs_fns = {
693 .connect_fn = vfs_vxfs_connect,
695 #ifdef VXFS_ACL_SHARE
696 .sys_acl_set_fd_fn = vxfs_sys_acl_set_fd,
697 #endif
699 .fset_dos_attributes_fn = vxfs_fset_ea_dos_attributes,
700 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
701 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
702 .fgetxattr_fn = vxfs_fget_xattr,
703 .flistxattr_fn = vxfs_flistxattr,
704 .fremovexattr_fn = vxfs_fremove_xattr,
705 .fsetxattr_fn = vxfs_fset_xattr,
708 static_decl_vfs;
709 NTSTATUS vfs_vxfs_init(TALLOC_CTX *ctx)
711 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "vxfs",
712 &vfs_vxfs_fns);