2 Unix SMB/CIFS implementation.
3 SMB NT Security Descriptor / Unix permission conversion.
4 Copyright (C) Jeremy Allison 1994-2000.
5 Copyright (C) Andreas Gruenbacher 2002.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 Print out a canon ace.
99 ****************************************************************************/
101 static void print_canon_ace(canon_ace *pace, int num)
105 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
106 dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
107 if (pace->owner_type == UID_ACE) {
108 const char *u_name = uidtoname(pace->unix_ug.uid);
109 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
110 } else if (pace->owner_type == GID_ACE) {
111 char *g_name = gidtoname(pace->unix_ug.gid);
112 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
115 switch (pace->type) {
117 dbgtext( "SMB_ACL_USER ");
119 case SMB_ACL_USER_OBJ:
120 dbgtext( "SMB_ACL_USER_OBJ ");
123 dbgtext( "SMB_ACL_GROUP ");
125 case SMB_ACL_GROUP_OBJ:
126 dbgtext( "SMB_ACL_GROUP_OBJ ");
129 dbgtext( "SMB_ACL_OTHER ");
133 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
134 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
135 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
138 /****************************************************************************
139 Print out a canon ace list.
140 ****************************************************************************/
142 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
146 if( DEBUGLVL( 10 )) {
147 dbgtext( "print_canon_ace_list: %s\n", name );
148 for (;ace_list; ace_list = ace_list->next, count++)
149 print_canon_ace(ace_list, count );
153 /****************************************************************************
154 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
155 ****************************************************************************/
157 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
161 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
162 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
163 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
168 /****************************************************************************
169 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
170 ****************************************************************************/
172 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
186 /****************************************************************************
187 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
188 an SMB_ACL_PERMSET_T.
189 ****************************************************************************/
191 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
193 if (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) == -1)
195 if (mode & S_IRUSR) {
196 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1)
199 if (mode & S_IWUSR) {
200 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1)
203 if (mode & S_IXUSR) {
204 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
209 /****************************************************************************
210 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
211 ****************************************************************************/
213 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
215 uid_to_sid( powner_sid, psbuf->st_uid );
216 gid_to_sid( pgroup_sid, psbuf->st_gid );
219 /****************************************************************************
220 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
221 delete the second one. If the first is deny, mask the permissions off and delete the allow
222 if the permissions become zero, delete the deny if the permissions are non zero.
223 ****************************************************************************/
225 static void merge_aces( canon_ace **pp_list_head )
227 canon_ace *list_head = *pp_list_head;
228 canon_ace *curr_ace_outer;
229 canon_ace *curr_ace_outer_next;
232 * First, merge allow entries with identical SIDs, and deny entries
233 * with identical SIDs.
236 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
238 canon_ace *curr_ace_next;
240 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
242 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
244 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
246 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
247 (curr_ace->attr == curr_ace_outer->attr)) {
249 if( DEBUGLVL( 10 )) {
250 dbgtext("merge_aces: Merging ACE's\n");
251 print_canon_ace( curr_ace_outer, 0);
252 print_canon_ace( curr_ace, 0);
255 /* Merge two allow or two deny ACE's. */
257 curr_ace_outer->perms |= curr_ace->perms;
258 DLIST_REMOVE(list_head, curr_ace);
260 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
266 * Now go through and mask off allow permissions with deny permissions.
267 * We can delete either the allow or deny here as we know that each SID
268 * appears only once in the list.
271 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
273 canon_ace *curr_ace_next;
275 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
277 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
279 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
282 * Subtract ACE's with different entries. Due to the ordering constraints
283 * we've put on the ACL, we know the deny must be the first one.
286 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
287 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
289 if( DEBUGLVL( 10 )) {
290 dbgtext("merge_aces: Masking ACE's\n");
291 print_canon_ace( curr_ace_outer, 0);
292 print_canon_ace( curr_ace, 0);
295 curr_ace->perms &= ~curr_ace_outer->perms;
297 if (curr_ace->perms == 0) {
300 * The deny overrides the allow. Remove the allow.
303 DLIST_REMOVE(list_head, curr_ace);
305 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
310 * Even after removing permissions, there
311 * are still allow permissions - delete the deny.
312 * It is safe to delete the deny here,
313 * as we are guarenteed by the deny first
314 * ordering that all the deny entries for
315 * this SID have already been merged into one
316 * before we can get to an allow ace.
319 DLIST_REMOVE(list_head, curr_ace_outer);
320 SAFE_FREE(curr_ace_outer);
325 } /* end for curr_ace */
326 } /* end for curr_ace_outer */
328 /* We may have modified the list. */
330 *pp_list_head = list_head;
333 /****************************************************************************
334 Check if we need to return NT4.x compatible ACL entries.
335 ****************************************************************************/
337 static BOOL nt4_compatible_acls(void)
339 const char *compat = lp_acl_compatibility();
341 if (*compat == '\0') {
342 enum remote_arch_types ra_type = get_remote_arch();
344 /* Automatically adapt to client */
345 return (ra_type <= RA_WINNT);
347 return (strequal(compat, "winnt"));
351 /****************************************************************************
352 Map canon_ace perms to permission bits NT.
353 The attr element is not used here - we only process deny entries on set,
354 not get. Deny entries are implicit on get with ace->perms = 0.
355 ****************************************************************************/
357 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
362 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
364 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
365 nt_mask = UNIX_ACCESS_RWX;
366 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
368 * Windows NT refuses to display ACEs with no permissions in them (but
369 * they are perfectly legal with Windows 2000). If the ACE has empty
370 * permissions we cannot use 0, so we use the otherwise unused
371 * WRITE_OWNER permission, which we ignore when we set an ACL.
372 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
373 * to be changed in the future.
376 if (nt4_compatible_acls())
377 nt_mask = UNIX_ACCESS_NONE;
381 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
382 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
383 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
386 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
387 (unsigned int)ace->perms, (unsigned int)nt_mask ));
389 init_sec_access(&sa,nt_mask);
393 /****************************************************************************
394 Map NT perms to a UNIX mode_t.
395 ****************************************************************************/
397 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
398 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
399 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
401 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
407 if(sec_access.mask & GENERIC_ALL_ACCESS)
408 mode = S_IRUSR|S_IWUSR|S_IXUSR;
410 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
411 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
412 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
416 if(sec_access.mask & GENERIC_ALL_ACCESS)
417 mode = S_IRGRP|S_IWGRP|S_IXGRP;
419 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
420 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
421 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
425 if(sec_access.mask & GENERIC_ALL_ACCESS)
426 mode = S_IROTH|S_IWOTH|S_IXOTH;
428 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
429 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
430 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
438 /****************************************************************************
439 Unpack a SEC_DESC into a UNIX owner and group.
440 ****************************************************************************/
442 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
450 if(security_info_sent == 0) {
451 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
456 * Validate the owner and group SID's.
459 memset(&owner_sid, '\0', sizeof(owner_sid));
460 memset(&grp_sid, '\0', sizeof(grp_sid));
462 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
465 * Don't immediately fail if the owner sid cannot be validated.
466 * This may be a group chown only set.
469 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
470 sid_copy(&owner_sid, psd->owner_sid);
471 if (NT_STATUS_IS_ERR(sid_to_uid(&owner_sid, puser))) {
472 #if ACL_FORCE_UNMAPPABLE
473 /* this allows take ownership to work reasonably */
474 extern struct current_user current_user;
475 *puser = current_user.uid;
477 DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
478 sid_string_static(&owner_sid)));
485 * Don't immediately fail if the group sid cannot be validated.
486 * This may be an owner chown only set.
489 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
490 sid_copy(&grp_sid, psd->grp_sid);
491 if (NT_STATUS_IS_ERR(sid_to_gid( &grp_sid, pgrp))) {
492 #if ACL_FORCE_UNMAPPABLE
493 /* this allows take group ownership to work reasonably */
494 extern struct current_user current_user;
495 *pgrp = current_user.gid;
497 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
503 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
508 /****************************************************************************
509 Ensure the enforced permissions for this share apply.
510 ****************************************************************************/
512 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
514 int snum = SNUM(fsp->conn);
515 mode_t and_bits = (mode_t)0;
516 mode_t or_bits = (mode_t)0;
518 /* Get the initial bits to apply. */
520 if (fsp->is_directory) {
521 and_bits = lp_dir_security_mask(snum);
522 or_bits = lp_force_dir_security_mode(snum);
524 and_bits = lp_security_mask(snum);
525 or_bits = lp_force_security_mode(snum);
528 /* Now bounce them into the S_USR space. */
531 /* Ensure owner has read access. */
532 pace->perms |= S_IRUSR;
533 if (fsp->is_directory)
534 pace->perms |= (S_IWUSR|S_IXUSR);
535 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
536 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
539 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
540 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
543 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
544 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
548 pace->perms = ((pace->perms & and_bits)|or_bits);
551 /****************************************************************************
552 Check if a given uid/SID is in a group gid/SID. This is probably very
553 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
554 ****************************************************************************/
556 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
558 extern DOM_SID global_sid_World;
561 extern struct current_user current_user;
563 /* "Everyone" always matches every uid. */
565 if (sid_equal(&group_ace->trustee, &global_sid_World))
568 /* Assume that the current user is in the current group (force group) */
570 if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
573 fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
574 fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
577 * Due to the winbind interfaces we need to do this via names,
581 return user_in_group_list(u_name, g_name, NULL, 0);
584 /****************************************************************************
585 A well formed POSIX file or default ACL has at least 3 entries, a
586 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
587 In addition, the owner must always have at least read access.
588 When using this call on get_acl, the pst struct is valid and contains
589 the mode of the file. When using this call on set_acl, the pst struct has
590 been modified to have a mode containing the default for this file or directory
592 ****************************************************************************/
594 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
596 DOM_SID *pfile_owner_sid,
597 DOM_SID *pfile_grp_sid,
598 SMB_STRUCT_STAT *pst,
601 extern DOM_SID global_sid_World;
603 BOOL got_user = False;
604 BOOL got_grp = False;
605 BOOL got_other = False;
606 canon_ace *pace_other = NULL;
607 canon_ace *pace_group = NULL;
608 connection_struct *conn = fsp->conn;
609 SMB_ACL_T current_posix_acl = NULL;
610 mode_t current_user_perms = 0;
611 mode_t current_grp_perms = 0;
612 mode_t current_other_perms = 0;
613 BOOL got_current_user = False;
614 BOOL got_current_grp = False;
615 BOOL got_current_other = False;
617 for (pace = *pp_ace; pace; pace = pace->next) {
618 if (pace->type == SMB_ACL_USER_OBJ) {
621 apply_default_perms(fsp, pace, S_IRUSR);
624 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
627 * Ensure create mask/force create mode is respected on set.
631 apply_default_perms(fsp, pace, S_IRGRP);
635 } else if (pace->type == SMB_ACL_OTHER) {
638 * Ensure create mask/force create mode is respected on set.
642 apply_default_perms(fsp, pace, S_IROTH);
649 * When setting ACLs and missing one out of SMB_ACL_USER_OBJ,
650 * SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER, try to retrieve current
651 * values. For user and other a simple vfs_stat would do, but
652 * we would get mask instead of group. Let's do it via ACL.
655 if (setting_acl && (!got_user || !got_grp || !got_other)) {
657 SMB_ACL_ENTRY_T entry;
658 int entry_id = SMB_ACL_FIRST_ENTRY;
660 if(fsp->is_directory || fsp->fd == -1) {
661 current_posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
663 current_posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
666 if (current_posix_acl) {
667 while (conn->vfs_ops.sys_acl_get_entry(conn, current_posix_acl, entry_id, &entry) == 1) {
668 SMB_ACL_TAG_T tagtype;
669 SMB_ACL_PERMSET_T permset;
672 if (entry_id == SMB_ACL_FIRST_ENTRY)
673 entry_id = SMB_ACL_NEXT_ENTRY;
675 /* Is this a MASK entry ? */
676 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
679 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
683 case SMB_ACL_USER_OBJ:
684 current_user_perms = convert_permset_to_mode_t(conn, permset);
685 got_current_user = True;
687 case SMB_ACL_GROUP_OBJ:
688 current_grp_perms = convert_permset_to_mode_t(conn, permset);
689 got_current_grp = True;
692 current_other_perms = convert_permset_to_mode_t(conn, permset);
693 got_current_other = True;
697 conn->vfs_ops.sys_acl_free_acl(conn, current_posix_acl);
699 DEBUG(10,("ensure_canon_entry_valid: failed to retrieve current ACL of %s\n",
705 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
706 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
711 pace->type = SMB_ACL_USER_OBJ;
712 pace->owner_type = UID_ACE;
713 pace->unix_ug.uid = pst->st_uid;
714 pace->trustee = *pfile_owner_sid;
715 pace->attr = ALLOW_ACE;
718 if (got_current_user) {
719 pace->perms = current_user_perms;
721 /* If we only got an "everyone" perm, just use that. */
722 if (!got_grp && got_other)
723 pace->perms = pace_other->perms;
724 else if (got_grp && uid_entry_in_group(pace, pace_group))
725 pace->perms = pace_group->perms;
731 apply_default_perms(fsp, pace, S_IRUSR);
733 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
736 DLIST_ADD(*pp_ace, pace);
740 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
741 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
746 pace->type = SMB_ACL_GROUP_OBJ;
747 pace->owner_type = GID_ACE;
748 pace->unix_ug.uid = pst->st_gid;
749 pace->trustee = *pfile_grp_sid;
750 pace->attr = ALLOW_ACE;
752 if (got_current_grp) {
753 pace->perms = current_grp_perms;
755 /* If we only got an "everyone" perm, just use that. */
757 pace->perms = pace_other->perms;
759 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
761 apply_default_perms(fsp, pace, S_IRGRP);
763 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
766 DLIST_ADD(*pp_ace, pace);
770 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
771 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
776 pace->type = SMB_ACL_OTHER;
777 pace->owner_type = WORLD_ACE;
778 pace->unix_ug.world = -1;
779 pace->trustee = global_sid_World;
780 pace->attr = ALLOW_ACE;
782 if (got_current_other)
783 pace->perms = current_other_perms;
786 apply_default_perms(fsp, pace, S_IROTH);
788 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
790 DLIST_ADD(*pp_ace, pace);
796 /****************************************************************************
797 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
798 If it does not have them, check if there are any entries where the trustee is the
799 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
800 ****************************************************************************/
802 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
804 BOOL got_user_obj, got_group_obj;
805 canon_ace *current_ace;
808 entries = count_canon_ace_list(ace);
809 got_user_obj = False;
810 got_group_obj = False;
812 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
813 if (current_ace->type == SMB_ACL_USER_OBJ)
815 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
816 got_group_obj = True;
818 if (got_user_obj && got_group_obj) {
819 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
823 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
824 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
825 sid_equal(¤t_ace->trustee, pfile_owner_sid)) {
826 current_ace->type = SMB_ACL_USER_OBJ;
829 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
830 sid_equal(¤t_ace->trustee, pfile_grp_sid)) {
831 current_ace->type = SMB_ACL_GROUP_OBJ;
832 got_group_obj = True;
836 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
838 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
841 /****************************************************************************
842 Unpack a SEC_DESC into two canonical ace lists.
843 ****************************************************************************/
845 static BOOL create_canon_ace_lists(files_struct *fsp,
846 DOM_SID *pfile_owner_sid,
847 DOM_SID *pfile_grp_sid,
848 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
851 extern DOM_SID global_sid_Creator_Owner;
852 extern DOM_SID global_sid_Creator_Group;
853 extern DOM_SID global_sid_World;
854 extern struct generic_mapping file_generic_mapping;
855 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
856 canon_ace *file_ace = NULL;
857 canon_ace *dir_ace = NULL;
858 canon_ace *tmp_ace = NULL;
859 canon_ace *current_ace = NULL;
860 BOOL got_dir_allow = False;
861 BOOL got_file_allow = False;
868 * Convert the incoming ACL into a more regular form.
871 for(i = 0; i < dacl->num_aces; i++) {
872 SEC_ACE *psa = &dacl->ace[i];
874 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
875 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
879 if (nt4_compatible_acls()) {
881 * The security mask may be UNIX_ACCESS_NONE which should map into
882 * no permissions (we overload the WRITE_OWNER bit for this) or it
883 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
884 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
888 * Convert GENERIC bits to specific bits.
891 se_map_generic(&psa->info.mask, &file_generic_mapping);
893 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
895 if(psa->info.mask != UNIX_ACCESS_NONE)
896 psa->info.mask &= ~UNIX_ACCESS_NONE;
901 * Deal with the fact that NT 4.x re-writes the canonical format
902 * that we return for default ACLs. If a directory ACE is identical
903 * to a inherited directory ACE then NT changes the bits so that the
904 * first ACE is set to OI|IO and the second ACE for this SID is set
905 * to CI. We need to repair this. JRA.
908 for(i = 0; i < dacl->num_aces; i++) {
909 SEC_ACE *psa1 = &dacl->ace[i];
911 for (j = i + 1; j < dacl->num_aces; j++) {
912 SEC_ACE *psa2 = &dacl->ace[j];
914 if (psa1->info.mask != psa2->info.mask)
917 if (!sid_equal(&psa1->trustee, &psa2->trustee))
921 * Ok - permission bits and SIDs are equal.
922 * Check if flags were re-written.
925 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
927 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
928 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
930 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
932 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
933 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
939 for(i = 0; i < dacl->num_aces; i++) {
940 SEC_ACE *psa = &dacl->ace[i];
943 * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
946 if (non_mappable_sid(&psa->trustee)) {
948 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
949 sid_to_string(str, &psa->trustee) ));
954 * Create a cannon_ace entry representing this NT DACL ACE.
957 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
958 free_canon_ace_list(file_ace);
959 free_canon_ace_list(dir_ace);
960 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
964 ZERO_STRUCTP(current_ace);
966 sid_copy(¤t_ace->trustee, &psa->trustee);
969 * Try and work out if the SID is a user or group
970 * as we need to flag these differently for POSIX.
971 * Note what kind of a POSIX ACL this should map to.
974 if( sid_equal(¤t_ace->trustee, &global_sid_World)) {
975 current_ace->owner_type = WORLD_ACE;
976 current_ace->unix_ug.world = -1;
977 current_ace->type = SMB_ACL_OTHER;
978 } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Owner)) {
979 current_ace->owner_type = UID_ACE;
980 current_ace->unix_ug.world = -1;
981 current_ace->type = SMB_ACL_USER_OBJ;
984 * The Creator Owner entry only specifies inheritable permissions,
985 * never access permissions. WinNT doesn't always set the ACE to
986 *INHERIT_ONLY, though.
989 if (nt4_compatible_acls())
990 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
991 } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) {
992 current_ace->owner_type = GID_ACE;
993 current_ace->unix_ug.world = -1;
994 current_ace->type = SMB_ACL_GROUP_OBJ;
997 * The Creator Group entry only specifies inheritable permissions,
998 * never access permissions. WinNT doesn't always set the ACE to
999 *INHERIT_ONLY, though.
1001 if (nt4_compatible_acls())
1002 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1004 } else if (NT_STATUS_IS_OK(sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid))) {
1005 current_ace->owner_type = GID_ACE;
1006 current_ace->type = SMB_ACL_GROUP;
1007 } else if (NT_STATUS_IS_OK(sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid))) {
1008 current_ace->owner_type = UID_ACE;
1009 current_ace->type = SMB_ACL_USER;
1013 free_canon_ace_list(file_ace);
1014 free_canon_ace_list(dir_ace);
1015 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
1016 sid_to_string(str, ¤t_ace->trustee) ));
1017 SAFE_FREE(current_ace);
1022 * Map the given NT permissions into a UNIX mode_t containing only
1023 * S_I(R|W|X)USR bits.
1026 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
1027 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1030 * Now add the created ace to either the file list, the directory
1031 * list, or both. We *MUST* preserve the order here (hence we use
1032 * DLIST_ADD_END) as NT ACLs are order dependent.
1035 if (fsp->is_directory) {
1038 * We can only add to the default POSIX ACE list if the ACE is
1039 * designed to be inherited by both files and directories.
1042 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1043 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1045 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
1048 * Note if this was an allow ace. We can't process
1049 * any further deny ace's after this.
1052 if (current_ace->attr == ALLOW_ACE)
1053 got_dir_allow = True;
1055 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1056 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1057 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1058 free_canon_ace_list(file_ace);
1059 free_canon_ace_list(dir_ace);
1060 SAFE_FREE(current_ace);
1064 if( DEBUGLVL( 10 )) {
1065 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1066 print_canon_ace( current_ace, 0);
1070 * If this is not an inherit only ACE we need to add a duplicate
1074 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1075 canon_ace *dup_ace = dup_canon_ace(current_ace);
1078 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1079 free_canon_ace_list(file_ace);
1080 free_canon_ace_list(dir_ace);
1085 * We must not free current_ace here as its
1086 * pointer is now owned by the dir_ace list.
1088 current_ace = dup_ace;
1091 * We must not free current_ace here as its
1092 * pointer is now owned by the dir_ace list.
1100 * Only add to the file ACL if not inherit only.
1103 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1104 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
1107 * Note if this was an allow ace. We can't process
1108 * any further deny ace's after this.
1111 if (current_ace->attr == ALLOW_ACE)
1112 got_file_allow = True;
1114 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1115 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1116 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1117 free_canon_ace_list(file_ace);
1118 free_canon_ace_list(dir_ace);
1119 SAFE_FREE(current_ace);
1123 if( DEBUGLVL( 10 )) {
1124 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1125 print_canon_ace( current_ace, 0);
1127 all_aces_are_inherit_only = False;
1129 * We must not free current_ace here as its
1130 * pointer is now owned by the file_ace list.
1136 * Free if ACE was not added.
1139 SAFE_FREE(current_ace);
1142 if (fsp->is_directory && all_aces_are_inherit_only) {
1144 * Windows 2000 is doing one of these weird 'inherit acl'
1145 * traverses to conserve NTFS ACL resources. Just pretend
1146 * there was no DACL sent. JRA.
1149 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1150 free_canon_ace_list(file_ace);
1151 free_canon_ace_list(dir_ace);
1156 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1157 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1158 * entries can be converted to *_OBJ. Usually we will already have these
1159 * entries in the Default ACL, and the Access ACL will not have them.
1161 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1162 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1165 *ppfile_ace = file_ace;
1166 *ppdir_ace = dir_ace;
1171 /****************************************************************************
1172 ASCII art time again... JRA :-).
1174 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1175 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1176 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1177 allow or deny have been merged, so the same SID can only appear once in the deny
1178 list or once in the allow list.
1180 We then process as follows :
1182 ---------------------------------------------------------------------------
1183 First pass - look for a Everyone DENY entry.
1185 If it is deny all (rwx) trunate the list at this point.
1186 Else, walk the list from this point and use the deny permissions of this
1187 entry as a mask on all following allow entries. Finally, delete
1188 the Everyone DENY entry (we have applied it to everything possible).
1190 In addition, in this pass we remove any DENY entries that have
1191 no permissions (ie. they are a DENY nothing).
1192 ---------------------------------------------------------------------------
1193 Second pass - only deal with deny user entries.
1195 DENY user1 (perms XXX)
1198 for all following allow group entries where user1 is in group
1199 new_perms |= group_perms;
1201 user1 entry perms = new_perms & ~ XXX;
1203 Convert the deny entry to an allow entry with the new perms and
1204 push to the end of the list. Note if the user was in no groups
1205 this maps to a specific allow nothing entry for this user.
1207 The common case from the NT ACL choser (userX deny all) is
1208 optimised so we don't do the group lookup - we just map to
1209 an allow nothing entry.
1211 What we're doing here is inferring the allow permissions the
1212 person setting the ACE on user1 wanted by looking at the allow
1213 permissions on the groups the user is currently in. This will
1214 be a snapshot, depending on group membership but is the best
1215 we can do and has the advantage of failing closed rather than
1217 ---------------------------------------------------------------------------
1218 Third pass - only deal with deny group entries.
1220 DENY group1 (perms XXX)
1222 for all following allow user entries where user is in group1
1223 user entry perms = user entry perms & ~ XXX;
1225 If there is a group Everyone allow entry with permissions YYY,
1226 convert the group1 entry to an allow entry and modify its
1229 new_perms = YYY & ~ XXX
1231 and push to the end of the list.
1233 If there is no group Everyone allow entry then convert the
1234 group1 entry to a allow nothing entry and push to the end of the list.
1236 Note that the common case from the NT ACL choser (groupX deny all)
1237 cannot be optimised here as we need to modify user entries who are
1238 in the group to change them to a deny all also.
1240 What we're doing here is modifying the allow permissions of
1241 user entries (which are more specific in POSIX ACLs) to mask
1242 out the explicit deny set on the group they are in. This will
1243 be a snapshot depending on current group membership but is the
1244 best we can do and has the advantage of failing closed rather
1246 ---------------------------------------------------------------------------
1248 Note we *MUST* do the deny user pass first as this will convert deny user
1249 entries into allow user entries which can then be processed by the deny
1252 The above algorithm took a *lot* of thinking about - hence this
1253 explaination :-). JRA.
1254 ****************************************************************************/
1256 /****************************************************************************
1257 Process a canon_ace list entries. This is very complex code. We need
1258 to go through and remove the "deny" permissions from any allow entry that matches
1259 the id of this entry. We have already refused any NT ACL that wasn't in correct
1260 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1261 we just remove it (to fail safe). We have already removed any duplicate ace
1262 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1264 ****************************************************************************/
1266 static void process_deny_list( canon_ace **pp_ace_list )
1268 extern DOM_SID global_sid_World;
1269 canon_ace *ace_list = *pp_ace_list;
1270 canon_ace *curr_ace = NULL;
1271 canon_ace *curr_ace_next = NULL;
1273 /* Pass 1 above - look for an Everyone, deny entry. */
1275 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1276 canon_ace *allow_ace_p;
1278 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1280 if (curr_ace->attr != DENY_ACE)
1283 if (curr_ace->perms == (mode_t)0) {
1285 /* Deny nothing entry - delete. */
1287 DLIST_REMOVE(ace_list, curr_ace);
1291 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1294 /* JRATEST - assert. */
1295 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1297 if (curr_ace->perms == ALL_ACE_PERMS) {
1300 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1301 * list at this point including this entry.
1304 canon_ace *prev_entry = curr_ace->prev;
1306 free_canon_ace_list( curr_ace );
1308 prev_entry->next = NULL;
1310 /* We deleted the entire list. */
1316 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1319 * Only mask off allow entries.
1322 if (allow_ace_p->attr != ALLOW_ACE)
1325 allow_ace_p->perms &= ~curr_ace->perms;
1329 * Now it's been applied, remove it.
1332 DLIST_REMOVE(ace_list, curr_ace);
1335 /* Pass 2 above - deal with deny user entries. */
1337 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1338 mode_t new_perms = (mode_t)0;
1339 canon_ace *allow_ace_p;
1342 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1344 if (curr_ace->attr != DENY_ACE)
1347 if (curr_ace->owner_type != UID_ACE)
1350 if (curr_ace->perms == ALL_ACE_PERMS) {
1353 * Optimisation - this is a deny everything to this user.
1354 * Convert to an allow nothing and push to the end of the list.
1357 curr_ace->attr = ALLOW_ACE;
1358 curr_ace->perms = (mode_t)0;
1359 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1363 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1365 if (allow_ace_p->attr != ALLOW_ACE)
1368 /* We process GID_ACE and WORLD_ACE entries only. */
1370 if (allow_ace_p->owner_type == UID_ACE)
1373 if (uid_entry_in_group( curr_ace, allow_ace_p))
1374 new_perms |= allow_ace_p->perms;
1378 * Convert to a allow entry, modify the perms and push to the end
1382 curr_ace->attr = ALLOW_ACE;
1383 curr_ace->perms = (new_perms & ~curr_ace->perms);
1384 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1387 /* Pass 3 above - deal with deny group entries. */
1389 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1391 canon_ace *allow_ace_p;
1392 canon_ace *allow_everyone_p = NULL;
1394 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1396 if (curr_ace->attr != DENY_ACE)
1399 if (curr_ace->owner_type != GID_ACE)
1402 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1404 if (allow_ace_p->attr != ALLOW_ACE)
1407 /* Store a pointer to the Everyone allow, if it exists. */
1408 if (allow_ace_p->owner_type == WORLD_ACE)
1409 allow_everyone_p = allow_ace_p;
1411 /* We process UID_ACE entries only. */
1413 if (allow_ace_p->owner_type != UID_ACE)
1416 /* Mask off the deny group perms. */
1418 if (uid_entry_in_group( allow_ace_p, curr_ace))
1419 allow_ace_p->perms &= ~curr_ace->perms;
1423 * Convert the deny to an allow with the correct perms and
1424 * push to the end of the list.
1427 curr_ace->attr = ALLOW_ACE;
1428 if (allow_everyone_p)
1429 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1431 curr_ace->perms = (mode_t)0;
1432 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1436 *pp_ace_list = ace_list;
1439 /****************************************************************************
1440 Create a default mode that will be used if a security descriptor entry has
1441 no user/group/world entries.
1442 ****************************************************************************/
1444 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1446 int snum = SNUM(fsp->conn);
1447 mode_t and_bits = (mode_t)0;
1448 mode_t or_bits = (mode_t)0;
1449 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1451 if (fsp->is_directory)
1452 mode |= (S_IWUSR|S_IXUSR);
1455 * Now AND with the create mode/directory mode bits then OR with the
1456 * force create mode/force directory mode bits.
1459 if (fsp->is_directory) {
1460 and_bits = lp_dir_security_mask(snum);
1461 or_bits = lp_force_dir_security_mode(snum);
1463 and_bits = lp_security_mask(snum);
1464 or_bits = lp_force_security_mode(snum);
1467 return ((mode & and_bits)|or_bits);
1470 /****************************************************************************
1471 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1473 ****************************************************************************/
1475 static BOOL unpack_canon_ace(files_struct *fsp,
1476 SMB_STRUCT_STAT *pst,
1477 DOM_SID *pfile_owner_sid,
1478 DOM_SID *pfile_grp_sid,
1479 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1480 uint32 security_info_sent, SEC_DESC *psd)
1482 canon_ace *file_ace = NULL;
1483 canon_ace *dir_ace = NULL;
1488 if(security_info_sent == 0) {
1489 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1494 * If no DACL then this is a chown only security descriptor.
1497 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1501 * Now go through the DACL and create the canon_ace lists.
1504 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1505 &file_ace, &dir_ace, psd->dacl))
1508 if ((file_ace == NULL) && (dir_ace == NULL)) {
1509 /* W2K traverse DACL set - ignore. */
1514 * Go through the canon_ace list and merge entries
1515 * belonging to identical users of identical allow or deny type.
1516 * We can do this as all deny entries come first, followed by
1517 * all allow entries (we have mandated this before accepting this acl).
1520 print_canon_ace_list( "file ace - before merge", file_ace);
1521 merge_aces( &file_ace );
1523 print_canon_ace_list( "dir ace - before merge", dir_ace);
1524 merge_aces( &dir_ace );
1527 * NT ACLs are order dependent. Go through the acl lists and
1528 * process DENY entries by masking the allow entries.
1531 print_canon_ace_list( "file ace - before deny", file_ace);
1532 process_deny_list( &file_ace);
1534 print_canon_ace_list( "dir ace - before deny", dir_ace);
1535 process_deny_list( &dir_ace);
1538 * A well formed POSIX file or default ACL has at least 3 entries, a
1539 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1540 * and optionally a mask entry. Ensure this is the case.
1543 print_canon_ace_list( "file ace - before valid", file_ace);
1546 * A default 3 element mode entry for a file should be r-- --- ---.
1547 * A default 3 element mode entry for a directory should be rwx --- ---.
1550 pst->st_mode = create_default_mode(fsp, False);
1552 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1553 free_canon_ace_list(file_ace);
1554 free_canon_ace_list(dir_ace);
1558 print_canon_ace_list( "dir ace - before valid", dir_ace);
1561 * A default inheritable 3 element mode entry for a directory should be the
1562 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1566 pst->st_mode = create_default_mode(fsp, True);
1568 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1569 free_canon_ace_list(file_ace);
1570 free_canon_ace_list(dir_ace);
1574 print_canon_ace_list( "file ace - return", file_ace);
1575 print_canon_ace_list( "dir ace - return", dir_ace);
1577 *ppfile_ace = file_ace;
1578 *ppdir_ace = dir_ace;
1583 /******************************************************************************
1584 When returning permissions, try and fit NT display
1585 semantics if possible. Note the the canon_entries here must have been malloced.
1586 The list format should be - first entry = owner, followed by group and other user
1587 entries, last entry = other.
1589 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1590 are not ordered, and match on the most specific entry rather than walking a list,
1591 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
1593 Entry 0: owner : deny all except read and write.
1594 Entry 1: group : deny all except read.
1595 Entry 2: owner : allow read and write.
1596 Entry 3: group : allow read.
1597 Entry 4: Everyone : allow read.
1599 But NT cannot display this in their ACL editor !
1600 ********************************************************************************/
1602 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1604 canon_ace *list_head = *pp_list_head;
1605 canon_ace *owner_ace = NULL;
1606 canon_ace *other_ace = NULL;
1607 canon_ace *ace = NULL;
1609 for (ace = list_head; ace; ace = ace->next) {
1610 if (ace->type == SMB_ACL_USER_OBJ)
1612 else if (ace->type == SMB_ACL_OTHER) {
1613 /* Last ace - this is "other" */
1618 if (!owner_ace || !other_ace) {
1619 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1625 * The POSIX algorithm applies to owner first, and other last,
1626 * so ensure they are arranged in this order.
1630 DLIST_PROMOTE(list_head, owner_ace);
1634 DLIST_DEMOTE(list_head, other_ace, ace);
1637 /* We have probably changed the head of the list. */
1639 *pp_list_head = list_head;
1642 /****************************************************************************
1643 Create a linked list of canonical ACE entries.
1644 ****************************************************************************/
1646 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1647 DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T the_acl_type)
1649 extern DOM_SID global_sid_World;
1650 connection_struct *conn = fsp->conn;
1651 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1652 canon_ace *list_head = NULL;
1653 canon_ace *ace = NULL;
1654 canon_ace *next_ace = NULL;
1655 int entry_id = SMB_ACL_FIRST_ENTRY;
1656 SMB_ACL_ENTRY_T entry;
1659 while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) {
1660 SMB_ACL_TAG_T tagtype;
1661 SMB_ACL_PERMSET_T permset;
1664 enum ace_owner owner_type;
1667 if (entry_id == SMB_ACL_FIRST_ENTRY)
1668 entry_id = SMB_ACL_NEXT_ENTRY;
1670 /* Is this a MASK entry ? */
1671 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
1674 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
1677 /* Decide which SID to use based on the ACL type. */
1679 case SMB_ACL_USER_OBJ:
1680 /* Get the SID from the owner. */
1681 sid_copy(&sid, powner);
1682 unix_ug.uid = psbuf->st_uid;
1683 owner_type = UID_ACE;
1687 uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1689 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1693 * A SMB_ACL_USER entry for the owner is shadowed by the
1694 * SMB_ACL_USER_OBJ entry and Windows also cannot represent
1695 * that entry, so we ignore it. We also don't create such
1696 * entries out of the blue when setting ACLs, so a get/set
1697 * cycle will drop them.
1699 if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
1701 uid_to_sid( &sid, *puid);
1702 unix_ug.uid = *puid;
1703 owner_type = UID_ACE;
1704 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype);
1707 case SMB_ACL_GROUP_OBJ:
1708 /* Get the SID from the owning group. */
1709 sid_copy(&sid, pgroup);
1710 unix_ug.gid = psbuf->st_gid;
1711 owner_type = GID_ACE;
1715 gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1717 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1720 gid_to_sid( &sid, *pgid);
1721 unix_ug.gid = *pgid;
1722 owner_type = GID_ACE;
1723 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype);
1727 acl_mask = convert_permset_to_mode_t(conn, permset);
1728 continue; /* Don't count the mask as an entry. */
1730 /* Use the Everyone SID */
1731 sid = global_sid_World;
1733 owner_type = WORLD_ACE;
1736 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1741 * Add this entry to the list.
1744 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1748 ace->type = tagtype;
1749 ace->perms = convert_permset_to_mode_t(conn, permset);
1750 ace->attr = ALLOW_ACE;
1752 ace->unix_ug = unix_ug;
1753 ace->owner_type = owner_type;
1755 DLIST_ADD(list_head, ace);
1759 * This next call will ensure we have at least a user/group/world set.
1762 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1765 arrange_posix_perms(fsp->fsp_name,&list_head );
1768 * Now go through the list, masking the permissions with the
1769 * acl_mask. Ensure all DENY Entries are at the start of the list.
1772 DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1774 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1775 next_ace = ace->next;
1777 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1778 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1779 ace->perms &= acl_mask;
1781 if (ace->perms == 0) {
1782 DLIST_PROMOTE(list_head, ace);
1785 if( DEBUGLVL( 10 ) ) {
1786 print_canon_ace(ace, ace_count);
1790 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1796 free_canon_ace_list(list_head);
1800 /****************************************************************************
1801 Attempt to apply an ACL to a file or directory.
1802 ****************************************************************************/
1804 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1806 connection_struct *conn = fsp->conn;
1808 SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1);
1811 SMB_ACL_ENTRY_T mask_entry;
1812 BOOL got_mask_entry = False;
1813 SMB_ACL_PERMSET_T mask_permset;
1814 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1815 BOOL needs_mask = False;
1816 mode_t mask_perms = 0;
1818 #if defined(POSIX_ACL_NEEDS_MASK)
1819 /* HP-UX always wants to have a mask (called "class" there). */
1823 if (the_acl == NULL) {
1825 if (errno != ENOSYS) {
1827 * Only print this error message if we have some kind of ACL
1828 * support that's not working. Otherwise we would always get this.
1830 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1831 default_ace ? "default" : "file", strerror(errno) ));
1833 *pacl_set_support = False;
1837 if( DEBUGLVL( 10 )) {
1838 dbgtext("set_canon_ace_list: setting ACL:\n");
1839 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1840 print_canon_ace( p_ace, i);
1844 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1845 SMB_ACL_ENTRY_T the_entry;
1846 SMB_ACL_PERMSET_T the_permset;
1849 * ACLs only "need" an ACL_MASK entry if there are any named user or
1850 * named group entries. But if there is an ACL_MASK entry, it applies
1851 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
1852 * so that it doesn't deny (i.e., mask off) any permissions.
1855 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
1857 mask_perms |= p_ace->perms;
1858 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
1859 mask_perms |= p_ace->perms;
1863 * Get the entry for this ACE.
1866 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) {
1867 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1868 i, strerror(errno) ));
1872 if (p_ace->type == SMB_ACL_MASK) {
1873 mask_entry = the_entry;
1874 got_mask_entry = True;
1878 * Ok - we now know the ACL calls should be working, don't
1879 * allow fallback to chmod.
1882 *pacl_set_support = True;
1885 * Initialise the entry from the canon_ace.
1889 * First tell the entry what type of ACE this is.
1892 if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) {
1893 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1894 i, strerror(errno) ));
1899 * Only set the qualifier (user or group id) if the entry is a user
1903 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1904 if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1905 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1906 i, strerror(errno) ));
1912 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1915 if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) {
1916 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1917 i, strerror(errno) ));
1921 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
1922 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1923 (unsigned int)p_ace->perms, i, strerror(errno) ));
1928 * ..and apply them to the entry.
1931 if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) {
1932 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1933 i, strerror(errno) ));
1938 print_canon_ace( p_ace, i);
1941 if (needs_mask && !got_mask_entry) {
1942 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &mask_entry) == -1) {
1943 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1947 if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) {
1948 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1952 if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) {
1953 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1957 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1958 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1962 if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) {
1963 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1969 * Check if the ACL is valid.
1972 if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) {
1973 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1974 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1980 * Finally apply it to the file or directory.
1983 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1984 if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
1986 * Some systems allow all the above calls and only fail with no ACL support
1987 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1989 if (errno == ENOSYS)
1990 *pacl_set_support = False;
1993 if (errno == ENOTSUP)
1994 *pacl_set_support = False;
1997 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1998 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1999 fsp->fsp_name, strerror(errno) ));
2003 if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) {
2005 * Some systems allow all the above calls and only fail with no ACL support
2006 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2008 if (errno == ENOSYS)
2009 *pacl_set_support = False;
2012 if (errno == ENOTSUP)
2013 *pacl_set_support = False;
2016 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2017 fsp->fsp_name, strerror(errno) ));
2026 if (the_acl != NULL)
2027 conn->vfs_ops.sys_acl_free_acl(conn, the_acl);
2032 /****************************************************************************
2033 Find a particular canon_ace entry.
2034 ****************************************************************************/
2036 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2039 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2040 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2041 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2048 /****************************************************************************
2050 ****************************************************************************/
2052 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T acl)
2054 SMB_ACL_ENTRY_T entry;
2058 if (conn->vfs_ops.sys_acl_get_entry(conn, acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2059 conn->vfs_ops.sys_acl_free_acl(conn, acl);
2065 /****************************************************************************
2066 Convert a canon_ace to a generic 3 element permission - if possible.
2067 ****************************************************************************/
2069 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2071 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2073 int snum = SNUM(fsp->conn);
2074 size_t ace_count = count_canon_ace_list(file_ace_list);
2076 canon_ace *owner_ace = NULL;
2077 canon_ace *group_ace = NULL;
2078 canon_ace *other_ace = NULL;
2082 if (ace_count != 3) {
2083 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2084 posix perms.\n", fsp->fsp_name ));
2088 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2089 if (ace_p->owner_type == UID_ACE)
2091 else if (ace_p->owner_type == GID_ACE)
2093 else if (ace_p->owner_type == WORLD_ACE)
2097 if (!owner_ace || !group_ace || !other_ace) {
2098 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2103 *posix_perms = (mode_t)0;
2105 *posix_perms |= owner_ace->perms;
2106 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2107 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2108 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2109 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2110 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2111 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2113 /* The owner must have at least read access. */
2115 *posix_perms |= S_IRUSR;
2116 if (fsp->is_directory)
2117 *posix_perms |= (S_IWUSR|S_IXUSR);
2119 /* If requested apply the masks. */
2121 /* Get the initial bits to apply. */
2123 if (fsp->is_directory) {
2124 and_bits = lp_dir_security_mask(snum);
2125 or_bits = lp_force_dir_security_mode(snum);
2127 and_bits = lp_security_mask(snum);
2128 or_bits = lp_force_security_mode(snum);
2131 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2133 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2134 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2140 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
2142 if (a1->type == a2->type)
2145 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
2150 /****************************************************************************
2151 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2152 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2153 with CI|OI set so it is inherited and also applies to the directory.
2154 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2155 ****************************************************************************/
2157 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2161 for (i = 0; i < num_aces; i++) {
2162 for (j = i+1; j < num_aces; j++) {
2163 /* We know the lower number ACE's are file entries. */
2164 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2165 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2166 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2167 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2168 (nt_ace_list[i].flags == 0) &&
2169 (nt_ace_list[j].flags == (SEC_ACE_FLAG_OBJECT_INHERIT|
2170 SEC_ACE_FLAG_CONTAINER_INHERIT|
2171 SEC_ACE_FLAG_INHERIT_ONLY))) {
2173 * These are identical except for the flags.
2174 * Merge the inherited ACE onto the non-inherited ACE.
2177 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT;
2178 if (num_aces - j - 1 > 0)
2179 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2189 /****************************************************************************
2190 Reply to query a security descriptor from an fsp. If it succeeds it allocates
2191 the space for the return elements and returns the size needed to return the
2192 security descriptor. This should be the only external function needed for
2193 the UNIX style get ACL.
2194 ****************************************************************************/
2196 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
2198 extern DOM_SID global_sid_Builtin_Administrators;
2199 extern DOM_SID global_sid_Builtin_Users;
2200 extern DOM_SID global_sid_Creator_Owner;
2201 extern DOM_SID global_sid_Creator_Group;
2202 connection_struct *conn = fsp->conn;
2203 SMB_STRUCT_STAT sbuf;
2204 SEC_ACE *nt_ace_list = NULL;
2208 SEC_ACL *psa = NULL;
2209 size_t num_acls = 0;
2210 size_t num_dir_acls = 0;
2211 size_t num_aces = 0;
2212 SMB_ACL_T posix_acl = NULL;
2213 SMB_ACL_T dir_acl = NULL;
2214 canon_ace *file_ace = NULL;
2215 canon_ace *dir_ace = NULL;
2216 size_t num_profile_acls = 0;
2220 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2222 if(fsp->is_directory || fsp->fd == -1) {
2224 /* Get the stat struct for the owner info. */
2225 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2229 * Get the ACL from the path.
2232 posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2235 * If it's a directory get the default POSIX ACL.
2238 if(fsp->is_directory) {
2239 dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2240 dir_acl = free_empty_sys_acl(conn, dir_acl);
2245 /* Get the stat struct for the owner info. */
2246 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
2250 * Get the ACL from the fd.
2252 posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
2255 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2256 posix_acl ? "present" : "absent",
2257 dir_acl ? "present" : "absent" ));
2260 * Get the owner, group and world SIDs.
2263 if (lp_profile_acls(SNUM(fsp->conn))) {
2264 /* For WXP SP1 the owner must be administrators. */
2265 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2266 sid_copy(&group_sid, &global_sid_Builtin_Users);
2267 num_profile_acls = 2;
2269 create_file_sids(&sbuf, &owner_sid, &group_sid);
2273 * In the optimum case Creator Owner and Creator Group would be used for
2274 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2275 * would lead to usability problems under Windows: The Creator entries
2276 * are only available in browse lists of directories and not for files;
2277 * additionally the identity of the owning group couldn't be determined.
2278 * We therefore use those identities only for Default ACLs.
2281 /* Create the canon_ace lists. */
2282 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS );
2284 /* We must have *some* ACLS. */
2286 if (count_canon_ace_list(file_ace) == 0) {
2287 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2291 if (fsp->is_directory && dir_acl) {
2292 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
2293 &global_sid_Creator_Owner,
2294 &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT );
2298 * Create the NT ACE list from the canonical ace lists.
2306 if (nt4_compatible_acls()) {
2308 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2309 * but no non-INHERIT_ONLY entry for one SID. So we only
2310 * remove entries from the Access ACL if the
2311 * corresponding Default ACL entries have also been
2312 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2313 * are exceptions. We can do nothing
2314 * intelligent if the Default ACL contains entries that
2315 * are not also contained in the Access ACL, so this
2316 * case will still fail under NT 4.
2320 goto simplify_file_ace_only;
2322 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2323 if (ace && !ace->perms) {
2324 DLIST_REMOVE(dir_ace, ace);
2327 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2328 if (ace && !ace->perms) {
2329 DLIST_REMOVE(file_ace, ace);
2335 * WinNT doesn't usually have Creator Group
2336 * in browse lists, so we send this entry to
2337 * WinNT even if it contains no relevant
2338 * permissions. Once we can add
2339 * Creator Group to browse lists we can
2344 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2345 if (ace && !ace->perms) {
2346 DLIST_REMOVE(dir_ace, ace);
2351 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2352 if (ace && !ace->perms) {
2353 DLIST_REMOVE(file_ace, ace);
2358 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2359 if (ace && !ace->perms) {
2360 DLIST_REMOVE(dir_ace, ace);
2363 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2364 if (ace && !ace->perms) {
2365 DLIST_REMOVE(dir_ace, ace);
2369 simplify_file_ace_only:
2371 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2372 if (ace && !ace->perms) {
2373 DLIST_REMOVE(file_ace, ace);
2377 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2378 if (ace && !ace->perms) {
2379 DLIST_REMOVE(file_ace, ace);
2384 num_acls = count_canon_ace_list(file_ace);
2385 num_dir_acls = count_canon_ace_list(dir_ace);
2387 /* Allocate the ace list. */
2388 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
2389 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2393 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
2396 * Create the NT ACE list from the canonical ace lists.
2401 for (i = 0; i < num_acls; i++, ace = ace->next) {
2404 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2405 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
2408 /* The User must have access to a profile share - even if we can't map the SID. */
2409 if (lp_profile_acls(SNUM(fsp->conn))) {
2412 init_sec_access(&acc,FILE_GENERIC_ALL);
2413 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0);
2418 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2421 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2422 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2423 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
2426 /* The User must have access to a profile share - even if we can't map the SID. */
2427 if (lp_profile_acls(SNUM(fsp->conn))) {
2430 init_sec_access(&acc,FILE_GENERIC_ALL);
2431 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2432 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2433 SEC_ACE_FLAG_INHERIT_ONLY);
2437 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2438 * Win2K needs this to get the inheritance correct when replacing ACLs
2439 * on a directory tree. Based on work by Jim @ IBM.
2442 num_aces = merge_default_aces(nt_ace_list, num_aces);
2445 * Sort to force deny entries to the front.
2449 qsort( nt_ace_list, num_aces, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2453 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2454 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2459 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
2462 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2466 * Windows 2000: The DACL_PROTECTED flag in the security
2467 * descriptor marks the ACL as non-inheriting, i.e., no
2468 * ACEs from higher level directories propagate to this
2469 * ACL. In the POSIX ACL model permissions are only
2470 * inherited at file create time, so ACLs never contain
2471 * any ACEs that are inherited dynamically. The DACL_PROTECTED
2472 * flag doesn't seem to bother Windows NT.
2474 (*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
2480 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2482 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2483 free_canon_ace_list(file_ace);
2484 free_canon_ace_list(dir_ace);
2485 SAFE_FREE(nt_ace_list);
2490 /****************************************************************************
2491 Try to chown a file. We will be able to chown it under the following conditions.
2493 1) If we have root privileges, then it will just work.
2494 2) If we have write permission to the file and dos_filemodes is set
2495 then allow chown to the currently authenticated user.
2496 ****************************************************************************/
2498 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2501 extern struct current_user current_user;
2505 /* try the direct way first */
2506 ret = vfs_chown(conn, fname, uid, gid);
2510 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2513 if (vfs_stat(conn,fname,&st))
2516 fsp = open_file_fchmod(conn,fname,&st);
2520 /* only allow chown to the current user. This is more secure,
2521 and also copes with the case where the SID in a take ownership ACL is
2522 a local SID on the users workstation
2524 uid = current_user.uid;
2527 /* Keep the current file gid the same. */
2528 ret = vfswrap_fchown(fsp, fsp->fd, uid, (gid_t)-1);
2531 close_file_fchmod(fsp);
2536 /****************************************************************************
2537 Reply to set a security descriptor on an fsp. security_info_sent is the
2538 description of the following NT ACL.
2539 This should be the only external function needed for the UNIX style set ACL.
2540 ****************************************************************************/
2542 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2544 connection_struct *conn = fsp->conn;
2545 uid_t user = (uid_t)-1;
2546 gid_t grp = (gid_t)-1;
2547 SMB_STRUCT_STAT sbuf;
2548 DOM_SID file_owner_sid;
2549 DOM_SID file_grp_sid;
2550 canon_ace *file_ace_list = NULL;
2551 canon_ace *dir_ace_list = NULL;
2552 BOOL acl_perms = False;
2553 mode_t orig_mode = (mode_t)0;
2556 BOOL need_chown = False;
2557 extern struct current_user current_user;
2559 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2561 if (!CAN_WRITE(conn)) {
2562 DEBUG(10,("set acl rejected on read-only share\n"));
2567 * Get the current state of the file.
2570 if(fsp->is_directory || fsp->fd == -1) {
2571 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2574 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2578 /* Save the original elements we check against. */
2579 orig_mode = sbuf.st_mode;
2580 orig_uid = sbuf.st_uid;
2581 orig_gid = sbuf.st_gid;
2584 * Unpack the user/group/world id's.
2587 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2591 * Do we need to chown ?
2594 if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
2598 * Chown before setting ACL only if we don't change the user, or
2599 * if we change to the current user, but not if we want to give away
2603 if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
2605 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2606 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2608 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2609 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2610 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2615 * Recheck the current state of the file, which may have changed.
2616 * (suid/sgid bits, for instance)
2619 if(fsp->is_directory) {
2620 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2628 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2630 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2636 /* Save the original elements we check against. */
2637 orig_mode = sbuf.st_mode;
2638 orig_uid = sbuf.st_uid;
2639 orig_gid = sbuf.st_gid;
2641 /* We did it, don't try again */
2645 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2647 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2648 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2650 /* Ignore W2K traverse DACL set. */
2651 if (file_ace_list || dir_ace_list) {
2654 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2655 free_canon_ace_list(file_ace_list);
2656 free_canon_ace_list(dir_ace_list);
2661 * Only change security if we got a DACL.
2664 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2666 BOOL acl_set_support = False;
2670 * Try using the POSIX ACL set first. Fall back to chmod if
2671 * we have no ACL support on this filesystem.
2674 if (acl_perms && file_ace_list) {
2675 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2676 if (acl_set_support && ret == False) {
2677 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2678 free_canon_ace_list(file_ace_list);
2679 free_canon_ace_list(dir_ace_list);
2684 if (acl_perms && acl_set_support && fsp->is_directory) {
2686 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2687 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2688 free_canon_ace_list(file_ace_list);
2689 free_canon_ace_list(dir_ace_list);
2695 * No default ACL - delete one if it exists.
2698 if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
2699 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2700 free_canon_ace_list(file_ace_list);
2701 free_canon_ace_list(dir_ace_list);
2708 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2711 if(!acl_set_support && acl_perms) {
2714 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2715 free_canon_ace_list(file_ace_list);
2716 free_canon_ace_list(dir_ace_list);
2717 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2722 if (orig_mode != posix_perms) {
2724 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2725 fsp->fsp_name, (unsigned int)posix_perms ));
2727 if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2728 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2729 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2730 free_canon_ace_list(file_ace_list);
2731 free_canon_ace_list(dir_ace_list);
2738 free_canon_ace_list(file_ace_list);
2739 free_canon_ace_list(dir_ace_list);
2742 /* Any chown pending? */
2745 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2746 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2748 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2749 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2750 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2758 /****************************************************************************
2759 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2760 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2761 ****************************************************************************/
2763 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2765 int entry_id = SMB_ACL_FIRST_ENTRY;
2766 SMB_ACL_ENTRY_T entry;
2767 int num_entries = 0;
2769 while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
2770 SMB_ACL_TAG_T tagtype;
2771 SMB_ACL_PERMSET_T permset;
2775 if (entry_id == SMB_ACL_FIRST_ENTRY)
2776 entry_id = SMB_ACL_NEXT_ENTRY;
2778 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
2781 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
2787 case SMB_ACL_USER_OBJ:
2788 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2790 case SMB_ACL_GROUP_OBJ:
2791 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2795 * FIXME: The ACL_MASK entry permissions should really be set to
2796 * the union of the permissions of all ACL_USER,
2797 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
2798 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
2800 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2803 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2809 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2812 if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
2817 * If this is a simple 3 element ACL or no elements then it's a standard
2818 * UNIX permission set. Just use chmod...
2821 if ((num_entries == 3) || (num_entries == 0))
2827 /****************************************************************************
2828 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
2829 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
2830 resulting ACL on TO. Note that name is in UNIX character set.
2831 ****************************************************************************/
2833 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
2835 SMB_ACL_T posix_acl = NULL;
2838 if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
2841 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2844 ret = conn->vfs_ops.sys_acl_set_file(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
2848 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2852 /****************************************************************************
2853 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2854 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2855 Note that name is in UNIX character set.
2856 ****************************************************************************/
2858 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2860 return copy_access_acl(conn, name, name, mode);
2863 /****************************************************************************
2864 If "inherit permissions" is set and the parent directory has no default
2865 ACL but it does have an Access ACL, inherit this Access ACL to file name.
2866 ****************************************************************************/
2868 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
2871 pstrcpy(dirname, parent_dirname(name));
2873 if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
2876 return copy_access_acl(conn, dirname, name, mode);
2879 /****************************************************************************
2880 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2881 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2882 ****************************************************************************/
2884 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2886 connection_struct *conn = fsp->conn;
2887 SMB_ACL_T posix_acl = NULL;
2890 if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
2893 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2896 ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
2900 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2904 /****************************************************************************
2905 Check for an existing default POSIX ACL on a directory.
2906 ****************************************************************************/
2908 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2910 SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
2911 BOOL has_acl = False;
2912 SMB_ACL_ENTRY_T entry;
2914 if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2918 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);