r4231: commiting changes to 3.0.10
[Samba.git] / source / smbd / posix_acls.c
blob321d5e20ab45b50bd4197180be72c98b31a4f9ee
1 /*
2 Unix SMB/CIFS implementation.
3 SMB NT Security Descriptor / Unix permission conversion.
4 Copyright (C) Jeremy Allison 1994-2000.
5 Copyright (C) Andreas Gruenbacher 2002.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_ACLS
27 /****************************************************************************
28 Data structures representing the internal ACE format.
29 ****************************************************************************/
31 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
32 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
34 typedef union posix_id {
35 uid_t uid;
36 gid_t gid;
37 int world;
38 } posix_id;
40 typedef struct canon_ace {
41 struct canon_ace *next, *prev;
42 SMB_ACL_TAG_T type;
43 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
44 DOM_SID trustee;
45 enum ace_owner owner_type;
46 enum ace_attribute attr;
47 posix_id unix_ug;
48 BOOL inherited;
49 } canon_ace;
51 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
54 * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
55 * attribute on disk.
57 * | 1 | 1 | 2 | 2 | ....
58 * +------+------+-------------+---------------------+-------------+--------------------+
59 * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
60 * +------+------+-------------+---------------------+-------------+--------------------+
63 #define PAI_VERSION_OFFSET 0
64 #define PAI_FLAG_OFFSET 1
65 #define PAI_NUM_ENTRIES_OFFSET 2
66 #define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
67 #define PAI_ENTRIES_BASE 6
69 #define PAI_VERSION 1
70 #define PAI_ACL_FLAG_PROTECTED 0x1
71 #define PAI_ENTRY_LENGTH 5
74 * In memory format of user.SAMBA_PAI attribute.
77 struct pai_entry {
78 struct pai_entry *next, *prev;
79 enum ace_owner owner_type;
80 posix_id unix_ug;
83 struct pai_val {
84 BOOL protected;
85 unsigned int num_entries;
86 struct pai_entry *entry_list;
87 unsigned int num_def_entries;
88 struct pai_entry *def_entry_list;
91 /************************************************************************
92 Return a uint32 of the pai_entry principal.
93 ************************************************************************/
95 static uint32 get_pai_entry_val(struct pai_entry *paie)
97 switch (paie->owner_type) {
98 case UID_ACE:
99 DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
100 return (uint32)paie->unix_ug.uid;
101 case GID_ACE:
102 DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
103 return (uint32)paie->unix_ug.gid;
104 case WORLD_ACE:
105 default:
106 DEBUG(10,("get_pai_entry_val: world ace\n"));
107 return (uint32)-1;
111 /************************************************************************
112 Return a uint32 of the entry principal.
113 ************************************************************************/
115 static uint32 get_entry_val(canon_ace *ace_entry)
117 switch (ace_entry->owner_type) {
118 case UID_ACE:
119 DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
120 return (uint32)ace_entry->unix_ug.uid;
121 case GID_ACE:
122 DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
123 return (uint32)ace_entry->unix_ug.gid;
124 case WORLD_ACE:
125 default:
126 DEBUG(10,("get_entry_val: world ace\n"));
127 return (uint32)-1;
131 /************************************************************************
132 Count the inherited entries.
133 ************************************************************************/
135 static unsigned int num_inherited_entries(canon_ace *ace_list)
137 unsigned int num_entries = 0;
139 for (; ace_list; ace_list = ace_list->next)
140 if (ace_list->inherited)
141 num_entries++;
142 return num_entries;
145 /************************************************************************
146 Create the on-disk format. Caller must free.
147 ************************************************************************/
149 static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL protected, size_t *store_size)
151 char *pai_buf = NULL;
152 canon_ace *ace_list = NULL;
153 char *entry_offset = NULL;
154 unsigned int num_entries = 0;
155 unsigned int num_def_entries = 0;
157 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
158 if (ace_list->inherited)
159 num_entries++;
161 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
162 if (ace_list->inherited)
163 num_def_entries++;
165 DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
167 *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
169 pai_buf = SMB_MALLOC(*store_size);
170 if (!pai_buf) {
171 return NULL;
174 /* Set up the header. */
175 memset(pai_buf, '\0', PAI_ENTRIES_BASE);
176 SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
177 SCVAL(pai_buf,PAI_FLAG_OFFSET,(protected ? PAI_ACL_FLAG_PROTECTED : 0));
178 SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
179 SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
181 entry_offset = pai_buf + PAI_ENTRIES_BASE;
183 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
184 if (ace_list->inherited) {
185 uint8 type_val = (unsigned char)ace_list->owner_type;
186 uint32 entry_val = get_entry_val(ace_list);
188 SCVAL(entry_offset,0,type_val);
189 SIVAL(entry_offset,1,entry_val);
190 entry_offset += PAI_ENTRY_LENGTH;
194 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
195 if (ace_list->inherited) {
196 uint8 type_val = (unsigned char)ace_list->owner_type;
197 uint32 entry_val = get_entry_val(ace_list);
199 SCVAL(entry_offset,0,type_val);
200 SIVAL(entry_offset,1,entry_val);
201 entry_offset += PAI_ENTRY_LENGTH;
205 return pai_buf;
208 /************************************************************************
209 Store the user.SAMBA_PAI attribute on disk.
210 ************************************************************************/
212 static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
213 canon_ace *dir_ace_list, BOOL protected)
215 int ret;
216 size_t store_size;
217 char *pai_buf;
219 if (!lp_map_acl_inherit(SNUM(fsp->conn)))
220 return;
223 * Don't store if this ACL isn't protected and
224 * none of the entries in it are marked as inherited.
227 if (!protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
228 /* Instead just remove the attribute if it exists. */
229 if (fsp->fd != -1)
230 SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
231 else
232 SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
233 return;
236 pai_buf = create_pai_buf(file_ace_list, dir_ace_list, protected, &store_size);
238 if (fsp->fd != -1)
239 ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
240 pai_buf, store_size, 0);
241 else
242 ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
243 pai_buf, store_size, 0);
245 SAFE_FREE(pai_buf);
247 DEBUG(10,("store_inheritance_attribute:%s for file %s\n", protected ? " (protected)" : "", fsp->fsp_name));
248 if (ret == -1 && !no_acl_syscall_error(errno))
249 DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
252 /************************************************************************
253 Delete the in memory inheritance info.
254 ************************************************************************/
256 static void free_inherited_info(struct pai_val *pal)
258 if (pal) {
259 struct pai_entry *paie, *paie_next;
260 for (paie = pal->entry_list; paie; paie = paie_next) {
261 paie_next = paie->next;
262 SAFE_FREE(paie);
264 for (paie = pal->def_entry_list; paie; paie = paie_next) {
265 paie_next = paie->next;
266 SAFE_FREE(paie);
268 SAFE_FREE(pal);
272 /************************************************************************
273 Was this ACL protected ?
274 ************************************************************************/
276 static BOOL get_protected_flag(struct pai_val *pal)
278 if (!pal)
279 return False;
280 return pal->protected;
283 /************************************************************************
284 Was this ACE inherited ?
285 ************************************************************************/
287 static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
289 struct pai_entry *paie;
291 if (!pal)
292 return False;
294 /* If the entry exists it is inherited. */
295 for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
296 if (ace_entry->owner_type == paie->owner_type &&
297 get_entry_val(ace_entry) == get_pai_entry_val(paie))
298 return True;
300 return False;
303 /************************************************************************
304 Ensure an attribute just read is valid.
305 ************************************************************************/
307 static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
309 uint16 num_entries;
310 uint16 num_def_entries;
312 if (pai_buf_data_size < PAI_ENTRIES_BASE) {
313 /* Corrupted - too small. */
314 return False;
317 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
318 return False;
320 num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
321 num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
323 /* Check the entry lists match. */
324 /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
326 if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
327 return False;
329 return True;
333 /************************************************************************
334 Convert to in-memory format.
335 ************************************************************************/
337 static struct pai_val *create_pai_val(char *buf, size_t size)
339 char *entry_offset;
340 struct pai_val *paiv = NULL;
341 int i;
343 if (!check_pai_ok(buf, size))
344 return NULL;
346 paiv = SMB_MALLOC_P(struct pai_val);
347 if (!paiv)
348 return NULL;
350 memset(paiv, '\0', sizeof(struct pai_val));
352 paiv->protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
354 paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
355 paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
357 entry_offset = buf + PAI_ENTRIES_BASE;
359 DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
360 paiv->protected ? " (protected)" : "", paiv->num_entries, paiv->num_def_entries ));
362 for (i = 0; i < paiv->num_entries; i++) {
363 struct pai_entry *paie;
365 paie = SMB_MALLOC_P(struct pai_entry);
366 if (!paie) {
367 free_inherited_info(paiv);
368 return NULL;
371 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
372 switch( paie->owner_type) {
373 case UID_ACE:
374 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
375 DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
376 break;
377 case GID_ACE:
378 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
379 DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
380 break;
381 case WORLD_ACE:
382 paie->unix_ug.world = -1;
383 DEBUG(10,("create_pai_val: world ace\n"));
384 break;
385 default:
386 free_inherited_info(paiv);
387 return NULL;
389 entry_offset += PAI_ENTRY_LENGTH;
390 DLIST_ADD(paiv->entry_list, paie);
393 for (i = 0; i < paiv->num_def_entries; i++) {
394 struct pai_entry *paie;
396 paie = SMB_MALLOC_P(struct pai_entry);
397 if (!paie) {
398 free_inherited_info(paiv);
399 return NULL;
402 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
403 switch( paie->owner_type) {
404 case UID_ACE:
405 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
406 DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
407 break;
408 case GID_ACE:
409 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
410 DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
411 break;
412 case WORLD_ACE:
413 paie->unix_ug.world = -1;
414 DEBUG(10,("create_pai_val: (def) world ace\n"));
415 break;
416 default:
417 free_inherited_info(paiv);
418 return NULL;
420 entry_offset += PAI_ENTRY_LENGTH;
421 DLIST_ADD(paiv->def_entry_list, paie);
424 return paiv;
427 /************************************************************************
428 Load the user.SAMBA_PAI attribute.
429 ************************************************************************/
431 static struct pai_val *load_inherited_info(files_struct *fsp)
433 char *pai_buf;
434 size_t pai_buf_size = 1024;
435 struct pai_val *paiv = NULL;
436 ssize_t ret;
438 if (!lp_map_acl_inherit(SNUM(fsp->conn)))
439 return NULL;
441 if ((pai_buf = SMB_MALLOC(pai_buf_size)) == NULL)
442 return NULL;
444 do {
445 if (fsp->fd != -1)
446 ret = SMB_VFS_FGETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
447 pai_buf, pai_buf_size);
448 else
449 ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
450 pai_buf, pai_buf_size);
452 if (ret == -1) {
453 if (errno != ERANGE) {
454 break;
456 /* Buffer too small - enlarge it. */
457 pai_buf_size *= 2;
458 SAFE_FREE(pai_buf);
459 if (pai_buf_size > 1024*1024) {
460 return NULL; /* Limit malloc to 1mb. */
462 if ((pai_buf = SMB_MALLOC(pai_buf_size)) == NULL)
463 return NULL;
465 } while (ret == -1);
467 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
469 if (ret == -1) {
470 /* No attribute or not supported. */
471 #if defined(ENOATTR)
472 if (errno != ENOATTR)
473 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
474 #else
475 if (errno != ENOSYS)
476 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
477 #endif
478 SAFE_FREE(pai_buf);
479 return NULL;
482 paiv = create_pai_val(pai_buf, ret);
484 if (paiv && paiv->protected)
485 DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
487 SAFE_FREE(pai_buf);
488 return paiv;
491 /****************************************************************************
492 Functions to manipulate the internal ACE format.
493 ****************************************************************************/
495 /****************************************************************************
496 Count a linked list of canonical ACE entries.
497 ****************************************************************************/
499 static size_t count_canon_ace_list( canon_ace *list_head )
501 size_t count = 0;
502 canon_ace *ace;
504 for (ace = list_head; ace; ace = ace->next)
505 count++;
507 return count;
510 /****************************************************************************
511 Free a linked list of canonical ACE entries.
512 ****************************************************************************/
514 static void free_canon_ace_list( canon_ace *list_head )
516 while (list_head) {
517 canon_ace *old_head = list_head;
518 DLIST_REMOVE(list_head, list_head);
519 SAFE_FREE(old_head);
523 /****************************************************************************
524 Function to duplicate a canon_ace entry.
525 ****************************************************************************/
527 static canon_ace *dup_canon_ace( canon_ace *src_ace)
529 canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
531 if (dst_ace == NULL)
532 return NULL;
534 *dst_ace = *src_ace;
535 dst_ace->prev = dst_ace->next = NULL;
536 return dst_ace;
539 /****************************************************************************
540 Print out a canon ace.
541 ****************************************************************************/
543 static void print_canon_ace(canon_ace *pace, int num)
545 fstring str;
547 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
548 dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
549 if (pace->owner_type == UID_ACE) {
550 const char *u_name = uidtoname(pace->unix_ug.uid);
551 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
552 } else if (pace->owner_type == GID_ACE) {
553 char *g_name = gidtoname(pace->unix_ug.gid);
554 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
555 } else
556 dbgtext( "other ");
557 switch (pace->type) {
558 case SMB_ACL_USER:
559 dbgtext( "SMB_ACL_USER ");
560 break;
561 case SMB_ACL_USER_OBJ:
562 dbgtext( "SMB_ACL_USER_OBJ ");
563 break;
564 case SMB_ACL_GROUP:
565 dbgtext( "SMB_ACL_GROUP ");
566 break;
567 case SMB_ACL_GROUP_OBJ:
568 dbgtext( "SMB_ACL_GROUP_OBJ ");
569 break;
570 case SMB_ACL_OTHER:
571 dbgtext( "SMB_ACL_OTHER ");
572 break;
574 if (pace->inherited)
575 dbgtext( "(inherited) ");
576 dbgtext( "perms ");
577 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
578 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
579 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
582 /****************************************************************************
583 Print out a canon ace list.
584 ****************************************************************************/
586 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
588 int count = 0;
590 if( DEBUGLVL( 10 )) {
591 dbgtext( "print_canon_ace_list: %s\n", name );
592 for (;ace_list; ace_list = ace_list->next, count++)
593 print_canon_ace(ace_list, count );
597 /****************************************************************************
598 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
599 ****************************************************************************/
601 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
603 mode_t ret = 0;
605 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
606 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
607 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
609 return ret;
612 /****************************************************************************
613 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
614 ****************************************************************************/
616 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
618 mode_t ret = 0;
620 if (mode & r_mask)
621 ret |= S_IRUSR;
622 if (mode & w_mask)
623 ret |= S_IWUSR;
624 if (mode & x_mask)
625 ret |= S_IXUSR;
627 return ret;
630 /****************************************************************************
631 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
632 an SMB_ACL_PERMSET_T.
633 ****************************************************************************/
635 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
637 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
638 return -1;
639 if (mode & S_IRUSR) {
640 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
641 return -1;
643 if (mode & S_IWUSR) {
644 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
645 return -1;
647 if (mode & S_IXUSR) {
648 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
649 return -1;
651 return 0;
653 /****************************************************************************
654 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
655 ****************************************************************************/
657 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
659 uid_to_sid( powner_sid, psbuf->st_uid );
660 gid_to_sid( pgroup_sid, psbuf->st_gid );
663 /****************************************************************************
664 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
665 delete the second one. If the first is deny, mask the permissions off and delete the allow
666 if the permissions become zero, delete the deny if the permissions are non zero.
667 ****************************************************************************/
669 static void merge_aces( canon_ace **pp_list_head )
671 canon_ace *list_head = *pp_list_head;
672 canon_ace *curr_ace_outer;
673 canon_ace *curr_ace_outer_next;
676 * First, merge allow entries with identical SIDs, and deny entries
677 * with identical SIDs.
680 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
681 canon_ace *curr_ace;
682 canon_ace *curr_ace_next;
684 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
686 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
688 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
690 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
691 (curr_ace->attr == curr_ace_outer->attr)) {
693 if( DEBUGLVL( 10 )) {
694 dbgtext("merge_aces: Merging ACE's\n");
695 print_canon_ace( curr_ace_outer, 0);
696 print_canon_ace( curr_ace, 0);
699 /* Merge two allow or two deny ACE's. */
701 curr_ace_outer->perms |= curr_ace->perms;
702 DLIST_REMOVE(list_head, curr_ace);
703 SAFE_FREE(curr_ace);
704 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
710 * Now go through and mask off allow permissions with deny permissions.
711 * We can delete either the allow or deny here as we know that each SID
712 * appears only once in the list.
715 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
716 canon_ace *curr_ace;
717 canon_ace *curr_ace_next;
719 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
721 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
723 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
726 * Subtract ACE's with different entries. Due to the ordering constraints
727 * we've put on the ACL, we know the deny must be the first one.
730 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
731 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
733 if( DEBUGLVL( 10 )) {
734 dbgtext("merge_aces: Masking ACE's\n");
735 print_canon_ace( curr_ace_outer, 0);
736 print_canon_ace( curr_ace, 0);
739 curr_ace->perms &= ~curr_ace_outer->perms;
741 if (curr_ace->perms == 0) {
744 * The deny overrides the allow. Remove the allow.
747 DLIST_REMOVE(list_head, curr_ace);
748 SAFE_FREE(curr_ace);
749 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
751 } else {
754 * Even after removing permissions, there
755 * are still allow permissions - delete the deny.
756 * It is safe to delete the deny here,
757 * as we are guarenteed by the deny first
758 * ordering that all the deny entries for
759 * this SID have already been merged into one
760 * before we can get to an allow ace.
763 DLIST_REMOVE(list_head, curr_ace_outer);
764 SAFE_FREE(curr_ace_outer);
765 break;
769 } /* end for curr_ace */
770 } /* end for curr_ace_outer */
772 /* We may have modified the list. */
774 *pp_list_head = list_head;
777 /****************************************************************************
778 Check if we need to return NT4.x compatible ACL entries.
779 ****************************************************************************/
781 static BOOL nt4_compatible_acls(void)
783 const char *compat = lp_acl_compatibility();
785 if (*compat == '\0') {
786 enum remote_arch_types ra_type = get_remote_arch();
788 /* Automatically adapt to client */
789 return (ra_type <= RA_WINNT);
790 } else
791 return (strequal(compat, "winnt"));
795 /****************************************************************************
796 Map canon_ace perms to permission bits NT.
797 The attr element is not used here - we only process deny entries on set,
798 not get. Deny entries are implicit on get with ace->perms = 0.
799 ****************************************************************************/
801 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
803 SEC_ACCESS sa;
804 uint32 nt_mask = 0;
806 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
808 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
809 nt_mask = UNIX_ACCESS_RWX;
810 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
812 * Windows NT refuses to display ACEs with no permissions in them (but
813 * they are perfectly legal with Windows 2000). If the ACE has empty
814 * permissions we cannot use 0, so we use the otherwise unused
815 * WRITE_OWNER permission, which we ignore when we set an ACL.
816 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
817 * to be changed in the future.
820 if (nt4_compatible_acls())
821 nt_mask = UNIX_ACCESS_NONE;
822 else
823 nt_mask = 0;
824 } else {
825 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
826 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
827 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
830 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
831 (unsigned int)ace->perms, (unsigned int)nt_mask ));
833 init_sec_access(&sa,nt_mask);
834 return sa;
837 /****************************************************************************
838 Map NT perms to a UNIX mode_t.
839 ****************************************************************************/
841 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
842 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
843 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
845 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
847 mode_t mode = 0;
849 switch(type) {
850 case S_IRUSR:
851 if(sec_access.mask & GENERIC_ALL_ACCESS)
852 mode = S_IRUSR|S_IWUSR|S_IXUSR;
853 else {
854 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
855 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
856 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
858 break;
859 case S_IRGRP:
860 if(sec_access.mask & GENERIC_ALL_ACCESS)
861 mode = S_IRGRP|S_IWGRP|S_IXGRP;
862 else {
863 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
864 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
865 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
867 break;
868 case S_IROTH:
869 if(sec_access.mask & GENERIC_ALL_ACCESS)
870 mode = S_IROTH|S_IWOTH|S_IXOTH;
871 else {
872 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
873 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
874 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
876 break;
879 return mode;
882 /****************************************************************************
883 Unpack a SEC_DESC into a UNIX owner and group.
884 ****************************************************************************/
886 static BOOL unpack_nt_owners(int snum, SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
888 DOM_SID owner_sid;
889 DOM_SID grp_sid;
891 *puser = (uid_t)-1;
892 *pgrp = (gid_t)-1;
894 if(security_info_sent == 0) {
895 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
896 return True;
900 * Validate the owner and group SID's.
903 memset(&owner_sid, '\0', sizeof(owner_sid));
904 memset(&grp_sid, '\0', sizeof(grp_sid));
906 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
909 * Don't immediately fail if the owner sid cannot be validated.
910 * This may be a group chown only set.
913 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
914 sid_copy(&owner_sid, psd->owner_sid);
915 if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) {
916 if (lp_force_unknown_acl_user(snum)) {
917 /* this allows take ownership to work
918 * reasonably */
919 extern struct current_user current_user;
920 *puser = current_user.uid;
921 } else {
922 DEBUG(3,("unpack_nt_owners: unable to validate"
923 " owner sid for %s\n",
924 sid_string_static(&owner_sid)));
925 return False;
931 * Don't immediately fail if the group sid cannot be validated.
932 * This may be an owner chown only set.
935 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
936 sid_copy(&grp_sid, psd->grp_sid);
937 if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) {
938 if (lp_force_unknown_acl_user(snum)) {
939 /* this allows take group ownership to work
940 * reasonably */
941 extern struct current_user current_user;
942 *pgrp = current_user.gid;
943 } else {
944 DEBUG(3,("unpack_nt_owners: unable to validate"
945 " group sid.\n"));
946 return False;
951 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
953 return True;
956 /****************************************************************************
957 Ensure the enforced permissions for this share apply.
958 ****************************************************************************/
960 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
962 int snum = SNUM(fsp->conn);
963 mode_t and_bits = (mode_t)0;
964 mode_t or_bits = (mode_t)0;
966 /* Get the initial bits to apply. */
968 if (fsp->is_directory) {
969 and_bits = lp_dir_security_mask(snum);
970 or_bits = lp_force_dir_security_mode(snum);
971 } else {
972 and_bits = lp_security_mask(snum);
973 or_bits = lp_force_security_mode(snum);
976 /* Now bounce them into the S_USR space. */
977 switch(type) {
978 case S_IRUSR:
979 /* Ensure owner has read access. */
980 pace->perms |= S_IRUSR;
981 if (fsp->is_directory)
982 pace->perms |= (S_IWUSR|S_IXUSR);
983 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
984 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
985 break;
986 case S_IRGRP:
987 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
988 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
989 break;
990 case S_IROTH:
991 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
992 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
993 break;
996 pace->perms = ((pace->perms & and_bits)|or_bits);
999 /****************************************************************************
1000 Check if a given uid/SID is in a group gid/SID. This is probably very
1001 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
1002 ****************************************************************************/
1004 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
1006 extern DOM_SID global_sid_World;
1007 fstring u_name;
1008 fstring g_name;
1009 extern struct current_user current_user;
1011 /* "Everyone" always matches every uid. */
1013 if (sid_equal(&group_ace->trustee, &global_sid_World))
1014 return True;
1016 /* Assume that the current user is in the current group (force group) */
1018 if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
1019 return True;
1021 fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
1022 fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
1025 * Due to the winbind interfaces we need to do this via names,
1026 * not uids/gids.
1029 return user_in_group_list(u_name, g_name, NULL, 0);
1032 /****************************************************************************
1033 A well formed POSIX file or default ACL has at least 3 entries, a
1034 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
1035 In addition, the owner must always have at least read access.
1036 When using this call on get_acl, the pst struct is valid and contains
1037 the mode of the file. When using this call on set_acl, the pst struct has
1038 been modified to have a mode containing the default for this file or directory
1039 type.
1040 ****************************************************************************/
1042 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
1043 files_struct *fsp,
1044 DOM_SID *pfile_owner_sid,
1045 DOM_SID *pfile_grp_sid,
1046 SMB_STRUCT_STAT *pst,
1047 BOOL setting_acl)
1049 extern DOM_SID global_sid_World;
1050 canon_ace *pace;
1051 BOOL got_user = False;
1052 BOOL got_grp = False;
1053 BOOL got_other = False;
1054 canon_ace *pace_other = NULL;
1055 canon_ace *pace_group = NULL;
1057 for (pace = *pp_ace; pace; pace = pace->next) {
1058 if (pace->type == SMB_ACL_USER_OBJ) {
1060 if (setting_acl)
1061 apply_default_perms(fsp, pace, S_IRUSR);
1062 got_user = True;
1064 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
1067 * Ensure create mask/force create mode is respected on set.
1070 if (setting_acl)
1071 apply_default_perms(fsp, pace, S_IRGRP);
1072 got_grp = True;
1073 pace_group = pace;
1075 } else if (pace->type == SMB_ACL_OTHER) {
1078 * Ensure create mask/force create mode is respected on set.
1081 if (setting_acl)
1082 apply_default_perms(fsp, pace, S_IROTH);
1083 got_other = True;
1084 pace_other = pace;
1088 if (!got_user) {
1089 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1090 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1091 return False;
1094 ZERO_STRUCTP(pace);
1095 pace->type = SMB_ACL_USER_OBJ;
1096 pace->owner_type = UID_ACE;
1097 pace->unix_ug.uid = pst->st_uid;
1098 pace->trustee = *pfile_owner_sid;
1099 pace->attr = ALLOW_ACE;
1101 if (setting_acl) {
1102 /* If we only got an "everyone" perm, just use that. */
1103 if (!got_grp && got_other)
1104 pace->perms = pace_other->perms;
1105 else if (got_grp && uid_entry_in_group(pace, pace_group))
1106 pace->perms = pace_group->perms;
1107 else
1108 pace->perms = 0;
1110 apply_default_perms(fsp, pace, S_IRUSR);
1111 } else {
1112 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
1115 DLIST_ADD(*pp_ace, pace);
1118 if (!got_grp) {
1119 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1120 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1121 return False;
1124 ZERO_STRUCTP(pace);
1125 pace->type = SMB_ACL_GROUP_OBJ;
1126 pace->owner_type = GID_ACE;
1127 pace->unix_ug.uid = pst->st_gid;
1128 pace->trustee = *pfile_grp_sid;
1129 pace->attr = ALLOW_ACE;
1130 if (setting_acl) {
1131 /* If we only got an "everyone" perm, just use that. */
1132 if (got_other)
1133 pace->perms = pace_other->perms;
1134 else
1135 pace->perms = 0;
1136 apply_default_perms(fsp, pace, S_IRGRP);
1137 } else {
1138 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
1141 DLIST_ADD(*pp_ace, pace);
1144 if (!got_other) {
1145 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1146 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1147 return False;
1150 ZERO_STRUCTP(pace);
1151 pace->type = SMB_ACL_OTHER;
1152 pace->owner_type = WORLD_ACE;
1153 pace->unix_ug.world = -1;
1154 pace->trustee = global_sid_World;
1155 pace->attr = ALLOW_ACE;
1156 if (setting_acl) {
1157 pace->perms = 0;
1158 apply_default_perms(fsp, pace, S_IROTH);
1159 } else
1160 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
1162 DLIST_ADD(*pp_ace, pace);
1165 return True;
1168 /****************************************************************************
1169 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
1170 If it does not have them, check if there are any entries where the trustee is the
1171 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
1172 ****************************************************************************/
1174 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
1176 BOOL got_user_obj, got_group_obj;
1177 canon_ace *current_ace;
1178 int i, entries;
1180 entries = count_canon_ace_list(ace);
1181 got_user_obj = False;
1182 got_group_obj = False;
1184 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1185 if (current_ace->type == SMB_ACL_USER_OBJ)
1186 got_user_obj = True;
1187 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
1188 got_group_obj = True;
1190 if (got_user_obj && got_group_obj) {
1191 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
1192 return;
1195 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1196 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
1197 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
1198 current_ace->type = SMB_ACL_USER_OBJ;
1199 got_user_obj = True;
1201 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
1202 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
1203 current_ace->type = SMB_ACL_GROUP_OBJ;
1204 got_group_obj = True;
1207 if (!got_user_obj)
1208 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
1209 if (!got_group_obj)
1210 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
1213 /****************************************************************************
1214 Unpack a SEC_DESC into two canonical ace lists.
1215 ****************************************************************************/
1217 static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
1218 DOM_SID *pfile_owner_sid,
1219 DOM_SID *pfile_grp_sid,
1220 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1221 SEC_ACL *dacl)
1223 extern DOM_SID global_sid_Creator_Owner;
1224 extern DOM_SID global_sid_Creator_Group;
1225 extern DOM_SID global_sid_World;
1226 extern struct generic_mapping file_generic_mapping;
1227 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
1228 canon_ace *file_ace = NULL;
1229 canon_ace *dir_ace = NULL;
1230 canon_ace *tmp_ace = NULL;
1231 canon_ace *current_ace = NULL;
1232 BOOL got_dir_allow = False;
1233 BOOL got_file_allow = False;
1234 int i, j;
1236 *ppfile_ace = NULL;
1237 *ppdir_ace = NULL;
1240 * Convert the incoming ACL into a more regular form.
1243 for(i = 0; i < dacl->num_aces; i++) {
1244 SEC_ACE *psa = &dacl->ace[i];
1246 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
1247 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
1248 return False;
1251 if (nt4_compatible_acls()) {
1253 * The security mask may be UNIX_ACCESS_NONE which should map into
1254 * no permissions (we overload the WRITE_OWNER bit for this) or it
1255 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
1256 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
1260 * Convert GENERIC bits to specific bits.
1263 se_map_generic(&psa->info.mask, &file_generic_mapping);
1265 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
1267 if(psa->info.mask != UNIX_ACCESS_NONE)
1268 psa->info.mask &= ~UNIX_ACCESS_NONE;
1273 * Deal with the fact that NT 4.x re-writes the canonical format
1274 * that we return for default ACLs. If a directory ACE is identical
1275 * to a inherited directory ACE then NT changes the bits so that the
1276 * first ACE is set to OI|IO and the second ACE for this SID is set
1277 * to CI. We need to repair this. JRA.
1280 for(i = 0; i < dacl->num_aces; i++) {
1281 SEC_ACE *psa1 = &dacl->ace[i];
1283 for (j = i + 1; j < dacl->num_aces; j++) {
1284 SEC_ACE *psa2 = &dacl->ace[j];
1286 if (psa1->info.mask != psa2->info.mask)
1287 continue;
1289 if (!sid_equal(&psa1->trustee, &psa2->trustee))
1290 continue;
1293 * Ok - permission bits and SIDs are equal.
1294 * Check if flags were re-written.
1297 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1299 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1300 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1302 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1304 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1305 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1311 for(i = 0; i < dacl->num_aces; i++) {
1312 SEC_ACE *psa = &dacl->ace[i];
1315 * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
1318 if (non_mappable_sid(&psa->trustee)) {
1319 fstring str;
1320 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
1321 sid_to_string(str, &psa->trustee) ));
1322 continue;
1326 * Create a cannon_ace entry representing this NT DACL ACE.
1329 if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
1330 free_canon_ace_list(file_ace);
1331 free_canon_ace_list(dir_ace);
1332 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
1333 return False;
1336 ZERO_STRUCTP(current_ace);
1338 sid_copy(&current_ace->trustee, &psa->trustee);
1341 * Try and work out if the SID is a user or group
1342 * as we need to flag these differently for POSIX.
1343 * Note what kind of a POSIX ACL this should map to.
1346 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
1347 current_ace->owner_type = WORLD_ACE;
1348 current_ace->unix_ug.world = -1;
1349 current_ace->type = SMB_ACL_OTHER;
1350 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
1351 current_ace->owner_type = UID_ACE;
1352 current_ace->unix_ug.uid = pst->st_uid;
1353 current_ace->type = SMB_ACL_USER_OBJ;
1356 * The Creator Owner entry only specifies inheritable permissions,
1357 * never access permissions. WinNT doesn't always set the ACE to
1358 *INHERIT_ONLY, though.
1361 if (nt4_compatible_acls())
1362 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1363 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
1364 current_ace->owner_type = GID_ACE;
1365 current_ace->unix_ug.gid = pst->st_gid;
1366 current_ace->type = SMB_ACL_GROUP_OBJ;
1369 * The Creator Group entry only specifies inheritable permissions,
1370 * never access permissions. WinNT doesn't always set the ACE to
1371 *INHERIT_ONLY, though.
1373 if (nt4_compatible_acls())
1374 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1376 } else if (NT_STATUS_IS_OK(sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid))) {
1377 current_ace->owner_type = UID_ACE;
1378 current_ace->type = SMB_ACL_USER;
1379 } else if (NT_STATUS_IS_OK(sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid))) {
1380 current_ace->owner_type = GID_ACE;
1381 current_ace->type = SMB_ACL_GROUP;
1382 } else {
1383 fstring str;
1385 free_canon_ace_list(file_ace);
1386 free_canon_ace_list(dir_ace);
1387 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
1388 sid_to_string(str, &current_ace->trustee) ));
1389 SAFE_FREE(current_ace);
1390 return False;
1394 * Map the given NT permissions into a UNIX mode_t containing only
1395 * S_I(R|W|X)USR bits.
1398 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
1399 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1400 current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
1403 * Now add the created ace to either the file list, the directory
1404 * list, or both. We *MUST* preserve the order here (hence we use
1405 * DLIST_ADD_END) as NT ACLs are order dependent.
1408 if (fsp->is_directory) {
1411 * We can only add to the default POSIX ACE list if the ACE is
1412 * designed to be inherited by both files and directories.
1415 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1416 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1418 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
1421 * Note if this was an allow ace. We can't process
1422 * any further deny ace's after this.
1425 if (current_ace->attr == ALLOW_ACE)
1426 got_dir_allow = True;
1428 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1429 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1430 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1431 free_canon_ace_list(file_ace);
1432 free_canon_ace_list(dir_ace);
1433 SAFE_FREE(current_ace);
1434 return False;
1437 if( DEBUGLVL( 10 )) {
1438 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1439 print_canon_ace( current_ace, 0);
1443 * If this is not an inherit only ACE we need to add a duplicate
1444 * to the file acl.
1447 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1448 canon_ace *dup_ace = dup_canon_ace(current_ace);
1450 if (!dup_ace) {
1451 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1452 free_canon_ace_list(file_ace);
1453 free_canon_ace_list(dir_ace);
1454 return False;
1458 * We must not free current_ace here as its
1459 * pointer is now owned by the dir_ace list.
1461 current_ace = dup_ace;
1462 } else {
1464 * We must not free current_ace here as its
1465 * pointer is now owned by the dir_ace list.
1467 current_ace = NULL;
1473 * Only add to the file ACL if not inherit only.
1476 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1477 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
1480 * Note if this was an allow ace. We can't process
1481 * any further deny ace's after this.
1484 if (current_ace->attr == ALLOW_ACE)
1485 got_file_allow = True;
1487 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1488 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1489 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1490 free_canon_ace_list(file_ace);
1491 free_canon_ace_list(dir_ace);
1492 SAFE_FREE(current_ace);
1493 return False;
1496 if( DEBUGLVL( 10 )) {
1497 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1498 print_canon_ace( current_ace, 0);
1500 all_aces_are_inherit_only = False;
1502 * We must not free current_ace here as its
1503 * pointer is now owned by the file_ace list.
1505 current_ace = NULL;
1509 * Free if ACE was not added.
1512 SAFE_FREE(current_ace);
1515 if (fsp->is_directory && all_aces_are_inherit_only) {
1517 * Windows 2000 is doing one of these weird 'inherit acl'
1518 * traverses to conserve NTFS ACL resources. Just pretend
1519 * there was no DACL sent. JRA.
1522 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1523 free_canon_ace_list(file_ace);
1524 free_canon_ace_list(dir_ace);
1525 file_ace = NULL;
1526 dir_ace = NULL;
1527 } else {
1529 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1530 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1531 * entries can be converted to *_OBJ. Usually we will already have these
1532 * entries in the Default ACL, and the Access ACL will not have them.
1534 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1535 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1538 *ppfile_ace = file_ace;
1539 *ppdir_ace = dir_ace;
1541 return True;
1544 /****************************************************************************
1545 ASCII art time again... JRA :-).
1547 We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1548 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1549 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1550 allow or deny have been merged, so the same SID can only appear once in the deny
1551 list or once in the allow list.
1553 We then process as follows :
1555 ---------------------------------------------------------------------------
1556 First pass - look for a Everyone DENY entry.
1558 If it is deny all (rwx) trunate the list at this point.
1559 Else, walk the list from this point and use the deny permissions of this
1560 entry as a mask on all following allow entries. Finally, delete
1561 the Everyone DENY entry (we have applied it to everything possible).
1563 In addition, in this pass we remove any DENY entries that have
1564 no permissions (ie. they are a DENY nothing).
1565 ---------------------------------------------------------------------------
1566 Second pass - only deal with deny user entries.
1568 DENY user1 (perms XXX)
1570 new_perms = 0
1571 for all following allow group entries where user1 is in group
1572 new_perms |= group_perms;
1574 user1 entry perms = new_perms & ~ XXX;
1576 Convert the deny entry to an allow entry with the new perms and
1577 push to the end of the list. Note if the user was in no groups
1578 this maps to a specific allow nothing entry for this user.
1580 The common case from the NT ACL choser (userX deny all) is
1581 optimised so we don't do the group lookup - we just map to
1582 an allow nothing entry.
1584 What we're doing here is inferring the allow permissions the
1585 person setting the ACE on user1 wanted by looking at the allow
1586 permissions on the groups the user is currently in. This will
1587 be a snapshot, depending on group membership but is the best
1588 we can do and has the advantage of failing closed rather than
1589 open.
1590 ---------------------------------------------------------------------------
1591 Third pass - only deal with deny group entries.
1593 DENY group1 (perms XXX)
1595 for all following allow user entries where user is in group1
1596 user entry perms = user entry perms & ~ XXX;
1598 If there is a group Everyone allow entry with permissions YYY,
1599 convert the group1 entry to an allow entry and modify its
1600 permissions to be :
1602 new_perms = YYY & ~ XXX
1604 and push to the end of the list.
1606 If there is no group Everyone allow entry then convert the
1607 group1 entry to a allow nothing entry and push to the end of the list.
1609 Note that the common case from the NT ACL choser (groupX deny all)
1610 cannot be optimised here as we need to modify user entries who are
1611 in the group to change them to a deny all also.
1613 What we're doing here is modifying the allow permissions of
1614 user entries (which are more specific in POSIX ACLs) to mask
1615 out the explicit deny set on the group they are in. This will
1616 be a snapshot depending on current group membership but is the
1617 best we can do and has the advantage of failing closed rather
1618 than open.
1619 ---------------------------------------------------------------------------
1620 Fourth pass - cope with cumulative permissions.
1622 for all allow user entries, if there exists an allow group entry with
1623 more permissive permissions, and the user is in that group, rewrite the
1624 allow user permissions to contain both sets of permissions.
1626 Currently the code for this is #ifdef'ed out as these semantics make
1627 no sense to me. JRA.
1628 ---------------------------------------------------------------------------
1630 Note we *MUST* do the deny user pass first as this will convert deny user
1631 entries into allow user entries which can then be processed by the deny
1632 group pass.
1634 The above algorithm took a *lot* of thinking about - hence this
1635 explaination :-). JRA.
1636 ****************************************************************************/
1638 /****************************************************************************
1639 Process a canon_ace list entries. This is very complex code. We need
1640 to go through and remove the "deny" permissions from any allow entry that matches
1641 the id of this entry. We have already refused any NT ACL that wasn't in correct
1642 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1643 we just remove it (to fail safe). We have already removed any duplicate ace
1644 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1645 allow entries.
1646 ****************************************************************************/
1648 static void process_deny_list( canon_ace **pp_ace_list )
1650 extern DOM_SID global_sid_World;
1651 canon_ace *ace_list = *pp_ace_list;
1652 canon_ace *curr_ace = NULL;
1653 canon_ace *curr_ace_next = NULL;
1655 /* Pass 1 above - look for an Everyone, deny entry. */
1657 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1658 canon_ace *allow_ace_p;
1660 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1662 if (curr_ace->attr != DENY_ACE)
1663 continue;
1665 if (curr_ace->perms == (mode_t)0) {
1667 /* Deny nothing entry - delete. */
1669 DLIST_REMOVE(ace_list, curr_ace);
1670 continue;
1673 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1674 continue;
1676 /* JRATEST - assert. */
1677 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1679 if (curr_ace->perms == ALL_ACE_PERMS) {
1682 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1683 * list at this point including this entry.
1686 canon_ace *prev_entry = curr_ace->prev;
1688 free_canon_ace_list( curr_ace );
1689 if (prev_entry)
1690 prev_entry->next = NULL;
1691 else {
1692 /* We deleted the entire list. */
1693 ace_list = NULL;
1695 break;
1698 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1701 * Only mask off allow entries.
1704 if (allow_ace_p->attr != ALLOW_ACE)
1705 continue;
1707 allow_ace_p->perms &= ~curr_ace->perms;
1711 * Now it's been applied, remove it.
1714 DLIST_REMOVE(ace_list, curr_ace);
1717 /* Pass 2 above - deal with deny user entries. */
1719 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1720 mode_t new_perms = (mode_t)0;
1721 canon_ace *allow_ace_p;
1722 canon_ace *tmp_ace;
1724 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1726 if (curr_ace->attr != DENY_ACE)
1727 continue;
1729 if (curr_ace->owner_type != UID_ACE)
1730 continue;
1732 if (curr_ace->perms == ALL_ACE_PERMS) {
1735 * Optimisation - this is a deny everything to this user.
1736 * Convert to an allow nothing and push to the end of the list.
1739 curr_ace->attr = ALLOW_ACE;
1740 curr_ace->perms = (mode_t)0;
1741 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1742 continue;
1745 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1747 if (allow_ace_p->attr != ALLOW_ACE)
1748 continue;
1750 /* We process GID_ACE and WORLD_ACE entries only. */
1752 if (allow_ace_p->owner_type == UID_ACE)
1753 continue;
1755 if (uid_entry_in_group( curr_ace, allow_ace_p))
1756 new_perms |= allow_ace_p->perms;
1760 * Convert to a allow entry, modify the perms and push to the end
1761 * of the list.
1764 curr_ace->attr = ALLOW_ACE;
1765 curr_ace->perms = (new_perms & ~curr_ace->perms);
1766 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1769 /* Pass 3 above - deal with deny group entries. */
1771 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1772 canon_ace *tmp_ace;
1773 canon_ace *allow_ace_p;
1774 canon_ace *allow_everyone_p = NULL;
1776 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1778 if (curr_ace->attr != DENY_ACE)
1779 continue;
1781 if (curr_ace->owner_type != GID_ACE)
1782 continue;
1784 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1786 if (allow_ace_p->attr != ALLOW_ACE)
1787 continue;
1789 /* Store a pointer to the Everyone allow, if it exists. */
1790 if (allow_ace_p->owner_type == WORLD_ACE)
1791 allow_everyone_p = allow_ace_p;
1793 /* We process UID_ACE entries only. */
1795 if (allow_ace_p->owner_type != UID_ACE)
1796 continue;
1798 /* Mask off the deny group perms. */
1800 if (uid_entry_in_group( allow_ace_p, curr_ace))
1801 allow_ace_p->perms &= ~curr_ace->perms;
1805 * Convert the deny to an allow with the correct perms and
1806 * push to the end of the list.
1809 curr_ace->attr = ALLOW_ACE;
1810 if (allow_everyone_p)
1811 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1812 else
1813 curr_ace->perms = (mode_t)0;
1814 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1818 /* Doing this fourth pass allows Windows semantics to be layered
1819 * on top of POSIX semantics. I'm not sure if this is desirable.
1820 * For example, in W2K ACLs there is no way to say, "Group X no
1821 * access, user Y full access" if user Y is a member of group X.
1822 * This seems completely broken semantics to me.... JRA.
1825 #if 0
1826 /* Pass 4 above - deal with allow entries. */
1828 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1829 canon_ace *allow_ace_p;
1831 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1833 if (curr_ace->attr != ALLOW_ACE)
1834 continue;
1836 if (curr_ace->owner_type != UID_ACE)
1837 continue;
1839 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1841 if (allow_ace_p->attr != ALLOW_ACE)
1842 continue;
1844 /* We process GID_ACE entries only. */
1846 if (allow_ace_p->owner_type != GID_ACE)
1847 continue;
1849 /* OR in the group perms. */
1851 if (uid_entry_in_group( curr_ace, allow_ace_p))
1852 curr_ace->perms |= allow_ace_p->perms;
1855 #endif
1857 *pp_ace_list = ace_list;
1860 /****************************************************************************
1861 Create a default mode that will be used if a security descriptor entry has
1862 no user/group/world entries.
1863 ****************************************************************************/
1865 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1867 int snum = SNUM(fsp->conn);
1868 mode_t and_bits = (mode_t)0;
1869 mode_t or_bits = (mode_t)0;
1870 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1872 if (fsp->is_directory)
1873 mode |= (S_IWUSR|S_IXUSR);
1876 * Now AND with the create mode/directory mode bits then OR with the
1877 * force create mode/force directory mode bits.
1880 if (fsp->is_directory) {
1881 and_bits = lp_dir_security_mask(snum);
1882 or_bits = lp_force_dir_security_mode(snum);
1883 } else {
1884 and_bits = lp_security_mask(snum);
1885 or_bits = lp_force_security_mode(snum);
1888 return ((mode & and_bits)|or_bits);
1891 /****************************************************************************
1892 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1893 succeeding.
1894 ****************************************************************************/
1896 static BOOL unpack_canon_ace(files_struct *fsp,
1897 SMB_STRUCT_STAT *pst,
1898 DOM_SID *pfile_owner_sid,
1899 DOM_SID *pfile_grp_sid,
1900 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1901 uint32 security_info_sent, SEC_DESC *psd)
1903 canon_ace *file_ace = NULL;
1904 canon_ace *dir_ace = NULL;
1906 *ppfile_ace = NULL;
1907 *ppdir_ace = NULL;
1909 if(security_info_sent == 0) {
1910 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1911 return False;
1915 * If no DACL then this is a chown only security descriptor.
1918 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1919 return True;
1922 * Now go through the DACL and create the canon_ace lists.
1925 if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
1926 &file_ace, &dir_ace, psd->dacl))
1927 return False;
1929 if ((file_ace == NULL) && (dir_ace == NULL)) {
1930 /* W2K traverse DACL set - ignore. */
1931 return True;
1935 * Go through the canon_ace list and merge entries
1936 * belonging to identical users of identical allow or deny type.
1937 * We can do this as all deny entries come first, followed by
1938 * all allow entries (we have mandated this before accepting this acl).
1941 print_canon_ace_list( "file ace - before merge", file_ace);
1942 merge_aces( &file_ace );
1944 print_canon_ace_list( "dir ace - before merge", dir_ace);
1945 merge_aces( &dir_ace );
1948 * NT ACLs are order dependent. Go through the acl lists and
1949 * process DENY entries by masking the allow entries.
1952 print_canon_ace_list( "file ace - before deny", file_ace);
1953 process_deny_list( &file_ace);
1955 print_canon_ace_list( "dir ace - before deny", dir_ace);
1956 process_deny_list( &dir_ace);
1959 * A well formed POSIX file or default ACL has at least 3 entries, a
1960 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1961 * and optionally a mask entry. Ensure this is the case.
1964 print_canon_ace_list( "file ace - before valid", file_ace);
1967 * A default 3 element mode entry for a file should be r-- --- ---.
1968 * A default 3 element mode entry for a directory should be rwx --- ---.
1971 pst->st_mode = create_default_mode(fsp, False);
1973 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1974 free_canon_ace_list(file_ace);
1975 free_canon_ace_list(dir_ace);
1976 return False;
1979 print_canon_ace_list( "dir ace - before valid", dir_ace);
1982 * A default inheritable 3 element mode entry for a directory should be the
1983 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1984 * it's a directory.
1987 pst->st_mode = create_default_mode(fsp, True);
1989 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1990 free_canon_ace_list(file_ace);
1991 free_canon_ace_list(dir_ace);
1992 return False;
1995 print_canon_ace_list( "file ace - return", file_ace);
1996 print_canon_ace_list( "dir ace - return", dir_ace);
1998 *ppfile_ace = file_ace;
1999 *ppdir_ace = dir_ace;
2000 return True;
2004 /******************************************************************************
2005 When returning permissions, try and fit NT display
2006 semantics if possible. Note the the canon_entries here must have been malloced.
2007 The list format should be - first entry = owner, followed by group and other user
2008 entries, last entry = other.
2010 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2011 are not ordered, and match on the most specific entry rather than walking a list,
2012 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2014 Entry 0: owner : deny all except read and write.
2015 Entry 1: group : deny all except read.
2016 Entry 2: owner : allow read and write.
2017 Entry 3: group : allow read.
2018 Entry 4: Everyone : allow read.
2020 But NT cannot display this in their ACL editor !
2021 ********************************************************************************/
2023 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
2025 canon_ace *list_head = *pp_list_head;
2026 canon_ace *owner_ace = NULL;
2027 canon_ace *other_ace = NULL;
2028 canon_ace *ace = NULL;
2030 for (ace = list_head; ace; ace = ace->next) {
2031 if (ace->type == SMB_ACL_USER_OBJ)
2032 owner_ace = ace;
2033 else if (ace->type == SMB_ACL_OTHER) {
2034 /* Last ace - this is "other" */
2035 other_ace = ace;
2039 if (!owner_ace || !other_ace) {
2040 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2041 filename ));
2042 return;
2046 * The POSIX algorithm applies to owner first, and other last,
2047 * so ensure they are arranged in this order.
2050 if (owner_ace) {
2051 DLIST_PROMOTE(list_head, owner_ace);
2054 if (other_ace) {
2055 DLIST_DEMOTE(list_head, other_ace, ace);
2058 /* We have probably changed the head of the list. */
2060 *pp_list_head = list_head;
2063 /****************************************************************************
2064 Create a linked list of canonical ACE entries.
2065 ****************************************************************************/
2067 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
2068 DOM_SID *powner, DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2070 extern DOM_SID global_sid_World;
2071 connection_struct *conn = fsp->conn;
2072 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2073 canon_ace *list_head = NULL;
2074 canon_ace *ace = NULL;
2075 canon_ace *next_ace = NULL;
2076 int entry_id = SMB_ACL_FIRST_ENTRY;
2077 SMB_ACL_ENTRY_T entry;
2078 size_t ace_count;
2080 while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2081 SMB_ACL_TAG_T tagtype;
2082 SMB_ACL_PERMSET_T permset;
2083 DOM_SID sid;
2084 posix_id unix_ug;
2085 enum ace_owner owner_type;
2087 /* get_next... */
2088 if (entry_id == SMB_ACL_FIRST_ENTRY)
2089 entry_id = SMB_ACL_NEXT_ENTRY;
2091 /* Is this a MASK entry ? */
2092 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2093 continue;
2095 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2096 continue;
2098 /* Decide which SID to use based on the ACL type. */
2099 switch(tagtype) {
2100 case SMB_ACL_USER_OBJ:
2101 /* Get the SID from the owner. */
2102 sid_copy(&sid, powner);
2103 unix_ug.uid = psbuf->st_uid;
2104 owner_type = UID_ACE;
2105 break;
2106 case SMB_ACL_USER:
2108 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2109 if (puid == NULL) {
2110 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2111 continue;
2114 * A SMB_ACL_USER entry for the owner is shadowed by the
2115 * SMB_ACL_USER_OBJ entry and Windows also cannot represent
2116 * that entry, so we ignore it. We also don't create such
2117 * entries out of the blue when setting ACLs, so a get/set
2118 * cycle will drop them.
2120 if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
2121 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2122 continue;
2124 uid_to_sid( &sid, *puid);
2125 unix_ug.uid = *puid;
2126 owner_type = UID_ACE;
2127 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2128 break;
2130 case SMB_ACL_GROUP_OBJ:
2131 /* Get the SID from the owning group. */
2132 sid_copy(&sid, pgroup);
2133 unix_ug.gid = psbuf->st_gid;
2134 owner_type = GID_ACE;
2135 break;
2136 case SMB_ACL_GROUP:
2138 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2139 if (pgid == NULL) {
2140 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2141 continue;
2143 gid_to_sid( &sid, *pgid);
2144 unix_ug.gid = *pgid;
2145 owner_type = GID_ACE;
2146 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2147 break;
2149 case SMB_ACL_MASK:
2150 acl_mask = convert_permset_to_mode_t(conn, permset);
2151 continue; /* Don't count the mask as an entry. */
2152 case SMB_ACL_OTHER:
2153 /* Use the Everyone SID */
2154 sid = global_sid_World;
2155 unix_ug.world = -1;
2156 owner_type = WORLD_ACE;
2157 break;
2158 default:
2159 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2160 continue;
2164 * Add this entry to the list.
2167 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2168 goto fail;
2170 ZERO_STRUCTP(ace);
2171 ace->type = tagtype;
2172 ace->perms = convert_permset_to_mode_t(conn, permset);
2173 ace->attr = ALLOW_ACE;
2174 ace->trustee = sid;
2175 ace->unix_ug = unix_ug;
2176 ace->owner_type = owner_type;
2177 ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2179 DLIST_ADD(list_head, ace);
2183 * This next call will ensure we have at least a user/group/world set.
2186 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
2187 goto fail;
2190 * Now go through the list, masking the permissions with the
2191 * acl_mask. Ensure all DENY Entries are at the start of the list.
2194 DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2196 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
2197 next_ace = ace->next;
2199 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2200 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2201 ace->perms &= acl_mask;
2203 if (ace->perms == 0) {
2204 DLIST_PROMOTE(list_head, ace);
2207 if( DEBUGLVL( 10 ) ) {
2208 print_canon_ace(ace, ace_count);
2212 arrange_posix_perms(fsp->fsp_name,&list_head );
2214 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
2216 return list_head;
2218 fail:
2220 free_canon_ace_list(list_head);
2221 return NULL;
2224 /****************************************************************************
2225 Attempt to apply an ACL to a file or directory.
2226 ****************************************************************************/
2228 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
2230 connection_struct *conn = fsp->conn;
2231 BOOL ret = False;
2232 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2233 canon_ace *p_ace;
2234 int i;
2235 SMB_ACL_ENTRY_T mask_entry;
2236 BOOL got_mask_entry = False;
2237 SMB_ACL_PERMSET_T mask_permset;
2238 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2239 BOOL needs_mask = False;
2240 mode_t mask_perms = 0;
2242 #if defined(POSIX_ACL_NEEDS_MASK)
2243 /* HP-UX always wants to have a mask (called "class" there). */
2244 needs_mask = True;
2245 #endif
2247 if (the_acl == NULL) {
2249 if (!no_acl_syscall_error(errno)) {
2251 * Only print this error message if we have some kind of ACL
2252 * support that's not working. Otherwise we would always get this.
2254 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2255 default_ace ? "default" : "file", strerror(errno) ));
2257 *pacl_set_support = False;
2258 return False;
2261 if( DEBUGLVL( 10 )) {
2262 dbgtext("set_canon_ace_list: setting ACL:\n");
2263 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2264 print_canon_ace( p_ace, i);
2268 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2269 SMB_ACL_ENTRY_T the_entry;
2270 SMB_ACL_PERMSET_T the_permset;
2273 * ACLs only "need" an ACL_MASK entry if there are any named user or
2274 * named group entries. But if there is an ACL_MASK entry, it applies
2275 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2276 * so that it doesn't deny (i.e., mask off) any permissions.
2279 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2280 needs_mask = True;
2281 mask_perms |= p_ace->perms;
2282 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2283 mask_perms |= p_ace->perms;
2287 * Get the entry for this ACE.
2290 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2291 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2292 i, strerror(errno) ));
2293 goto done;
2296 if (p_ace->type == SMB_ACL_MASK) {
2297 mask_entry = the_entry;
2298 got_mask_entry = True;
2302 * Ok - we now know the ACL calls should be working, don't
2303 * allow fallback to chmod.
2306 *pacl_set_support = True;
2309 * Initialise the entry from the canon_ace.
2313 * First tell the entry what type of ACE this is.
2316 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2317 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2318 i, strerror(errno) ));
2319 goto done;
2323 * Only set the qualifier (user or group id) if the entry is a user
2324 * or group id ACE.
2327 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2328 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2329 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2330 i, strerror(errno) ));
2331 goto done;
2336 * Convert the mode_t perms in the canon_ace to a POSIX permset.
2339 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2340 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2341 i, strerror(errno) ));
2342 goto done;
2345 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2346 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2347 (unsigned int)p_ace->perms, i, strerror(errno) ));
2348 goto done;
2352 * ..and apply them to the entry.
2355 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2356 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2357 i, strerror(errno) ));
2358 goto done;
2361 if( DEBUGLVL( 10 ))
2362 print_canon_ace( p_ace, i);
2366 if (needs_mask && !got_mask_entry) {
2367 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2368 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2369 goto done;
2372 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2373 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2374 goto done;
2377 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2378 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2379 goto done;
2382 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2383 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2384 goto done;
2387 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2388 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2389 goto done;
2394 * Check if the ACL is valid.
2397 if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) {
2398 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
2399 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2400 strerror(errno) ));
2401 goto done;
2405 * Finally apply it to the file or directory.
2408 if(default_ace || fsp->is_directory || fsp->fd == -1) {
2409 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
2411 * Some systems allow all the above calls and only fail with no ACL support
2412 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2414 if (no_acl_syscall_error(errno)) {
2415 *pacl_set_support = False;
2418 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
2419 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2420 fsp->fsp_name, strerror(errno) ));
2421 goto done;
2423 } else {
2424 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fd, the_acl) == -1) {
2426 * Some systems allow all the above calls and only fail with no ACL support
2427 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2429 if (no_acl_syscall_error(errno)) {
2430 *pacl_set_support = False;
2433 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2434 fsp->fsp_name, strerror(errno) ));
2435 goto done;
2439 ret = True;
2441 done:
2443 if (the_acl != NULL)
2444 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2446 return ret;
2449 /****************************************************************************
2450 Find a particular canon_ace entry.
2451 ****************************************************************************/
2453 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2455 while (list) {
2456 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2457 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2458 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2459 break;
2460 list = list->next;
2462 return list;
2465 /****************************************************************************
2467 ****************************************************************************/
2469 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2471 SMB_ACL_ENTRY_T entry;
2473 if (!the_acl)
2474 return NULL;
2475 if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2476 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2477 return NULL;
2479 return the_acl;
2482 /****************************************************************************
2483 Convert a canon_ace to a generic 3 element permission - if possible.
2484 ****************************************************************************/
2486 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2488 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2490 int snum = SNUM(fsp->conn);
2491 size_t ace_count = count_canon_ace_list(file_ace_list);
2492 canon_ace *ace_p;
2493 canon_ace *owner_ace = NULL;
2494 canon_ace *group_ace = NULL;
2495 canon_ace *other_ace = NULL;
2496 mode_t and_bits;
2497 mode_t or_bits;
2499 if (ace_count != 3) {
2500 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2501 posix perms.\n", fsp->fsp_name ));
2502 return False;
2505 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2506 if (ace_p->owner_type == UID_ACE)
2507 owner_ace = ace_p;
2508 else if (ace_p->owner_type == GID_ACE)
2509 group_ace = ace_p;
2510 else if (ace_p->owner_type == WORLD_ACE)
2511 other_ace = ace_p;
2514 if (!owner_ace || !group_ace || !other_ace) {
2515 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2516 fsp->fsp_name ));
2517 return False;
2520 *posix_perms = (mode_t)0;
2522 *posix_perms |= owner_ace->perms;
2523 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2524 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2525 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2526 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2527 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2528 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2530 /* The owner must have at least read access. */
2532 *posix_perms |= S_IRUSR;
2533 if (fsp->is_directory)
2534 *posix_perms |= (S_IWUSR|S_IXUSR);
2536 /* If requested apply the masks. */
2538 /* Get the initial bits to apply. */
2540 if (fsp->is_directory) {
2541 and_bits = lp_dir_security_mask(snum);
2542 or_bits = lp_force_dir_security_mode(snum);
2543 } else {
2544 and_bits = lp_security_mask(snum);
2545 or_bits = lp_force_security_mode(snum);
2548 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2550 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2551 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2552 fsp->fsp_name ));
2554 return True;
2557 /****************************************************************************
2558 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2559 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2560 with CI|OI set so it is inherited and also applies to the directory.
2561 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2562 ****************************************************************************/
2564 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2566 size_t i, j;
2568 for (i = 0; i < num_aces; i++) {
2569 for (j = i+1; j < num_aces; j++) {
2570 uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2571 uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2572 BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2573 BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2575 /* We know the lower number ACE's are file entries. */
2576 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2577 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2578 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2579 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2580 (i_inh == j_inh) &&
2581 (i_flags_ni == 0) &&
2582 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
2583 SEC_ACE_FLAG_CONTAINER_INHERIT|
2584 SEC_ACE_FLAG_INHERIT_ONLY))) {
2586 * W2K wants to have access allowed zero access ACE's
2587 * at the end of the list. If the mask is zero, merge
2588 * the non-inherited ACE onto the inherited ACE.
2591 if (nt_ace_list[i].info.mask == 0) {
2592 nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2593 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2594 if (num_aces - i - 1 > 0)
2595 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
2596 sizeof(SEC_ACE));
2598 DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
2599 (unsigned int)i, (unsigned int)j ));
2600 } else {
2602 * These are identical except for the flags.
2603 * Merge the inherited ACE onto the non-inherited ACE.
2606 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2607 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2608 if (num_aces - j - 1 > 0)
2609 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2610 sizeof(SEC_ACE));
2612 DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
2613 (unsigned int)j, (unsigned int)i ));
2615 num_aces--;
2616 break;
2621 return num_aces;
2623 /****************************************************************************
2624 Reply to query a security descriptor from an fsp. If it succeeds it allocates
2625 the space for the return elements and returns the size needed to return the
2626 security descriptor. This should be the only external function needed for
2627 the UNIX style get ACL.
2628 ****************************************************************************/
2630 size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
2632 extern DOM_SID global_sid_Builtin_Administrators;
2633 extern DOM_SID global_sid_Builtin_Users;
2634 extern DOM_SID global_sid_Creator_Owner;
2635 extern DOM_SID global_sid_Creator_Group;
2636 connection_struct *conn = fsp->conn;
2637 SMB_STRUCT_STAT sbuf;
2638 SEC_ACE *nt_ace_list = NULL;
2639 DOM_SID owner_sid;
2640 DOM_SID group_sid;
2641 size_t sd_size = 0;
2642 SEC_ACL *psa = NULL;
2643 size_t num_acls = 0;
2644 size_t num_dir_acls = 0;
2645 size_t num_aces = 0;
2646 SMB_ACL_T posix_acl = NULL;
2647 SMB_ACL_T dir_acl = NULL;
2648 canon_ace *file_ace = NULL;
2649 canon_ace *dir_ace = NULL;
2650 size_t num_profile_acls = 0;
2651 struct pai_val *pal = NULL;
2652 SEC_DESC *psd = NULL;
2654 *ppdesc = NULL;
2656 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2658 if(fsp->is_directory || fsp->fd == -1) {
2660 /* Get the stat struct for the owner info. */
2661 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2662 return 0;
2665 * Get the ACL from the path.
2668 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2671 * If it's a directory get the default POSIX ACL.
2674 if(fsp->is_directory) {
2675 dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2676 dir_acl = free_empty_sys_acl(conn, dir_acl);
2679 } else {
2681 /* Get the stat struct for the owner info. */
2682 if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
2683 return 0;
2686 * Get the ACL from the fd.
2688 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
2691 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2692 posix_acl ? "present" : "absent",
2693 dir_acl ? "present" : "absent" ));
2695 pal = load_inherited_info(fsp);
2698 * Get the owner, group and world SIDs.
2701 if (lp_profile_acls(SNUM(fsp->conn))) {
2702 /* For WXP SP1 the owner must be administrators. */
2703 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2704 sid_copy(&group_sid, &global_sid_Builtin_Users);
2705 num_profile_acls = 2;
2706 } else {
2707 create_file_sids(&sbuf, &owner_sid, &group_sid);
2710 if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
2713 * In the optimum case Creator Owner and Creator Group would be used for
2714 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2715 * would lead to usability problems under Windows: The Creator entries
2716 * are only available in browse lists of directories and not for files;
2717 * additionally the identity of the owning group couldn't be determined.
2718 * We therefore use those identities only for Default ACLs.
2721 /* Create the canon_ace lists. */
2722 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
2724 /* We must have *some* ACLS. */
2726 if (count_canon_ace_list(file_ace) == 0) {
2727 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2728 return 0;
2731 if (fsp->is_directory && dir_acl) {
2732 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
2733 &global_sid_Creator_Owner,
2734 &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
2738 * Create the NT ACE list from the canonical ace lists.
2742 canon_ace *ace;
2743 int nt_acl_type;
2744 int i;
2746 if (nt4_compatible_acls() && dir_ace) {
2748 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2749 * but no non-INHERIT_ONLY entry for one SID. So we only
2750 * remove entries from the Access ACL if the
2751 * corresponding Default ACL entries have also been
2752 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2753 * are exceptions. We can do nothing
2754 * intelligent if the Default ACL contains entries that
2755 * are not also contained in the Access ACL, so this
2756 * case will still fail under NT 4.
2759 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2760 if (ace && !ace->perms) {
2761 DLIST_REMOVE(dir_ace, ace);
2762 SAFE_FREE(ace);
2764 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2765 if (ace && !ace->perms) {
2766 DLIST_REMOVE(file_ace, ace);
2767 SAFE_FREE(ace);
2772 * WinNT doesn't usually have Creator Group
2773 * in browse lists, so we send this entry to
2774 * WinNT even if it contains no relevant
2775 * permissions. Once we can add
2776 * Creator Group to browse lists we can
2777 * re-enable this.
2780 #if 0
2781 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2782 if (ace && !ace->perms) {
2783 DLIST_REMOVE(dir_ace, ace);
2784 SAFE_FREE(ace);
2786 #endif
2788 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2789 if (ace && !ace->perms) {
2790 DLIST_REMOVE(file_ace, ace);
2791 SAFE_FREE(ace);
2795 num_acls = count_canon_ace_list(file_ace);
2796 num_dir_acls = count_canon_ace_list(dir_ace);
2798 /* Allocate the ace list. */
2799 if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE, num_acls + num_profile_acls + num_dir_acls)) == NULL) {
2800 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2801 goto done;
2804 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
2807 * Create the NT ACE list from the canonical ace lists.
2810 ace = file_ace;
2812 for (i = 0; i < num_acls; i++, ace = ace->next) {
2813 SEC_ACCESS acc;
2815 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2816 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2819 /* The User must have access to a profile share - even if we can't map the SID. */
2820 if (lp_profile_acls(SNUM(fsp->conn))) {
2821 SEC_ACCESS acc;
2823 init_sec_access(&acc,FILE_GENERIC_ALL);
2824 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
2825 acc, 0);
2828 ace = dir_ace;
2830 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2831 SEC_ACCESS acc;
2833 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2834 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2835 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2836 SEC_ACE_FLAG_INHERIT_ONLY|
2837 (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
2840 /* The User must have access to a profile share - even if we can't map the SID. */
2841 if (lp_profile_acls(SNUM(fsp->conn))) {
2842 SEC_ACCESS acc;
2844 init_sec_access(&acc,FILE_GENERIC_ALL);
2845 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2846 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2847 SEC_ACE_FLAG_INHERIT_ONLY|0);
2851 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2852 * Win2K needs this to get the inheritance correct when replacing ACLs
2853 * on a directory tree. Based on work by Jim @ IBM.
2856 num_aces = merge_default_aces(nt_ace_list, num_aces);
2860 if (num_aces) {
2861 if((psa = make_sec_acl( main_loop_talloc_get(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2862 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2863 goto done;
2866 } /* security_info & DACL_SECURITY_INFORMATION */
2868 psd = make_standard_sec_desc( main_loop_talloc_get(),
2869 (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
2870 (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
2871 psa,
2872 &sd_size);
2874 if(!psd) {
2875 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2876 sd_size = 0;
2877 } else {
2879 * Windows 2000: The DACL_PROTECTED flag in the security
2880 * descriptor marks the ACL as non-inheriting, i.e., no
2881 * ACEs from higher level directories propagate to this
2882 * ACL. In the POSIX ACL model permissions are only
2883 * inherited at file create time, so ACLs never contain
2884 * any ACEs that are inherited dynamically. The DACL_PROTECTED
2885 * flag doesn't seem to bother Windows NT.
2887 if (get_protected_flag(pal))
2888 psd->type |= SE_DESC_DACL_PROTECTED;
2891 if (psd->dacl)
2892 dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces);
2894 *ppdesc = psd;
2896 done:
2898 if (posix_acl)
2899 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2900 if (dir_acl)
2901 SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
2902 free_canon_ace_list(file_ace);
2903 free_canon_ace_list(dir_ace);
2904 free_inherited_info(pal);
2905 SAFE_FREE(nt_ace_list);
2907 return sd_size;
2910 /****************************************************************************
2911 Try to chown a file. We will be able to chown it under the following conditions.
2913 1) If we have root privileges, then it will just work.
2914 2) If we have write permission to the file and dos_filemodes is set
2915 then allow chown to the currently authenticated user.
2916 ****************************************************************************/
2918 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2920 int ret;
2921 extern struct current_user current_user;
2922 files_struct *fsp;
2923 SMB_STRUCT_STAT st;
2925 /* try the direct way first */
2926 ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
2927 if (ret == 0)
2928 return 0;
2930 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2931 return -1;
2933 if (SMB_VFS_STAT(conn,fname,&st))
2934 return -1;
2936 fsp = open_file_fchmod(conn,fname,&st);
2937 if (!fsp)
2938 return -1;
2940 /* only allow chown to the current user. This is more secure,
2941 and also copes with the case where the SID in a take ownership ACL is
2942 a local SID on the users workstation
2944 uid = current_user.uid;
2946 become_root();
2947 /* Keep the current file gid the same. */
2948 ret = SMB_VFS_FCHOWN(fsp, fsp->fd, uid, (gid_t)-1);
2949 unbecome_root();
2951 close_file_fchmod(fsp);
2953 return ret;
2956 /****************************************************************************
2957 Reply to set a security descriptor on an fsp. security_info_sent is the
2958 description of the following NT ACL.
2959 This should be the only external function needed for the UNIX style set ACL.
2960 ****************************************************************************/
2962 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2964 connection_struct *conn = fsp->conn;
2965 uid_t user = (uid_t)-1;
2966 gid_t grp = (gid_t)-1;
2967 SMB_STRUCT_STAT sbuf;
2968 DOM_SID file_owner_sid;
2969 DOM_SID file_grp_sid;
2970 canon_ace *file_ace_list = NULL;
2971 canon_ace *dir_ace_list = NULL;
2972 BOOL acl_perms = False;
2973 mode_t orig_mode = (mode_t)0;
2974 uid_t orig_uid;
2975 gid_t orig_gid;
2976 BOOL need_chown = False;
2977 extern struct current_user current_user;
2979 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2981 if (!CAN_WRITE(conn)) {
2982 DEBUG(10,("set acl rejected on read-only share\n"));
2983 return False;
2987 * Get the current state of the file.
2990 if(fsp->is_directory || fsp->fd == -1) {
2991 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2992 return False;
2993 } else {
2994 if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0)
2995 return False;
2998 /* Save the original elements we check against. */
2999 orig_mode = sbuf.st_mode;
3000 orig_uid = sbuf.st_uid;
3001 orig_gid = sbuf.st_gid;
3004 * Unpack the user/group/world id's.
3007 if (!unpack_nt_owners( SNUM(conn), &sbuf, &user, &grp, security_info_sent, psd))
3008 return False;
3011 * Do we need to chown ?
3014 if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
3015 need_chown = True;
3018 * Chown before setting ACL only if we don't change the user, or
3019 * if we change to the current user, but not if we want to give away
3020 * the file.
3023 if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
3025 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3026 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3028 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3029 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3030 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3031 return False;
3035 * Recheck the current state of the file, which may have changed.
3036 * (suid/sgid bits, for instance)
3039 if(fsp->is_directory) {
3040 if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
3041 return False;
3043 } else {
3045 int ret;
3047 if(fsp->fd == -1)
3048 ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
3049 else
3050 ret = SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf);
3052 if(ret != 0)
3053 return False;
3056 /* Save the original elements we check against. */
3057 orig_mode = sbuf.st_mode;
3058 orig_uid = sbuf.st_uid;
3059 orig_gid = sbuf.st_gid;
3061 /* We did it, don't try again */
3062 need_chown = False;
3065 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
3067 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
3068 &file_ace_list, &dir_ace_list, security_info_sent, psd);
3070 /* Ignore W2K traverse DACL set. */
3071 if (file_ace_list || dir_ace_list) {
3073 if (!acl_perms) {
3074 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3075 free_canon_ace_list(file_ace_list);
3076 free_canon_ace_list(dir_ace_list);
3077 return False;
3081 * Only change security if we got a DACL.
3084 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
3086 BOOL acl_set_support = False;
3087 BOOL ret = False;
3090 * Try using the POSIX ACL set first. Fall back to chmod if
3091 * we have no ACL support on this filesystem.
3094 if (acl_perms && file_ace_list) {
3095 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
3096 if (acl_set_support && ret == False) {
3097 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3098 free_canon_ace_list(file_ace_list);
3099 free_canon_ace_list(dir_ace_list);
3100 return False;
3104 if (acl_perms && acl_set_support && fsp->is_directory) {
3105 if (dir_ace_list) {
3106 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
3107 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3108 free_canon_ace_list(file_ace_list);
3109 free_canon_ace_list(dir_ace_list);
3110 return False;
3112 } else {
3115 * No default ACL - delete one if it exists.
3118 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
3119 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
3120 free_canon_ace_list(file_ace_list);
3121 free_canon_ace_list(dir_ace_list);
3122 return False;
3127 if (acl_set_support)
3128 store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
3129 (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
3132 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
3135 if(!acl_set_support && acl_perms) {
3136 mode_t posix_perms;
3138 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
3139 free_canon_ace_list(file_ace_list);
3140 free_canon_ace_list(dir_ace_list);
3141 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
3142 fsp->fsp_name ));
3143 return False;
3146 if (orig_mode != posix_perms) {
3148 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
3149 fsp->fsp_name, (unsigned int)posix_perms ));
3151 if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
3152 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
3153 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
3154 free_canon_ace_list(file_ace_list);
3155 free_canon_ace_list(dir_ace_list);
3156 return False;
3162 free_canon_ace_list(file_ace_list);
3163 free_canon_ace_list(dir_ace_list);
3166 /* Any chown pending? */
3167 if (need_chown) {
3169 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3170 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3172 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3173 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3174 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3175 return False;
3179 return True;
3182 /****************************************************************************
3183 Get the actual group bits stored on a file with an ACL. Has no effect if
3184 the file has no ACL. Needed in dosmode code where the stat() will return
3185 the mask bits, not the real group bits, for a file with an ACL.
3186 ****************************************************************************/
3188 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
3190 int entry_id = SMB_ACL_FIRST_ENTRY;
3191 SMB_ACL_ENTRY_T entry;
3192 SMB_ACL_T posix_acl;
3193 int result = -1;
3195 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
3196 if (posix_acl == (SMB_ACL_T)NULL)
3197 return -1;
3199 while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3200 SMB_ACL_TAG_T tagtype;
3201 SMB_ACL_PERMSET_T permset;
3203 /* get_next... */
3204 if (entry_id == SMB_ACL_FIRST_ENTRY)
3205 entry_id = SMB_ACL_NEXT_ENTRY;
3207 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
3208 break;
3210 if (tagtype == SMB_ACL_GROUP_OBJ) {
3211 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3212 break;
3213 } else {
3214 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
3215 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
3216 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
3217 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
3218 result = 0;
3219 break;
3223 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3224 return result;
3227 /****************************************************************************
3228 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3229 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3230 ****************************************************************************/
3232 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
3234 int entry_id = SMB_ACL_FIRST_ENTRY;
3235 SMB_ACL_ENTRY_T entry;
3236 int num_entries = 0;
3238 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3239 SMB_ACL_TAG_T tagtype;
3240 SMB_ACL_PERMSET_T permset;
3241 mode_t perms;
3243 /* get_next... */
3244 if (entry_id == SMB_ACL_FIRST_ENTRY)
3245 entry_id = SMB_ACL_NEXT_ENTRY;
3247 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
3248 return -1;
3250 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
3251 return -1;
3253 num_entries++;
3255 switch(tagtype) {
3256 case SMB_ACL_USER_OBJ:
3257 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
3258 break;
3259 case SMB_ACL_GROUP_OBJ:
3260 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
3261 break;
3262 case SMB_ACL_MASK:
3264 * FIXME: The ACL_MASK entry permissions should really be set to
3265 * the union of the permissions of all ACL_USER,
3266 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
3267 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
3269 perms = S_IRUSR|S_IWUSR|S_IXUSR;
3270 break;
3271 case SMB_ACL_OTHER:
3272 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
3273 break;
3274 default:
3275 continue;
3278 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
3279 return -1;
3281 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
3282 return -1;
3286 * If this is a simple 3 element ACL or no elements then it's a standard
3287 * UNIX permission set. Just use chmod...
3290 if ((num_entries == 3) || (num_entries == 0))
3291 return -1;
3293 return 0;
3296 /****************************************************************************
3297 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
3298 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
3299 resulting ACL on TO. Note that name is in UNIX character set.
3300 ****************************************************************************/
3302 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
3304 SMB_ACL_T posix_acl = NULL;
3305 int ret = -1;
3307 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
3308 return -1;
3310 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3311 goto done;
3313 ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
3315 done:
3317 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3318 return ret;
3321 /****************************************************************************
3322 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3323 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3324 Note that name is in UNIX character set.
3325 ****************************************************************************/
3327 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
3329 return copy_access_acl(conn, name, name, mode);
3332 /****************************************************************************
3333 If "inherit permissions" is set and the parent directory has no default
3334 ACL but it does have an Access ACL, inherit this Access ACL to file name.
3335 ****************************************************************************/
3337 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
3339 pstring dirname;
3340 pstrcpy(dirname, parent_dirname(name));
3342 if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
3343 return 0;
3345 return copy_access_acl(conn, dirname, name, mode);
3348 /****************************************************************************
3349 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3350 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3351 ****************************************************************************/
3353 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
3355 connection_struct *conn = fsp->conn;
3356 SMB_ACL_T posix_acl = NULL;
3357 int ret = -1;
3359 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
3360 return -1;
3362 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3363 goto done;
3365 ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
3367 done:
3369 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3370 return ret;
3373 /****************************************************************************
3374 Check for an existing default POSIX ACL on a directory.
3375 ****************************************************************************/
3377 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
3379 SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
3380 BOOL has_acl = False;
3381 SMB_ACL_ENTRY_T entry;
3383 if (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
3384 has_acl = True;
3386 if (dir_acl)
3387 SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
3388 return has_acl;