Fix bug #7996 - sgid bit lost on folder rename.
[samba.git] / source3 / smbd / posix_acls.c
index ae36492ed1382fff4414c3ff0d8aaf49aeadfddc..714a4d34996b0793656b8cdcb5fd76e87691dd24 100644 (file)
@@ -912,34 +912,13 @@ void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID
        gid_to_sid( pgroup_sid, psbuf->st_ex_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
  if the permissions become zero, delete the deny if the permissions are non zero.
 ****************************************************************************/
 
-static void merge_aces( canon_ace **pp_list_head )
+static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
 {
        canon_ace *l_head = *pp_list_head;
        canon_ace *curr_ace_outer;
@@ -957,12 +936,24 @@ static void merge_aces( canon_ace **pp_list_head )
                curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
 
                for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+                       bool can_merge = false;
 
                        curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
 
-                       if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
-                               (curr_ace->attr == curr_ace_outer->attr)) {
+                       /* For file ACLs we can merge if the SIDs and ALLOW/DENY
+                        * types are the same. For directory acls we must also
+                        * ensure the POSIX ACL types are the same. */
 
+                       if (!dir_acl) {
+                               can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                                               (curr_ace->attr == curr_ace_outer->attr));
+                       } else {
+                               can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+                                               (curr_ace->type == curr_ace_outer->type) &&
+                                               (curr_ace->attr == curr_ace_outer->attr));
+                       }
+
+                       if (can_merge) {
                                if( DEBUGLVL( 10 )) {
                                        dbgtext("merge_aces: Merging ACE's\n");
                                        print_canon_ace( curr_ace_outer, 0);
@@ -971,7 +962,13 @@ static void merge_aces( canon_ace **pp_list_head )
 
                                /* Merge two allow or two deny ACE's. */
 
+                               /* Theoretically we shouldn't merge a dir ACE if
+                                * one ACE has the CI flag set, and the other
+                                * ACE has the OI flag set, but this is rare
+                                * enough we can ignore it. */
+
                                curr_ace_outer->perms |= curr_ace->perms;
+                               curr_ace_outer->ace_flags |= curr_ace->ace_flags;
                                DLIST_REMOVE(l_head, curr_ace);
                                SAFE_FREE(curr_ace);
                                curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
@@ -1000,7 +997,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 (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
+                       if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
                                (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
 
                                if( DEBUGLVL( 10 )) {
@@ -1071,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)
@@ -1112,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 ));
 
@@ -1524,12 +1525,56 @@ 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,
@@ -1655,11 +1700,11 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        /*
                         * The Creator Owner entry only specifies inheritable permissions,
                         * never access permissions. WinNT doesn't always set the ACE to
-                        *INHERIT_ONLY, though.
+                        * INHERIT_ONLY, though.
                         */
 
-                       if (nt4_compatible_acls())
-                               psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
                } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
                        current_ace->owner_type = GID_ACE;
                        current_ace->unix_ug.gid = pst->st_ex_gid;
@@ -1668,10 +1713,9 @@ static bool create_canon_ace_lists(files_struct *fsp,
                        /*
                         * The Creator Group entry only specifies inheritable permissions,
                         * never access permissions. WinNT doesn't always set the ACE to
-                        *INHERIT_ONLY, though.
+                        * INHERIT_ONLY, though.
                         */
-                       if (nt4_compatible_acls())
-                               psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
 
                } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
                        current_ace->owner_type = UID_ACE;
@@ -1704,6 +1748,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 "
@@ -1767,6 +1819,34 @@ 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.
@@ -1787,6 +1867,13 @@ static bool create_canon_ace_lists(files_struct *fsp,
                                         * pointer is now owned by the dir_ace list.
                                         */
                                        current_ace = dup_ace;
+                                       /* We've essentially split this ace into two,
+                                        * and added the ace with inheritance request
+                                        * bits to the directory ACL. Drop those bits for
+                                        * the ACE we're adding to the file list. */
+                                       current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
+                                                               SEC_ACE_FLAG_CONTAINER_INHERIT|
+                                                               SEC_ACE_FLAG_INHERIT_ONLY);
                                } else {
                                        /*
                                         * We must not free current_ace here as its
@@ -2230,7 +2317,7 @@ static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
 ****************************************************************************/
 
 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,
@@ -2238,6 +2325,7 @@ 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;
 
@@ -2277,10 +2365,10 @@ static bool unpack_canon_ace(files_struct *fsp,
         */
 
        print_canon_ace_list( "file ace - before merge", file_ace);
-       merge_aces( &file_ace );
+       merge_aces( &file_ace, false);
 
        print_canon_ace_list( "dir ace - before merge", dir_ace);
-       merge_aces( &dir_ace );
+       merge_aces( &dir_ace, true);
 
        /*
         * NT ACLs are order dependent. Go through the acl lists and
@@ -2301,14 +2389,17 @@ 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 --- ---.
         */
 
-       pst->st_ex_mode = create_default_mode(fsp, False);
+       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, pst, True)) {
+       if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2322,9 +2413,10 @@ static bool unpack_canon_ace(files_struct *fsp,
         * it's a directory.
         */
 
-       pst->st_ex_mode = create_default_mode(fsp, True);
+       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, pst, True)) {
+       if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params,
+                       fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
                free_canon_ace_list(file_ace);
                free_canon_ace_list(dir_ace);
                return False;
@@ -2446,17 +2538,6 @@ static canon_ace *canonicalise_acl(struct connection_struct *conn,
                                                DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
                                                continue;
                                        }
-                                       /*
-                                        * A SMB_ACL_USER entry for the owner is shadowed by the
-                                        * SMB_ACL_USER_OBJ entry and Windows also cannot represent
-                                        * that entry, so we ignore it. We also don't create such
-                                        * entries out of the blue when setting ACLs, so a get/set
-                                        * cycle will drop them.
-                                        */
-                                       if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_ex_uid) {
-                                               SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
-                                               continue;
-                                       }
                                        uid_to_sid( &sid, *puid);
                                        unix_ug.uid = *puid;
                                        owner_type = UID_ACE;
@@ -2563,7 +2644,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;
 
@@ -2625,7 +2706,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). */
@@ -3396,49 +3479,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);
 }
 
 /****************************************************************************
@@ -3524,16 +3599,26 @@ 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;
        }
 
        become_root();
        /* Keep the current file gid the same. */
-       ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+       if (fsp->fh->fd == -1) {
+               if (lp_posix_pathnames()) {
+                       ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
+                                           (gid_t)-1);
+               } else {
+                       ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
+                                           (gid_t)-1);
+               }
+       } else {
+               ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+       }
        unbecome_root();
 
-       close_file_fchmod(NULL, fsp);
+       close_file(NULL, fsp, NORMAL_CLOSE);
 
        return ret;
 }
@@ -3745,7 +3830,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;
@@ -3760,6 +3845,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)));
@@ -3769,6 +3855,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.
         */
@@ -3785,6 +3880,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;
@@ -3835,6 +3938,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);
@@ -4020,6 +4156,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;
 }
 
@@ -4676,3 +4815,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;
+}