nfs4acls: Use talloc_realloc()
[Samba.git] / source3 / modules / vfs_vxfs.c
blob4bfbef3530bbc0f5c114c5b616ab5fcdb47bc378
1 /*
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/>.
22 #include "includes.h"
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"
29 #undef DBGC_CLASS
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
42 * to ANY USER.
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
50 * the file system.
52 * If you need to modify this define, do
53 * so using CFLAGS on your build command
54 * line.
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"
63 #endif
65 /* type values */
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
76 * Compare aces
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)
83 int ret = 0;
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);
92 if (!ret) {
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);
97 ret = id_a1 - id_a2;
100 return ret;
103 static void vxfs_print_ace_buf(char *buf, int count) {
105 int i, offset = 0;
106 uint16_t type, perm;
107 uint32_t id;
109 DEBUG(10, ("vfs_vxfs: Printing aces:\n"));
110 for (i = 0; i < count; i++) {
111 type = SVAL(buf, offset);
112 offset += 2;
113 perm = SVAL(buf, offset);
114 offset += 2;
115 id = IVAL(buf, offset);
116 offset += 4;
118 DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n",
119 (unsigned int)type, (unsigned int)perm,
120 (unsigned int)id));
125 * Sort aces so that comparing 2 ACLs will be straight forward.
126 * This function will fill buffer as follows:
127 * For each ace:
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
133 * becomes easy.
135 static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx,
136 uint32_t o_uid,
137 uint32_t o_gid) {
139 struct smb_acl_entry *smb_ace;
140 int i, count;
141 uint16_t type, perm;
142 uint32_t id;
143 int offset = 0;
144 char *buf = NULL;
146 count = theacl->count;
148 buf = talloc_zero_size(mem_ctx, count * 8);
149 if (!buf) {
150 return NULL;
153 smb_ace = theacl->acl;
155 for (i = 0; i < count; i++) {
156 /* Calculate type */
157 /* Map type to SMB_ACL_* to VXFS_ACL_* */
158 switch(smb_ace->a_type) {
159 case SMB_ACL_USER:
160 type = VXFS_ACL_USER;
161 break;
162 case SMB_ACL_USER_OBJ:
163 type = VXFS_ACL_USER_OBJ;
164 break;
165 case SMB_ACL_GROUP:
166 type = VXFS_ACL_GROUP;
167 break;
168 case SMB_ACL_GROUP_OBJ:
169 type = VXFS_ACL_GROUP_OBJ;
170 break;
171 case SMB_ACL_OTHER:
172 type = VXFS_ACL_OTHER;
173 break;
174 case SMB_ACL_MASK:
175 type = VXFS_ACL_MASK;
176 break;
177 default:
178 type = -1;
179 talloc_free(buf);
180 return NULL;
183 type = type & 0xff;
185 /* Calculate id:
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) {
190 case SMB_ACL_USER:
191 id = smb_ace->info.user.uid;
192 break;
193 case SMB_ACL_GROUP:
194 id = smb_ace->info.group.gid;
195 break;
196 case SMB_ACL_USER_OBJ:
197 id = o_uid;
198 break;
199 case SMB_ACL_GROUP_OBJ:
200 id = o_gid;
201 break;
202 case SMB_ACL_MASK:
203 case SMB_ACL_OTHER:
204 id = -1;
205 break;
206 default:
207 /* Can't happen.. */
208 id = -1;
209 break;
212 /* Calculate perm */
213 perm = smb_ace->a_perm & 0xff;
215 /* TYPE is the first 2 bytes of an entry */
216 SSVAL(buf, offset, type);
217 offset += 2;
219 /* PERM is the next 2 bytes of an entry */
220 SSVAL(buf, offset, perm);
221 offset += 2;
223 /* ID is the last 4 bytes of an entry */
224 SIVAL(buf, offset, id);
225 offset += 4;
227 smb_ace++;
230 qsort(buf, count, 8, vxfs_ace_cmp);
232 DEBUG(10, ("vfs_vxfs: Print sorted aces:\n"));
233 vxfs_print_ace_buf(buf, count);
235 return buf;
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
241 * respectively.
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,
247 TALLOC_CTX *mem_ctx)
249 int i, e_offset = 0, c_offset = 0;
250 uint16_t type, perm, o_perm;
251 uint32_t id, owner_id, group_id;
252 char *c_buf = NULL;
255 if (count < 2) {
256 return NULL;
259 c_buf = talloc_zero_size(mem_ctx, count * 8);
260 if (!c_buf) {
261 return NULL;
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);
270 (*new_count) = 2;
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);
281 e_offset += 2;
282 perm = SVAL(e_buf, e_offset);
283 e_offset += 2;
284 id = IVAL(e_buf, e_offset);
285 e_offset += 4;
287 switch(type) {
288 case VXFS_ACL_USER:
289 if (id == owner_id) {
290 o_perm = SVAL(c_buf, 2);
291 o_perm |= perm;
292 SSVAL(c_buf, 2, o_perm);
293 DEBUG(10, ("vfs_vxfs: merging with owner"
294 "e_type = %u,"
295 "e_perm = %u,"
296 "e_id = %u\n", (unsigned int)type,
297 (unsigned int)perm,
298 (unsigned int)id));
299 continue;
301 break;
302 case VXFS_ACL_GROUP:
303 if (id == group_id) {
304 o_perm = SVAL(c_buf, 10);
305 o_perm |= perm;
306 SSVAL(c_buf, 10, o_perm);
307 DEBUG(10, ("vfs_vxfs: merging with owner group"
308 "e_type = %u,"
309 "e_perm = %u,"
310 "e_id = %u\n", (unsigned int)type,
311 (unsigned int)perm,
312 (unsigned int)id));
313 continue;
315 break;
318 SSVAL(c_buf, c_offset, type);
319 c_offset += 2;
321 SSVAL(c_buf, c_offset, perm);
322 c_offset += 2;
324 SIVAL(c_buf, c_offset, id);
325 c_offset += 4;
327 (*new_count)++;
329 DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count));
330 return c_buf;
333 /* Actually compare New ACL and existing ACL buf */
334 static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count,
335 int e_count) {
337 uint16_t e_type, n_type, e_perm, n_perm;
338 uint32_t e_id, n_id;
339 int i, offset = 0;
341 if (!e_buf && !n_buf) {
342 DEBUG(10, ("vfs_vxfs: Empty buffers!\n"));
343 return false;
346 if ((e_count < 2) || (n_count < 2)) {
347 return false;
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"));
370 return false;
373 if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) {
374 DEBUG(10, ("vfs_vxfs: Compare with memcmp,"
375 "buffers not same!\n"));
376 return false;
379 return true;
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
384 * from parent.
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;
401 bool ret = false;
402 int i, count = 0;
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;
406 int status;
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,
410 mem_ctx);
411 if (existing_acl == NULL) {
412 DEBUG(10, ("vfs_vxfs: Failed to get ACL\n"));
413 goto out;
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"));
421 goto out;
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"));
427 goto out;
430 status = SMB_VFS_STAT(conn, smb_fname);
431 if (status == -1) {
432 DEBUG(10, ("vfs_vxfs: stat failed!\n"));
433 goto out;
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);
440 if (!existing_buf)
441 goto out;
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);
446 if (!new_buf) {
447 goto out;
450 DEBUG(10, ("vfs_vxfs: Compact existing buf\n"));
451 compact_buf = vxfs_compact_buf(existing_buf, &count,
452 existing_acl->count,
453 mem_ctx);
454 if (!compact_buf) {
455 goto out;
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,
466 count)) {
467 DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n"));
468 ret = true;
469 goto out;
470 } else
471 DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n"));
472 } else {
473 DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n"));
476 out:
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);
484 return ret;
487 static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp,
488 SMB_ACL_T theacl)
491 if (vxfs_compare(fsp->conn, fsp->fsp_name->base_name, theacl,
492 SMB_ACL_TYPE_ACCESS)) {
493 return 0;
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)) {
503 return 0;
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,
511 int flags){
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,
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_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,
537 value, size, flags);
540 /* Clients can't set XATTR_USER_NTACL directly. */
541 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
542 errno = EACCES;
543 return -1;
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,
557 value, size);
560 /* Clients can't see XATTR_USER_NTACL directly. */
561 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
562 errno = ENOATTR;
563 return -1;
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,
577 value, size);
580 /* Clients can't see XATTR_USER_NTACL directly. */
581 if (strcasecmp(name, XATTR_USER_NTACL) == 0) {
582 errno = ENOATTR;
583 return -1;
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) {
600 errno = ENOATTR;
601 return -1;
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) {
618 errno = ENOATTR;
619 return -1;
622 return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name);
625 static size_t vxfs_filter_list(char *list, size_t size)
627 char *str = list;
629 while (str - list < size) {
630 size_t element_len = strlen(str) + 1;
631 if (strcasecmp(str, XATTR_USER_NTACL) == 0) {
632 memmove(str,
633 str + element_len,
634 size - (str - list) - element_len);
635 size -= element_len;
636 continue;
638 str += element_len;
640 return size;
643 static ssize_t vxfs_listxattr(vfs_handle_struct *handle, const char *path,
644 char *list, size_t size)
646 ssize_t result;
648 result = SMB_VFS_NEXT_LISTXATTR(handle, path, list, size);
650 if (result <= 0) {
651 return result;
654 /* Remove any XATTR_USER_NTACL elements from the returned list. */
655 result = vxfs_filter_list(list, result);
657 return result;
660 static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle,
661 struct files_struct *fsp, char *list,
662 size_t size)
664 ssize_t result;
666 result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size);
668 if (result <= 0) {
669 return result;
672 /* Remove any XATTR_USER_NTACL elements from the returned list. */
673 result = vxfs_filter_list(list, result);
675 return 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);
684 if (ret < 0) {
685 return ret;
687 return 0;
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",
710 &vfs_vxfs_fns);