2 Unix SMB/Netbios implementation.
4 SMB NT Security Descriptor / Unix permission conversion.
5 Copyright (C) Jeremy Allison 1994-2000
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.
24 /****************************************************************************
25 Data structures representing the internal ACE format.
26 ****************************************************************************/
28 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
29 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
31 typedef union posix_id {
37 typedef struct canon_ace {
38 struct canon_ace *next, *prev;
40 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
42 enum ace_owner owner_type;
43 enum ace_attribute attr;
47 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
49 /****************************************************************************
50 Functions to manipulate the internal ACE format.
51 ****************************************************************************/
53 /****************************************************************************
54 Count a linked list of canonical ACE entries.
55 ****************************************************************************/
57 static size_t count_canon_ace_list( canon_ace *list_head )
62 for (ace = list_head; ace; ace = ace->next)
68 /****************************************************************************
69 Free a linked list of canonical ACE entries.
70 ****************************************************************************/
72 static void free_canon_ace_list( canon_ace *list_head )
75 canon_ace *old_head = list_head;
76 DLIST_REMOVE(list_head, list_head);
81 /****************************************************************************
82 Function to duplicate a canon_ace entry.
83 ****************************************************************************/
85 static canon_ace *dup_canon_ace( canon_ace *src_ace)
87 canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
93 dst_ace->prev = dst_ace->next = NULL;
97 /****************************************************************************
98 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
99 ****************************************************************************/
101 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
103 uid_to_sid( powner_sid, psbuf->st_uid );
104 gid_to_sid( pgroup_sid, psbuf->st_gid );
107 /****************************************************************************
108 Print out a canon ace.
109 ****************************************************************************/
111 static void print_canon_ace(canon_ace *pace, int num)
115 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
116 dbgtext( "SID = %s ", sid_to_string( str, &pace->sid));
117 if (pace->owner_type == UID_ACE) {
118 struct passwd *pass = sys_getpwuid(pace->unix_ug.uid);
119 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
120 } else if (pace->owner_type == GID_ACE) {
121 struct group *grp = getgrgid(pace->unix_ug.gid);
122 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
125 switch (pace->type) {
127 dbgtext( "SMB_ACL_USER ");
129 case SMB_ACL_USER_OBJ:
130 dbgtext( "SMB_ACL_USER_OBJ ");
133 dbgtext( "SMB_ACL_GROUP ");
135 case SMB_ACL_GROUP_OBJ:
136 dbgtext( "SMB_ACL_GROUP_OBJ ");
139 dbgtext( "SMB_ACL_OTHER ");
143 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
144 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
145 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
148 /****************************************************************************
149 Print out a canon ace list.
150 ****************************************************************************/
152 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
156 if( DEBUGLVL( 10 )) {
157 dbgtext( "print_canon_ace_list: %s\n", name );
158 for (;ace_list; ace_list = ace_list->next, count++)
159 print_canon_ace(ace_list, count );
163 /****************************************************************************
164 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
165 delete the second one. If the first is deny, mask the permissions off and delete the allow
166 if the permissions become zero, delete the deny if the permissions are non zero.
167 ****************************************************************************/
169 static void merge_aces( canon_ace **pp_list_head )
171 canon_ace *list_head = *pp_list_head;
172 canon_ace *curr_ace_outer;
173 canon_ace *curr_ace_outer_next;
176 * First, merge allow entries with identical SIDs, and deny entries
177 * with identical SIDs.
180 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
182 canon_ace *curr_ace_next;
184 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
186 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
188 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
190 if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
191 (curr_ace->attr == curr_ace_outer->attr)) {
193 if( DEBUGLVL( 10 )) {
194 dbgtext("merge_aces: Merging ACE's\n");
195 print_canon_ace( curr_ace_outer, 0);
196 print_canon_ace( curr_ace, 0);
199 /* Merge two allow or two deny ACE's. */
201 curr_ace_outer->perms |= curr_ace->perms;
202 DLIST_REMOVE(list_head, curr_ace);
204 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
210 * Now go through and mask off allow permissions with deny permissions.
211 * We can delete either the allow or deny here as we know that each SID
212 * appears only once in the list.
215 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
217 canon_ace *curr_ace_next;
219 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
221 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
223 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
226 * Subtract ACE's with different entries. Due to the ordering constraints
227 * we've put on the ACL, we know the deny must be the first one.
230 if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
231 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
233 if( DEBUGLVL( 10 )) {
234 dbgtext("merge_aces: Masking ACE's\n");
235 print_canon_ace( curr_ace_outer, 0);
236 print_canon_ace( curr_ace, 0);
239 curr_ace->perms &= ~curr_ace_outer->perms;
241 if (curr_ace->perms == 0) {
244 * The deny overrides the allow. Remove the allow.
247 DLIST_REMOVE(list_head, curr_ace);
249 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
254 * Even after removing permissions, there
255 * are still allow permissions - delete the deny.
256 * It is safe to delete the deny here,
257 * as we are guarenteed by the deny first
258 * ordering that all the deny entries for
259 * this SID have already been merged into one
260 * before we can get to an allow ace.
263 DLIST_REMOVE(list_head, curr_ace_outer);
264 free(curr_ace_outer);
268 } /* end for curr_ace */
269 } /* end for curr_ace_outer */
271 /* We may have modified the list. */
273 *pp_list_head = list_head;
276 /****************************************************************************
277 Map canon_ace perms to permission bits NT.
278 The attr element is not used here - we only process deny entries on set,
279 not get. Deny entries are implicit on get with ace->perms = 0.
280 ****************************************************************************/
282 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
287 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
289 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
290 nt_mask = UNIX_ACCESS_RWX;
291 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
293 * Here we differentiate between the owner and any other user.
295 if (sid_equal(powner_sid, &ace->sid)) {
296 nt_mask = UNIX_ACCESS_NONE;
298 /* Not owner, no access. */
299 if (ace->type == SMB_ACL_USER) {
300 /* user objects can be deny entries. */
301 *pacl_type = SEC_ACE_TYPE_ACCESS_DENIED;
302 nt_mask = GENERIC_ALL_ACCESS;
305 nt_mask = UNIX_ACCESS_NONE;
308 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
309 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
310 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
313 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
314 (unsigned int)ace->perms, (unsigned int)nt_mask ));
316 init_sec_access(&sa,nt_mask);
320 /****************************************************************************
321 Map NT perms to a UNIX mode_t.
322 ****************************************************************************/
324 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
325 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
326 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
328 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
334 if(sec_access.mask & GENERIC_ALL_ACCESS)
335 mode = S_IRUSR|S_IWUSR|S_IXUSR;
337 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
338 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
339 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
343 if(sec_access.mask & GENERIC_ALL_ACCESS)
344 mode = S_IRGRP|S_IWGRP|S_IXGRP;
346 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
347 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
348 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
352 if(sec_access.mask & GENERIC_ALL_ACCESS)
353 mode = S_IROTH|S_IWOTH|S_IXOTH;
355 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
356 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
357 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
365 /****************************************************************************
366 Unpack a SEC_DESC into a UNIX owner and group.
367 ****************************************************************************/
369 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
373 enum SID_NAME_USE sid_type;
378 if(security_info_sent == 0) {
379 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
384 * Validate the owner and group SID's.
387 memset(&owner_sid, '\0', sizeof(owner_sid));
388 memset(&grp_sid, '\0', sizeof(grp_sid));
390 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
393 * Don't immediately fail if the owner sid cannot be validated.
394 * This may be a group chown only set.
397 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
398 sid_copy(&owner_sid, psd->owner_sid);
399 if (!sid_to_uid( &owner_sid, puser, &sid_type))
400 DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
404 * Don't immediately fail if the group sid cannot be validated.
405 * This may be an owner chown only set.
408 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
409 sid_copy(&grp_sid, psd->grp_sid);
410 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
411 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
414 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
419 /****************************************************************************
420 Create a default mode for a directory default ACE.
421 ****************************************************************************/
423 static mode_t get_default_ace_mode(files_struct *fsp, int type)
425 mode_t force_mode = lp_force_dir_security_mode(SNUM(fsp->conn));
428 DEBUG(10,("get_default_ace_mode: force_mode = 0%o\n", (int)force_mode ));
432 mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
433 mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
434 mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
437 mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
438 mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
439 mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
442 mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
443 mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
444 mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
448 DEBUG(10,("get_default_ace_mode: returning mode = 0%o\n", (int)mode ));
453 /****************************************************************************
454 A well formed POSIX file or default ACL has at least 3 entries, a
455 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
456 In addition, the owner must always have at least read access.
457 ****************************************************************************/
459 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
461 DOM_SID *pfile_owner_sid,
462 DOM_SID *pfile_grp_sid,
463 SMB_STRUCT_STAT *pst,
466 extern DOM_SID global_sid_World;
468 BOOL got_user = False;
469 BOOL got_grp = False;
470 BOOL got_other = False;
472 for (pace = *pp_ace; pace; pace = pace->next) {
473 if (pace->type == SMB_ACL_USER_OBJ) {
474 /* Ensure owner has read access. */
475 if (pace->perms == (mode_t)0)
476 pace->perms = S_IRUSR;
478 } else if (pace->type == SMB_ACL_GROUP_OBJ)
480 else if (pace->type == SMB_ACL_OTHER)
485 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
486 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
491 pace->type = SMB_ACL_USER_OBJ;
492 pace->owner_type = UID_ACE;
493 pace->unix_ug.uid = pst->st_uid;
494 pace->sid = *pfile_owner_sid;
495 /* Ensure owner has read access. */
496 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR) : S_IRUSR;
497 if (pace->perms == (mode_t)0)
498 pace->perms = S_IRUSR;
499 pace->attr = ALLOW_ACE;
501 DLIST_ADD(*pp_ace, pace);
505 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
506 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
511 pace->type = SMB_ACL_GROUP_OBJ;
512 pace->owner_type = GID_ACE;
513 pace->unix_ug.uid = pst->st_gid;
514 pace->sid = *pfile_grp_sid;
515 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
516 pace->attr = ALLOW_ACE;
518 DLIST_ADD(*pp_ace, pace);
522 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
523 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
528 pace->type = SMB_ACL_OTHER;
529 pace->owner_type = WORLD_ACE;
530 pace->unix_ug.world = -1;
531 pace->sid = global_sid_World;
532 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
533 pace->attr = ALLOW_ACE;
535 DLIST_ADD(*pp_ace, pace);
541 /****************************************************************************
542 Unpack a SEC_DESC into two canonical ace lists.
543 ****************************************************************************/
545 static BOOL create_canon_ace_lists(files_struct *fsp,
546 DOM_SID *pfile_owner_sid,
547 DOM_SID *pfile_grp_sid,
548 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
551 extern DOM_SID global_sid_World;
552 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
553 canon_ace *file_ace = NULL;
554 canon_ace *dir_ace = NULL;
555 canon_ace *tmp_ace = NULL;
556 canon_ace *current_ace = NULL;
557 BOOL got_dir_allow = False;
558 BOOL got_file_allow = False;
564 for(i = 0; i < dacl->num_aces; i++) {
565 enum SID_NAME_USE sid_type;
566 SEC_ACE *psa = &dacl->ace[i];
568 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
569 free_canon_ace_list(file_ace);
570 free_canon_ace_list(dir_ace);
571 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
576 * The security mask may be UNIX_ACCESS_NONE which should map into
577 * no permissions (we overload the WRITE_OWNER bit for this) or it
578 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
579 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
582 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
583 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
585 if(psa->info.mask != UNIX_ACCESS_NONE)
586 psa->info.mask &= ~UNIX_ACCESS_NONE;
589 * Create a cannon_ace entry representing this NT DACL ACE.
592 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
593 free_canon_ace_list(file_ace);
594 free_canon_ace_list(dir_ace);
595 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
599 ZERO_STRUCTP(current_ace);
601 sid_copy(¤t_ace->sid, &psa->sid);
604 * Try and work out if the SID is a user or group
605 * as we need to flag these differently for POSIX.
608 if( sid_equal(¤t_ace->sid, &global_sid_World)) {
609 current_ace->owner_type = WORLD_ACE;
610 current_ace->unix_ug.world = -1;
611 } else if (sid_to_uid( ¤t_ace->sid, ¤t_ace->unix_ug.uid, &sid_type)) {
612 current_ace->owner_type = UID_ACE;
613 } else if (sid_to_gid( ¤t_ace->sid, ¤t_ace->unix_ug.gid, &sid_type)) {
614 current_ace->owner_type = GID_ACE;
618 free_canon_ace_list(file_ace);
619 free_canon_ace_list(dir_ace);
621 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
622 sid_to_string(str, ¤t_ace->sid) ));
627 * Map the given NT permissions into a UNIX mode_t containing only
628 * S_I(R|W|X)USR bits.
631 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
632 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
635 * Now note what kind of a POSIX ACL this should map to.
638 if(sid_equal(¤t_ace->sid, pfile_owner_sid)) {
639 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
640 current_ace->type = SMB_ACL_USER_OBJ;
641 } else if( sid_equal(¤t_ace->sid, pfile_grp_sid)) {
642 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
643 current_ace->type = SMB_ACL_GROUP_OBJ;
644 } else if( sid_equal(¤t_ace->sid, &global_sid_World)) {
645 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
646 current_ace->type = SMB_ACL_OTHER;
649 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
650 * looking at owner_type.
653 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
657 * Now add the created ace to either the file list, the directory
658 * list, or both. We *MUST* preserve the order here (hence we use
659 * DLIST_ADD_END) as NT ACLs are order dependent.
662 if (fsp->is_directory) {
665 * We can only add to the default POSIX ACE list if the ACE is
666 * designed to be inherited by both files and directories.
669 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
670 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
672 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
675 * Note if this was an allow ace. We can't process
676 * any further deny ace's after this.
679 if (current_ace->attr == ALLOW_ACE)
680 got_dir_allow = True;
682 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
683 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
684 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
685 free_canon_ace_list(file_ace);
686 free_canon_ace_list(dir_ace);
691 if( DEBUGLVL( 10 )) {
692 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
693 print_canon_ace( current_ace, 0);
697 * If this is not an inherit only ACE we need to add a duplicate
701 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
702 canon_ace *dup_ace = dup_canon_ace(current_ace);
705 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
706 free_canon_ace_list(file_ace);
707 free_canon_ace_list(dir_ace);
711 current_ace = dup_ace;
719 * Only add to the file ACL if not inherit only.
722 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
723 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
726 * Note if this was an allow ace. We can't process
727 * any further deny ace's after this.
730 if (current_ace->attr == ALLOW_ACE)
731 got_file_allow = True;
733 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
734 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
735 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
736 free_canon_ace_list(file_ace);
737 free_canon_ace_list(dir_ace);
742 if( DEBUGLVL( 10 )) {
743 dbgtext("create_canon_ace_lists: adding file ACL:\n");
744 print_canon_ace( current_ace, 0);
746 all_aces_are_inherit_only = False;
751 * Free if ACE was not added.
758 if (fsp->is_directory && all_aces_are_inherit_only) {
760 * Windows 2000 is doing one of these weird 'inherit acl'
761 * traverses to conserve NTFS ACL resources. Just pretend
762 * there was no DACL sent. JRA.
765 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
766 free_canon_ace_list(file_ace);
767 free_canon_ace_list(dir_ace);
772 *ppfile_ace = file_ace;
773 *ppdir_ace = dir_ace;
778 /****************************************************************************
779 Check if a given uid/SID is in a group gid/SID. This is probably very
780 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
781 ****************************************************************************/
783 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
785 extern DOM_SID global_sid_World;
786 struct passwd *pass = NULL;
787 struct group *gptr = NULL;
789 /* "Everyone" always matches every uid. */
791 if (sid_equal(&group_ace->sid, &global_sid_World))
794 if (!(pass = sys_getpwuid(uid_ace->unix_ug.uid)))
797 if (!(gptr = getgrgid(group_ace->unix_ug.gid)))
801 * Due to the winbind interfaces we need to do this via names,
805 return user_in_group_list(pass->pw_name, gptr->gr_name );
808 /****************************************************************************
809 ASCII art time again... JRA :-).
811 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
812 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
813 entries). Secondly, the merge code has ensured that all duplicate SID entries for
814 allow or deny have been merged, so the same SID can only appear once in the deny
815 list or once in the allow list.
817 We then process as follows :
819 ---------------------------------------------------------------------------
820 First pass - look for a Everyone DENY entry.
822 If it is deny all (rwx) trunate the list at this point.
823 Else, walk the list from this point and use the deny permissions of this
824 entry as a mask on all following allow entries. Finally, delete
825 the Everyone DENY entry (we have applied it to everything possible).
827 In addition, in this pass we remove any DENY entries that have
828 no permissions (ie. they are a DENY nothing).
829 ---------------------------------------------------------------------------
830 Second pass - only deal with deny user entries.
832 DENY user1 (perms XXX)
835 for all following allow group entries where user1 is in group
836 new_perms |= group_perms;
838 user1 entry perms = new_perms & ~ XXX;
840 Convert the deny entry to an allow entry with the new perms and
841 push to the end of the list. Note if the user was in no groups
842 this maps to a specific allow nothing entry for this user.
844 The common case from the NT ACL choser (userX deny all) is
845 optimised so we don't do the group lookup - we just map to
846 an allow nothing entry.
848 What we're doing here is inferring the allow permissions the
849 person setting the ACE on user1 wanted by looking at the allow
850 permissions on the groups the user is currently in. This will
851 be a snapshot, depending on group membership but is the best
852 we can do and has the advantage of failing closed rather than
854 ---------------------------------------------------------------------------
855 Third pass - only deal with deny group entries.
857 DENY group1 (perms XXX)
859 for all following allow user entries where user is in group1
860 user entry perms = user entry perms & ~ XXX;
862 If there is a group Everyone allow entry with permissions YYY,
863 convert the group1 entry to an allow entry and modify its
866 new_perms = YYY & ~ XXX
868 and push to the end of the list.
870 If there is no group Everyone allow entry then convert the
871 group1 entry to a allow nothing entry and push to the end of the list.
873 Note that the common case from the NT ACL choser (groupX deny all)
874 cannot be optimised here as we need to modify user entries who are
875 in the group to change them to a deny all also.
877 What we're doing here is modifying the allow permissions of
878 user entries (which are more specific in POSIX ACLs) to mask
879 out the explicit deny set on the group they are in. This will
880 be a snapshot depending on current group membership but is the
881 best we can do and has the advantage of failing closed rather
883 ---------------------------------------------------------------------------
885 Note we *MUST* do the deny user pass first as this will convert deny user
886 entries into allow user entries which can then be processed by the deny
889 The above algorithm took a *lot* of thinking about - hence this
890 explaination :-). JRA.
891 ****************************************************************************/
893 /****************************************************************************
894 Process a canon_ace list entries. This is very complex code. We need
895 to go through and remove the "deny" permissions from any allow entry that matches
896 the id of this entry. We have already refused any NT ACL that wasn't in correct
897 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
898 we just remove it (to fail safe). We have already removed any duplicate ace
899 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
901 ****************************************************************************/
903 static void process_deny_list( canon_ace **pp_ace_list )
905 extern DOM_SID global_sid_World;
906 canon_ace *ace_list = *pp_ace_list;
907 canon_ace *curr_ace = NULL;
908 canon_ace *curr_ace_next = NULL;
910 /* Pass 1 above - look for an Everyone, deny entry. */
912 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
913 canon_ace *allow_ace_p;
915 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
917 if (curr_ace->attr != DENY_ACE)
920 if (curr_ace->perms == (mode_t)0) {
922 /* Deny nothing entry - delete. */
924 DLIST_REMOVE(ace_list, curr_ace);
928 if (!sid_equal(&curr_ace->sid, &global_sid_World))
931 /* JRATEST - assert. */
932 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
934 if (curr_ace->perms == ALL_ACE_PERMS) {
937 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
938 * list at this point including this entry.
941 canon_ace *prev_entry = curr_ace->prev;
943 free_canon_ace_list( curr_ace );
945 prev_entry->next = NULL;
947 /* We deleted the entire list. */
953 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
956 * Only mask off allow entries.
959 if (allow_ace_p->attr != ALLOW_ACE)
962 allow_ace_p->perms &= ~curr_ace->perms;
966 * Now it's been applied, remove it.
969 DLIST_REMOVE(ace_list, curr_ace);
972 /* Pass 2 above - deal with deny user entries. */
974 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
975 mode_t new_perms = (mode_t)0;
976 canon_ace *allow_ace_p;
979 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
981 if (curr_ace->attr != DENY_ACE)
984 if (curr_ace->owner_type != UID_ACE)
987 if (curr_ace->perms == ALL_ACE_PERMS) {
990 * Optimisation - this is a deny everything to this user.
991 * Convert to an allow nothing and push to the end of the list.
994 curr_ace->attr = ALLOW_ACE;
995 curr_ace->perms = (mode_t)0;
996 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1000 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1002 if (allow_ace_p->attr != ALLOW_ACE)
1005 /* We process GID_ACE and WORLD_ACE entries only. */
1007 if (allow_ace_p->owner_type == UID_ACE)
1010 if (uid_entry_in_group( curr_ace, allow_ace_p))
1011 new_perms |= allow_ace_p->perms;
1015 * Convert to a allow entry, modify the perms and push to the end
1019 curr_ace->attr = ALLOW_ACE;
1020 curr_ace->perms = (new_perms & ~curr_ace->perms);
1021 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1024 /* Pass 3 above - deal with deny group entries. */
1026 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1028 canon_ace *allow_ace_p;
1029 canon_ace *allow_everyone_p = NULL;
1031 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1033 if (curr_ace->attr != DENY_ACE)
1036 if (curr_ace->owner_type != GID_ACE)
1039 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1041 if (allow_ace_p->attr != ALLOW_ACE)
1044 /* Store a pointer to the Everyone allow, if it exists. */
1045 if (allow_ace_p->owner_type == WORLD_ACE)
1046 allow_everyone_p = allow_ace_p;
1048 /* We process UID_ACE entries only. */
1050 if (allow_ace_p->owner_type != UID_ACE)
1053 /* Mask off the deny group perms. */
1055 if (uid_entry_in_group( allow_ace_p, curr_ace))
1056 allow_ace_p->perms &= ~curr_ace->perms;
1060 * Convert the deny to an allow with the correct perms and
1061 * push to the end of the list.
1064 curr_ace->attr = ALLOW_ACE;
1065 if (allow_everyone_p)
1066 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1068 curr_ace->perms = (mode_t)0;
1069 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1073 *pp_ace_list = ace_list;
1076 /****************************************************************************
1077 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1079 ****************************************************************************/
1081 static BOOL unpack_canon_ace(files_struct *fsp,
1082 SMB_STRUCT_STAT *pst,
1083 DOM_SID *pfile_owner_sid,
1084 DOM_SID *pfile_grp_sid,
1085 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1086 uint32 security_info_sent, SEC_DESC *psd)
1088 canon_ace *file_ace = NULL;
1089 canon_ace *dir_ace = NULL;
1094 if(security_info_sent == 0) {
1095 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1100 * If no DACL then this is a chown only security descriptor.
1103 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1107 * Now go through the DACL and create the canon_ace lists.
1110 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1111 &file_ace, &dir_ace, psd->dacl))
1114 if ((file_ace == NULL) && (dir_ace == NULL)) {
1115 /* W2K traverse DACL set - ignore. */
1120 * Go through the canon_ace list and merge entries
1121 * belonging to identical users of identical allow or deny type.
1122 * We can do this as all deny entries come first, followed by
1123 * all allow entries (we have mandated this before accepting this acl).
1126 print_canon_ace_list( "file ace - before merge", file_ace);
1127 merge_aces( &file_ace );
1129 print_canon_ace_list( "dir ace - before merge", dir_ace);
1130 merge_aces( &dir_ace );
1133 * NT ACLs are order dependent. Go through the acl lists and
1134 * process DENY entries by masking the allow entries.
1137 print_canon_ace_list( "file ace - before deny", file_ace);
1138 process_deny_list( &file_ace);
1140 print_canon_ace_list( "dir ace - before deny", dir_ace);
1141 process_deny_list( &dir_ace);
1144 * A well formed POSIX file or default ACL has at least 3 entries, a
1145 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1146 * and optionally a mask entry. Ensure this is the case.
1149 print_canon_ace_list( "file ace - before valid", file_ace);
1151 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
1152 free_canon_ace_list(file_ace);
1153 free_canon_ace_list(dir_ace);
1157 print_canon_ace_list( "dir ace - before valid", dir_ace);
1159 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1160 free_canon_ace_list(file_ace);
1161 free_canon_ace_list(dir_ace);
1165 print_canon_ace_list( "file ace - return", file_ace);
1166 print_canon_ace_list( "dir ace - return", dir_ace);
1168 *ppfile_ace = file_ace;
1169 *ppdir_ace = dir_ace;
1174 /****************************************************************************
1175 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1176 ****************************************************************************/
1178 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
1182 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
1183 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
1184 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
1189 /****************************************************************************
1190 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1191 ****************************************************************************/
1193 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
1207 /****************************************************************************
1208 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
1209 an SMB_ACL_PERMSET_T.
1210 ****************************************************************************/
1212 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
1214 if (sys_acl_clear_perms(*p_permset) == -1)
1216 if (mode & S_IRUSR) {
1217 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
1220 if (mode & S_IWUSR) {
1221 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
1224 if (mode & S_IXUSR) {
1225 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
1231 /******************************************************************************
1232 When returning permissions, try and fit NT display
1233 semantics if possible. Note the the canon_entries here must have been malloced.
1234 The list format should be - first entry = owner, followed by group and other user
1235 entries, last entry = other.
1237 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1238 are not ordered, and match on the most specific entry rather than walking a list,
1239 then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
1241 Entry 0: owner : deny all except read and write.
1242 Entry 1: group : deny all except read.
1243 Entry 2: Everyone : deny all except read.
1244 Entry 3: owner : allow read and write.
1245 Entry 4: group : allow read.
1246 Entry 5: Everyone : allow read.
1248 But NT cannot display this in their ACL editor !
1249 ********************************************************************************/
1251 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1253 extern DOM_SID global_sid_World;
1254 canon_ace *list_head = *pp_list_head;
1255 canon_ace *owner_ace = NULL;
1256 canon_ace *other_ace = NULL;
1257 canon_ace *ace = NULL;
1258 mode_t owner_perms = 0;
1259 mode_t group_perms = 0;
1260 mode_t other_perms = 0;
1262 for (ace = list_head; ace; ace = ace->next) {
1263 if (ace->type == SMB_ACL_USER_OBJ)
1265 else if (ace->type == SMB_ACL_OTHER) {
1266 /* Last ace - this is "other" */
1269 /* Get the union of all the group and supplementary user perms. */
1270 group_perms |= ace->perms;
1274 if (!owner_ace || !other_ace) {
1275 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1281 * The POSIX algorithm applies to owner first, and other last,
1282 * so ensure they are arranged in this order.
1286 DLIST_PROMOTE(list_head, owner_ace);
1290 DLIST_DEMOTE(list_head, other_ace, ace);
1293 owner_perms = owner_ace->perms;
1294 other_perms = other_ace->perms;
1297 * We have to be clever here. NT4.x won't display anything other
1298 * Than an "Everyone, No access" DENY acl. Truncate blank perms
1299 * from the end, but we can't truncate blank permissions from
1300 * anywhere except the end, as they have an effect on allowing access
1304 if ((owner_perms || group_perms) && !other_perms) {
1305 DLIST_REMOVE(list_head, other_ace);
1306 safe_free(other_ace);
1309 if (owner_perms && !group_perms && !other_perms) {
1310 /* Free everything except the list head. */
1311 free_canon_ace_list(owner_ace->next);
1312 owner_ace->next = NULL;
1315 if (!owner_perms && !group_perms && !other_perms) {
1317 * Special case - no one has any access.
1318 * Return a 1 element ACL - other has "no access".
1321 if (owner_ace->next) {
1322 free_canon_ace_list(owner_ace->next);
1323 owner_ace->next = NULL;
1326 owner_ace->type = SMB_ACL_OTHER;
1327 owner_ace->sid = global_sid_World;
1328 owner_ace->unix_ug.world = -1;
1329 owner_ace->owner_type = WORLD_ACE;
1330 owner_ace->attr = DENY_ACE;
1331 owner_ace->perms = 0;
1334 /* We have probably changed the head of the list. */
1336 *pp_list_head = list_head;
1339 /******************************************************************************
1340 Fall back to the generic 3 element UNIX permissions.
1341 ********************************************************************************/
1343 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
1344 DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1346 extern DOM_SID global_sid_World;
1347 canon_ace *list_head = NULL;
1348 canon_ace *owner_ace = NULL;
1349 canon_ace *group_ace = NULL;
1350 canon_ace *other_ace = NULL;
1357 * Create 3 linked list entries.
1360 if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1363 if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1366 if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1369 ZERO_STRUCTP(owner_ace);
1370 ZERO_STRUCTP(group_ace);
1371 ZERO_STRUCTP(other_ace);
1373 owner_ace->type = SMB_ACL_USER_OBJ;
1374 owner_ace->sid = *powner;
1375 owner_ace->unix_ug.uid = psbuf->st_uid;
1376 owner_ace->owner_type = UID_ACE;
1377 owner_ace->attr = ALLOW_ACE;
1379 group_ace->type = SMB_ACL_GROUP_OBJ;
1380 group_ace->sid = *pgroup;
1381 group_ace->unix_ug.gid = psbuf->st_gid;
1382 group_ace->owner_type = GID_ACE;
1383 group_ace->attr = ALLOW_ACE;
1385 other_ace->type = SMB_ACL_OTHER;
1386 other_ace->sid = global_sid_World;
1387 other_ace->unix_ug.world = -1;
1388 other_ace->owner_type = WORLD_ACE;
1389 other_ace->attr = ALLOW_ACE;
1391 mode = psbuf->st_mode;
1393 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
1394 owner_ace->attr = owner_ace->perms ? ALLOW_ACE : DENY_ACE;
1396 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
1397 group_ace->attr = group_ace->perms ? ALLOW_ACE : DENY_ACE;
1399 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
1400 other_ace->attr = other_ace->perms ? ALLOW_ACE : DENY_ACE;
1402 DLIST_ADD(list_head, other_ace);
1403 DLIST_ADD(list_head, group_ace);
1404 DLIST_ADD(list_head, owner_ace);
1406 arrange_posix_perms(fsp->fsp_name,&list_head );
1412 safe_free(owner_ace);
1413 safe_free(group_ace);
1414 safe_free(other_ace);
1419 /****************************************************************************
1420 Create a linked list of canonical ACE entries.
1421 ****************************************************************************/
1423 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1424 DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1426 extern DOM_SID global_sid_World;
1427 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1428 canon_ace *list_head = NULL;
1429 canon_ace *ace = NULL;
1430 canon_ace *next_ace = NULL;
1431 int entry_id = SMB_ACL_FIRST_ENTRY;
1432 SMB_ACL_ENTRY_T entry;
1435 if (posix_acl == NULL)
1436 return unix_canonicalise_acl( fsp, psbuf, powner, pgroup, default_acl);
1438 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
1439 SMB_ACL_TAG_T tagtype;
1440 SMB_ACL_PERMSET_T permset;
1443 enum ace_owner owner_type;
1446 if (entry_id == SMB_ACL_FIRST_ENTRY)
1447 entry_id = SMB_ACL_NEXT_ENTRY;
1449 /* Is this a MASK entry ? */
1450 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1453 if (sys_acl_get_permset(entry, &permset) == -1)
1456 /* Decide which SID to use based on the ACL type. */
1458 case SMB_ACL_USER_OBJ:
1459 /* Get the SID from the owner. */
1460 uid_to_sid( &sid, psbuf->st_uid );
1461 unix_ug.uid = psbuf->st_uid;
1462 owner_type = UID_ACE;
1466 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
1468 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1471 uid_to_sid( &sid, *puid);
1472 unix_ug.uid = *puid;
1473 owner_type = UID_ACE;
1476 case SMB_ACL_GROUP_OBJ:
1477 /* Get the SID from the owning group. */
1478 gid_to_sid( &sid, psbuf->st_gid );
1479 unix_ug.gid = psbuf->st_gid;
1480 owner_type = GID_ACE;
1484 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
1486 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1489 gid_to_sid( &sid, *pgid);
1490 unix_ug.gid = *pgid;
1491 owner_type = GID_ACE;
1495 acl_mask = convert_permset_to_mode_t(permset);
1496 continue; /* Don't count the mask as an entry. */
1498 /* Use the Everyone SID */
1499 sid = global_sid_World;
1501 owner_type = WORLD_ACE;
1504 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1509 * Add this entry to the list.
1512 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1516 ace->type = tagtype;
1517 ace->perms = convert_permset_to_mode_t(permset);
1518 ace->attr = ALLOW_ACE;
1520 ace->unix_ug = unix_ug;
1521 ace->owner_type = owner_type;
1523 DLIST_ADD(list_head, ace);
1526 arrange_posix_perms(fsp->fsp_name,&list_head );
1529 * Now go through the list, masking the permissions with the
1530 * acl_mask. Ensure all DENY Entries are at the start of the list.
1533 DEBUG(10,("canonicalize_acl: ace entries before arrange :\n"));
1535 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1536 next_ace = ace->next;
1538 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1539 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1540 ace->perms &= acl_mask;
1542 if (ace->perms == 0) {
1543 DLIST_PROMOTE(list_head, ace);
1546 if( DEBUGLVL( 10 ) ) {
1547 print_canon_ace(ace, ace_count);
1551 print_canon_ace_list( "canonicalize_acl: ace entries after arrange", list_head );
1557 free_canon_ace_list(list_head);
1561 /****************************************************************************
1562 Attempt to apply an ACL to a file or directory.
1563 ****************************************************************************/
1565 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1568 SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
1571 SMB_ACL_ENTRY_T mask_entry;
1572 SMB_ACL_PERMSET_T mask_permset;
1573 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1575 if (the_acl == NULL) {
1577 if (errno != ENOSYS) {
1579 * Only print this error message if we have some kind of ACL
1580 * support that's not working. Otherwise we would always get this.
1582 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1583 default_ace ? "default" : "file", strerror(errno) ));
1585 *pacl_set_support = False;
1589 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1590 SMB_ACL_ENTRY_T the_entry;
1591 SMB_ACL_PERMSET_T the_permset;
1594 * Get the entry for this ACE.
1597 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
1598 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1599 i, strerror(errno) ));
1604 * Ok - we now know the ACL calls should be working, don't
1605 * allow fallback to chmod.
1608 *pacl_set_support = True;
1611 * Initialise the entry from the canon_ace.
1615 * First tell the entry what type of ACE this is.
1618 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
1619 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1620 i, strerror(errno) ));
1625 * Only set the qualifier (user or group id) if the entry is a user
1629 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1630 if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1631 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1632 i, strerror(errno) ));
1638 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1641 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
1642 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1643 i, strerror(errno) ));
1647 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
1648 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1649 p_ace->perms, i, strerror(errno) ));
1654 * ..and apply them to the entry.
1657 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1658 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1659 i, strerror(errno) ));
1664 print_canon_ace( p_ace, i);
1668 * Add in a mask of rwx.
1671 if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1672 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1676 if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1677 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1681 if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1682 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1686 if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1687 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1691 if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1692 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1697 * Check if the ACL is valid.
1700 if (sys_acl_valid(the_acl) == -1) {
1701 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1702 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1708 * Finally apply it to the file or directory.
1711 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1712 if (sys_acl_set_file(dos_to_unix(fsp->fsp_name,False), the_acl_type, the_acl) == -1) {
1713 DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1714 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1715 fsp->fsp_name, strerror(errno) ));
1719 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1720 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1721 fsp->fsp_name, strerror(errno) ));
1730 if (the_acl != NULL)
1731 sys_acl_free_acl(the_acl);
1736 /****************************************************************************
1737 Convert a canon_ace to a generic 3 element permission - if possible.
1738 ****************************************************************************/
1740 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
1742 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
1744 size_t ace_count = count_canon_ace_list(file_ace_list);
1746 canon_ace *owner_ace = NULL;
1747 canon_ace *group_ace = NULL;
1748 canon_ace *other_ace = NULL;
1750 if (ace_count != 3) {
1751 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
1752 posix perms.\n", fsp->fsp_name ));
1756 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
1757 if (ace_p->owner_type == UID_ACE)
1759 else if (ace_p->owner_type == GID_ACE)
1761 else if (ace_p->owner_type == WORLD_ACE)
1765 if (!owner_ace || !group_ace || !other_ace) {
1766 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
1771 *posix_perms = (mode_t)0;
1773 *posix_perms |= owner_ace->perms;
1774 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
1775 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
1776 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
1777 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
1778 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
1779 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
1781 /* The owner must have at least read access. */
1783 if (*posix_perms == (mode_t)0)
1784 *posix_perms = S_IRUSR;
1786 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
1787 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
1793 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
1795 if (a1->type == a2->type)
1798 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
1803 /****************************************************************************
1804 Reply to query a security descriptor from an fsp. If it succeeds it allocates
1805 the space for the return elements and returns the size needed to return the
1806 security descriptor. This should be the only external function needed for
1807 the UNIX style get ACL.
1808 ****************************************************************************/
1810 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1812 SMB_STRUCT_STAT sbuf;
1813 SEC_ACE *nt_ace_list = NULL;
1817 SEC_ACL *psa = NULL;
1818 size_t num_acls = 0;
1819 size_t num_dir_acls = 0;
1820 size_t num_aces = 0;
1821 SMB_ACL_T posix_acl = NULL;
1822 SMB_ACL_T dir_acl = NULL;
1823 canon_ace *file_ace = NULL;
1824 canon_ace *dir_ace = NULL;
1828 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1830 if(fsp->is_directory || fsp->fd == -1) {
1832 /* Get the stat struct for the owner info. */
1833 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1837 * Get the ACL from the path.
1840 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1843 * If it's a directory get the default POSIX ACL.
1846 if(fsp->is_directory)
1847 dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1851 /* Get the stat struct for the owner info. */
1852 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1856 * Get the ACL from the fd.
1858 posix_acl = sys_acl_get_fd(fsp->fd);
1861 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1862 posix_acl ? "present" : "absent",
1863 dir_acl ? "present" : "absent" ));
1866 * Get the owner, group and world SIDs.
1869 create_file_sids(&sbuf, &owner_sid, &group_sid);
1871 /* Create the canon_ace lists. */
1872 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, False);
1873 num_acls = count_canon_ace_list(file_ace);
1875 /* We must have *some* ACLS. */
1877 if (num_acls == 0) {
1878 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1882 if (fsp->is_directory) {
1883 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid, True);
1884 num_dir_acls = count_canon_ace_list(dir_ace);
1887 /* Allocate the ace list. */
1888 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1889 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1893 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1896 * Create the NT ACE list from the canonical ace lists.
1906 for (i = 0; i < num_acls; i++, ace = ace->next) {
1907 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1908 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1913 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1914 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1915 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
1916 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1920 * Sort to force deny entries to the front.
1923 if (num_acls + num_dir_acls)
1924 qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
1928 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1929 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1934 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
1937 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1944 sys_acl_free_acl(posix_acl);
1946 sys_acl_free_acl(dir_acl);
1947 free_canon_ace_list(file_ace);
1948 free_canon_ace_list(dir_ace);
1955 /****************************************************************************
1956 Reply to set a security descriptor on an fsp. security_info_sent is the
1957 description of the following NT ACL.
1958 This should be the only external function needed for the UNIX style set ACL.
1959 ****************************************************************************/
1961 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1963 connection_struct *conn = fsp->conn;
1964 uid_t user = (uid_t)-1;
1965 gid_t grp = (gid_t)-1;
1966 SMB_STRUCT_STAT sbuf;
1967 DOM_SID file_owner_sid;
1968 DOM_SID file_grp_sid;
1969 canon_ace *file_ace_list = NULL;
1970 canon_ace *dir_ace_list = NULL;
1971 BOOL acl_perms = False;
1973 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1976 * Get the current state of the file.
1979 if(fsp->is_directory || fsp->fd == -1) {
1980 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1983 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1988 * Unpack the user/group/world id's.
1991 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
1995 * Do we need to chown ?
1998 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
2000 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2001 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2003 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2004 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2005 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2010 * Recheck the current state of the file, which may have changed.
2011 * (suid/sgid bits, for instance)
2014 if(fsp->is_directory) {
2015 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2023 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2025 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2032 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2034 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2035 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2037 if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2038 /* W2K traverse DACL set - ignore. */
2043 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2044 free_canon_ace_list(file_ace_list);
2045 free_canon_ace_list(dir_ace_list);
2050 * Only change security if we got a DACL.
2053 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2055 BOOL acl_set_support = False;
2059 * Try using the POSIX ACL set first. Fall back to chmod if
2060 * we have no ACL support on this filesystem.
2063 if (acl_perms && file_ace_list) {
2064 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2065 if (acl_set_support && ret == False) {
2066 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2067 free_canon_ace_list(file_ace_list);
2068 free_canon_ace_list(dir_ace_list);
2073 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
2074 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2075 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2076 free_canon_ace_list(file_ace_list);
2077 free_canon_ace_list(dir_ace_list);
2083 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2086 if(!acl_set_support && acl_perms) {
2089 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2090 free_canon_ace_list(file_ace_list);
2091 free_canon_ace_list(dir_ace_list);
2092 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2097 if (sbuf.st_mode != posix_perms) {
2099 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2100 fsp->fsp_name, (unsigned int)posix_perms ));
2102 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), posix_perms) == -1) {
2103 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2104 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2105 free_canon_ace_list(file_ace_list);
2106 free_canon_ace_list(dir_ace_list);
2113 free_canon_ace_list(file_ace_list);
2114 free_canon_ace_list(dir_ace_list);
2119 /****************************************************************************
2120 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2121 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2122 ****************************************************************************/
2124 static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
2126 int entry_id = SMB_ACL_FIRST_ENTRY;
2127 SMB_ACL_ENTRY_T entry;
2128 int num_entries = 0;
2130 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
2131 SMB_ACL_TAG_T tagtype;
2132 SMB_ACL_PERMSET_T permset;
2136 if (entry_id == SMB_ACL_FIRST_ENTRY)
2137 entry_id = SMB_ACL_NEXT_ENTRY;
2139 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
2142 if (sys_acl_get_permset(entry, &permset) == -1)
2148 case SMB_ACL_USER_OBJ:
2149 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2151 case SMB_ACL_GROUP_OBJ:
2152 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2155 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2158 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2164 if (map_acl_perms_to_permset(perms, &permset) == -1)
2167 if (sys_acl_set_permset(entry, permset) == -1)
2172 * If this is a simple 3 element ACL then it's a standard
2173 * UNIX permission set. Just use chmod...
2176 if (num_entries == 3)
2182 /****************************************************************************
2183 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2184 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2185 Note that name is in UNIX character set.
2186 ****************************************************************************/
2188 int chmod_acl(char *name, mode_t mode)
2190 SMB_ACL_T posix_acl = NULL;
2193 if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
2196 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2199 ret = sys_acl_set_file(name, SMB_ACL_TYPE_ACCESS, posix_acl);
2203 sys_acl_free_acl(posix_acl);
2207 /****************************************************************************
2208 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2209 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2210 ****************************************************************************/
2212 int fchmod_acl(int fd, mode_t mode)
2214 SMB_ACL_T posix_acl = NULL;
2217 if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
2220 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2223 ret = sys_acl_set_fd(fd, posix_acl);
2227 sys_acl_free_acl(posix_acl);