r23779: Change from v2 or later to v3 or later.
[nivanova/samba-autobuild/.git] / source3 / smbd / posix_acls.c
index a1903ef4bbfcf326c426384bf25e6add6b19f9dc..49ca5b86b74b4ee92913be4242a0655242491af0 100644 (file)
@@ -6,7 +6,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -47,7 +47,7 @@ typedef struct canon_ace {
        DOM_SID trustee;
        enum ace_owner owner_type;
        enum ace_attribute attr;
-       posix_id unix_ug; 
+       posix_id unix_ug;
        BOOL inherited;
 } canon_ace;
 
@@ -658,6 +658,7 @@ static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_AC
        }
        return 0;
 }
+
 /****************************************************************************
  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
 ****************************************************************************/
@@ -668,6 +669,27 @@ static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SI
        gid_to_sid( pgroup_sid, psbuf->st_gid );
 }
 
+/****************************************************************************
+ Is the identity in two ACEs equal ? Check both SID and uid/gid.
+****************************************************************************/
+
+static BOOL identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
+{
+       if (sid_equal(&ace1->trustee, &ace2->trustee)) {
+               return True;
+       }
+       if (ace1->owner_type == ace2->owner_type) {
+               if (ace1->owner_type == UID_ACE &&
+                               ace1->unix_ug.uid == ace2->unix_ug.uid) {
+                       return True;
+               } else if (ace1->owner_type == GID_ACE &&
+                               ace1->unix_ug.gid == ace2->unix_ug.gid) {
+                       return True;
+               }
+       }
+       return False;
+}
+
 /****************************************************************************
  Merge aces with a common sid - if both are allow or deny, OR the permissions together and
  delete the second one. If the first is deny, mask the permissions off and delete the allow
@@ -695,7 +717,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->trustee, &curr_ace_outer->trustee) &&
+                       if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
                                (curr_ace->attr == curr_ace_outer->attr)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -735,7 +757,7 @@ 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 (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                       if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
                                (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -806,20 +828,23 @@ static BOOL nt4_compatible_acls(void)
  not get. Deny entries are implicit on get with ace->perms = 0.
 ****************************************************************************/
 
-static SEC_ACCESS map_canon_ace_perms(int snum, int *pacl_type, DOM_SID *powner_sid, canon_ace *ace, BOOL directory_ace)
+static SEC_ACCESS map_canon_ace_perms(int snum,
+                               int *pacl_type,
+                               mode_t perms,
+                               BOOL directory_ace)
 {
        SEC_ACCESS sa;
        uint32 nt_mask = 0;
 
        *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
 
-       if (lp_acl_map_full_control(snum) && ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
+       if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
                if (directory_ace) {
                        nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
                } else {
                        nt_mask = UNIX_ACCESS_RWX;
                }
-       } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+       } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
                /*
                 * Windows NT refuses to display ACEs with no permissions in them (but
                 * they are perfectly legal with Windows 2000). If the ACE has empty
@@ -835,18 +860,18 @@ static SEC_ACCESS map_canon_ace_perms(int snum, int *pacl_type, DOM_SID *powner_
                        nt_mask = 0;
        } else {
                if (directory_ace) {
-                       nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
-                       nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
-                       nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
+                       nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
+                       nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
+                       nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
                } else {
-                       nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
-                       nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
-                       nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
+                       nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
+                       nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
+                       nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
                }
        }
 
        DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
-                       (unsigned int)ace->perms, (unsigned int)nt_mask ));
+                       (unsigned int)perms, (unsigned int)nt_mask ));
 
        init_sec_access(&sa,nt_mask);
        return sa;
@@ -860,36 +885,36 @@ static SEC_ACCESS map_canon_ace_perms(int snum, int *pacl_type, DOM_SID *powner_
 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
 
-static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
+static mode_t map_nt_perms( uint32 *mask, int type)
 {
        mode_t mode = 0;
 
        switch(type) {
        case S_IRUSR:
-               if(sec_access.mask & GENERIC_ALL_ACCESS)
+               if((*mask) & GENERIC_ALL_ACCESS)
                        mode = S_IRUSR|S_IWUSR|S_IXUSR;
                else {
-                       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
-                       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
-                       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+                       mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+                       mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+                       mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
                }
                break;
        case S_IRGRP:
-               if(sec_access.mask & GENERIC_ALL_ACCESS)
+               if((*mask) & GENERIC_ALL_ACCESS)
                        mode = S_IRGRP|S_IWGRP|S_IXGRP;
                else {
-                       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
-                       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
-                       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+                       mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+                       mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+                       mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
                }
                break;
        case S_IROTH:
-               if(sec_access.mask & GENERIC_ALL_ACCESS)
+               if((*mask) & GENERIC_ALL_ACCESS)
                        mode = S_IROTH|S_IWOTH|S_IXOTH;
                else {
-                       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
-                       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
-                       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+                       mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+                       mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+                       mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
                }
                break;
        }
@@ -901,7 +926,7 @@ static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
  Unpack a SEC_DESC into a UNIX owner and group.
 ****************************************************************************/
 
-BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
 {
        DOM_SID owner_sid;
        DOM_SID grp_sid;
@@ -911,7 +936,7 @@ BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_
 
        if(security_info_sent == 0) {
                DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
-               return True;
+               return NT_STATUS_OK;
        }
 
        /*
@@ -939,9 +964,11 @@ BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_
                                DEBUG(3,("unpack_nt_owners: unable to validate"
                                         " owner sid for %s\n",
                                         sid_string_static(&owner_sid)));
-                               return False;
+                               return NT_STATUS_INVALID_OWNER;
                        }
                }
+               DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n",
+                        (unsigned int)*puser ));
        }
 
        /*
@@ -950,7 +977,7 @@ BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_
         */
 
        if (security_info_sent & GROUP_SECURITY_INFORMATION) {
-               sid_copy(&grp_sid, psd->grp_sid);
+               sid_copy(&grp_sid, psd->group_sid);
                if (!sid_to_gid( &grp_sid, pgrp)) {
                        if (lp_force_unknown_acl_user(snum)) {
                                /* this allows take group ownership to work
@@ -959,14 +986,16 @@ BOOL unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_
                        } else {
                                DEBUG(3,("unpack_nt_owners: unable to validate"
                                         " group sid.\n"));
-                               return False;
+                               return NT_STATUS_INVALID_OWNER;
                        }
                }
-       }
+               DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n",
+                        (unsigned int)*pgrp));
+       }
 
        DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -1241,7 +1270,6 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
        BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
-       canon_ace *tmp_ace = NULL;
        canon_ace *current_ace = NULL;
        BOOL got_dir_allow = False;
        BOOL got_file_allow = False;
@@ -1255,7 +1283,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
         */
 
        for(i = 0; i < dacl->num_aces; i++) {
-               SEC_ACE *psa = &dacl->ace[i];
+               SEC_ACE *psa = &dacl->aces[i];
 
                if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
                        DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
@@ -1274,12 +1302,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                         * Convert GENERIC bits to specific bits.
                         */
  
-                       se_map_generic(&psa->info.mask, &file_generic_mapping);
+                       se_map_generic(&psa->access_mask, &file_generic_mapping);
 
-                       psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
+                       psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
 
-                       if(psa->info.mask != UNIX_ACCESS_NONE)
-                               psa->info.mask &= ~UNIX_ACCESS_NONE;
+                       if(psa->access_mask != UNIX_ACCESS_NONE)
+                               psa->access_mask &= ~UNIX_ACCESS_NONE;
                }
        }
 
@@ -1292,12 +1320,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
         */
 
        for(i = 0; i < dacl->num_aces; i++) {
-               SEC_ACE *psa1 = &dacl->ace[i];
+               SEC_ACE *psa1 = &dacl->aces[i];
 
                for (j = i + 1; j < dacl->num_aces; j++) {
-                       SEC_ACE *psa2 = &dacl->ace[j];
+                       SEC_ACE *psa2 = &dacl->aces[j];
 
-                       if (psa1->info.mask != psa2->info.mask)
+                       if (psa1->access_mask != psa2->access_mask)
                                continue;
 
                        if (!sid_equal(&psa1->trustee, &psa2->trustee))
@@ -1323,18 +1351,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
        }
 
        for(i = 0; i < dacl->num_aces; i++) {
-               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;
-               }
+               SEC_ACE *psa = &dacl->aces[i];
 
                /*
                 * Create a cannon_ace entry representing this NT DACL ACE.
@@ -1396,6 +1413,17 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                } else {
                        fstring str;
 
+                       /*
+                        * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
+                        */
+
+                       if (non_mappable_sid(&psa->trustee)) {
+                               DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
+                                       sid_to_string(str, &psa->trustee) ));
+                               SAFE_FREE(current_ace);
+                               continue;
+                       }
+
                        free_canon_ace_list(file_ace);
                        free_canon_ace_list(dir_ace);
                        DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
@@ -1409,7 +1437,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                 * S_I(R|W|X)USR bits.
                 */
 
-               current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
+               current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
                current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
                current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
 
@@ -1429,7 +1457,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
                        if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
                                (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
 
-                               DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
+                               DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
 
                                /*
                                 * Note if this was an allow ace. We can't process
@@ -1487,7 +1515,7 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
                 */
 
                if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
-                       DLIST_ADD_END(file_ace, current_ace, tmp_ace);
+                       DLIST_ADD_END(file_ace, current_ace, canon_ace *);
 
                        /*
                         * Note if this was an allow ace. We can't process
@@ -1734,7 +1762,6 @@ static void process_deny_list( canon_ace **pp_ace_list )
        for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
                mode_t new_perms = (mode_t)0;
                canon_ace *allow_ace_p;
-               canon_ace *tmp_ace;
 
                curr_ace_next = curr_ace->next; /* So we can't lose the link. */
 
@@ -1753,7 +1780,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
 
                        curr_ace->attr = ALLOW_ACE;
                        curr_ace->perms = (mode_t)0;
-                       DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+                       DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
                        continue;
                }
 
@@ -1778,13 +1805,12 @@ static void process_deny_list( canon_ace **pp_ace_list )
 
                curr_ace->attr = ALLOW_ACE;
                curr_ace->perms = (new_perms & ~curr_ace->perms);
-               DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+               DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
        }
 
        /* Pass 3 above - deal with deny group entries. */
 
        for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
-               canon_ace *tmp_ace;
                canon_ace *allow_ace_p;
                canon_ace *allow_everyone_p = NULL;
 
@@ -1826,8 +1852,7 @@ static void process_deny_list( canon_ace **pp_ace_list )
                        curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
                else
                        curr_ace->perms = (mode_t)0;
-               DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
-
+               DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
        }
 
        /* Doing this fourth pass allows Windows semantics to be layered
@@ -1882,7 +1907,10 @@ 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, False) : S_IRUSR;
+       mode_t mode = interitable_mode
+               ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
+                            NULL )
+               : S_IRUSR;
 
        if (fsp->is_directory)
                mode |= (S_IWUSR|S_IXUSR);
@@ -2067,7 +2095,7 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
        }
 
        if (other_ace) {
-               DLIST_DEMOTE(list_head, other_ace, ace);
+               DLIST_DEMOTE(list_head, other_ace, canon_ace *);
        }
 
        /* We have probably changed the head of the list. */
@@ -2260,8 +2288,8 @@ static BOOL current_user_in_group(gid_t gid)
 static BOOL acl_group_override(connection_struct *conn, gid_t prim_gid)
 {
        if ( (errno == EACCES || errno == EPERM) 
-               && (lp_acl_group_control(SNUM(conn) || lp_dos_filemode(SNUM(conn)))) 
-               && current_user_in_group(prim_gid) 
+               && (lp_acl_group_control(SNUM(conn)) || lp_dos_filemode(SNUM(conn)))
+               && current_user_in_group(prim_gid)) 
        {
                return True;
        } 
@@ -2645,7 +2673,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                        /* We know the lower number ACE's are file entries. */
                        if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
                                (nt_ace_list[i].size == nt_ace_list[j].size) &&
-                               (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
+                               (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
                                sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
                                (i_inh == j_inh) &&
                                (i_flags_ni == 0) &&
@@ -2658,7 +2686,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
                                 * the non-inherited ACE onto the inherited ACE.
                                 */
 
-                               if (nt_ace_list[i].info.mask == 0) {
+                               if (nt_ace_list[i].access_mask == 0) {
                                        nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
                                                                (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
                                        if (num_aces - i - 1 > 0)
@@ -2868,26 +2896,37 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
                        }
 
                        memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
-                                                                                                       
+
                        /*
                         * Create the NT ACE list from the canonical ace lists.
                         */
-       
+
                        ace = file_ace;
 
                        for (i = 0; i < num_acls; i++, ace = ace->next) {
                                SEC_ACCESS acc;
 
-                               acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
-                               init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+                               acc = map_canon_ace_perms(SNUM(conn),
+                                               &nt_acl_type,
+                                               ace->perms,
+                                               fsp->is_directory);
+                               init_sec_ace(&nt_ace_list[num_aces++],
+                                       &ace->trustee,
+                                       nt_acl_type,
+                                       acc,
+                                       ace->inherited ?
+                                               SEC_ACE_FLAG_INHERITED_ACE : 0);
                        }
 
-                       /* The User must have access to a profile share - even if we can't map the SID. */
+                       /* The User must have access to a profile share - even
+                        * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
                                SEC_ACCESS acc;
 
                                init_sec_access(&acc,FILE_GENERIC_ALL);
-                               init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               init_sec_ace(&nt_ace_list[num_aces++],
+                                               &global_sid_Builtin_Users,
+                                               SEC_ACE_TYPE_ACCESS_ALLOWED,
                                                acc, 0);
                        }
 
@@ -2895,18 +2934,27 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
 
                        for (i = 0; i < num_def_acls; i++, ace = ace->next) {
                                SEC_ACCESS acc;
-       
-                               acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
-                               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|
-                                               (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
+
+                               acc = map_canon_ace_perms(SNUM(conn),
+                                               &nt_acl_type,
+                                               ace->perms,
+                                               fsp->is_directory);
+                               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|
+                                       (ace->inherited ?
+                                          SEC_ACE_FLAG_INHERITED_ACE : 0));
                        }
 
-                       /* The User must have access to a profile share - even if we can't map the SID. */
+                       /* The User must have access to a profile share - even
+                        * if we can't map the SID. */
                        if (lp_profile_acls(SNUM(conn))) {
                                SEC_ACCESS acc;
-                       
+
                                init_sec_access(&acc,FILE_GENERIC_ALL);
                                init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
                                                SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
@@ -2958,7 +3006,7 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
        }
 
        if (psd->dacl) {
-               dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces);
+               dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
        }
 
        *ppdesc = psd;
@@ -3028,6 +3076,7 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
 
        /* Case (4). */
        if (!lp_dos_filemode(SNUM(conn))) {
+               errno = EPERM;
                return -1;
        }
 
@@ -3055,33 +3104,224 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
        return ret;
 }
 
+static NTSTATUS append_ugw_ace(files_struct *fsp,
+                       SMB_STRUCT_STAT *psbuf,
+                       mode_t unx_mode,
+                       int ugw,
+                       SEC_ACE *se)
+{
+       mode_t perms;
+       SEC_ACCESS acc;
+       int acl_type;
+       DOM_SID trustee;
+
+       switch (ugw) {
+               case S_IRUSR:
+                       perms = unix_perms_to_acl_perms(unx_mode,
+                                                       S_IRUSR,
+                                                       S_IWUSR,
+                                                       S_IXUSR);
+                       uid_to_sid(&trustee, psbuf->st_uid );
+                       break;
+               case S_IRGRP:
+                       perms = unix_perms_to_acl_perms(unx_mode,
+                                                       S_IRGRP,
+                                                       S_IWGRP,
+                                                       S_IXGRP);
+                       gid_to_sid(&trustee, psbuf->st_gid );
+                       break;
+               case S_IROTH:
+                       perms = unix_perms_to_acl_perms(unx_mode,
+                                                       S_IROTH,
+                                                       S_IWOTH,
+                                                       S_IXOTH);
+                       sid_copy(&trustee, &global_sid_World);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_PARAMETER;
+       }
+       acc = map_canon_ace_perms(SNUM(fsp->conn),
+                               &acl_type,
+                               perms,
+                               fsp->is_directory);
+
+       init_sec_ace(se,
+               &trustee,
+               acl_type,
+               acc,
+               0);
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ If this is an
+****************************************************************************/
+
+static NTSTATUS append_parent_acl(files_struct *fsp,
+                               SMB_STRUCT_STAT *psbuf,
+                               SEC_DESC *psd,
+                               SEC_DESC **pp_new_sd)
+{
+       SEC_DESC *parent_sd = NULL;
+       files_struct *parent_fsp = NULL;
+       TALLOC_CTX *mem_ctx = talloc_parent(psd);
+       char *parent_name = NULL;
+       SEC_ACE *new_ace = NULL;
+       unsigned int num_aces = psd->dacl->num_aces;
+       SMB_STRUCT_STAT sbuf;
+       NTSTATUS status;
+       int info;
+       size_t sd_size;
+       unsigned int i, j;
+       mode_t unx_mode;
+
+       ZERO_STRUCT(sbuf);
+
+       if (mem_ctx == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!parent_dirname_talloc(mem_ctx,
+                               fsp->fsp_name,
+                               &parent_name,
+                               NULL)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Create a default mode for u/g/w. */
+       unx_mode = unix_mode(fsp->conn,
+                       aARCH | (fsp->is_directory ? aDIR : 0),
+                       fsp->fsp_name,
+                       parent_name);
+
+       status = open_directory(fsp->conn,
+                               NULL,
+                               parent_name,
+                               &sbuf,
+                               FILE_READ_ATTRIBUTES, /* Just a stat open */
+                               FILE_SHARE_NONE, /* Ignored for stat opens */
+                               FILE_OPEN,
+                               0,
+                               INTERNAL_OPEN_ONLY,
+                               &info,
+                               &parent_fsp);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       sd_size = SMB_VFS_GET_NT_ACL(parent_fsp, parent_fsp->fsp_name,
+                       DACL_SECURITY_INFORMATION, &parent_sd );
+
+       close_file(parent_fsp, NORMAL_CLOSE);
+
+       if (!sd_size) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /*
+        * Make room for potentially all the ACLs from
+        * the parent, plus the user/group/other triple.
+        */
+
+       num_aces += parent_sd->dacl->num_aces + 3;
+
+       if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
+                                       num_aces)) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       DEBUG(10,("append_parent_acl: parent ACL has %u entries. New "
+               "ACL has %u entries\n",
+               parent_sd->dacl->num_aces, num_aces ));
+
+       /* Start by copying in all the given ACE entries. */
+       for (i = 0; i < psd->dacl->num_aces; i++) {
+               sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
+       }
+
+       /*
+        * Note that we're ignoring "inherit permissions" here
+        * as that really only applies to newly created files. JRA.
+        */
+
+        /*
+         * Append u/g/w.
+         */
+
+       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRUSR, &new_ace[i++]);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRGRP, &new_ace[i++]);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       status = append_ugw_ace(fsp, psbuf, unx_mode, S_IROTH, &new_ace[i++]);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* Finally append any inherited ACEs. */
+       for (j = 0; j < parent_sd->dacl->num_aces; j++) {
+               SEC_ACE *se = &parent_sd->dacl->aces[j];
+               uint32 i_flags = se->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+                                       SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                       SEC_ACE_FLAG_INHERIT_ONLY);
+
+               if (fsp->is_directory) {
+                       if (i_flags == SEC_ACE_FLAG_OBJECT_INHERIT) {
+                               /* Should only apply to a file - ignore. */
+                               continue;
+                       }
+               } else {
+                       if ((i_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
+                                       SEC_ACE_FLAG_INHERIT_ONLY)) !=
+                                       SEC_ACE_FLAG_OBJECT_INHERIT) {
+                               /* Should not apply to a file - ignore. */
+                               continue;
+                       }
+               }
+               sec_ace_copy(&new_ace[i], se);
+               if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+                       new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
+               }
+               new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
+               i++;
+       }
+
+       parent_sd->dacl->aces = new_ace;
+       parent_sd->dacl->num_aces = i;
+
+       *pp_new_sd = parent_sd;
+       return status;
+}
+
 /****************************************************************************
  Reply to set a security descriptor on an fsp. security_info_sent is the
  description of the following NT ACL.
  This should be the only external function needed for the UNIX style set ACL.
 ****************************************************************************/
 
-BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
 {
        connection_struct *conn = fsp->conn;
        uid_t user = (uid_t)-1;
        gid_t grp = (gid_t)-1;
-       SMB_STRUCT_STAT sbuf;  
+       SMB_STRUCT_STAT sbuf;
        DOM_SID file_owner_sid;
        DOM_SID file_grp_sid;
        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;
-       BOOL need_chown = False;
+       NTSTATUS status;
 
        DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
 
        if (!CAN_WRITE(conn)) {
                DEBUG(10,("set acl rejected on read-only share\n"));
-               return False;
+               return NT_STATUS_MEDIA_WRITE_PROTECTED;
        }
 
        /*
@@ -3090,40 +3330,29 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
 
        if(fsp->is_directory || fsp->fh->fd == -1) {
                if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
-                       return False;
+                       return map_nt_error_from_unix(errno);
        } else {
                if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0)
-                       return False;
+                       return map_nt_error_from_unix(errno);
        }
 
        /* 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.
         */
 
-       if (!unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd)) {
-               return False;
+       status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        /*
         * Do we need to chown ?
         */
 
-       if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
-               need_chown = True;
-       }
-
-       /*
-        * Chown before setting ACL only if we don't change the user, or
-        * if we change to the current user, but not if we want to give away
-        * the file.
-        */
-
-       if (need_chown && (user == (uid_t)-1 || user == current_user.ut.uid)) {
+       if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) {
 
                DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
                                fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
@@ -3131,7 +3360,10 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
                        DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
                                fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
-                       return False;
+                       if (errno == EPERM) {
+                               return NT_STATUS_INVALID_OWNER;
+                       }
+                       return map_nt_error_from_unix(errno);
                }
 
                /*
@@ -3141,32 +3373,39 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
 
                if(fsp->is_directory) {
                        if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
-                               return False;
+                               return map_nt_error_from_unix(errno);
                        }
                } else {
 
                        int ret;
-    
+
                        if(fsp->fh->fd == -1)
                                ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
                        else
                                ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf);
-  
+
                        if(ret != 0)
-                               return False;
+                               return map_nt_error_from_unix(errno);
                }
 
                /* Save the original elements we check against. */
                orig_mode = sbuf.st_mode;
-               orig_uid = sbuf.st_uid;
-               orig_gid = sbuf.st_gid;
-
-               /* We did it, don't try again */
-               need_chown = False;
        }
 
        create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
 
+       if ((security_info_sent & DACL_SECURITY_INFORMATION) &&
+               psd->dacl != NULL &&
+               (psd->type & (SE_DESC_DACL_AUTO_INHERITED|
+                             SE_DESC_DACL_AUTO_INHERIT_REQ))==
+                       (SE_DESC_DACL_AUTO_INHERITED|
+                        SE_DESC_DACL_AUTO_INHERIT_REQ) ) {
+               status = append_parent_acl(fsp, &sbuf, psd, &psd);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
        acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
                                        &file_ace_list, &dir_ace_list, security_info_sent, psd);
 
@@ -3177,7 +3416,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                        DEBUG(3,("set_nt_acl: cannot set permissions\n"));
                        free_canon_ace_list(file_ace_list);
                        free_canon_ace_list(dir_ace_list); 
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
 
                /*
@@ -3200,7 +3439,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                        DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
                                        free_canon_ace_list(file_ace_list);
                                        free_canon_ace_list(dir_ace_list); 
-                                       return False;
+                                       return map_nt_error_from_unix(errno);
                                }
                        }
 
@@ -3210,7 +3449,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                                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;
+                                               return map_nt_error_from_unix(errno);
                                        }
                                } else {
 
@@ -3235,7 +3474,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                                        DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
                                                        free_canon_ace_list(file_ace_list);
                                                        free_canon_ace_list(dir_ace_list);
-                                                       return False;
+                                                       return map_nt_error_from_unix(errno);
                                                }
                                        }
                                }
@@ -3258,7 +3497,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                        free_canon_ace_list(dir_ace_list);
                                        DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
                                                fsp->fsp_name ));
-                                       return False;
+                                       return NT_STATUS_ACCESS_DENIED;
                                }
 
                                if (orig_mode != posix_perms) {
@@ -3283,7 +3522,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                                                                fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
                                                        free_canon_ace_list(file_ace_list);
                                                        free_canon_ace_list(dir_ace_list);
-                                                       return False;
+                                                       return map_nt_error_from_unix(errno);
                                                }
                                        }
                                }
@@ -3294,20 +3533,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
                free_canon_ace_list(dir_ace_list); 
        }
 
-       /* Any chown pending? */
-       if (need_chown) {
-
-               DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
-                       fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
-
-               if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
-                       DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
-                               fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
-                       return False;
-               }
-       }
-
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -3461,19 +3687,17 @@ int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
 }
 
 /****************************************************************************
- If "inherit permissions" is set and the parent directory has no default
ACL but it does have an Access ACL, inherit this Access ACL to file name.
+ If the parent directory has no default ACL but it does have an Access ACL,
+ inherit this Access ACL to file name.
 ****************************************************************************/
 
-int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
+int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir,
+                      const char *name, mode_t mode)
 {
-       pstring dirname;
-       pstrcpy(dirname, parent_dirname(name));
-
-       if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
+       if (directory_has_default_acl(conn, inherit_from_dir))
                return 0;
 
-       return copy_access_acl(conn, dirname, name, mode);
+       return copy_access_acl(conn, inherit_from_dir, name, mode);
 }
 
 /****************************************************************************
@@ -4224,6 +4448,11 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
        if (sbuf.st_mode & S_ISVTX) {
                SMB_STRUCT_STAT sbuf_file;  
                if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
+                       if (errno == ENOENT) {
+                               /* If the file doesn't already exist then
+                                * yes we'll be able to delete it. */
+                               return True;
+                       }
                        return False;
                }
                /*