Implemented default ACL patch (set inherit acls = true on a per share basis).
[sfrench/samba-autobuild/.git] / source3 / smbd / posix_acls.c
index 22d1038fcc93dbd7fc37cc29dec7783cc730f76b..34f0b77002991aad8c1990a01a438c81377e6e7f 100644 (file)
@@ -1,6 +1,5 @@
 /*
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    SMB NT Security Descriptor / Unix permission conversion.
    Copyright (C) Jeremy Allison 1994-2000
 
@@ -38,7 +37,7 @@ typedef struct canon_ace {
        struct canon_ace *next, *prev;
        SMB_ACL_TAG_T type;
        mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
-       DOM_SID sid;
+       DOM_SID trustee;
        enum ace_owner owner_type;
        enum ace_attribute attr;
        posix_id unix_ug; 
@@ -74,7 +73,7 @@ static void free_canon_ace_list( canon_ace *list_head )
        while (list_head) {
                canon_ace *old_head = list_head;
                DLIST_REMOVE(list_head, list_head);
-               free(old_head);
+               SAFE_FREE(old_head);
        }
 }
 
@@ -94,35 +93,25 @@ static canon_ace *dup_canon_ace( canon_ace *src_ace)
        return dst_ace;
 }
 
-/****************************************************************************
- Function to create owner and group SIDs from a SMB_STRUCT_STAT.
-****************************************************************************/
-
-static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
-{
-       uid_to_sid( powner_sid, psbuf->st_uid );
-       gid_to_sid( pgroup_sid, psbuf->st_gid );
-}
-
 /****************************************************************************
  Print out a canon ace.
 ****************************************************************************/
 
-static void print_canon_ace(canon_ace *ace, int num)
+static void print_canon_ace(canon_ace *pace, int num)
 {
        fstring str;
 
-       dbgtext( "canon_ace index %d. Type = %s ", num, ace->attr == ALLOW_ACE ? "allow" : "deny" );
-    dbgtext( "SID = %s ", sid_to_string( str, &ace->sid));
-       if (ace->owner_type == UID_ACE) {
-               struct passwd *pass = sys_getpwuid(ace->unix_ug.uid);
-               dbgtext( "uid %u (%s) ", (unsigned int)ace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
-       } else if (ace->owner_type == GID_ACE) {
-               struct group *grp = getgrgid(ace->unix_ug.gid);
-               dbgtext( "gid %u (%s) ", (unsigned int)ace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
+       dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
+       dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
+       if (pace->owner_type == UID_ACE) {
+               char *u_name = uidtoname(pace->unix_ug.uid);
+               dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
+       } else if (pace->owner_type == GID_ACE) {
+               char *g_name = gidtoname(pace->unix_ug.gid);
+               dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
        } else
                dbgtext( "other ");
-       switch (ace->type) {
+       switch (pace->type) {
                case SMB_ACL_USER:
                        dbgtext( "SMB_ACL_USER ");
                        break;
@@ -140,9 +129,90 @@ static void print_canon_ace(canon_ace *ace, int num)
                        break;
        }
        dbgtext( "perms ");
-       dbgtext( "%c", ace->perms & S_IRUSR ? 'r' : '-');
-       dbgtext( "%c", ace->perms & S_IWUSR ? 'w' : '-');
-       dbgtext( "%c\n", ace->perms & S_IXUSR ? 'x' : '-');
+       dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
+       dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
+       dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
+}
+
+/****************************************************************************
+ Print out a canon ace list.
+****************************************************************************/
+
+static void print_canon_ace_list(const char *name, canon_ace *ace_list)
+{
+       int count = 0;
+
+       if( DEBUGLVL( 10 )) {
+               dbgtext( "print_canon_ace_list: %s\n", name );
+               for (;ace_list; ace_list = ace_list->next, count++)
+                       print_canon_ace(ace_list, count );
+       }
+}
+
+/****************************************************************************
+ Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
+{
+       mode_t ret = 0;
+
+       ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
+       ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
+       ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
+
+       return ret;
+}
+
+/****************************************************************************
+ Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
+{
+       mode_t ret = 0;
+
+       if (mode & r_mask)
+               ret |= S_IRUSR;
+       if (mode & w_mask)
+               ret |= S_IWUSR;
+       if (mode & x_mask)
+               ret |= S_IXUSR;
+
+       return ret;
+}
+
+/****************************************************************************
+ Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
+ an SMB_ACL_PERMSET_T.
+****************************************************************************/
+
+static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
+{
+       if (sys_acl_clear_perms(*p_permset) ==  -1)
+               return -1;
+       if (mode & S_IRUSR) {
+               if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
+                       return -1;
+       }
+       if (mode & S_IWUSR) {
+               if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
+                       return -1;
+       }
+       if (mode & S_IXUSR) {
+               if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
+                       return -1;
+       }
+       return 0;
+}
+/****************************************************************************
+ Function to create owner and group SIDs from a SMB_STRUCT_STAT.
+****************************************************************************/
+
+static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+{
+       uid_to_sid( powner_sid, psbuf->st_uid );
+       gid_to_sid( pgroup_sid, psbuf->st_gid );
 }
 
 /****************************************************************************
@@ -172,7 +242,7 @@ static void merge_aces( canon_ace **pp_list_head )
 
                        curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
 
-                       if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
+                       if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
                                (curr_ace->attr == curr_ace_outer->attr)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -185,7 +255,7 @@ static void merge_aces( canon_ace **pp_list_head )
 
                                curr_ace_outer->perms |= curr_ace->perms;
                                DLIST_REMOVE(list_head, curr_ace);
-                               free(curr_ace);
+                               SAFE_FREE(curr_ace);
                                curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
                        }
                }
@@ -212,7 +282,8 @@ static void merge_aces( canon_ace **pp_list_head )
                         * we've put on the ACL, we know the deny must be the first one.
                         */
 
-                       if ((curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
+                       if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                               (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
 
                                if( DEBUGLVL( 10 )) {
                                        dbgtext("merge_aces: Masking ACE's\n");
@@ -229,7 +300,7 @@ static void merge_aces( canon_ace **pp_list_head )
                                         */
 
                                        DLIST_REMOVE(list_head, curr_ace);
-                                       free(curr_ace);
+                                       SAFE_FREE(curr_ace);
                                        curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
 
                                } else {
@@ -245,7 +316,8 @@ static void merge_aces( canon_ace **pp_list_head )
                                         */
 
                                        DLIST_REMOVE(list_head, curr_ace_outer);
-                                       free(curr_ace_outer);
+                                       SAFE_FREE(curr_ace_outer);
+                                       break;
                                }
                        }
 
@@ -273,16 +345,7 @@ static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon
        if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
                        nt_mask = UNIX_ACCESS_RWX;
        } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
-               /*
-                * Here we differentiate between the owner and any other user.
-                */
-               if (sid_equal(powner_sid, &ace->sid)) {
-                       nt_mask = UNIX_ACCESS_NONE;
-               } else {
-                       /* Not owner, no access. */
-                       *pacl_type = SEC_ACE_TYPE_ACCESS_DENIED;
-                       nt_mask = GENERIC_ALL_ACCESS;
-               }
+               nt_mask = UNIX_ACCESS_NONE;
        } else {
                nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
                nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
@@ -356,7 +419,7 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
 
        if(security_info_sent == 0) {
                DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
-               return False;
+               return True;
        }
 
        /*
@@ -375,8 +438,11 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
 
        if (security_info_sent & OWNER_SECURITY_INFORMATION) {
                sid_copy(&owner_sid, psd->owner_sid);
-               if (!sid_to_uid( &owner_sid, puser, &sid_type))
-                       DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
+               if (!sid_to_uid( &owner_sid, puser, &sid_type)) {
+                       DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
+                                sid_string_static(&owner_sid)));
+                       return False;
+               }
        }
 
        /*
@@ -386,8 +452,10 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
 
        if (security_info_sent & GROUP_SECURITY_INFORMATION) {
                sid_copy(&grp_sid, psd->grp_sid);
-               if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
+               if (!sid_to_gid( &grp_sid, pgrp, &sid_type)) {
                        DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
+                       return False;
+               }
        }
 
        DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
@@ -396,39 +464,52 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp,
 }
 
 /****************************************************************************
Create a default mode for a directory default ACE.
Ensure the enforced permissions for this share apply.
 ****************************************************************************/
 
-static mode_t get_default_ace_mode(files_struct *fsp, int type)
+static mode_t apply_default_perms(files_struct *fsp, mode_t perms, mode_t type)
 {
-    mode_t force_mode = lp_force_dir_security_mode(SNUM(fsp->conn));
-       mode_t mode = 0;
+       int snum = SNUM(fsp->conn);
+       mode_t and_bits = (mode_t)0;
+       mode_t or_bits = (mode_t)0;
 
+       /* Get the initial bits to apply. */
+
+       if (fsp->is_directory) {
+               and_bits = lp_dir_security_mask(snum);
+               or_bits = lp_force_dir_security_mode(snum);
+       } else {
+               and_bits = lp_security_mask(snum);
+               or_bits = lp_force_security_mode(snum);
+       }
+
+       /* Now bounce them into the S_USR space. */     
        switch(type) {
-               case S_IRUSR:
-                       mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
-                       mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
-                       mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
-                       break;
-               case S_IRGRP:
-                       mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
-                       mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
-                       mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
-                       break;
-               case S_IROTH:
-                       mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
-                       mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
-                       mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
-                       break;
+       case S_IRUSR:
+               and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+               or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+               break;
+       case S_IRGRP:
+               and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+               or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+               break;
+       case S_IROTH:
+               and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
+               or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
+               break;
        }
 
-       return mode;
+       return ((perms & and_bits)|or_bits);
 }
 
 /****************************************************************************
  A well formed POSIX file or default ACL has at least 3 entries, a 
  SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
  In addition, the owner must always have at least read access.
+ When using this call on get_acl, the pst struct is valid and contains
+ the mode of the file. When using this call on set_acl, the pst struct has
+ been modified to have a mode containing the default for this file or directory
+ type.
 ****************************************************************************/
 
 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
@@ -436,7 +517,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                                                        DOM_SID *pfile_owner_sid,
                                                        DOM_SID *pfile_grp_sid,
                                                        SMB_STRUCT_STAT *pst,
-                                                       BOOL default_acl)
+                                                       BOOL setting_acl)
 {
        extern DOM_SID global_sid_World;
        canon_ace *pace;
@@ -446,14 +527,40 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
 
        for (pace = *pp_ace; pace; pace = pace->next) {
                if (pace->type == SMB_ACL_USER_OBJ) {
-                       /* Ensure owner has read access. */
-                       if (pace->perms == (mode_t)0)
-                               pace->perms = S_IRUSR;
+
+                       if (setting_acl) {
+                               /* Ensure owner has read access. */
+                               pace->perms |= S_IRUSR;
+                               if (fsp->is_directory)
+                                       pace->perms |= (S_IWUSR|S_IXUSR);
+
+                               /*
+                                * Ensure create mask/force create mode is respected on set.
+                                */
+
+                               pace->perms = apply_default_perms(fsp, pace->perms, S_IRUSR);
+                       }
+
                        got_user = True;
-               } else if (pace->type == SMB_ACL_GROUP_OBJ)
+               } else if (pace->type == SMB_ACL_GROUP_OBJ) {
+
+                       /*
+                        * Ensure create mask/force create mode is respected on set.
+                        */
+
+                       if (setting_acl)
+                               pace->perms = apply_default_perms(fsp, pace->perms, S_IRGRP);
                        got_grp = True;
-               else if (pace->type == SMB_ACL_OTHER)
+               } else if (pace->type == SMB_ACL_OTHER) {
+
+                       /*
+                        * Ensure create mask/force create mode is respected on set.
+                        */
+
+                       if (setting_acl)
+                               pace->perms = apply_default_perms(fsp, pace->perms, S_IROTH);
                        got_other = True;
+               }
        }
 
        if (!got_user) {
@@ -466,8 +573,8 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->type = SMB_ACL_USER_OBJ;
                pace->owner_type = UID_ACE;
                pace->unix_ug.uid = pst->st_uid;
-               pace->sid = *pfile_owner_sid;
-               pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR): 0;
+               pace->trustee = *pfile_owner_sid;
+               pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
                pace->attr = ALLOW_ACE;
 
                DLIST_ADD(*pp_ace, pace);
@@ -483,8 +590,8 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->type = SMB_ACL_GROUP_OBJ;
                pace->owner_type = GID_ACE;
                pace->unix_ug.uid = pst->st_gid;
-               pace->sid = *pfile_grp_sid;
-               pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
+               pace->trustee = *pfile_grp_sid;
+               pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
                pace->attr = ALLOW_ACE;
 
                DLIST_ADD(*pp_ace, pace);
@@ -500,8 +607,8 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->type = SMB_ACL_OTHER;
                pace->owner_type = WORLD_ACE;
                pace->unix_ug.world = -1;
-               pace->sid = global_sid_World;
-               pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
+               pace->trustee = global_sid_World;
+               pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
                pace->attr = ALLOW_ACE;
 
                DLIST_ADD(*pp_ace, pace);
@@ -521,6 +628,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
                                                        SEC_ACL *dacl)
 {
        extern DOM_SID global_sid_World;
+       extern struct generic_mapping file_generic_mapping;
        BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
@@ -528,18 +636,19 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
        canon_ace *current_ace = NULL;
        BOOL got_dir_allow = False;
        BOOL got_file_allow = False;
-       int i;
+       int i, j;
 
        *ppfile_ace = NULL;
        *ppdir_ace = NULL;
 
+       /*
+        * Convert the incoming ACL into a more regular form.
+        */
+
        for(i = 0; i < dacl->num_aces; i++) {
-               enum SID_NAME_USE sid_type;
                SEC_ACE *psa = &dacl->ace[i];
 
                if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
-                       free_canon_ace_list(file_ace);
-                       free_canon_ace_list(dir_ace);
                        DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
                        return False;
                }
@@ -551,11 +660,71 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
                 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
                 */
 
-               psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
-                                                       GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
+               /*
+                * Convert GENERIC bits to specific bits.
+                */
+               se_map_generic(&psa->info.mask, &file_generic_mapping);
+
+               psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
 
                if(psa->info.mask != UNIX_ACCESS_NONE)
                        psa->info.mask &= ~UNIX_ACCESS_NONE;
+       }
+
+       /*
+        * Deal with the fact that NT 4.x re-writes the canonical format
+        * that we return for default ACLs. If a directory ACE is identical
+        * to a inherited directory ACE then NT changes the bits so that the
+        * first ACE is set to OI|IO and the second ACE for this SID is set
+        * to CI. We need to repair this. JRA.
+        */
+
+       for(i = 0; i < dacl->num_aces; i++) {
+               SEC_ACE *psa1 = &dacl->ace[i];
+
+               for (j = i + 1; j < dacl->num_aces; j++) {
+                       SEC_ACE *psa2 = &dacl->ace[j];
+
+                       if (psa1->info.mask != psa2->info.mask)
+                               continue;
+
+                       if (!sid_equal(&psa1->trustee, &psa2->trustee))
+                               continue;
+
+                       /*
+                        * Ok - permission bits and SIDs are equal.
+                        * Check if flags were re-written.
+                        */
+
+                       if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+                               psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+                               psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+                               
+                       } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+                               psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+                               psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+                               
+                       }
+               }
+       }
+
+       for(i = 0; i < dacl->num_aces; i++) {
+               enum SID_NAME_USE sid_type;
+               SEC_ACE *psa = &dacl->ace[i];
+
+               /*
+                * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
+                */
+
+               if (non_mappable_sid(&psa->trustee)) {
+                       fstring str;
+                       DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
+                               sid_to_string(str, &psa->trustee) ));
+                       continue;
+               }
 
                /*
                 * Create a cannon_ace entry representing this NT DACL ACE.
@@ -570,28 +739,28 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
 
                ZERO_STRUCTP(current_ace);
 
-               sid_copy(&current_ace->sid, &psa->sid);
+               sid_copy(&current_ace->trustee, &psa->trustee);
 
                /*
                 * Try and work out if the SID is a user or group
                 * as we need to flag these differently for POSIX.
                 */
 
-               if( sid_equal(&current_ace->sid, &global_sid_World)) {
+               if( sid_equal(&current_ace->trustee, &global_sid_World)) {
                        current_ace->owner_type = WORLD_ACE;
                        current_ace->unix_ug.world = -1;
-               } else if (sid_to_uid( &current_ace->sid, &current_ace->unix_ug.uid, &sid_type)) {
+               } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid, &sid_type)) {
                        current_ace->owner_type = UID_ACE;
-               } else if (sid_to_gid( &current_ace->sid, &current_ace->unix_ug.gid, &sid_type)) {
+               } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid, &sid_type)) {
                        current_ace->owner_type = GID_ACE;
                } else {
                        fstring str;
 
                        free_canon_ace_list(file_ace);
                        free_canon_ace_list(dir_ace);
-                       free(current_ace);
                        DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
-                               sid_to_string(str, &current_ace->sid) ));
+                               sid_to_string(str, &current_ace->trustee) ));
+                       SAFE_FREE(current_ace);
                        return False;
                }
 
@@ -607,15 +776,18 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
                 * Now note what kind of a POSIX ACL this should map to.
                 */
 
-               if(sid_equal(&current_ace->sid, pfile_owner_sid)) {
-                       /* Note we should apply the default mode/mask here.... FIXME ! JRA */
+               if(sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+
                        current_ace->type = SMB_ACL_USER_OBJ;
-               } else if( sid_equal(&current_ace->sid, pfile_grp_sid)) {
-                       /* Note we should apply the default mode/mask here.... FIXME ! JRA */
+
+               } else if( sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+
                        current_ace->type = SMB_ACL_GROUP_OBJ;
-               } else if( sid_equal(&current_ace->sid, &global_sid_World)) {
-                       /* Note we should apply the default mode/mask here.... FIXME ! JRA */
+
+               } else if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+
                        current_ace->type = SMB_ACL_OTHER;
+
                } else {
                        /*
                         * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
@@ -656,7 +828,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                        free_canon_ace_list(file_ace);
                                        free_canon_ace_list(dir_ace);
-                                       free(current_ace);
+                                       SAFE_FREE(current_ace);
                                        return False;
                                }       
 
@@ -707,7 +879,7 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                                free_canon_ace_list(file_ace);
                                free_canon_ace_list(dir_ace);
-                               free(current_ace);
+                               SAFE_FREE(current_ace);
                                return False;
                        }       
 
@@ -723,8 +895,7 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                 * Free if ACE was not added.
                 */
 
-               if (current_ace)
-                       free(current_ace);
+               SAFE_FREE(current_ace);
        }
 
        if (fsp->is_directory && all_aces_are_inherit_only) {
@@ -755,26 +926,23 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
 {
        extern DOM_SID global_sid_World;
-       struct passwd *pass = NULL;
-       struct group *gptr = NULL;
+       fstring u_name;
+       fstring g_name;
 
        /* "Everyone" always matches every uid. */
 
-       if (sid_equal(&group_ace->sid, &global_sid_World))
+       if (sid_equal(&group_ace->trustee, &global_sid_World))
                return True;
 
-       if (!(pass = sys_getpwuid(uid_ace->unix_ug.uid)))
-               return False;
-
-       if (!(gptr = getgrgid(uid_ace->unix_ug.gid)))
-               return False;
+       fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
+       fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
 
        /*
         * Due to the winbind interfaces we need to do this via names,
         * not uids/gids.
         */
 
-       return user_in_group_list(pass->pw_name, gptr->gr_name );
+       return user_in_group_list(u_name, g_name );
 }
 
 /****************************************************************************
@@ -897,7 +1065,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
                        continue;
                }
 
-               if (!sid_equal(&curr_ace->sid, &global_sid_World))
+               if (!sid_equal(&curr_ace->trustee, &global_sid_World))
                        continue;
 
                /* JRATEST - assert. */
@@ -1045,6 +1213,37 @@ static void process_deny_list( canon_ace **pp_ace_list )
        *pp_ace_list = ace_list;
 }
 
+/****************************************************************************
+ Create a default mode that will be used if a security descriptor entry has
+ no user/group/world entries.
+****************************************************************************/
+
+static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
+{
+       int snum = SNUM(fsp->conn);
+       mode_t and_bits = (mode_t)0;
+       mode_t or_bits = (mode_t)0;
+       mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
+
+       if (fsp->is_directory)
+               mode |= (S_IWUSR|S_IXUSR);
+
+       /*
+        * Now AND with the create mode/directory mode bits then OR with the
+        * force create mode/force directory mode bits.
+        */
+
+       if (fsp->is_directory) {
+               and_bits = lp_dir_security_mask(snum);
+               or_bits = lp_force_dir_security_mode(snum);
+       } else {
+               and_bits = lp_security_mask(snum);
+               or_bits = lp_force_security_mode(snum);
+       }
+
+       return ((mode & and_bits)|or_bits);
+}
+
 /****************************************************************************
  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
  succeeding.
@@ -1059,8 +1258,6 @@ static BOOL unpack_canon_ace(files_struct *fsp,
 {
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
-       canon_ace *current_ace = NULL;
-       int i;
 
        *ppfile_ace = NULL;
        *ppdir_ace = NULL;
@@ -1085,6 +1282,11 @@ static BOOL unpack_canon_ace(files_struct *fsp,
                                                                &file_ace, &dir_ace, psd->dacl))
                return False;
 
+       if ((file_ace == NULL) && (dir_ace == NULL)) {
+               /* W2K traverse DACL set - ignore. */
+               return True;
+       }
+
        /*
         * Go through the canon_ace list and merge entries
         * belonging to identical users of identical allow or deny type.
@@ -1092,7 +1294,10 @@ static BOOL unpack_canon_ace(files_struct *fsp,
         * all allow entries (we have mandated this before accepting this acl).
         */
 
+       print_canon_ace_list( "file ace - before merge", file_ace);
        merge_aces( &file_ace );
+
+       print_canon_ace_list( "dir ace - before merge", dir_ace);
        merge_aces( &dir_ace );
 
        /*
@@ -1100,7 +1305,10 @@ static BOOL unpack_canon_ace(files_struct *fsp,
         * process DENY entries by masking the allow entries.
         */
 
+       print_canon_ace_list( "file ace - before deny", file_ace);
        process_deny_list( &file_ace);
+
+       print_canon_ace_list( "dir ace - before deny", dir_ace);
        process_deny_list( &dir_ace);
 
        /*
@@ -1109,29 +1317,39 @@ static BOOL unpack_canon_ace(files_struct *fsp,
         * and optionally a mask entry. Ensure this is the case.
         */
 
-       if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
+       print_canon_ace_list( "file ace - before valid", file_ace);
+
+       /*
+        * A default 3 element mode entry for a file should be r-- --- ---.
+        * A default 3 element mode entry for a directory should be rwx --- ---.
+        */
+
+       pst->st_mode = create_default_mode(fsp, False);
+
+       if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
        }
 
+       print_canon_ace_list( "dir ace - before valid", dir_ace);
+
+       /*
+        * A default inheritable 3 element mode entry for a directory should be the
+        * mode Samba will use to create a file within. Ensure user rwx bits are set if
+        * it's a directory.
+        */
+
+       pst->st_mode = create_default_mode(fsp, True);
+
        if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
        }
 
-       if( DEBUGLVL( 10 )) {
-               dbgtext("unpack_canon_ace: File ACL:\n");
-               for (i = 0, current_ace = file_ace; current_ace; current_ace = current_ace->next, i++ ) {
-                       print_canon_ace( current_ace, i);
-               }
-
-               dbgtext("unpack_canon_ace: Directory ACL:\n");
-               for (i = 0, current_ace = dir_ace; current_ace; current_ace = current_ace->next, i++ ) {
-                       print_canon_ace( current_ace, i);
-               }
-       }
+       print_canon_ace_list( "file ace - return", file_ace);
+       print_canon_ace_list( "dir ace - return", dir_ace);
 
        *ppfile_ace = file_ace;
        *ppdir_ace = dir_ace;
@@ -1139,63 +1357,6 @@ static BOOL unpack_canon_ace(files_struct *fsp,
 
 }
 
-/****************************************************************************
- Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
-****************************************************************************/
-
-static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
-{
-       mode_t ret = 0;
-
-       ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
-       ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
-       ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
-
-       return ret;
-}
-
-/****************************************************************************
- Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
-****************************************************************************/
-
-static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
-{
-       mode_t ret = 0;
-
-       if (mode & r_mask)
-               ret |= S_IRUSR;
-       if (mode & w_mask)
-               ret |= S_IWUSR;
-       if (mode & x_mask)
-               ret |= S_IXUSR;
-
-       return ret;
-}
-
-/****************************************************************************
- Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
- an SMB_ACL_PERMSET_T.
-****************************************************************************/
-
-static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
-{
-       if (sys_acl_clear_perms(*p_permset) ==  -1)
-               return -1;
-       if (mode & S_IRUSR) {
-               if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
-                       return -1;
-       }
-       if (mode & S_IWUSR) {
-               if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
-                       return -1;
-       }
-       if (mode & S_IXUSR) {
-               if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
-                       return -1;
-       }
-       return 0;
-}
-
 /******************************************************************************
  When returning permissions, try and fit NT display
  semantics if possible. Note the the canon_entries here must have been malloced.
@@ -1218,14 +1379,10 @@ static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
 
 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
 {
-       extern DOM_SID global_sid_World;
        canon_ace *list_head = *pp_list_head;
        canon_ace *owner_ace = NULL;
        canon_ace *other_ace = NULL;
        canon_ace *ace = NULL;
-       mode_t owner_perms = 0;
-       mode_t group_perms = 0;
-       mode_t other_perms = 0;
 
        for (ace = list_head; ace; ace = ace->next) {
                if (ace->type == SMB_ACL_USER_OBJ)
@@ -1233,9 +1390,6 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
                else if (ace->type == SMB_ACL_OTHER) {
                        /* Last ace - this is "other" */
                        other_ace = ace;
-               } else {
-                       /* Get the union of all the group and supplementary user perms. */
-                       group_perms |= ace->perms;
                }
        }
                
@@ -1258,155 +1412,28 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
                DLIST_DEMOTE(list_head, other_ace, ace);
        }
 
-       owner_perms = owner_ace->perms;
-       other_perms = other_ace->perms;
-
-       /*
-        * We have to be clever here. NT4.x won't display anything other
-        * Than an "Everyone, No access" DENY acl. Truncate blank perms
-        * from the end, but we can't truncate blank permissions from
-        * anywhere except the end, as they have an effect on allowing access
-        * under POSIX.
-        */
-
-       if ((owner_perms || group_perms) && !other_perms) {
-               DLIST_REMOVE(list_head, other_ace);
-               safe_free(other_ace);
-       }
-
-       if (owner_perms && !group_perms && !other_perms) {
-               /* Free everything except the list head. */
-               free_canon_ace_list(owner_ace->next);
-               owner_ace->next = NULL;
-       }
-
-       if (!owner_perms && !group_perms && !other_perms) {
-               /*
-                * Special case - no one has any access.
-                * Return a 1 element ACL - other has "no access".
-                */
-
-               if (owner_ace->next) {
-                       free_canon_ace_list(owner_ace->next);
-                       owner_ace->next = NULL;
-               }
-
-               owner_ace->type = SMB_ACL_OTHER;
-               owner_ace->sid = global_sid_World;
-               owner_ace->unix_ug.world = -1;
-               owner_ace->owner_type = WORLD_ACE;
-               owner_ace->attr = DENY_ACE;
-               owner_ace->perms = 0;
-       }
-
        /* We have probably changed the head of the list. */
 
        *pp_list_head = list_head;
 }
                
-/******************************************************************************
- Fall back to the generic 3 element UNIX permissions.
-********************************************************************************/
-
-static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
-                                                                               DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
-{
-       extern DOM_SID global_sid_World;
-       canon_ace *list_head = NULL;
-       canon_ace *owner_ace = NULL;
-       canon_ace *group_ace = NULL;
-       canon_ace *other_ace = NULL;
-       mode_t mode;
-
-       if (default_acl)
-               return NULL;
-
-       /*
-        * Create 3 linked list entries.
-        */
-
-       if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
-               goto fail;
-
-       if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
-               goto fail;
-
-       if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
-               goto fail;
-
-       ZERO_STRUCTP(owner_ace);
-       ZERO_STRUCTP(group_ace);
-       ZERO_STRUCTP(other_ace);
-
-       owner_ace->type = SMB_ACL_USER_OBJ;
-       owner_ace->sid = *powner;
-       owner_ace->unix_ug.uid = psbuf->st_uid;
-       owner_ace->owner_type = UID_ACE;
-       owner_ace->attr = ALLOW_ACE;
-
-       group_ace->type = SMB_ACL_GROUP_OBJ;
-       group_ace->sid = *pgroup;
-       group_ace->unix_ug.gid = psbuf->st_gid;
-       group_ace->owner_type = GID_ACE;
-       group_ace->attr = ALLOW_ACE;
-
-       other_ace->type = SMB_ACL_OTHER;
-       other_ace->sid = global_sid_World;
-       other_ace->unix_ug.world = -1;
-       other_ace->owner_type = WORLD_ACE;
-       other_ace->attr = ALLOW_ACE;
-
-       mode = psbuf->st_mode;
-
-       owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
-       owner_ace->attr = owner_ace->perms ? ALLOW_ACE : DENY_ACE;
-       
-       group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
-       group_ace->attr = group_ace->perms ? ALLOW_ACE : DENY_ACE;
-
-       other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
-       other_ace->attr = other_ace->perms ? ALLOW_ACE : DENY_ACE;
-
-       DLIST_ADD(list_head, other_ace);
-       DLIST_ADD(list_head, group_ace);
-       DLIST_ADD(list_head, owner_ace);
-
-       arrange_posix_perms(fsp->fsp_name,&list_head );
-
-       return list_head;
-
-  fail:
-
-       safe_free(owner_ace);
-       safe_free(group_ace);
-       safe_free(other_ace);
-
-       return NULL;
-}
-
 /****************************************************************************
  Create a linked list of canonical ACE entries.
 ****************************************************************************/
 
 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
-                                                                       DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
+                                                                       DOM_SID *powner, DOM_SID *pgroup)
 {
        extern DOM_SID global_sid_World;
        mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
        canon_ace *list_head = NULL;
        canon_ace *ace = NULL;
        canon_ace *next_ace = NULL;
-       canon_ace *owner_ace = NULL;
-       canon_ace *group_ace = NULL;
-       canon_ace *other_ace = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
        SMB_ACL_ENTRY_T entry;
        size_t ace_count;
 
-       if (posix_acl == NULL)
-               return unix_canonicalise_acl( fsp, psbuf, powner, pgroup, default_acl);
-
-       while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
+       while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
                SMB_ACL_TAG_T tagtype;
                SMB_ACL_PERMSET_T permset;
                DOM_SID sid;
@@ -1442,6 +1469,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_
                                        uid_to_sid( &sid, *puid);
                                        unix_ug.uid = *puid;
                                        owner_type = UID_ACE;
+                                       sys_acl_free_qualifier((void *)puid,tagtype);
                                        break;
                                }
                        case SMB_ACL_GROUP_OBJ:
@@ -1460,6 +1488,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_
                                        gid_to_sid( &sid, *pgid);
                                        unix_ug.gid = *pgid;
                                        owner_type = GID_ACE;
+                                       sys_acl_free_qualifier((void *)pgid,tagtype);
                                        break;
                                }
                        case SMB_ACL_MASK:
@@ -1486,31 +1515,29 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_
                ZERO_STRUCTP(ace);
                ace->type = tagtype;
                ace->perms = convert_permset_to_mode_t(permset);
-               ace->attr = ace->perms ? ALLOW_ACE : DENY_ACE;
-               ace->sid = sid;
+               ace->attr = ALLOW_ACE;
+               ace->trustee = sid;
                ace->unix_ug = unix_ug;
                ace->owner_type = owner_type;
 
-               /*
-                * Remember the user/group/other ACE entries.
-                */
-
-               if (tagtype == SMB_ACL_USER_OBJ)
-                       owner_ace = ace;
-               else if (tagtype == SMB_ACL_GROUP_OBJ)
-                       group_ace = ace;
-               else if (tagtype == SMB_ACL_OTHER)
-                       other_ace = ace;
-
                DLIST_ADD(list_head, ace);
        }
 
+       /*
+        * This next call will ensure we have at least a user/group/world set.
+        */
+
+       if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
+               goto fail;
+
+       arrange_posix_perms(fsp->fsp_name,&list_head );
+
        /*
         * Now go through the list, masking the permissions with the
         * acl_mask. Ensure all DENY Entries are at the start of the list.
         */
 
-       DEBUG(10,("canonicalize_acl: ace entries before arrange :\n"));
+       DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
 
        for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
                next_ace = ace->next;
@@ -1528,21 +1555,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_
                }
        }
 
-       arrange_posix_perms(fsp->fsp_name,&list_head );
-
-       if( DEBUGLVL( 10 ) ) {
-               char *acl_text = sys_acl_to_text( posix_acl, NULL);
-
-               dbgtext("canonicalize_acl: processed acl %s\n", acl_text == NULL ? "NULL" : acl_text );
-               if (acl_text)
-                       sys_acl_free_text(acl_text);
-
-               dbgtext("canonicalize_acl: ace entries after arrange :\n");
-
-               for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
-                       print_canon_ace(ace, ace_count);
-               }
-       }
+       print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
 
        return list_head;
 
@@ -1640,7 +1653,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
 
                if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
                        DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
-                               p_ace->perms, i, strerror(errno) ));
+                               (unsigned int)p_ace->perms, i, strerror(errno) ));
                        goto done;
                }
 
@@ -1703,15 +1716,27 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
         */
 
        if(default_ace || fsp->is_directory || fsp->fd == -1) {
-               if (sys_acl_set_file(dos_to_unix(fsp->fsp_name,False), the_acl_type, the_acl) == -1) {
-                       DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
+               if (sys_acl_set_file(fsp->fsp_name, the_acl_type, the_acl) == -1) {
+                       /*
+                        * Some systems allow all the above calls and only fail with no ACL support
+                        * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+                        */
+                       if (errno == ENOSYS)
+                               *pacl_set_support = False;
+                       DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
                                        the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
                                        fsp->fsp_name, strerror(errno) ));
                        goto done;
                }
        } else {
                if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
-                       DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
+                       /*
+                        * Some systems allow all the above calls and only fail with no ACL support
+                        * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+                        */
+                       if (errno == ENOSYS)
+                               *pacl_set_support = False;
+                       DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
                                        fsp->fsp_name, strerror(errno) ));
                        goto done;
                }
@@ -1735,11 +1760,14 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
 
 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
 {
+       int snum = SNUM(fsp->conn);
        size_t ace_count = count_canon_ace_list(file_ace_list);
        canon_ace *ace_p;
        canon_ace *owner_ace = NULL;
        canon_ace *group_ace = NULL;
        canon_ace *other_ace = NULL;
+       mode_t and_bits;
+       mode_t or_bits;
 
        if (ace_count != 3) {
                DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
@@ -1774,8 +1802,23 @@ posix perms.\n", fsp->fsp_name ));
 
        /* The owner must have at least read access. */
 
-       if (*posix_perms == (mode_t)0)
-               *posix_perms = S_IRUSR;
+       *posix_perms |= S_IRUSR;
+       if (fsp->is_directory)
+               *posix_perms |= (S_IWUSR|S_IXUSR);
+
+       /* If requested apply the masks. */
+
+       /* Get the initial bits to apply. */
+
+       if (fsp->is_directory) {
+               and_bits = lp_dir_security_mask(snum);
+               or_bits = lp_force_dir_security_mode(snum);
+       } else {
+               and_bits = lp_security_mask(snum);
+               or_bits = lp_force_security_mode(snum);
+       }
+
+       *posix_perms = (((*posix_perms) & and_bits)|or_bits);
 
        DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
                (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
@@ -1784,6 +1827,16 @@ posix perms.\n", fsp->fsp_name ));
        return True;
 }
 
+static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+       if (a1->type == a2->type)
+               return 0;
+
+       if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+               return -1;
+       return 1;
+}
+
 /****************************************************************************
  Reply to query a security descriptor from an fsp. If it succeeds it allocates
  the space for the return elements and returns the size needed to return the
@@ -1821,14 +1874,14 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
                 * Get the ACL from the path.
                 */
 
-               posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
+               posix_acl = sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
 
                /*
                 * If it's a directory get the default POSIX ACL.
                 */
 
                if(fsp->is_directory)
-                       dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
+                       dir_acl = sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
 
        } else {
 
@@ -1853,7 +1906,7 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
        create_file_sids(&sbuf, &owner_sid, &group_sid);
 
        /* Create the canon_ace lists. */
-       file_ace = canonicalise_acl( fsp, posix_acl, &sbuf,  &owner_sid, &group_sid, False);
+       file_ace = canonicalise_acl( fsp, posix_acl, &sbuf,  &owner_sid, &group_sid);
        num_acls = count_canon_ace_list(file_ace);
 
        /* We must have *some* ACLS. */
@@ -1864,7 +1917,12 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
        }
 
        if (fsp->is_directory) { 
-               dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid, True);
+               /*
+                * If we have to fake a default ACL then this is the mode to use.
+                */
+               sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
+
+               dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
                num_dir_acls = count_canon_ace_list(dir_ace);
        }
 
@@ -1889,16 +1947,23 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
 
                for (i = 0; i < num_acls; i++, ace = ace->next) {
                        SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
-                       init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
+                       init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
                }
 
                ace = dir_ace;
 
                for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
                        SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
-                       init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 
+                       init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 
                                        SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
                }
+
+               /*
+                * Sort to force deny entries to the front.
+                */
+
+               if (num_acls + num_dir_acls)
+                       qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
        }
 
        if (num_acls) {
@@ -1923,8 +1988,7 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
                sys_acl_free_acl(dir_acl);
        free_canon_ace_list(file_ace);
        free_canon_ace_list(dir_ace);
-       if (nt_ace_list)
-               free(nt_ace_list);
+       SAFE_FREE(nt_ace_list);
 
        return sd_size;
 }
@@ -1946,6 +2010,9 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
        canon_ace *file_ace_list = NULL;
        canon_ace *dir_ace_list = NULL;
        BOOL acl_perms = False;
+       mode_t orig_mode = (mode_t)0;
+       uid_t orig_uid;
+       gid_t orig_gid;
 
        DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
 
@@ -1961,6 +2028,11 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                        return False;
        }
 
+       /* Save the original elements we check against. */
+       orig_mode = sbuf.st_mode;
+       orig_uid = sbuf.st_uid;
+       orig_gid = sbuf.st_gid;
+
        /*
         * Unpack the user/group/world id's.
         */
@@ -1972,7 +2044,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
         * Do we need to chown ?
         */
 
-       if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
+       if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) {
 
                DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
                                fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
@@ -2004,6 +2076,11 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                        if(ret != 0)
                                return False;
                }
+
+               /* Save the original elements we check against. */
+               orig_mode = sbuf.st_mode;
+               orig_uid = sbuf.st_uid;
+               orig_gid = sbuf.st_gid;
        }
 
        create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
@@ -2011,6 +2088,11 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
        acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
                                                                        &file_ace_list, &dir_ace_list, security_info_sent, psd);
 
+       if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
+               /* W2K traverse DACL set - ignore. */
+               return True;
+    }
+
        if (!acl_perms) {
                DEBUG(3,("set_nt_acl: cannot set permissions\n"));
                free_canon_ace_list(file_ace_list);
@@ -2042,12 +2124,25 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                        }
                }
 
-               if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
-                       if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
-                               DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
-                               free_canon_ace_list(file_ace_list);
-                               free_canon_ace_list(dir_ace_list); 
-                               return False;
+               if (acl_perms && acl_set_support && fsp->is_directory) {
+                       if (dir_ace_list) {
+                               if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
+                                       DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+                                       free_canon_ace_list(file_ace_list);
+                                       free_canon_ace_list(dir_ace_list); 
+                                       return False;
+                               }
+                       } else {
+
+                               /*
+                                * No default ACL - delete one if it exists.
+                                */
+
+                               if (sys_acl_delete_def_file(fsp->fsp_name) == -1) {
+                                       DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
+                                       free_canon_ace_list(file_ace_list);
+                                       return False;
+                               }
                        }
                }
 
@@ -2066,12 +2161,12 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                return False;
                        }
 
-                       if (sbuf.st_mode != posix_perms) {
+                       if (orig_mode != posix_perms) {
 
                                DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
                                        fsp->fsp_name, (unsigned int)posix_perms ));
 
-                               if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), posix_perms) == -1) {
+                               if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
                                        DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
                                                        fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
                                        free_canon_ace_list(file_ace_list);
@@ -2141,11 +2236,11 @@ static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
        }
 
        /*
-        * If this is a simple 3 element ACL then it's a standard
+        * If this is a simple 3 element ACL or no elements then it's a standard
         * UNIX permission set. Just use chmod...       
         */
 
-       if (num_entries == 3)
+       if ((num_entries == 3) || (num_entries == 0))
                return -1;
 
        return 0;
@@ -2157,7 +2252,7 @@ static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
  Note that name is in UNIX character set.
 ****************************************************************************/
 
-int chmod_acl(char *name, mode_t mode)
+int chmod_acl(const char *name, mode_t mode)
 {
        SMB_ACL_T posix_acl = NULL;
        int ret = -1;
@@ -2199,3 +2294,16 @@ int fchmod_acl(int fd, mode_t mode)
        sys_acl_free_acl(posix_acl);
        return ret;
 }
+
+BOOL directory_has_default_acl(const char *fname)
+{
+        SMB_ACL_T dir_acl = sys_acl_get_file( fname, SMB_ACL_TYPE_DEFAULT);
+        BOOL has_acl = False;
+        SMB_ACL_ENTRY_T entry;
+
+        if (dir_acl != NULL && (sys_acl_get_entry(dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
+                has_acl = True;
+
+        sys_acl_free_acl(dir_acl);
+        return has_acl;
+}