Another fix needed for bug #9236 - ACL masks incorrectly applied when setting ACLs.
[samba.git] / source3 / smbd / posix_acls.c
index 0e25ed561530295a9eacf1dad64d8993793a2d43..65a77d4e57475fff721328be401d6a45ff337421 100644 (file)
@@ -1123,8 +1123,8 @@ 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)
@@ -1342,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;
@@ -1358,8 +1359,13 @@ 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) {
+                               /*
+                                * Ensure we have default parameters for the
+                                * user (owner) even on default ACLs.
+                                */
                                apply_default_perms(params, is_directory, pace, S_IRUSR);
+                       }
                        got_user = True;
 
                } else if (pace->type == SMB_ACL_GROUP_OBJ) {
@@ -1368,8 +1374,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) {
@@ -1378,10 +1385,21 @@ 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;
+
+               } else if (pace->type == SMB_ACL_USER || pace->type == SMB_ACL_GROUP) {
+
+                       /*
+                        * Ensure create mask/force create mode is respected on set.
+                        */
+
+                       if (setting_acl && !is_default_acl) {
+                               apply_default_perms(params, is_directory, pace, S_IRGRP);
+                       }
                }
        }
 
@@ -1397,31 +1415,38 @@ 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;
                        }
 
+                       /*
+                        * Ensure we have default parameters for the
+                        * user (owner) even on default ACLs.
+                        */
                        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);
@@ -1448,7 +1473,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);
                }
@@ -1470,7 +1497,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);
 
@@ -1484,6 +1513,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)
@@ -1525,50 +1555,6 @@ 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.
 ****************************************************************************/
@@ -1748,6 +1734,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 "
@@ -1784,6 +1778,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 *);
 
                                /*
@@ -1811,34 +1806,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.
@@ -1873,6 +1840,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;
+                                       }
+                               }
                        }
                }
 
@@ -1934,17 +1938,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;
@@ -2265,44 +2267,6 @@ 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.
@@ -2317,7 +2281,6 @@ static bool unpack_canon_ace(files_struct *fsp,
                                uint32 security_info_sent,
                                const SEC_DESC *psd)
 {
-       SMB_STRUCT_STAT st;
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
 
@@ -2381,17 +2344,8 @@ static bool unpack_canon_ace(files_struct *fsp,
 
        print_canon_ace_list( "file ace - before valid", file_ace);
 
-       st = *pst;
-
-       /*
-        * A default 3 element mode entry for a file should be r-- --- ---.
-        * A default 3 element mode entry for a directory should be rwx --- ---.
-        */
-
-       st.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, &st, 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;
@@ -2399,16 +2353,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.
-        */
-
-       st.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, &st, 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;
@@ -2496,6 +2442,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;
 
@@ -2583,7 +2530,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);
        }
@@ -2592,7 +2539,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;
@@ -2602,7 +2549,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;
@@ -2636,7 +2583,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;
 
@@ -3591,7 +3538,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;
        }
 
@@ -3610,7 +3557,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;
 }
@@ -3872,6 +3819,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;
@@ -3922,6 +3877,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);
@@ -4766,3 +4754,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;
+}