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/>.
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"
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
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
56 * If you need to modify this define, do
57 * so using CFLAGS on your build command
59 * e.g. CFLAGS=-DXATTR_USER_NTACL="user.NTACL"
61 * Added by: <jra@samba.org> 17 Sept. 2014.
67 * XATTR_USER_NTACL: This extended attribute is used
68 * to store Access Control List system objects by VxFS from DLV11 onwards.
69 * XATTR_USER_NTACL_V0: This extended attribute was used
70 * to store Access Control List system objects by VxFS till DLV10.
72 #ifndef XATTR_USER_NTACL
73 #define XATTR_USER_NTACL "system.NTACL"
74 #define XATTR_USER_NTACL_V0 "user.NTACL"
78 #define VXFS_ACL_UNDEFINED_TYPE 0
79 #define VXFS_ACL_USER_OBJ 1
80 #define VXFS_ACL_GROUP_OBJ 2
81 #define VXFS_ACL_USER 3
82 #define VXFS_ACL_GROUP 4
83 #define VXFS_ACL_OTHER 5
84 #define VXFS_ACL_MASK 6
87 * Helper function for comparing two strings
89 static int vxfs_strcasecmp(const char *str1
, const char *str2
)
91 bool match
= strequal_m(str1
, str2
);
100 * This will compare two ace entries for sorting
101 * each entry contains: type, perms and id
102 * Sort by type first, if type is same sort by id.
104 static int vxfs_ace_cmp(const void *ace1
, const void *ace2
)
107 uint16_t type_a1
, type_a2
;
108 uint32_t id_a1
, id_a2
;
110 /* Type must be compared first */
111 type_a1
= SVAL(ace1
, 0);
112 type_a2
= SVAL(ace2
, 0);
114 ret
= NUMERIC_CMP(type_a1
, type_a2
);
116 /* Compare ID under type */
117 /* skip perm thus take offset as 4*/
118 id_a1
= IVAL(ace1
, 4);
119 id_a2
= IVAL(ace2
, 4);
120 ret
= NUMERIC_CMP(id_a1
, id_a2
);
126 static void vxfs_print_ace_buf(char *buf
, int count
) {
132 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
133 for (i
= 0; i
< count
; i
++) {
134 type
= SVAL(buf
, offset
);
136 perm
= SVAL(buf
, offset
);
138 id
= IVAL(buf
, offset
);
141 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
142 (unsigned int)type
, (unsigned int)perm
,
148 * Sort aces so that comparing 2 ACLs will be straight forward.
149 * This function will fill buffer as follows:
151 * 1. ace->a_type will be filled as first 2 bytes in buf.
152 * 2. ace->a_perm will be filled as next 2 bytes.
153 * 3. ace->xid will be filled as next 4 bytes.
154 * Thus each ace entry in buf is equal to 8 bytes.
155 * Also a_type is mapped to VXFS_ACL_* so that ordering aces
158 static char * vxfs_sort_acl(SMB_ACL_T theacl
, TALLOC_CTX
*mem_ctx
,
162 struct smb_acl_entry
*smb_ace
;
169 count
= theacl
->count
;
171 buf
= talloc_zero_size(mem_ctx
, count
* 8);
176 smb_ace
= theacl
->acl
;
178 for (i
= 0; i
< count
; i
++) {
180 /* Map type to SMB_ACL_* to VXFS_ACL_* */
181 switch(smb_ace
->a_type
) {
183 type
= VXFS_ACL_USER
;
185 case SMB_ACL_USER_OBJ
:
186 type
= VXFS_ACL_USER_OBJ
;
189 type
= VXFS_ACL_GROUP
;
191 case SMB_ACL_GROUP_OBJ
:
192 type
= VXFS_ACL_GROUP_OBJ
;
195 type
= VXFS_ACL_OTHER
;
198 type
= VXFS_ACL_MASK
;
209 * We get owner uid and owner group gid in o_uid and o_gid
210 * Put these ids instead of -1
212 switch(smb_ace
->a_type
) {
214 id
= smb_ace
->info
.user
.uid
;
217 id
= smb_ace
->info
.group
.gid
;
219 case SMB_ACL_USER_OBJ
:
222 case SMB_ACL_GROUP_OBJ
:
236 perm
= smb_ace
->a_perm
& 0xff;
238 /* TYPE is the first 2 bytes of an entry */
239 SSVAL(buf
, offset
, type
);
242 /* PERM is the next 2 bytes of an entry */
243 SSVAL(buf
, offset
, perm
);
246 /* ID is the last 4 bytes of an entry */
247 SIVAL(buf
, offset
, id
);
253 qsort(buf
, count
, 8, vxfs_ace_cmp
);
255 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
256 vxfs_print_ace_buf(buf
, count
);
261 /* This function gets e_buf as an arg which is sorted and created out of
262 * existing ACL. This function will compact this e_buf to c_buf where USER
263 * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged
265 * This is similar to what posix_acls.c does. This will make sure existing
266 * acls are converted much similar to what posix_acls calculates.
269 static char * vxfs_compact_buf(char *e_buf
, int *new_count
, int count
,
272 int i
, e_offset
= 0, c_offset
= 0;
273 uint16_t type
, perm
, o_perm
;
274 uint32_t id
, owner_id
, group_id
;
282 c_buf
= talloc_zero_size(mem_ctx
, count
* 8);
287 /*Copy first two enries from e_buf to c_buf
288 *These are USER_OBJ and GROUP_OBJ
291 memcpy(c_buf
, e_buf
, 16);
295 owner_id
= IVAL(e_buf
, 4);
296 group_id
= IVAL(e_buf
, 12);
298 c_offset
= e_offset
= 16;
300 /* Start comparing other entries */
301 for (i
= 2; i
< count
; i
++) {
303 type
= SVAL(e_buf
, e_offset
);
305 perm
= SVAL(e_buf
, e_offset
);
307 id
= IVAL(e_buf
, e_offset
);
312 if (id
== owner_id
) {
313 o_perm
= SVAL(c_buf
, 2);
315 SSVAL(c_buf
, 2, o_perm
);
316 DEBUG(10, ("vfs_vxfs: merging with owner"
319 "e_id = %u\n", (unsigned int)type
,
326 if (id
== group_id
) {
327 o_perm
= SVAL(c_buf
, 10);
329 SSVAL(c_buf
, 10, o_perm
);
330 DEBUG(10, ("vfs_vxfs: merging with owner group"
333 "e_id = %u\n", (unsigned int)type
,
341 SSVAL(c_buf
, c_offset
, type
);
344 SSVAL(c_buf
, c_offset
, perm
);
347 SIVAL(c_buf
, c_offset
, id
);
352 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count
));
356 /* Actually compare New ACL and existing ACL buf */
357 static bool vxfs_compare_acls(char *e_buf
, char *n_buf
, int n_count
,
360 uint16_t e_type
, n_type
;
363 if (!e_buf
&& !n_buf
) {
364 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
368 if ((e_count
< 2) || (n_count
< 2)) {
371 /*Get type from last entry from both buffers.
372 * It may or may not be ACL_MASK
374 n_type
= SVAL(n_buf
, offset
+ (8 * (n_count
-1)));
375 e_type
= SVAL(e_buf
, offset
+ (8 * (e_count
-1)));
377 /* Check for ACL_MASK entry properly. Handle all 4 cases*/
379 /* If ACL_MASK entry is present in any of the buffers,
380 * it will be always the last one. Calculate count to compare
381 * based on if ACL_MASK is present on new and existing ACL
383 if ((n_type
!= VXFS_ACL_MASK
) && (e_type
== VXFS_ACL_MASK
)){
384 DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry,"
385 "reduce count by 1 and compare\n"));
386 e_count
= e_count
-1;
388 if ((n_type
== VXFS_ACL_MASK
) && (e_type
!= VXFS_ACL_MASK
)){
389 DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask"
390 "existing ACL does not have mask entry\n"
391 "Need to set New ACL\n"));
395 if (memcmp(e_buf
, n_buf
, (e_count
* 8)) != 0) {
396 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
397 "buffers not same!\n"));
404 /* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir.
405 * However, files/dir share same POSIX ACL inode if ACLs are inherited
407 * To retain this behaviour, below function avoids ACL set call if
408 * underlying ACLs are already same and thus saves creating extra inode.
410 * This function will execute following steps:
411 * 1. Get existing ACL
412 * 2. Sort New ACL and existing ACL into buffers
413 * 3. Compact existing ACL buf
414 * 4. Finally compare New ACL buf and Compact buf
415 * 5. If same, return true
416 * 6. Else need to set New ACL
419 static bool vxfs_compare(struct files_struct
*fsp
,
421 SMB_ACL_TYPE_T the_acl_type
)
423 SMB_ACL_T existing_acl
= NULL
;
426 TALLOC_CTX
*mem_ctx
= talloc_tos();
427 char *existing_buf
= NULL
, *new_buf
= NULL
, *compact_buf
= NULL
;
431 DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", fsp_str_dbg(fsp
)));
433 existing_acl
= SMB_VFS_SYS_ACL_GET_FD(fsp
, the_acl_type
, mem_ctx
);
434 if (existing_acl
== NULL
) {
435 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
439 DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl
->count
));
440 DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl
->count
));
442 if (existing_acl
->count
== 0) {
443 DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n"));
447 ntstatus
= vfs_stat_fsp(fsp
);
448 if (!NT_STATUS_IS_OK(ntstatus
)) {
449 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
450 errno
= map_errno_from_nt_status(ntstatus
);
454 DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n"));
455 existing_buf
= vxfs_sort_acl(existing_acl
, mem_ctx
,
456 fsp
->fsp_name
->st
.st_ex_uid
,
457 fsp
->fsp_name
->st
.st_ex_gid
);
461 DEBUG(10, ("vfs_vxfs: Sorting new ACL\n"));
462 new_buf
= vxfs_sort_acl(the_acl
, mem_ctx
, fsp
->fsp_name
->st
.st_ex_uid
,
463 fsp
->fsp_name
->st
.st_ex_gid
);
468 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
469 compact_buf
= vxfs_compact_buf(existing_buf
, &count
,
476 vxfs_print_ace_buf(compact_buf
, count
);
478 /* COmpare ACLs only if count is same or mismatch by 1 */
479 if ((count
== the_acl
->count
) ||
480 (count
== the_acl
->count
+ 1) ||
481 (count
+1 == the_acl
->count
)) {
483 if (vxfs_compare_acls(compact_buf
, new_buf
, the_acl
->count
,
485 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
489 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
491 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
496 TALLOC_FREE(existing_acl
);
497 TALLOC_FREE(existing_buf
);
498 TALLOC_FREE(compact_buf
);
499 TALLOC_FREE(new_buf
);
504 #ifdef VXFS_ACL_SHARE
505 static int vxfs_sys_acl_set_fd(vfs_handle_struct
*handle
,
506 struct files_struct
*fsp
,
511 if (vxfs_compare(fsp
, theacl
, type
)) {
515 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle
, fsp
, type
, theacl
);
519 static int vxfs_fset_xattr(struct vfs_handle_struct
*handle
,
520 struct files_struct
*fsp
, const char *name
,
521 const void *value
, size_t size
, int flags
){
525 DBG_DEBUG("In vxfs_fset_xattr\n");
527 ret
= vxfs_setxattr_fd(fsp_get_io_fd(fsp
), name
, value
, size
, flags
);
529 ((ret
== -1) && (errno
!= ENOTSUP
) && (errno
!= ENOSYS
))) {
531 * version 1: user.NTACL xattr without inheritance supported
532 * version 2: user.NTACL xattr with inheritance supported
533 * version 3: new styled xattr security.NTACL with inheritance supported
534 * Hence, the old styled xattr user.NTACL should be removed
536 tmp_ret
= vxfs_strcasecmp(name
, XATTR_NTACL_NAME
);
538 SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, XATTR_USER_NTACL_V0
);
539 DBG_DEBUG("Old style xattr %s removed...\n", XATTR_USER_NTACL_V0
);
545 DBG_DEBUG("Fallback to xattr\n");
546 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
547 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, XATTR_USER_NTACL
,
551 /* Clients can't set XATTR_USER_NTACL directly. */
552 if (vxfs_strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
557 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, value
, size
, flags
);
560 static ssize_t
vxfs_fget_xattr(struct vfs_handle_struct
*handle
,
561 struct files_struct
*fsp
, const char *name
,
562 void *value
, size_t size
){
565 DEBUG(10, ("In vxfs_fget_xattr\n"));
567 ret
= vxfs_getxattr_fd(fsp_get_io_fd(fsp
), name
, value
, size
);
568 if ((ret
!= -1) || ((errno
!= ENOTSUP
) &&
569 (errno
!= ENOSYS
) && (errno
!= ENODATA
))) {
573 DEBUG(10, ("Fallback to xattr\n"));
574 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
575 return SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, XATTR_USER_NTACL
,
579 /* Clients can't see XATTR_USER_NTACL directly. */
580 if (vxfs_strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
585 return SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, name
, value
, size
);
588 static int vxfs_fremove_xattr(struct vfs_handle_struct
*handle
,
589 struct files_struct
*fsp
, const char *name
){
590 int ret
= 0, ret_new
= 0, old_errno
;
592 DEBUG(10, ("In vxfs_fremove_xattr\n"));
594 /* Remove with old way */
595 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
596 ret
= SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
,
599 /* Clients can't remove XATTR_USER_NTACL directly. */
600 if (vxfs_strcasecmp(name
, XATTR_USER_NTACL
) != 0) {
601 ret
= SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
,
607 /* Remove with new way */
608 ret_new
= vxfs_removexattr_fd(fsp_get_io_fd(fsp
), name
);
610 * If both fail, return failure else return whichever succeeded
612 if (errno
== ENOTSUP
|| errno
== ENOSYS
) {
615 if ((ret_new
!= -1) && (ret
== -1)) {
623 static size_t vxfs_filter_list(char *list
, size_t size
)
627 while (str
- list
< size
) {
628 size_t element_len
= strlen(str
) + 1;
629 if (vxfs_strcasecmp(str
, XATTR_USER_NTACL
) == 0) {
632 size
- (str
- list
) - element_len
);
641 static ssize_t
vxfs_flistxattr(struct vfs_handle_struct
*handle
,
642 struct files_struct
*fsp
, char *list
,
647 result
= vxfs_listxattr_fd(fsp_get_io_fd(fsp
), list
, size
);
648 if (result
>= 0 || ((errno
!= ENOTSUP
) && (errno
!= ENOSYS
))) {
652 result
= SMB_VFS_NEXT_FLISTXATTR(handle
, fsp
, list
, size
);
658 /* Remove any XATTR_USER_NTACL elements from the returned list. */
659 result
= vxfs_filter_list(list
, result
);
664 static NTSTATUS
vxfs_fset_ea_dos_attributes(struct vfs_handle_struct
*handle
,
665 struct files_struct
*fsp
,
671 DBG_DEBUG("Entered function\n");
673 err
= SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle
, fsp
, dosmode
);
674 if (!NT_STATUS_IS_OK(err
)) {
675 DBG_DEBUG("err:%d\n", err
);
678 if (!(dosmode
& FILE_ATTRIBUTE_READONLY
)) {
679 ret
= vxfs_checkwxattr_fd(fsp_get_io_fd(fsp
));
681 DBG_DEBUG("ret:%d\n", ret
);
682 if ((errno
!= EOPNOTSUPP
) && (errno
!= ENOENT
)) {
683 return map_nt_error_from_unix(errno
);
687 if (dosmode
& FILE_ATTRIBUTE_READONLY
) {
688 ret
= vxfs_setwxattr_fd(fsp_get_io_fd(fsp
));
689 DBG_DEBUG("ret:%d\n", ret
);
691 if ((errno
!= EOPNOTSUPP
) && (errno
!= EINVAL
)) {
692 return map_nt_error_from_unix(errno
);
699 static int vfs_vxfs_connect(struct vfs_handle_struct
*handle
,
700 const char *service
, const char *user
)
705 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
715 static struct vfs_fn_pointers vfs_vxfs_fns
= {
716 .connect_fn
= vfs_vxfs_connect
,
718 #ifdef VXFS_ACL_SHARE
719 .sys_acl_set_fd_fn
= vxfs_sys_acl_set_fd
,
722 .fset_dos_attributes_fn
= vxfs_fset_ea_dos_attributes
,
723 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
724 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
725 .fgetxattr_fn
= vxfs_fget_xattr
,
726 .flistxattr_fn
= vxfs_flistxattr
,
727 .fremovexattr_fn
= vxfs_fremove_xattr
,
728 .fsetxattr_fn
= vxfs_fset_xattr
,
732 NTSTATUS
vfs_vxfs_init(TALLOC_CTX
*ctx
)
734 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "vxfs",