smbd: replace CHECK_WRITE() macro with calls to check_any_access_fsp()
[Samba.git] / source3 / modules / vfs_vxfs.c
blobc41d050e4ef1d5347325a72dc3d7a9ce03c14152
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.
66 * Note:
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"
75 #endif
77 /* type values */
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);
92 if (match) {
93 return 0;
95 return 1;
99 * Compare aces
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)
106 int ret = 0;
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 = (type_a1 - type_a2);
115 if (!ret) {
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 = id_a1 - id_a2;
123 return ret;
126 static void vxfs_print_ace_buf(char *buf, int count) {
128 int i, offset = 0;
129 uint16_t type, perm;
130 uint32_t id;
132 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
133 for (i = 0; i < count; i++) {
134 type = SVAL(buf, offset);
135 offset += 2;
136 perm = SVAL(buf, offset);
137 offset += 2;
138 id = IVAL(buf, offset);
139 offset += 4;
141 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
142 (unsigned int)type, (unsigned int)perm,
143 (unsigned int)id));
148 * Sort aces so that comparing 2 ACLs will be straight forward.
149 * This function will fill buffer as follows:
150 * For each ace:
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
156 * becomes easy.
158 static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx,
159 uint32_t o_uid,
160 uint32_t o_gid) {
162 struct smb_acl_entry *smb_ace;
163 int i, count;
164 uint16_t type, perm;
165 uint32_t id;
166 int offset = 0;
167 char *buf = NULL;
169 count = theacl->count;
171 buf = talloc_zero_size(mem_ctx, count * 8);
172 if (!buf) {
173 return NULL;
176 smb_ace = theacl->acl;
178 for (i = 0; i < count; i++) {
179 /* Calculate type */
180 /* Map type to SMB_ACL_* to VXFS_ACL_* */
181 switch(smb_ace->a_type) {
182 case SMB_ACL_USER:
183 type = VXFS_ACL_USER;
184 break;
185 case SMB_ACL_USER_OBJ:
186 type = VXFS_ACL_USER_OBJ;
187 break;
188 case SMB_ACL_GROUP:
189 type = VXFS_ACL_GROUP;
190 break;
191 case SMB_ACL_GROUP_OBJ:
192 type = VXFS_ACL_GROUP_OBJ;
193 break;
194 case SMB_ACL_OTHER:
195 type = VXFS_ACL_OTHER;
196 break;
197 case SMB_ACL_MASK:
198 type = VXFS_ACL_MASK;
199 break;
200 default:
201 type = -1;
202 talloc_free(buf);
203 return NULL;
206 type = type & 0xff;
208 /* Calculate id:
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) {
213 case SMB_ACL_USER:
214 id = smb_ace->info.user.uid;
215 break;
216 case SMB_ACL_GROUP:
217 id = smb_ace->info.group.gid;
218 break;
219 case SMB_ACL_USER_OBJ:
220 id = o_uid;
221 break;
222 case SMB_ACL_GROUP_OBJ:
223 id = o_gid;
224 break;
225 case SMB_ACL_MASK:
226 case SMB_ACL_OTHER:
227 id = -1;
228 break;
229 default:
230 /* Can't happen.. */
231 id = -1;
232 break;
235 /* Calculate perm */
236 perm = smb_ace->a_perm & 0xff;
238 /* TYPE is the first 2 bytes of an entry */
239 SSVAL(buf, offset, type);
240 offset += 2;
242 /* PERM is the next 2 bytes of an entry */
243 SSVAL(buf, offset, perm);
244 offset += 2;
246 /* ID is the last 4 bytes of an entry */
247 SIVAL(buf, offset, id);
248 offset += 4;
250 smb_ace++;
253 qsort(buf, count, 8, vxfs_ace_cmp);
255 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
256 vxfs_print_ace_buf(buf, count);
258 return buf;
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
264 * respectively.
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,
270 TALLOC_CTX *mem_ctx)
272 int i, e_offset = 0, c_offset = 0;
273 uint16_t type, perm, o_perm;
274 uint32_t id, owner_id, group_id;
275 char *c_buf = NULL;
278 if (count < 2) {
279 return NULL;
282 c_buf = talloc_zero_size(mem_ctx, count * 8);
283 if (!c_buf) {
284 return NULL;
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);
293 (*new_count) = 2;
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);
304 e_offset += 2;
305 perm = SVAL(e_buf, e_offset);
306 e_offset += 2;
307 id = IVAL(e_buf, e_offset);
308 e_offset += 4;
310 switch(type) {
311 case VXFS_ACL_USER:
312 if (id == owner_id) {
313 o_perm = SVAL(c_buf, 2);
314 o_perm |= perm;
315 SSVAL(c_buf, 2, o_perm);
316 DEBUG(10, ("vfs_vxfs: merging with owner"
317 "e_type = %u,"
318 "e_perm = %u,"
319 "e_id = %u\n", (unsigned int)type,
320 (unsigned int)perm,
321 (unsigned int)id));
322 continue;
324 break;
325 case VXFS_ACL_GROUP:
326 if (id == group_id) {
327 o_perm = SVAL(c_buf, 10);
328 o_perm |= perm;
329 SSVAL(c_buf, 10, o_perm);
330 DEBUG(10, ("vfs_vxfs: merging with owner group"
331 "e_type = %u,"
332 "e_perm = %u,"
333 "e_id = %u\n", (unsigned int)type,
334 (unsigned int)perm,
335 (unsigned int)id));
336 continue;
338 break;
341 SSVAL(c_buf, c_offset, type);
342 c_offset += 2;
344 SSVAL(c_buf, c_offset, perm);
345 c_offset += 2;
347 SIVAL(c_buf, c_offset, id);
348 c_offset += 4;
350 (*new_count)++;
352 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count));
353 return c_buf;
356 /* Actually compare New ACL and existing ACL buf */
357 static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count,
358 int e_count) {
360 uint16_t e_type, n_type;
361 int offset = 0;
363 if (!e_buf && !n_buf) {
364 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
365 return false;
368 if ((e_count < 2) || (n_count < 2)) {
369 return false;
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"));
392 return false;
395 if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) {
396 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
397 "buffers not same!\n"));
398 return false;
401 return true;
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
406 * from parent.
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,
420 SMB_ACL_T the_acl,
421 SMB_ACL_TYPE_T the_acl_type)
423 SMB_ACL_T existing_acl = NULL;
424 bool ret = false;
425 int count = 0;
426 TALLOC_CTX *mem_ctx = talloc_tos();
427 char *existing_buf = NULL, *new_buf = NULL, *compact_buf = NULL;
428 int status;
429 NTSTATUS ntstatus;
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"));
436 goto out;
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"));
444 goto out;
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);
451 goto out;
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);
458 if (!existing_buf)
459 goto out;
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);
464 if (!new_buf) {
465 goto out;
468 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
469 compact_buf = vxfs_compact_buf(existing_buf, &count,
470 existing_acl->count,
471 mem_ctx);
472 if (!compact_buf) {
473 goto out;
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,
484 count)) {
485 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
486 ret = true;
487 goto out;
488 } else
489 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
490 } else {
491 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
494 out:
496 TALLOC_FREE(existing_acl);
497 TALLOC_FREE(existing_buf);
498 TALLOC_FREE(compact_buf);
499 TALLOC_FREE(new_buf);
501 return ret;
504 #ifdef VXFS_ACL_SHARE
505 static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle,
506 struct files_struct *fsp,
507 SMB_ACL_TYPE_T type,
508 SMB_ACL_T theacl)
511 if (vxfs_compare(fsp, theacl, type)) {
512 return 0;
515 return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
517 #endif
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){
522 int ret = 0;
523 int tmp_ret = 0;
525 DBG_DEBUG("In vxfs_fset_xattr\n");
527 ret = vxfs_setxattr_fd(fsp_get_io_fd(fsp), name, value, size, flags);
528 if ((ret == 0) ||
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);
537 if (tmp_ret == 0) {
538 SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, XATTR_USER_NTACL_V0);
539 DBG_DEBUG("Old style xattr %s removed...\n", XATTR_USER_NTACL_V0);
542 return ret;
545 DBG_DEBUG("Fallback to xattr");
546 if (strcmp(name, XATTR_NTACL_NAME) == 0) {
547 return SMB_VFS_NEXT_FSETXATTR(handle, fsp, XATTR_USER_NTACL,
548 value, size, flags);
551 /* Clients can't set XATTR_USER_NTACL directly. */
552 if (vxfs_strcasecmp(name, XATTR_USER_NTACL) == 0) {
553 errno = EACCES;
554 return -1;
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){
563 int ret;
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))) {
570 return ret;
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,
576 value, size);
579 /* Clients can't see XATTR_USER_NTACL directly. */
580 if (vxfs_strcasecmp(name, XATTR_USER_NTACL) == 0) {
581 errno = ENOATTR;
582 return -1;
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,
597 XATTR_USER_NTACL);
598 } else {
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,
602 name);
605 old_errno = errno;
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) {
613 errno = old_errno;
615 if ((ret_new != -1) && (ret == -1)) {
616 ret = ret_new;
619 return ret;
623 static size_t vxfs_filter_list(char *list, size_t size)
625 char *str = list;
627 while (str - list < size) {
628 size_t element_len = strlen(str) + 1;
629 if (vxfs_strcasecmp(str, XATTR_USER_NTACL) == 0) {
630 memmove(str,
631 str + element_len,
632 size - (str - list) - element_len);
633 size -= element_len;
634 continue;
636 str += element_len;
638 return size;
641 static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle,
642 struct files_struct *fsp, char *list,
643 size_t size)
645 ssize_t result;
647 result = vxfs_listxattr_fd(fsp_get_io_fd(fsp), list, size);
648 if (result >= 0 || ((errno != ENOTSUP) && (errno != ENOSYS))) {
649 return result;
652 result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
654 if (result <= 0) {
655 return result;
658 /* Remove any XATTR_USER_NTACL elements from the returned list. */
659 result = vxfs_filter_list(list, result);
661 return result;
664 static NTSTATUS vxfs_fset_ea_dos_attributes(struct vfs_handle_struct *handle,
665 struct files_struct *fsp,
666 uint32_t dosmode)
668 NTSTATUS err;
669 int ret = 0;
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);
676 return err;
678 if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
679 ret = vxfs_checkwxattr_fd(fsp_get_io_fd(fsp));
680 if (ret == -1) {
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);
690 if (ret == -1) {
691 if ((errno != EOPNOTSUPP) && (errno != EINVAL)) {
692 return map_nt_error_from_unix(errno);
696 return NT_STATUS_OK;
699 static int vfs_vxfs_connect(struct vfs_handle_struct *handle,
700 const char *service, const char *user)
703 int ret;
705 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
706 if (ret < 0) {
707 return ret;
710 vxfs_init();
712 return 0;
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,
720 #endif
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,
731 static_decl_vfs;
732 NTSTATUS vfs_vxfs_init(TALLOC_CTX *ctx)
734 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "vxfs",
735 &vfs_vxfs_fns);