Only apply masks on non-default ACL entries when setting the ACL.
[samba.git] / source3 / smbd / posix_acls.c
index 15ea4d5fb2a92fad08163d2279fb0ee07faab938..f7258bd66d779938101183bd92ead522d2b05f0a 100644 (file)
@@ -1068,7 +1068,7 @@ bool nt4_compatible_acls(void)
  not get. Deny entries are implicit on get with ace->perms = 0.
 ****************************************************************************/
 
-static uint32_t map_canon_ace_perms(int snum,
+uint32_t map_canon_ace_perms(int snum,
                                enum security_ace_type *pacl_type,
                                mode_t perms,
                                bool directory_ace)
@@ -1109,6 +1109,10 @@ static uint32_t map_canon_ace_perms(int snum,
                }
        }
 
+       if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
+               nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
+       }
+
        DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
                        (unsigned int)perms, (unsigned int)nt_mask ));
 
@@ -1119,8 +1123,8 @@ static uint32_t map_canon_ace_perms(int snum,
  Map NT perms to a UNIX mode_t.
 ****************************************************************************/
 
-#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
-#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA)
 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
 
 static mode_t map_nt_perms( uint32 *mask, int type)
@@ -1338,12 +1342,13 @@ static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
 ****************************************************************************/
 
 static bool ensure_canon_entry_valid(canon_ace **pp_ace,
-                                    const struct share_params *params,
-                                    const bool is_directory,
-                                                       const DOM_SID *pfile_owner_sid,
-                                                       const DOM_SID *pfile_grp_sid,
-                                                       const SMB_STRUCT_STAT *pst,
-                                                       bool setting_acl)
+                               bool is_default_acl,
+                               const struct share_params *params,
+                               const bool is_directory,
+                               const DOM_SID *pfile_owner_sid,
+                               const DOM_SID *pfile_grp_sid,
+                               const SMB_STRUCT_STAT *pst,
+                               bool setting_acl)
 {
        canon_ace *pace;
        bool got_user = False;
@@ -1354,8 +1359,9 @@ 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) {
 
-                       if (setting_acl)
+                       if (setting_acl && !is_default_acl) {
                                apply_default_perms(params, is_directory, pace, S_IRUSR);
+                       }
                        got_user = True;
 
                } else if (pace->type == SMB_ACL_GROUP_OBJ) {
@@ -1364,8 +1370,9 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                         * Ensure create mask/force create mode is respected on set.
                         */
 
-                       if (setting_acl)
+                       if (setting_acl && !is_default_acl) {
                                apply_default_perms(params, is_directory, pace, S_IRGRP);
+                       }
                        got_grp = True;
 
                } else if (pace->type == SMB_ACL_OTHER) {
@@ -1374,8 +1381,9 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                         * Ensure create mask/force create mode is respected on set.
                         */
 
-                       if (setting_acl)
+                       if (setting_acl && !is_default_acl) {
                                apply_default_perms(params, is_directory, pace, S_IROTH);
+                       }
                        got_other = True;
                        pace_other = pace;
                }
@@ -1393,32 +1401,37 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->unix_ug.uid = pst->st_ex_uid;
                pace->trustee = *pfile_owner_sid;
                pace->attr = ALLOW_ACE;
+               /* Start with existing permissions, principle of least
+                  surprises for the user. */
+               pace->perms = pst->st_ex_mode;
 
                if (setting_acl) {
                        /* See if the owning user is in any of the other groups in
-                          the ACE. If so, OR in the permissions from that group. */
+                          the ACE, or if there's a matching user entry.
+                          If so, OR in the permissions from that entry. */
 
-                       bool group_matched = False;
                        canon_ace *pace_iter;
 
                        for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
-                               if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
+                               if (pace_iter->type == SMB_ACL_USER &&
+                                               pace_iter->unix_ug.uid == pace->unix_ug.uid) {
+                                       pace->perms |= pace_iter->perms;
+                               } else if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
                                        if (uid_entry_in_group(pace, pace_iter)) {
                                                pace->perms |= pace_iter->perms;
-                                               group_matched = True;
                                        }
                                }
                        }
 
-                       /* If we only got an "everyone" perm, just use that. */
-                       if (!group_matched) {
+                       if (pace->perms == 0) {
+                               /* If we only got an "everyone" perm, just use that. */
                                if (got_other)
                                        pace->perms = pace_other->perms;
-                               else
-                                       pace->perms = 0;
                        }
 
-                       apply_default_perms(params, is_directory, pace, S_IRUSR);
+                       if (!is_default_acl) {
+                               apply_default_perms(params, is_directory, pace, S_IRUSR);
+                       }
                } else {
                        pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
                }
@@ -1444,7 +1457,9 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                                pace->perms = pace_other->perms;
                        else
                                pace->perms = 0;
-                       apply_default_perms(params, is_directory, pace, S_IRGRP);
+                       if (!is_default_acl) {
+                               apply_default_perms(params, is_directory, pace, S_IRGRP);
+                       }
                } else {
                        pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
                }
@@ -1466,7 +1481,9 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
                pace->attr = ALLOW_ACE;
                if (setting_acl) {
                        pace->perms = 0;
-                       apply_default_perms(params, is_directory, pace, S_IROTH);
+                       if (!is_default_acl) {
+                               apply_default_perms(params, is_directory, pace, S_IROTH);
+                       }
                } else
                        pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
 
@@ -1480,6 +1497,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace,
  Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
  If it does not have them, check if there are any entries where the trustee is the
  file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
+ Note we must not do this to default directory ACLs.
 ****************************************************************************/
 
 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
@@ -1521,56 +1539,12 @@ static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID
                DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
 }
 
-/****************************************************************************
- If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
- If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
-****************************************************************************/
-
-static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
-{
-       /* dir ace must be followings.
-          SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
-          SMB_ACL_USER     : not trustee    -> Posix ACL u:user:perm
-          SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
-          Posix ACL u:trustee:perm
-
-          SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
-          SMB_ACL_GROUP    : not trustee   -> Posix ACL g:group:perm
-          SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
-          Posix ACL g:trustee:perm
-       */
-
-       if (ace->type == SMB_ACL_USER_OBJ &&
-                       !(sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
-               canon_ace *dup_ace = dup_canon_ace(ace);
-
-               if (dup_ace == NULL) {
-                       return false;
-               }
-               dup_ace->type = SMB_ACL_USER;
-               DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
-       }
-
-       if (ace->type == SMB_ACL_GROUP_OBJ &&
-                       !(sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
-               canon_ace *dup_ace = dup_canon_ace(ace);
-
-               if (dup_ace == NULL) {
-                       return false;
-               }
-               dup_ace->type = SMB_ACL_GROUP;
-               DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
-       }
-
-       return true;
-}
-
 /****************************************************************************
  Unpack a SEC_DESC into two canonical ace lists.
 ****************************************************************************/
 
 static bool create_canon_ace_lists(files_struct *fsp,
-                                       SMB_STRUCT_STAT *pst,
+                                       const SMB_STRUCT_STAT *pst,
                                        DOM_SID *pfile_owner_sid,
                                        DOM_SID *pfile_grp_sid,
                                        canon_ace **ppfile_ace,
@@ -1744,6 +1718,14 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                continue;
                        }
 
+                       if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+                               DEBUG(10, ("create_canon_ace_lists: ignoring "
+                                       "unknown or foreign SID %s\n",
+                                       sid_string_dbg(&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 "
@@ -1780,6 +1762,7 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
                                (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
 
+                               canon_ace *current_dir_ace = current_ace;
                                DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
 
                                /*
@@ -1807,34 +1790,6 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                        print_canon_ace( current_ace, 0);
                                }
 
-                               /*
-                                * We have a lossy mapping: directory ACE entries
-                                * CREATOR_OWNER ------\
-                                *     (map to)         +---> SMB_ACL_USER_OBJ
-                                * owning sid    ------/
-                                *
-                                * CREATOR_GROUP ------\
-                                *     (map to)         +---> SMB_ACL_GROUP_OBJ
-                                * primary group sid --/
-                                *
-                                * on set. And on read of a directory ACL
-                                *
-                                * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
-                                * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
-                                *
-                                * Deal with this on set by duplicating
-                                * owning sid and primary group sid ACE
-                                * entries into the directory ACL.
-                                * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
-                                */
-
-                               if (!dup_owning_ace(dir_ace, current_ace)) {
-                                       DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
-                                       free_canon_ace_list(file_ace);
-                                       free_canon_ace_list(dir_ace);
-                                       return false;
-                               }
-
                                /*
                                 * If this is not an inherit only ACE we need to add a duplicate
                                 * to the file acl.
@@ -1869,6 +1824,43 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                         */
                                        current_ace = NULL;
                                }
+
+                               /*
+                                * current_ace is now either owned by file_ace
+                                * or is NULL. We can safely operate on current_dir_ace
+                                * to treat mapping for default acl entries differently
+                                * than access acl entries.
+                                */
+
+                               if (current_dir_ace->owner_type == UID_ACE) {
+                                       /*
+                                        * We already decided above this is a uid,
+                                        * for default acls ace's only CREATOR_OWNER
+                                        * maps to ACL_USER_OBJ. All other uid
+                                        * ace's are ACL_USER.
+                                        */
+                                       if (sid_equal(&current_dir_ace->trustee,
+                                                       &global_sid_Creator_Owner)) {
+                                               current_dir_ace->type = SMB_ACL_USER_OBJ;
+                                       } else {
+                                               current_dir_ace->type = SMB_ACL_USER;
+                                       }
+                               }
+
+                               if (current_dir_ace->owner_type == GID_ACE) {
+                                       /*
+                                        * We already decided above this is a gid,
+                                        * for default acls ace's only CREATOR_GROUP
+                                        * maps to ACL_GROUP_OBJ. All other uid
+                                        * ace's are ACL_GROUP.
+                                        */
+                                       if (sid_equal(&current_dir_ace->trustee,
+                                                       &global_sid_Creator_Group)) {
+                                               current_dir_ace->type = SMB_ACL_GROUP_OBJ;
+                                       } else {
+                                               current_dir_ace->type = SMB_ACL_GROUP;
+                                       }
+                               }
                        }
                }
 
@@ -1930,17 +1922,15 @@ static bool create_canon_ace_lists(files_struct *fsp,
                dir_ace = NULL;
        } else {
                /*
-                * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
-                * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
-                * entries can be converted to *_OBJ. Usually we will already have these
-                * entries in the Default ACL, and the Access ACL will not have them.
+                * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in
+                * the file ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+                * entries can be converted to *_OBJ. Don't do this for the default
+                * ACL, we will create them separately for this if needed inside
+                * ensure_canon_entry_valid().
                 */
                if (file_ace) {
                        check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
                }
-               if (dir_ace) {
-                       check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
-               }
        }
 
        *ppfile_ace = file_ace;
@@ -2261,51 +2251,13 @@ 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;
-
-       if (interitable_mode) {
-               mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
-                                fsp->fsp_name, NULL);
-       } else {
-               mode = 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.
 ****************************************************************************/
 
 static bool unpack_canon_ace(files_struct *fsp,
-                               SMB_STRUCT_STAT *pst,
+                               const SMB_STRUCT_STAT *pst,
                                DOM_SID *pfile_owner_sid,
                                DOM_SID *pfile_grp_sid,
                                canon_ace **ppfile_ace,
@@ -2376,14 +2328,8 @@ static bool unpack_canon_ace(files_struct *fsp,
 
        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_ex_mode = create_default_mode(fsp, False);
-
-       if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+       if (!ensure_canon_entry_valid(&file_ace, false, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2391,15 +2337,8 @@ static bool unpack_canon_ace(files_struct *fsp,
 
        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_ex_mode = create_default_mode(fsp, True);
-
-       if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+       if (dir_ace && !ensure_canon_entry_valid(&dir_ace, true, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2487,6 +2426,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
        canon_ace *ace = NULL;
        canon_ace *next_ace = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
+       bool is_default_acl = (the_acl_type == SMB_ACL_TYPE_DEFAULT);
        SMB_ACL_ENTRY_T entry;
        size_t ace_count;
 
@@ -2574,7 +2514,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                ace->trustee = sid;
                ace->unix_ug = unix_ug;
                ace->owner_type = owner_type;
-               ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+               ace->ace_flags = get_pai_flags(pal, ace, is_default_acl);
 
                DLIST_ADD(l_head, ace);
        }
@@ -2583,7 +2523,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
         * This next call will ensure we have at least a user/group/world set.
         */
 
-       if (!ensure_canon_entry_valid(&l_head, conn->params,
+       if (!ensure_canon_entry_valid(&l_head, is_default_acl, conn->params,
                                      S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
                                      psbuf, False))
                goto fail;
@@ -2593,7 +2533,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
         * acl_mask. Ensure all DENY Entries are at the start of the list.
         */
 
-       DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
+       DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", is_default_acl ?  "Default" : "Access"));
 
        for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
                next_ace = ace->next;
@@ -2627,7 +2567,7 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
  Check if the current user group list contains a given group.
 ****************************************************************************/
 
-static bool current_user_in_group(gid_t gid)
+bool current_user_in_group(gid_t gid)
 {
        int i;
 
@@ -2689,7 +2629,9 @@ static bool set_canon_ace_list(files_struct *fsp,
        mode_t mask_perms = 0;
 
        /* Use the psbuf that was passed in. */
-       fsp->fsp_name->st = *psbuf;
+       if (psbuf != &fsp->fsp_name->st) {
+               fsp->fsp_name->st = *psbuf;
+       }
 
 #if defined(POSIX_ACL_NEEDS_MASK)
        /* HP-UX always wants to have a mask (called "class" there). */
@@ -3460,49 +3402,41 @@ NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
        SMB_ACL_T posix_acl = NULL;
        SMB_ACL_T def_acl = NULL;
        struct pai_val *pal;
-       struct smb_filename *smb_fname = NULL;
-       NTSTATUS status;
+       struct smb_filename smb_fname;
        int ret;
 
        *ppdesc = NULL;
 
        DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
 
-       status = create_synthetic_smb_fname(talloc_tos(), name, NULL, NULL,
-                                           &smb_fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       ZERO_STRUCT(smb_fname);
+       smb_fname.base_name = discard_const_p(char, name);
 
        /* Get the stat struct for the owner info. */
        if (lp_posix_pathnames()) {
-               ret = SMB_VFS_LSTAT(conn, smb_fname);
+               ret = SMB_VFS_LSTAT(conn, &smb_fname);
        } else {
-               ret = SMB_VFS_STAT(conn, smb_fname);
+               ret = SMB_VFS_STAT(conn, &smb_fname);
        }
 
        if (ret == -1) {
-               status = map_nt_error_from_unix(errno);
-               goto out;
+               return map_nt_error_from_unix(errno);
        }
 
        /* Get the ACL from the path. */
        posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
 
        /* If it's a directory get the default POSIX ACL. */
-       if(S_ISDIR(smb_fname->st.st_ex_mode)) {
+       if(S_ISDIR(smb_fname.st.st_ex_mode)) {
                def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
                def_acl = free_empty_sys_acl(conn, def_acl);
        }
 
        pal = load_inherited_info(conn, name);
 
-       status = posix_get_nt_acl_common(conn, name, &smb_fname->st, pal,
-                                        posix_acl, def_acl, security_info,
-                                        ppdesc);
- out:
-       TALLOC_FREE(smb_fname);
-       return status;
+       return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
+                                      posix_acl, def_acl, security_info,
+                                      ppdesc);
 }
 
 /****************************************************************************
@@ -3588,7 +3522,7 @@ int try_chown(connection_struct *conn, struct smb_filename *smb_fname,
                return -1;
        }
 
-       if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname, &fsp))) {
+       if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname, &fsp))) {
                return -1;
        }
 
@@ -3607,7 +3541,7 @@ int try_chown(connection_struct *conn, struct smb_filename *smb_fname,
        }
        unbecome_root();
 
-       close_file_fchmod(NULL, fsp);
+       close_file(NULL, fsp, NORMAL_CLOSE);
 
        return ret;
 }
@@ -3819,7 +3753,7 @@ NTSTATUS append_parent_acl(files_struct *fsp,
  This should be the only external function needed for the UNIX style set ACL.
 ****************************************************************************/
 
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd_orig)
 {
        connection_struct *conn = fsp->conn;
        uid_t user = (uid_t)-1;
@@ -3834,6 +3768,7 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
        bool set_acl_as_root = false;
        bool acl_set_support = false;
        bool ret = false;
+       SEC_DESC *psd = NULL;
 
        DEBUG(10,("set_nt_acl: called for file %s\n",
                  fsp_str_dbg(fsp)));
@@ -3843,6 +3778,15 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
                return NT_STATUS_MEDIA_WRITE_PROTECTED;
        }
 
+       if (!psd_orig) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       psd = dup_sec_desc(talloc_tos(), psd_orig);
+       if (!psd) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        /*
         * Get the current state of the file.
         */
@@ -3859,6 +3803,14 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
         * Unpack the user/group/world id's.
         */
 
+       /* POSIX can't cope with missing owner/group. */
+       if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
+               security_info_sent &= ~SECINFO_OWNER;
+       }
+       if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
+               security_info_sent &= ~SECINFO_GROUP;
+       }
+
        status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -3909,6 +3861,39 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
 
        create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
 
+       if((security_info_sent & SECINFO_DACL) &&
+                       (psd->type & SEC_DESC_DACL_PRESENT) &&
+                       (psd->dacl == NULL)) {
+               SEC_ACE ace[3];
+
+               /* We can't have NULL DACL in POSIX.
+                  Use owner/group/Everyone -> full access. */
+
+               init_sec_ace(&ace[0],
+                               &file_owner_sid,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               init_sec_ace(&ace[1],
+                               &file_grp_sid,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               init_sec_ace(&ace[2],
+                               &global_sid_World,
+                               SEC_ACE_TYPE_ACCESS_ALLOWED,
+                               GENERIC_ALL_ACCESS,
+                               0);
+               psd->dacl = make_sec_acl(talloc_tos(),
+                                       NT4_ACL_REVISION,
+                                       3,
+                                       ace);
+               if (psd->dacl == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               security_acl_map_generic(psd->dacl, &file_generic_mapping);
+       }
+
        acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
                                     &file_grp_sid, &file_ace_list,
                                     &dir_ace_list, security_info_sent, psd);
@@ -4094,6 +4079,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC
        free_canon_ace_list(file_ace_list);
        free_canon_ace_list(dir_ace_list);
 
+       /* Ensure the stat struct in the fsp is correct. */
+       status = vfs_stat_fsp(fsp);
+
        return NT_STATUS_OK;
 }
 
@@ -4750,3 +4738,113 @@ SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
 
        return ret_sd;
 }
+
+/* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
+
+NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+                                       const char *name,
+                                       SMB_STRUCT_STAT *psbuf,
+                                       SEC_DESC **ppdesc)
+{
+       struct dom_sid owner_sid, group_sid;
+       size_t size = 0;
+       SEC_ACE aces[4];
+       uint32_t access_mask = 0;
+       mode_t mode = psbuf->st_ex_mode;
+       SEC_ACL *new_dacl = NULL;
+       int idx = 0;
+
+       DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+               name, (int)mode ));
+
+       uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+       gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+       /*
+        We provide up to 4 ACEs
+               - Owner
+               - Group
+               - Everyone
+               - NT System
+       */
+
+       if (mode & S_IRUSR) {
+               if (mode & S_IWUSR) {
+                       access_mask |= SEC_RIGHTS_FILE_ALL;
+               } else {
+                       access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+               }
+       }
+       if (mode & S_IWUSR) {
+               access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+       }
+
+       init_sec_ace(&aces[idx],
+                       &owner_sid,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+       idx++;
+
+       access_mask = 0;
+       if (mode & S_IRGRP) {
+               access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+       }
+       if (mode & S_IWGRP) {
+               /* note that delete is not granted - this matches posix behaviour */
+               access_mask |= SEC_RIGHTS_FILE_WRITE;
+       }
+       if (access_mask) {
+               init_sec_ace(&aces[idx],
+                       &group_sid,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+               idx++;
+       }
+
+       access_mask = 0;
+       if (mode & S_IROTH) {
+               access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+       }
+       if (mode & S_IWOTH) {
+               access_mask |= SEC_RIGHTS_FILE_WRITE;
+       }
+       if (access_mask) {
+               init_sec_ace(&aces[idx],
+                       &global_sid_World,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       access_mask,
+                       0);
+               idx++;
+       }
+
+       init_sec_ace(&aces[idx],
+                       &global_sid_System,
+                       SEC_ACE_TYPE_ACCESS_ALLOWED,
+                       SEC_RIGHTS_FILE_ALL,
+                       0);
+       idx++;
+
+       new_dacl = make_sec_acl(ctx,
+                       NT4_ACL_REVISION,
+                       idx,
+                       aces);
+
+       if (!new_dacl) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *ppdesc = make_sec_desc(ctx,
+                       SECURITY_DESCRIPTOR_REVISION_1,
+                       SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+                       &owner_sid,
+                       &group_sid,
+                       NULL,
+                       new_dacl,
+                       &size);
+       if (!*ppdesc) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}