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));
430 mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
431 mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
432 mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
435 mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
436 mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
437 mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
440 mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
441 mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
442 mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
449 /****************************************************************************
450 A well formed POSIX file or default ACL has at least 3 entries, a
451 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
452 In addition, the owner must always have at least read access.
453 ****************************************************************************/
455 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
457 DOM_SID *pfile_owner_sid,
458 DOM_SID *pfile_grp_sid,
459 SMB_STRUCT_STAT *pst,
462 extern DOM_SID global_sid_World;
464 BOOL got_user = False;
465 BOOL got_grp = False;
466 BOOL got_other = False;
468 for (pace = *pp_ace; pace; pace = pace->next) {
469 if (pace->type == SMB_ACL_USER_OBJ) {
470 /* Ensure owner has read access. */
471 if (pace->perms == (mode_t)0)
472 pace->perms = S_IRUSR;
474 } else if (pace->type == SMB_ACL_GROUP_OBJ)
476 else if (pace->type == SMB_ACL_OTHER)
481 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
482 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
487 pace->type = SMB_ACL_USER_OBJ;
488 pace->owner_type = UID_ACE;
489 pace->unix_ug.uid = pst->st_uid;
490 pace->sid = *pfile_owner_sid;
491 /* Ensure owner has read access. */
492 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR): S_IRUSR;
493 pace->attr = ALLOW_ACE;
495 DLIST_ADD(*pp_ace, pace);
499 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
500 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
505 pace->type = SMB_ACL_GROUP_OBJ;
506 pace->owner_type = GID_ACE;
507 pace->unix_ug.uid = pst->st_gid;
508 pace->sid = *pfile_grp_sid;
509 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
510 pace->attr = ALLOW_ACE;
512 DLIST_ADD(*pp_ace, pace);
516 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
517 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
522 pace->type = SMB_ACL_OTHER;
523 pace->owner_type = WORLD_ACE;
524 pace->unix_ug.world = -1;
525 pace->sid = global_sid_World;
526 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
527 pace->attr = ALLOW_ACE;
529 DLIST_ADD(*pp_ace, pace);
535 /****************************************************************************
536 Unpack a SEC_DESC into two canonical ace lists.
537 ****************************************************************************/
539 static BOOL create_canon_ace_lists(files_struct *fsp,
540 DOM_SID *pfile_owner_sid,
541 DOM_SID *pfile_grp_sid,
542 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
545 extern DOM_SID global_sid_World;
546 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
547 canon_ace *file_ace = NULL;
548 canon_ace *dir_ace = NULL;
549 canon_ace *tmp_ace = NULL;
550 canon_ace *current_ace = NULL;
551 BOOL got_dir_allow = False;
552 BOOL got_file_allow = False;
558 for(i = 0; i < dacl->num_aces; i++) {
559 enum SID_NAME_USE sid_type;
560 SEC_ACE *psa = &dacl->ace[i];
562 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
563 free_canon_ace_list(file_ace);
564 free_canon_ace_list(dir_ace);
565 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
570 * The security mask may be UNIX_ACCESS_NONE which should map into
571 * no permissions (we overload the WRITE_OWNER bit for this) or it
572 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
573 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
576 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
577 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
579 if(psa->info.mask != UNIX_ACCESS_NONE)
580 psa->info.mask &= ~UNIX_ACCESS_NONE;
583 * Create a cannon_ace entry representing this NT DACL ACE.
586 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
587 free_canon_ace_list(file_ace);
588 free_canon_ace_list(dir_ace);
589 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
593 ZERO_STRUCTP(current_ace);
595 sid_copy(¤t_ace->sid, &psa->sid);
598 * Try and work out if the SID is a user or group
599 * as we need to flag these differently for POSIX.
602 if( sid_equal(¤t_ace->sid, &global_sid_World)) {
603 current_ace->owner_type = WORLD_ACE;
604 current_ace->unix_ug.world = -1;
605 } else if (sid_to_uid( ¤t_ace->sid, ¤t_ace->unix_ug.uid, &sid_type)) {
606 current_ace->owner_type = UID_ACE;
607 } else if (sid_to_gid( ¤t_ace->sid, ¤t_ace->unix_ug.gid, &sid_type)) {
608 current_ace->owner_type = GID_ACE;
612 free_canon_ace_list(file_ace);
613 free_canon_ace_list(dir_ace);
615 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
616 sid_to_string(str, ¤t_ace->sid) ));
621 * Map the given NT permissions into a UNIX mode_t containing only
622 * S_I(R|W|X)USR bits.
625 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
626 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
629 * Now note what kind of a POSIX ACL this should map to.
632 if(sid_equal(¤t_ace->sid, pfile_owner_sid)) {
633 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
634 current_ace->type = SMB_ACL_USER_OBJ;
635 } else if( sid_equal(¤t_ace->sid, pfile_grp_sid)) {
636 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
637 current_ace->type = SMB_ACL_GROUP_OBJ;
638 } else if( sid_equal(¤t_ace->sid, &global_sid_World)) {
639 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
640 current_ace->type = SMB_ACL_OTHER;
643 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
644 * looking at owner_type.
647 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
651 * Now add the created ace to either the file list, the directory
652 * list, or both. We *MUST* preserve the order here (hence we use
653 * DLIST_ADD_END) as NT ACLs are order dependent.
656 if (fsp->is_directory) {
659 * We can only add to the default POSIX ACE list if the ACE is
660 * designed to be inherited by both files and directories.
663 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
664 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
666 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
669 * Note if this was an allow ace. We can't process
670 * any further deny ace's after this.
673 if (current_ace->attr == ALLOW_ACE)
674 got_dir_allow = True;
676 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
677 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
678 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
679 free_canon_ace_list(file_ace);
680 free_canon_ace_list(dir_ace);
685 if( DEBUGLVL( 10 )) {
686 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
687 print_canon_ace( current_ace, 0);
691 * If this is not an inherit only ACE we need to add a duplicate
695 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
696 canon_ace *dup_ace = dup_canon_ace(current_ace);
699 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
700 free_canon_ace_list(file_ace);
701 free_canon_ace_list(dir_ace);
705 current_ace = dup_ace;
713 * Only add to the file ACL if not inherit only.
716 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
717 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
720 * Note if this was an allow ace. We can't process
721 * any further deny ace's after this.
724 if (current_ace->attr == ALLOW_ACE)
725 got_file_allow = True;
727 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
728 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
729 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
730 free_canon_ace_list(file_ace);
731 free_canon_ace_list(dir_ace);
736 if( DEBUGLVL( 10 )) {
737 dbgtext("create_canon_ace_lists: adding file ACL:\n");
738 print_canon_ace( current_ace, 0);
740 all_aces_are_inherit_only = False;
745 * Free if ACE was not added.
752 if (fsp->is_directory && all_aces_are_inherit_only) {
754 * Windows 2000 is doing one of these weird 'inherit acl'
755 * traverses to conserve NTFS ACL resources. Just pretend
756 * there was no DACL sent. JRA.
759 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
760 free_canon_ace_list(file_ace);
761 free_canon_ace_list(dir_ace);
766 *ppfile_ace = file_ace;
767 *ppdir_ace = dir_ace;
772 /****************************************************************************
773 Check if a given uid/SID is in a group gid/SID. This is probably very
774 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
775 ****************************************************************************/
777 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
779 extern DOM_SID global_sid_World;
780 struct passwd *pass = NULL;
781 struct group *gptr = NULL;
783 /* "Everyone" always matches every uid. */
785 if (sid_equal(&group_ace->sid, &global_sid_World))
788 if (!(pass = sys_getpwuid(uid_ace->unix_ug.uid)))
791 if (!(gptr = getgrgid(group_ace->unix_ug.gid)))
795 * Due to the winbind interfaces we need to do this via names,
799 return user_in_group_list(pass->pw_name, gptr->gr_name );
802 /****************************************************************************
803 ASCII art time again... JRA :-).
805 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
806 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
807 entries). Secondly, the merge code has ensured that all duplicate SID entries for
808 allow or deny have been merged, so the same SID can only appear once in the deny
809 list or once in the allow list.
811 We then process as follows :
813 ---------------------------------------------------------------------------
814 First pass - look for a Everyone DENY entry.
816 If it is deny all (rwx) trunate the list at this point.
817 Else, walk the list from this point and use the deny permissions of this
818 entry as a mask on all following allow entries. Finally, delete
819 the Everyone DENY entry (we have applied it to everything possible).
821 In addition, in this pass we remove any DENY entries that have
822 no permissions (ie. they are a DENY nothing).
823 ---------------------------------------------------------------------------
824 Second pass - only deal with deny user entries.
826 DENY user1 (perms XXX)
829 for all following allow group entries where user1 is in group
830 new_perms |= group_perms;
832 user1 entry perms = new_perms & ~ XXX;
834 Convert the deny entry to an allow entry with the new perms and
835 push to the end of the list. Note if the user was in no groups
836 this maps to a specific allow nothing entry for this user.
838 The common case from the NT ACL choser (userX deny all) is
839 optimised so we don't do the group lookup - we just map to
840 an allow nothing entry.
842 What we're doing here is inferring the allow permissions the
843 person setting the ACE on user1 wanted by looking at the allow
844 permissions on the groups the user is currently in. This will
845 be a snapshot, depending on group membership but is the best
846 we can do and has the advantage of failing closed rather than
848 ---------------------------------------------------------------------------
849 Third pass - only deal with deny group entries.
851 DENY group1 (perms XXX)
853 for all following allow user entries where user is in group1
854 user entry perms = user entry perms & ~ XXX;
856 If there is a group Everyone allow entry with permissions YYY,
857 convert the group1 entry to an allow entry and modify its
860 new_perms = YYY & ~ XXX
862 and push to the end of the list.
864 If there is no group Everyone allow entry then convert the
865 group1 entry to a allow nothing entry and push to the end of the list.
867 Note that the common case from the NT ACL choser (groupX deny all)
868 cannot be optimised here as we need to modify user entries who are
869 in the group to change them to a deny all also.
871 What we're doing here is modifying the allow permissions of
872 user entries (which are more specific in POSIX ACLs) to mask
873 out the explicit deny set on the group they are in. This will
874 be a snapshot depending on current group membership but is the
875 best we can do and has the advantage of failing closed rather
877 ---------------------------------------------------------------------------
879 Note we *MUST* do the deny user pass first as this will convert deny user
880 entries into allow user entries which can then be processed by the deny
883 The above algorithm took a *lot* of thinking about - hence this
884 explaination :-). JRA.
885 ****************************************************************************/
887 /****************************************************************************
888 Process a canon_ace list entries. This is very complex code. We need
889 to go through and remove the "deny" permissions from any allow entry that matches
890 the id of this entry. We have already refused any NT ACL that wasn't in correct
891 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
892 we just remove it (to fail safe). We have already removed any duplicate ace
893 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
895 ****************************************************************************/
897 static void process_deny_list( canon_ace **pp_ace_list )
899 extern DOM_SID global_sid_World;
900 canon_ace *ace_list = *pp_ace_list;
901 canon_ace *curr_ace = NULL;
902 canon_ace *curr_ace_next = NULL;
904 /* Pass 1 above - look for an Everyone, deny entry. */
906 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
907 canon_ace *allow_ace_p;
909 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
911 if (curr_ace->attr != DENY_ACE)
914 if (curr_ace->perms == (mode_t)0) {
916 /* Deny nothing entry - delete. */
918 DLIST_REMOVE(ace_list, curr_ace);
922 if (!sid_equal(&curr_ace->sid, &global_sid_World))
925 /* JRATEST - assert. */
926 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
928 if (curr_ace->perms == ALL_ACE_PERMS) {
931 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
932 * list at this point including this entry.
935 canon_ace *prev_entry = curr_ace->prev;
937 free_canon_ace_list( curr_ace );
939 prev_entry->next = NULL;
941 /* We deleted the entire list. */
947 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
950 * Only mask off allow entries.
953 if (allow_ace_p->attr != ALLOW_ACE)
956 allow_ace_p->perms &= ~curr_ace->perms;
960 * Now it's been applied, remove it.
963 DLIST_REMOVE(ace_list, curr_ace);
966 /* Pass 2 above - deal with deny user entries. */
968 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
969 mode_t new_perms = (mode_t)0;
970 canon_ace *allow_ace_p;
973 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
975 if (curr_ace->attr != DENY_ACE)
978 if (curr_ace->owner_type != UID_ACE)
981 if (curr_ace->perms == ALL_ACE_PERMS) {
984 * Optimisation - this is a deny everything to this user.
985 * Convert to an allow nothing and push to the end of the list.
988 curr_ace->attr = ALLOW_ACE;
989 curr_ace->perms = (mode_t)0;
990 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
994 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
996 if (allow_ace_p->attr != ALLOW_ACE)
999 /* We process GID_ACE and WORLD_ACE entries only. */
1001 if (allow_ace_p->owner_type == UID_ACE)
1004 if (uid_entry_in_group( curr_ace, allow_ace_p))
1005 new_perms |= allow_ace_p->perms;
1009 * Convert to a allow entry, modify the perms and push to the end
1013 curr_ace->attr = ALLOW_ACE;
1014 curr_ace->perms = (new_perms & ~curr_ace->perms);
1015 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1018 /* Pass 3 above - deal with deny group entries. */
1020 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1022 canon_ace *allow_ace_p;
1023 canon_ace *allow_everyone_p = NULL;
1025 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1027 if (curr_ace->attr != DENY_ACE)
1030 if (curr_ace->owner_type != GID_ACE)
1033 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1035 if (allow_ace_p->attr != ALLOW_ACE)
1038 /* Store a pointer to the Everyone allow, if it exists. */
1039 if (allow_ace_p->owner_type == WORLD_ACE)
1040 allow_everyone_p = allow_ace_p;
1042 /* We process UID_ACE entries only. */
1044 if (allow_ace_p->owner_type != UID_ACE)
1047 /* Mask off the deny group perms. */
1049 if (uid_entry_in_group( allow_ace_p, curr_ace))
1050 allow_ace_p->perms &= ~curr_ace->perms;
1054 * Convert the deny to an allow with the correct perms and
1055 * push to the end of the list.
1058 curr_ace->attr = ALLOW_ACE;
1059 if (allow_everyone_p)
1060 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1062 curr_ace->perms = (mode_t)0;
1063 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1067 *pp_ace_list = ace_list;
1070 /****************************************************************************
1071 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1073 ****************************************************************************/
1075 static BOOL unpack_canon_ace(files_struct *fsp,
1076 SMB_STRUCT_STAT *pst,
1077 DOM_SID *pfile_owner_sid,
1078 DOM_SID *pfile_grp_sid,
1079 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1080 uint32 security_info_sent, SEC_DESC *psd)
1082 canon_ace *file_ace = NULL;
1083 canon_ace *dir_ace = NULL;
1088 if(security_info_sent == 0) {
1089 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1094 * If no DACL then this is a chown only security descriptor.
1097 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1101 * Now go through the DACL and create the canon_ace lists.
1104 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1105 &file_ace, &dir_ace, psd->dacl))
1108 if ((file_ace == NULL) && (dir_ace == NULL)) {
1109 /* W2K traverse DACL set - ignore. */
1114 * Go through the canon_ace list and merge entries
1115 * belonging to identical users of identical allow or deny type.
1116 * We can do this as all deny entries come first, followed by
1117 * all allow entries (we have mandated this before accepting this acl).
1120 print_canon_ace_list( "file ace - before merge", file_ace);
1121 merge_aces( &file_ace );
1123 print_canon_ace_list( "dir ace - before merge", dir_ace);
1124 merge_aces( &dir_ace );
1127 * NT ACLs are order dependent. Go through the acl lists and
1128 * process DENY entries by masking the allow entries.
1131 print_canon_ace_list( "file ace - before deny", file_ace);
1132 process_deny_list( &file_ace);
1134 print_canon_ace_list( "dir ace - before deny", dir_ace);
1135 process_deny_list( &dir_ace);
1138 * A well formed POSIX file or default ACL has at least 3 entries, a
1139 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1140 * and optionally a mask entry. Ensure this is the case.
1143 print_canon_ace_list( "file ace - before valid", file_ace);
1145 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
1146 free_canon_ace_list(file_ace);
1147 free_canon_ace_list(dir_ace);
1151 print_canon_ace_list( "dir ace - before valid", dir_ace);
1153 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1154 free_canon_ace_list(file_ace);
1155 free_canon_ace_list(dir_ace);
1159 print_canon_ace_list( "file ace - return", file_ace);
1160 print_canon_ace_list( "dir ace - return", dir_ace);
1162 *ppfile_ace = file_ace;
1163 *ppdir_ace = dir_ace;
1168 /****************************************************************************
1169 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1170 ****************************************************************************/
1172 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
1176 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
1177 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
1178 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
1183 /****************************************************************************
1184 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1185 ****************************************************************************/
1187 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
1201 /****************************************************************************
1202 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
1203 an SMB_ACL_PERMSET_T.
1204 ****************************************************************************/
1206 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
1208 if (sys_acl_clear_perms(*p_permset) == -1)
1210 if (mode & S_IRUSR) {
1211 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
1214 if (mode & S_IWUSR) {
1215 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
1218 if (mode & S_IXUSR) {
1219 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
1225 /******************************************************************************
1226 When returning permissions, try and fit NT display
1227 semantics if possible. Note the the canon_entries here must have been malloced.
1228 The list format should be - first entry = owner, followed by group and other user
1229 entries, last entry = other.
1231 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1232 are not ordered, and match on the most specific entry rather than walking a list,
1233 then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
1235 Entry 0: owner : deny all except read and write.
1236 Entry 1: group : deny all except read.
1237 Entry 2: Everyone : deny all except read.
1238 Entry 3: owner : allow read and write.
1239 Entry 4: group : allow read.
1240 Entry 5: Everyone : allow read.
1242 But NT cannot display this in their ACL editor !
1243 ********************************************************************************/
1245 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1247 extern DOM_SID global_sid_World;
1248 canon_ace *list_head = *pp_list_head;
1249 canon_ace *owner_ace = NULL;
1250 canon_ace *other_ace = NULL;
1251 canon_ace *ace = NULL;
1252 mode_t owner_perms = 0;
1253 mode_t group_perms = 0;
1254 mode_t other_perms = 0;
1256 for (ace = list_head; ace; ace = ace->next) {
1257 if (ace->type == SMB_ACL_USER_OBJ)
1259 else if (ace->type == SMB_ACL_OTHER) {
1260 /* Last ace - this is "other" */
1263 /* Get the union of all the group and supplementary user perms. */
1264 group_perms |= ace->perms;
1268 if (!owner_ace || !other_ace) {
1269 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1275 * The POSIX algorithm applies to owner first, and other last,
1276 * so ensure they are arranged in this order.
1280 DLIST_PROMOTE(list_head, owner_ace);
1284 DLIST_DEMOTE(list_head, other_ace, ace);
1287 owner_perms = owner_ace->perms;
1288 other_perms = other_ace->perms;
1291 * We have to be clever here. NT4.x won't display anything other
1292 * Than an "Everyone, No access" DENY acl. Truncate blank perms
1293 * from the end, but we can't truncate blank permissions from
1294 * anywhere except the end, as they have an effect on allowing access
1298 if ((owner_perms || group_perms) && !other_perms) {
1299 DLIST_REMOVE(list_head, other_ace);
1300 safe_free(other_ace);
1303 if (owner_perms && !group_perms && !other_perms) {
1304 /* Free everything except the list head. */
1305 free_canon_ace_list(owner_ace->next);
1306 owner_ace->next = NULL;
1309 if (!owner_perms && !group_perms && !other_perms) {
1311 * Special case - no one has any access.
1312 * Return a 1 element ACL - other has "no access".
1315 if (owner_ace->next) {
1316 free_canon_ace_list(owner_ace->next);
1317 owner_ace->next = NULL;
1320 owner_ace->type = SMB_ACL_OTHER;
1321 owner_ace->sid = global_sid_World;
1322 owner_ace->unix_ug.world = -1;
1323 owner_ace->owner_type = WORLD_ACE;
1324 owner_ace->attr = DENY_ACE;
1325 owner_ace->perms = 0;
1328 /* We have probably changed the head of the list. */
1330 *pp_list_head = list_head;
1333 /******************************************************************************
1334 Fall back to the generic 3 element UNIX permissions.
1335 ********************************************************************************/
1337 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
1338 DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1340 extern DOM_SID global_sid_World;
1341 canon_ace *list_head = NULL;
1342 canon_ace *owner_ace = NULL;
1343 canon_ace *group_ace = NULL;
1344 canon_ace *other_ace = NULL;
1351 * Create 3 linked list entries.
1354 if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1357 if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1360 if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1363 ZERO_STRUCTP(owner_ace);
1364 ZERO_STRUCTP(group_ace);
1365 ZERO_STRUCTP(other_ace);
1367 owner_ace->type = SMB_ACL_USER_OBJ;
1368 owner_ace->sid = *powner;
1369 owner_ace->unix_ug.uid = psbuf->st_uid;
1370 owner_ace->owner_type = UID_ACE;
1371 owner_ace->attr = ALLOW_ACE;
1373 group_ace->type = SMB_ACL_GROUP_OBJ;
1374 group_ace->sid = *pgroup;
1375 group_ace->unix_ug.gid = psbuf->st_gid;
1376 group_ace->owner_type = GID_ACE;
1377 group_ace->attr = ALLOW_ACE;
1379 other_ace->type = SMB_ACL_OTHER;
1380 other_ace->sid = global_sid_World;
1381 other_ace->unix_ug.world = -1;
1382 other_ace->owner_type = WORLD_ACE;
1383 other_ace->attr = ALLOW_ACE;
1385 mode = psbuf->st_mode;
1387 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
1388 owner_ace->attr = owner_ace->perms ? ALLOW_ACE : DENY_ACE;
1390 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
1391 group_ace->attr = group_ace->perms ? ALLOW_ACE : DENY_ACE;
1393 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
1394 other_ace->attr = other_ace->perms ? ALLOW_ACE : DENY_ACE;
1396 DLIST_ADD(list_head, other_ace);
1397 DLIST_ADD(list_head, group_ace);
1398 DLIST_ADD(list_head, owner_ace);
1400 arrange_posix_perms(fsp->fsp_name,&list_head );
1406 safe_free(owner_ace);
1407 safe_free(group_ace);
1408 safe_free(other_ace);
1413 /****************************************************************************
1414 Create a linked list of canonical ACE entries.
1415 ****************************************************************************/
1417 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1418 DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1420 extern DOM_SID global_sid_World;
1421 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1422 canon_ace *list_head = NULL;
1423 canon_ace *ace = NULL;
1424 canon_ace *next_ace = NULL;
1425 canon_ace *owner_ace = NULL;
1426 canon_ace *group_ace = NULL;
1427 canon_ace *other_ace = NULL;
1428 int entry_id = SMB_ACL_FIRST_ENTRY;
1429 SMB_ACL_ENTRY_T entry;
1432 if (posix_acl == NULL)
1433 return unix_canonicalise_acl( fsp, psbuf, powner, pgroup, default_acl);
1435 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
1436 SMB_ACL_TAG_T tagtype;
1437 SMB_ACL_PERMSET_T permset;
1440 enum ace_owner owner_type;
1443 if (entry_id == SMB_ACL_FIRST_ENTRY)
1444 entry_id = SMB_ACL_NEXT_ENTRY;
1446 /* Is this a MASK entry ? */
1447 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1450 if (sys_acl_get_permset(entry, &permset) == -1)
1453 /* Decide which SID to use based on the ACL type. */
1455 case SMB_ACL_USER_OBJ:
1456 /* Get the SID from the owner. */
1457 uid_to_sid( &sid, psbuf->st_uid );
1458 unix_ug.uid = psbuf->st_uid;
1459 owner_type = UID_ACE;
1463 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
1465 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1468 uid_to_sid( &sid, *puid);
1469 unix_ug.uid = *puid;
1470 owner_type = UID_ACE;
1473 case SMB_ACL_GROUP_OBJ:
1474 /* Get the SID from the owning group. */
1475 gid_to_sid( &sid, psbuf->st_gid );
1476 unix_ug.gid = psbuf->st_gid;
1477 owner_type = GID_ACE;
1481 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
1483 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1486 gid_to_sid( &sid, *pgid);
1487 unix_ug.gid = *pgid;
1488 owner_type = GID_ACE;
1492 acl_mask = convert_permset_to_mode_t(permset);
1493 continue; /* Don't count the mask as an entry. */
1495 /* Use the Everyone SID */
1496 sid = global_sid_World;
1498 owner_type = WORLD_ACE;
1501 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1506 * Add this entry to the list.
1509 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1513 ace->type = tagtype;
1514 ace->perms = convert_permset_to_mode_t(permset);
1515 ace->attr = ALLOW_ACE;
1517 ace->unix_ug = unix_ug;
1518 ace->owner_type = owner_type;
1521 * Remember the user/group/other ACE entries.
1524 if (tagtype == SMB_ACL_USER_OBJ)
1526 else if (tagtype == SMB_ACL_GROUP_OBJ)
1528 else if (tagtype == SMB_ACL_OTHER)
1531 DLIST_ADD(list_head, ace);
1534 arrange_posix_perms(fsp->fsp_name,&list_head );
1537 * Now go through the list, masking the permissions with the
1538 * acl_mask. Ensure all DENY Entries are at the start of the list.
1541 DEBUG(10,("canonicalize_acl: ace entries before arrange :\n"));
1543 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1544 next_ace = ace->next;
1546 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1547 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1548 ace->perms &= acl_mask;
1550 if (ace->perms == 0) {
1551 DLIST_PROMOTE(list_head, ace);
1554 if( DEBUGLVL( 10 ) ) {
1555 print_canon_ace(ace, ace_count);
1559 print_canon_ace_list( "canonicalize_acl: ace entries after arrange", list_head );
1565 free_canon_ace_list(list_head);
1569 /****************************************************************************
1570 Attempt to apply an ACL to a file or directory.
1571 ****************************************************************************/
1573 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1576 SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
1579 SMB_ACL_ENTRY_T mask_entry;
1580 SMB_ACL_PERMSET_T mask_permset;
1581 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1583 if (the_acl == NULL) {
1585 if (errno != ENOSYS) {
1587 * Only print this error message if we have some kind of ACL
1588 * support that's not working. Otherwise we would always get this.
1590 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1591 default_ace ? "default" : "file", strerror(errno) ));
1593 *pacl_set_support = False;
1597 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1598 SMB_ACL_ENTRY_T the_entry;
1599 SMB_ACL_PERMSET_T the_permset;
1602 * Get the entry for this ACE.
1605 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
1606 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1607 i, strerror(errno) ));
1612 * Ok - we now know the ACL calls should be working, don't
1613 * allow fallback to chmod.
1616 *pacl_set_support = True;
1619 * Initialise the entry from the canon_ace.
1623 * First tell the entry what type of ACE this is.
1626 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
1627 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1628 i, strerror(errno) ));
1633 * Only set the qualifier (user or group id) if the entry is a user
1637 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1638 if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1639 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1640 i, strerror(errno) ));
1646 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1649 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
1650 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1651 i, strerror(errno) ));
1655 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
1656 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1657 p_ace->perms, i, strerror(errno) ));
1662 * ..and apply them to the entry.
1665 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1666 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1667 i, strerror(errno) ));
1672 print_canon_ace( p_ace, i);
1676 * Add in a mask of rwx.
1679 if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1680 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1684 if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1685 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1689 if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1690 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1694 if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1695 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1699 if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1700 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1705 * Check if the ACL is valid.
1708 if (sys_acl_valid(the_acl) == -1) {
1709 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1710 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1716 * Finally apply it to the file or directory.
1719 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1720 if (sys_acl_set_file(dos_to_unix(fsp->fsp_name,False), the_acl_type, the_acl) == -1) {
1721 DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1722 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1723 fsp->fsp_name, strerror(errno) ));
1727 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1728 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1729 fsp->fsp_name, strerror(errno) ));
1738 if (the_acl != NULL)
1739 sys_acl_free_acl(the_acl);
1744 /****************************************************************************
1745 Convert a canon_ace to a generic 3 element permission - if possible.
1746 ****************************************************************************/
1748 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
1750 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
1752 size_t ace_count = count_canon_ace_list(file_ace_list);
1754 canon_ace *owner_ace = NULL;
1755 canon_ace *group_ace = NULL;
1756 canon_ace *other_ace = NULL;
1758 if (ace_count != 3) {
1759 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
1760 posix perms.\n", fsp->fsp_name ));
1764 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
1765 if (ace_p->owner_type == UID_ACE)
1767 else if (ace_p->owner_type == GID_ACE)
1769 else if (ace_p->owner_type == WORLD_ACE)
1773 if (!owner_ace || !group_ace || !other_ace) {
1774 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
1779 *posix_perms = (mode_t)0;
1781 *posix_perms |= owner_ace->perms;
1782 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
1783 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
1784 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
1785 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
1786 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
1787 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
1789 /* The owner must have at least read access. */
1791 if (*posix_perms == (mode_t)0)
1792 *posix_perms = S_IRUSR;
1794 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
1795 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
1801 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
1803 if (a1->type == a2->type)
1806 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
1811 /****************************************************************************
1812 Reply to query a security descriptor from an fsp. If it succeeds it allocates
1813 the space for the return elements and returns the size needed to return the
1814 security descriptor. This should be the only external function needed for
1815 the UNIX style get ACL.
1816 ****************************************************************************/
1818 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1820 SMB_STRUCT_STAT sbuf;
1821 SEC_ACE *nt_ace_list = NULL;
1825 SEC_ACL *psa = NULL;
1826 size_t num_acls = 0;
1827 size_t num_dir_acls = 0;
1828 size_t num_aces = 0;
1829 SMB_ACL_T posix_acl = NULL;
1830 SMB_ACL_T dir_acl = NULL;
1831 canon_ace *file_ace = NULL;
1832 canon_ace *dir_ace = NULL;
1836 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1838 if(fsp->is_directory || fsp->fd == -1) {
1840 /* Get the stat struct for the owner info. */
1841 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1845 * Get the ACL from the path.
1848 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1851 * If it's a directory get the default POSIX ACL.
1854 if(fsp->is_directory)
1855 dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1859 /* Get the stat struct for the owner info. */
1860 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1864 * Get the ACL from the fd.
1866 posix_acl = sys_acl_get_fd(fsp->fd);
1869 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1870 posix_acl ? "present" : "absent",
1871 dir_acl ? "present" : "absent" ));
1874 * Get the owner, group and world SIDs.
1877 create_file_sids(&sbuf, &owner_sid, &group_sid);
1879 /* Create the canon_ace lists. */
1880 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, False);
1881 num_acls = count_canon_ace_list(file_ace);
1883 /* We must have *some* ACLS. */
1885 if (num_acls == 0) {
1886 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1890 if (fsp->is_directory) {
1891 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid, True);
1892 num_dir_acls = count_canon_ace_list(dir_ace);
1895 /* Allocate the ace list. */
1896 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1897 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1901 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1904 * Create the NT ACE list from the canonical ace lists.
1914 for (i = 0; i < num_acls; i++, ace = ace->next) {
1915 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1916 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1921 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1922 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1923 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
1924 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1928 * Sort to force deny entries to the front.
1931 if (num_acls + num_dir_acls)
1932 qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
1936 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1937 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1942 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
1945 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1952 sys_acl_free_acl(posix_acl);
1954 sys_acl_free_acl(dir_acl);
1955 free_canon_ace_list(file_ace);
1956 free_canon_ace_list(dir_ace);
1963 /****************************************************************************
1964 Reply to set a security descriptor on an fsp. security_info_sent is the
1965 description of the following NT ACL.
1966 This should be the only external function needed for the UNIX style set ACL.
1967 ****************************************************************************/
1969 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1971 connection_struct *conn = fsp->conn;
1972 uid_t user = (uid_t)-1;
1973 gid_t grp = (gid_t)-1;
1974 SMB_STRUCT_STAT sbuf;
1975 DOM_SID file_owner_sid;
1976 DOM_SID file_grp_sid;
1977 canon_ace *file_ace_list = NULL;
1978 canon_ace *dir_ace_list = NULL;
1979 BOOL acl_perms = False;
1981 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1984 * Get the current state of the file.
1987 if(fsp->is_directory || fsp->fd == -1) {
1988 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1991 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1996 * Unpack the user/group/world id's.
1999 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2003 * Do we need to chown ?
2006 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
2008 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2009 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2011 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2012 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2013 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2018 * Recheck the current state of the file, which may have changed.
2019 * (suid/sgid bits, for instance)
2022 if(fsp->is_directory) {
2023 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2031 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2033 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2040 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2042 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2043 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2045 if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2046 /* W2K traverse DACL set - ignore. */
2051 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2052 free_canon_ace_list(file_ace_list);
2053 free_canon_ace_list(dir_ace_list);
2058 * Only change security if we got a DACL.
2061 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2063 BOOL acl_set_support = False;
2067 * Try using the POSIX ACL set first. Fall back to chmod if
2068 * we have no ACL support on this filesystem.
2071 if (acl_perms && file_ace_list) {
2072 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2073 if (acl_set_support && ret == False) {
2074 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2075 free_canon_ace_list(file_ace_list);
2076 free_canon_ace_list(dir_ace_list);
2081 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
2082 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2083 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2084 free_canon_ace_list(file_ace_list);
2085 free_canon_ace_list(dir_ace_list);
2091 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2094 if(!acl_set_support && acl_perms) {
2097 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2098 free_canon_ace_list(file_ace_list);
2099 free_canon_ace_list(dir_ace_list);
2100 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2105 if (sbuf.st_mode != posix_perms) {
2107 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2108 fsp->fsp_name, (unsigned int)posix_perms ));
2110 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), posix_perms) == -1) {
2111 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2112 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2113 free_canon_ace_list(file_ace_list);
2114 free_canon_ace_list(dir_ace_list);
2121 free_canon_ace_list(file_ace_list);
2122 free_canon_ace_list(dir_ace_list);
2127 /****************************************************************************
2128 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2129 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2130 ****************************************************************************/
2132 static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
2134 int entry_id = SMB_ACL_FIRST_ENTRY;
2135 SMB_ACL_ENTRY_T entry;
2136 int num_entries = 0;
2138 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
2139 SMB_ACL_TAG_T tagtype;
2140 SMB_ACL_PERMSET_T permset;
2144 if (entry_id == SMB_ACL_FIRST_ENTRY)
2145 entry_id = SMB_ACL_NEXT_ENTRY;
2147 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
2150 if (sys_acl_get_permset(entry, &permset) == -1)
2156 case SMB_ACL_USER_OBJ:
2157 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2159 case SMB_ACL_GROUP_OBJ:
2160 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2163 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2166 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2172 if (map_acl_perms_to_permset(perms, &permset) == -1)
2175 if (sys_acl_set_permset(entry, permset) == -1)
2180 * If this is a simple 3 element ACL then it's a standard
2181 * UNIX permission set. Just use chmod...
2184 if (num_entries == 3)
2190 /****************************************************************************
2191 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2192 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2193 Note that name is in UNIX character set.
2194 ****************************************************************************/
2196 int chmod_acl(char *name, mode_t mode)
2198 SMB_ACL_T posix_acl = NULL;
2201 if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
2204 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2207 ret = sys_acl_set_file(name, SMB_ACL_TYPE_ACCESS, posix_acl);
2211 sys_acl_free_acl(posix_acl);
2215 /****************************************************************************
2216 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2217 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2218 ****************************************************************************/
2220 int fchmod_acl(int fd, mode_t mode)
2222 SMB_ACL_T posix_acl = NULL;
2225 if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
2228 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2231 ret = sys_acl_set_fd(fd, posix_acl);
2235 sys_acl_free_acl(posix_acl);