2 Unix SMB/CIFS implementation.
3 Wrap VxFS calls in vfs functions.
4 This module is for ACL handling.
6 Copyright (C) Symantec Corporation <www.symantec.com> 2014
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "smbd/smbd.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "../libcli/security/security.h"
26 #include "../librpc/gen_ndr/ndr_security.h"
27 #include "system/filesys.h"
30 #define DBGC_CLASS DBGC_VFS
32 #define MODULE_NAME "vxfs"
35 * WARNING !! WARNING !!
37 * DO NOT CHANGE THIS FROM "system." space to
38 * "user." space unless you are shipping a product
39 * that RESTRICTS access to extended attributes
40 * to smbd-only. "system." space is restricted
41 * to root access only, "user." space is available
44 * If this is changed to "user." and access
45 * to extended attributes is available via
46 * local processes or other remote file system
47 * (e.g. NFS) then the security of the system
48 * WILL BE COMPROMISED. i.e. non-root users
49 * WILL be able to overwrite Samba ACLs on
52 * If you need to modify this define, do
53 * so using CFLAGS on your build command
55 * e.g. CFLAGS=-DXATTR_USER_NTACL="user.NTACL"
57 * Added by: <jra@samba.org> 17 Sept. 2014.
61 #ifndef XATTR_USER_NTACL
62 #define XATTR_USER_NTACL "system.NTACL"
66 #define VXFS_ACL_UNDEFINED_TYPE 0
67 #define VXFS_ACL_USER_OBJ 1
68 #define VXFS_ACL_GROUP_OBJ 2
69 #define VXFS_ACL_USER 3
70 #define VXFS_ACL_GROUP 4
71 #define VXFS_ACL_OTHER 5
72 #define VXFS_ACL_MASK 6
77 * This will compare two ace entries for sorting
78 * each entry contains: type, perms and id
79 * Sort by type first, if type is same sort by id.
81 static int vxfs_ace_cmp(const void *ace1
, const void *ace2
)
84 uint16_t type_a1
, type_a2
;
85 uint32_t id_a1
, id_a2
;
87 /* Type must be compared first */
88 type_a1
= SVAL(ace1
, 0);
89 type_a2
= SVAL(ace2
, 0);
91 ret
= (type_a1
- type_a2
);
93 /* Compare ID under type */
94 /* skip perm thus take offset as 4*/
95 id_a1
= IVAL(ace1
, 4);
96 id_a2
= IVAL(ace2
, 4);
103 static void vxfs_print_ace_buf(char *buf
, int count
) {
109 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
110 for (i
= 0; i
< count
; i
++) {
111 type
= SVAL(buf
, offset
);
113 perm
= SVAL(buf
, offset
);
115 id
= IVAL(buf
, offset
);
118 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
119 (unsigned int)type
, (unsigned int)perm
,
125 * Sort aces so that comparing 2 ACLs will be straight forward.
126 * This function will fill buffer as follows:
128 * 1. ace->a_type will be filled as first 2 bytes in buf.
129 * 2. ace->a_perm will be filled as next 2 bytes.
130 * 3. ace->xid will be filled as next 4 bytes.
131 * Thus each ace entry in buf is equal to 8 bytes.
132 * Also a_type is mapped to VXFS_ACL_* so that ordering aces
135 static char * vxfs_sort_acl(SMB_ACL_T theacl
, TALLOC_CTX
*mem_ctx
,
139 struct smb_acl_entry
*smb_ace
;
146 count
= theacl
->count
;
148 buf
= talloc_zero_size(mem_ctx
, count
* 8);
153 smb_ace
= theacl
->acl
;
155 for (i
= 0; i
< count
; i
++) {
157 /* Map type to SMB_ACL_* to VXFS_ACL_* */
158 switch(smb_ace
->a_type
) {
160 type
= VXFS_ACL_USER
;
162 case SMB_ACL_USER_OBJ
:
163 type
= VXFS_ACL_USER_OBJ
;
166 type
= VXFS_ACL_GROUP
;
168 case SMB_ACL_GROUP_OBJ
:
169 type
= VXFS_ACL_GROUP_OBJ
;
172 type
= VXFS_ACL_OTHER
;
175 type
= VXFS_ACL_MASK
;
186 * We get owner uid and owner group gid in o_uid and o_gid
187 * Put these ids instead of -1
189 switch(smb_ace
->a_type
) {
191 id
= smb_ace
->info
.user
.uid
;
194 id
= smb_ace
->info
.group
.gid
;
196 case SMB_ACL_USER_OBJ
:
199 case SMB_ACL_GROUP_OBJ
:
213 perm
= smb_ace
->a_perm
& 0xff;
215 /* TYPE is the first 2 bytes of an entry */
216 SSVAL(buf
, offset
, type
);
219 /* PERM is the next 2 bytes of an entry */
220 SSVAL(buf
, offset
, perm
);
223 /* ID is the last 4 bytes of an entry */
224 SIVAL(buf
, offset
, id
);
230 qsort(buf
, count
, 8, vxfs_ace_cmp
);
232 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
233 vxfs_print_ace_buf(buf
, count
);
238 /* This function gets e_buf as an arg which is sorted and created out of
239 * existing ACL. This function will compact this e_buf to c_buf where USER
240 * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged
242 * This is similar to what posix_acls.c does. This will make sure existing
243 * acls are converted much similar to what posix_acls calculates.
246 static char * vxfs_compact_buf(char *e_buf
, int *new_count
, int count
,
249 int i
, e_offset
= 0, c_offset
= 0;
250 uint16_t type
, perm
, o_perm
;
251 uint32_t id
, owner_id
, group_id
;
259 c_buf
= talloc_zero_size(mem_ctx
, count
* 8);
264 /*Copy first two enries from e_buf to c_buf
265 *These are USER_OBJ and GROUP_OBJ
268 memcpy(c_buf
, e_buf
, 16);
272 owner_id
= IVAL(e_buf
, 4);
273 group_id
= IVAL(e_buf
, 12);
275 c_offset
= e_offset
= 16;
277 /* Start comparing other entries */
278 for (i
= 2; i
< count
; i
++) {
280 type
= SVAL(e_buf
, e_offset
);
282 perm
= SVAL(e_buf
, e_offset
);
284 id
= IVAL(e_buf
, e_offset
);
289 if (id
== owner_id
) {
290 o_perm
= SVAL(c_buf
, 2);
292 SSVAL(c_buf
, 2, o_perm
);
293 DEBUG(10, ("vfs_vxfs: merging with owner"
296 "e_id = %u\n", (unsigned int)type
,
303 if (id
== group_id
) {
304 o_perm
= SVAL(c_buf
, 10);
306 SSVAL(c_buf
, 10, o_perm
);
307 DEBUG(10, ("vfs_vxfs: merging with owner group"
310 "e_id = %u\n", (unsigned int)type
,
318 SSVAL(c_buf
, c_offset
, type
);
321 SSVAL(c_buf
, c_offset
, perm
);
324 SIVAL(c_buf
, c_offset
, id
);
329 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count
));
333 /* Actually compare New ACL and existing ACL buf */
334 static bool vxfs_compare_acls(char *e_buf
, char *n_buf
, int n_count
,
337 uint16_t e_type
, n_type
, e_perm
, n_perm
;
341 if (!e_buf
&& !n_buf
) {
342 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
346 if ((e_count
< 2) || (n_count
< 2)) {
349 /*Get type from last entry from both buffers.
350 * It may or may not be ACL_MASK
352 n_type
= SVAL(n_buf
, offset
+ (8 * (n_count
-1)));
353 e_type
= SVAL(e_buf
, offset
+ (8 * (e_count
-1)));
355 /* Check for ACL_MASK entry properly. Handle all 4 cases*/
357 /* If ACL_MASK entry is present in any of the buffers,
358 * it will be always the last one. Calculate count to compare
359 * based on if ACL_MASK is present on new and existing ACL
361 if ((n_type
!= VXFS_ACL_MASK
) && (e_type
== VXFS_ACL_MASK
)){
362 DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry,"
363 "reduce count by 1 and compare\n"));
364 e_count
= e_count
-1;
366 if ((n_type
== VXFS_ACL_MASK
) && (e_type
!= VXFS_ACL_MASK
)){
367 DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask"
368 "existing ACL does not have mask entry\n"
369 "Need to set New ACL\n"));
373 if (memcmp(e_buf
, n_buf
, (e_count
* 8)) != 0) {
374 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
375 "buffers not same!\n"));
382 /* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir.
383 * However, files/dir share same POSIX ACL inode if ACLs are inherited
385 * To retain this behaviour, below function avoids ACL set call if
386 * underlying ACLs are already same and thus saves creating extra inode.
388 * This function will execute following steps:
389 * 1. Get existing ACL
390 * 2. Sort New ACL and existing ACL into buffers
391 * 3. Compact existing ACL buf
392 * 4. Finally compare New ACL buf and Compact buf
393 * 5. If same, return true
394 * 6. Else need to set New ACL
397 static bool vxfs_compare(connection_struct
*conn
, char *name
, SMB_ACL_T the_acl
,
398 SMB_ACL_TYPE_T the_acl_type
)
400 SMB_ACL_T existing_acl
= NULL
;
403 TALLOC_CTX
*mem_ctx
= talloc_tos();
404 char *existing_buf
= NULL
, *new_buf
= NULL
, *compact_buf
= NULL
;
405 struct smb_filename
*smb_fname
= NULL
;
408 DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", name
));
409 existing_acl
= SMB_VFS_SYS_ACL_GET_FILE(conn
, name
, the_acl_type
,
411 if (existing_acl
== NULL
) {
412 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
416 DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl
->count
));
417 DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl
->count
));
419 if (existing_acl
->count
== 0) {
420 DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n"));
424 smb_fname
= synthetic_smb_fname(mem_ctx
, name
, NULL
, NULL
);
425 if (smb_fname
== NULL
) {
426 DEBUG(10, ("vfs_vxfs: Failed to create smb_fname\n"));
430 status
= SMB_VFS_STAT(conn
, smb_fname
);
432 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
436 DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n"));
437 existing_buf
= vxfs_sort_acl(existing_acl
, mem_ctx
,
438 smb_fname
->st
.st_ex_uid
,
439 smb_fname
->st
.st_ex_gid
);
443 DEBUG(10, ("vfs_vxfs: Sorting new ACL\n"));
444 new_buf
= vxfs_sort_acl(the_acl
, mem_ctx
, smb_fname
->st
.st_ex_uid
,
445 smb_fname
->st
.st_ex_gid
);
450 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
451 compact_buf
= vxfs_compact_buf(existing_buf
, &count
,
458 vxfs_print_ace_buf(compact_buf
, count
);
460 /* COmpare ACLs only if count is same or mismatch by 1 */
461 if ((count
== the_acl
->count
) ||
462 (count
== the_acl
->count
+ 1) ||
463 (count
+1 == the_acl
->count
)) {
465 if (vxfs_compare_acls(compact_buf
, new_buf
, the_acl
->count
,
467 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
471 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
473 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
478 TALLOC_FREE(existing_acl
);
479 TALLOC_FREE(smb_fname
);
480 TALLOC_FREE(existing_buf
);
481 TALLOC_FREE(compact_buf
);
482 TALLOC_FREE(new_buf
);
487 static int vxfs_sys_acl_set_fd(vfs_handle_struct
*handle
, files_struct
*fsp
,
491 if (vxfs_compare(fsp
->conn
, fsp
->fsp_name
->base_name
, theacl
,
492 SMB_ACL_TYPE_ACCESS
)) {
496 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle
, fsp
, theacl
);
499 static int vxfs_sys_acl_set_file(vfs_handle_struct
*handle
, const char *name
,
500 SMB_ACL_TYPE_T acltype
, SMB_ACL_T theacl
)
502 if (vxfs_compare(handle
->conn
, (char *)name
, theacl
, acltype
)) {
506 return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle
, name
, acltype
, theacl
);
509 static int vxfs_set_xattr(struct vfs_handle_struct
*handle
, const char *path
,
510 const char *name
, const void *value
, size_t size
,
513 DEBUG(10, ("In vxfs_set_xattr\n"));
515 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
516 return SMB_VFS_NEXT_SETXATTR(handle
, path
, XATTR_USER_NTACL
,
520 /* Clients can't set XATTR_USER_NTACL directly. */
521 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
526 return SMB_VFS_NEXT_SETXATTR(handle
, path
, name
, value
, size
, flags
);
529 static int vxfs_fset_xattr(struct vfs_handle_struct
*handle
,
530 struct files_struct
*fsp
, const char *name
,
531 const void *value
, size_t size
, int flags
){
533 DEBUG(10, ("In vxfs_fset_xattr\n"));
535 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
536 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, XATTR_USER_NTACL
,
540 /* Clients can't set XATTR_USER_NTACL directly. */
541 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
546 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
, name
, value
, size
, flags
);
549 static ssize_t
vxfs_get_xattr(struct vfs_handle_struct
*handle
,
550 const char *path
, const char *name
,
551 void *value
, size_t size
){
553 DEBUG(10, ("In vxfs_get_xattr\n"));
555 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
556 return SMB_VFS_NEXT_GETXATTR(handle
, path
, XATTR_USER_NTACL
,
560 /* Clients can't see XATTR_USER_NTACL directly. */
561 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
566 return SMB_VFS_NEXT_GETXATTR(handle
, path
, name
, value
, size
);
569 static ssize_t
vxfs_fget_xattr(struct vfs_handle_struct
*handle
,
570 struct files_struct
*fsp
, const char *name
,
571 void *value
, size_t size
){
573 DEBUG(10, ("In vxfs_fget_xattr\n"));
575 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
576 return SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, XATTR_USER_NTACL
,
580 /* Clients can't see XATTR_USER_NTACL directly. */
581 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
586 return SMB_VFS_NEXT_FGETXATTR(handle
, fsp
, name
, value
, size
);
589 static int vxfs_remove_xattr(struct vfs_handle_struct
*handle
,
590 const char *path
, const char *name
){
592 DEBUG(10, ("In vxfs_remove_xattr\n"));
594 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
595 return SMB_VFS_NEXT_REMOVEXATTR(handle
, path
, XATTR_USER_NTACL
);
598 /* Clients can't see XATTR_USER_NTACL directly. */
599 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
604 return SMB_VFS_NEXT_REMOVEXATTR(handle
, path
, name
);
607 static int vxfs_fremove_xattr(struct vfs_handle_struct
*handle
,
608 struct files_struct
*fsp
, const char *name
){
610 DEBUG(10, ("In vxfs_fremove_xattr\n"));
612 if (strcmp(name
, XATTR_NTACL_NAME
) == 0) {
613 return SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, XATTR_USER_NTACL
);
616 /* Clients can't remove XATTR_USER_NTACL directly. */
617 if (strcasecmp(name
, XATTR_USER_NTACL
) == 0) {
622 return SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
625 static size_t vxfs_filter_list(char *list
, size_t size
)
629 while (str
- list
< size
) {
630 size_t element_len
= strlen(str
) + 1;
631 if (strcasecmp(str
, XATTR_USER_NTACL
) == 0) {
634 size
- (str
- list
) - element_len
);
643 static ssize_t
vxfs_listxattr(vfs_handle_struct
*handle
, const char *path
,
644 char *list
, size_t size
)
648 result
= SMB_VFS_NEXT_LISTXATTR(handle
, path
, list
, size
);
654 /* Remove any XATTR_USER_NTACL elements from the returned list. */
655 result
= vxfs_filter_list(list
, result
);
660 static ssize_t
vxfs_flistxattr(struct vfs_handle_struct
*handle
,
661 struct files_struct
*fsp
, char *list
,
666 result
= SMB_VFS_NEXT_FLISTXATTR(handle
, fsp
, list
, size
);
672 /* Remove any XATTR_USER_NTACL elements from the returned list. */
673 result
= vxfs_filter_list(list
, result
);
678 static int vfs_vxfs_connect(struct vfs_handle_struct
*handle
,
679 const char *service
, const char *user
)
682 int ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
690 static struct vfs_fn_pointers vfs_vxfs_fns
= {
691 .connect_fn
= vfs_vxfs_connect
,
693 .sys_acl_set_file_fn
= vxfs_sys_acl_set_file
,
694 .sys_acl_set_fd_fn
= vxfs_sys_acl_set_fd
,
696 .getxattr_fn
= vxfs_get_xattr
,
697 .fgetxattr_fn
= vxfs_fget_xattr
,
698 .listxattr_fn
= vxfs_listxattr
,
699 .flistxattr_fn
= vxfs_flistxattr
,
700 .removexattr_fn
= vxfs_remove_xattr
,
701 .fremovexattr_fn
= vxfs_fremove_xattr
,
702 .setxattr_fn
= vxfs_set_xattr
,
703 .fsetxattr_fn
= vxfs_fset_xattr
,
706 NTSTATUS
vfs_vxfs_init(void);
707 NTSTATUS
vfs_vxfs_init(void)
709 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "vxfs",