First part of fix for bug #8663 - deleting a symlink fails if the symlink target...
[mat/samba.git] / source3 / smbd / open.c
index 4d70603f53eba0ce223d756ee7ab6756e75ecb88..5abc64dc99d5027347c47dea3dec8434d094df4b 100644 (file)
@@ -27,6 +27,7 @@
 #include "fake_file.h"
 #include "../libcli/security/security.h"
 #include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/open_files.h"
 #include "auth.h"
 #include "messages.h"
 
@@ -38,45 +39,18 @@ struct deferred_open_record {
 };
 
 /****************************************************************************
- SMB1 file varient of se_access_check. Never test FILE_READ_ATTRIBUTES.
-****************************************************************************/
-
-NTSTATUS smb1_file_se_access_check(struct connection_struct *conn,
-                               const struct security_descriptor *sd,
-                               const struct security_token *token,
-                               uint32_t access_desired,
-                               uint32_t *access_granted)
-{
-       *access_granted = 0;
-
-       if (get_current_uid(conn) == (uid_t)0) {
-               /* I'm sorry sir, I didn't know you were root... */
-               *access_granted = access_desired;
-               if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
-                       *access_granted |= FILE_GENERIC_ALL;
-               }
-               return NT_STATUS_OK;
-       }
-
-       return se_access_check(sd,
-                               token,
-                               (access_desired & ~FILE_READ_ATTRIBUTES),
-                               access_granted);
-}
-
-/****************************************************************************
- If the requester wanted DELETE_ACCESS and was only rejected because
+ If the requester wanted DELETE_ACCESS and was rejected because
  the file ACL didn't include DELETE_ACCESS, see if the parent ACL
  ovverrides this.
 ****************************************************************************/
 
 static bool parent_override_delete(connection_struct *conn,
-                                       struct smb_filename *smb_fname,
+                                       const struct smb_filename *smb_fname,
                                        uint32_t access_mask,
                                        uint32_t rejected_mask)
 {
        if ((access_mask & DELETE_ACCESS) &&
-                   (rejected_mask == DELETE_ACCESS) &&
+                   (rejected_mask & DELETE_ACCESS) &&
                    can_delete_file_in_directory(conn, smb_fname)) {
                return true;
        }
@@ -87,30 +61,41 @@ static bool parent_override_delete(connection_struct *conn,
  Check if we have open rights.
 ****************************************************************************/
 
-static NTSTATUS smbd_check_open_rights(struct connection_struct *conn,
+NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                                const struct smb_filename *smb_fname,
-                               uint32_t access_mask,
-                               uint32_t *access_granted)
+                               uint32_t access_mask)
 {
        /* Check if we have rights to open. */
        NTSTATUS status;
        struct security_descriptor *sd = NULL;
        uint32_t rejected_share_access;
+       uint32_t rejected_mask = 0;
 
        rejected_share_access = access_mask & ~(conn->share_access);
 
        if (rejected_share_access) {
-               *access_granted = rejected_share_access;
+               DEBUG(10, ("smbd_check_access_rights: rejected share access 0x%x "
+                       "on %s (0x%x)\n",
+                       (unsigned int)access_mask,
+                       smb_fname_str_dbg(smb_fname),
+                       (unsigned int)rejected_share_access ));
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       if ((access_mask & DELETE_ACCESS) && !lp_acl_check_permissions(SNUM(conn))) {
-               *access_granted = access_mask;
+       if (get_current_uid(conn) == (uid_t)0) {
+               /* I'm sorry sir, I didn't know you were root... */
+               DEBUG(10,("smbd_check_access_rights: root override "
+                       "on %s. Granting 0x%x\n",
+                       smb_fname_str_dbg(smb_fname),
+                       (unsigned int)access_mask ));
+               return NT_STATUS_OK;
+       }
 
-               DEBUG(10,("smbd_check_open_rights: not checking ACL "
+       if ((access_mask & DELETE_ACCESS) && !lp_acl_check_permissions(SNUM(conn))) {
+               DEBUG(10,("smbd_check_access_rights: not checking ACL "
                        "on DELETE_ACCESS on file %s. Granting 0x%x\n",
                        smb_fname_str_dbg(smb_fname),
-                       (unsigned int)*access_granted ));
+                       (unsigned int)access_mask ));
                return NT_STATUS_OK;
        }
 
@@ -120,29 +105,32 @@ static NTSTATUS smbd_check_open_rights(struct connection_struct *conn,
                        SECINFO_DACL),&sd);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("smbd_check_open_rights: Could not get acl "
+               DEBUG(10, ("smbd_check_access_rights: Could not get acl "
                        "on %s: %s\n",
                        smb_fname_str_dbg(smb_fname),
                        nt_errstr(status)));
                return status;
        }
 
-       status = smb1_file_se_access_check(conn,
-                               sd,
+       /*
+        * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of
+        * owner WRITE_DAC and READ_CONTROL.
+        */
+       status = se_access_check(sd,
                                get_current_nttok(conn),
-                               access_mask,
-                               access_granted);
+                               (access_mask & ~FILE_READ_ATTRIBUTES),
+                               &rejected_mask);
 
-       DEBUG(10,("smbd_check_open_rights: file %s requesting "
+       DEBUG(10,("smbd_check_access_rights: file %s requesting "
                "0x%x returning 0x%x (%s)\n",
                smb_fname_str_dbg(smb_fname),
                (unsigned int)access_mask,
-               (unsigned int)*access_granted,
+               (unsigned int)rejected_mask,
                nt_errstr(status) ));
 
        if (!NT_STATUS_IS_OK(status)) {
                if (DEBUGLEVEL >= 10) {
-                       DEBUG(10,("smbd_check_open_rights: acl for %s is:\n",
+                       DEBUG(10,("smbd_check_access_rights: acl for %s is:\n",
                                smb_fname_str_dbg(smb_fname) ));
                        NDR_PRINT_DEBUG(security_descriptor, sd);
                }
@@ -150,14 +138,59 @@ static NTSTATUS smbd_check_open_rights(struct connection_struct *conn,
 
        TALLOC_FREE(sd);
 
-       return status;
+       if (NT_STATUS_IS_OK(status) ||
+                       !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+               return status;
+       }
+
+       /* Here we know status == NT_STATUS_ACCESS_DENIED. */
+       if ((access_mask & FILE_WRITE_ATTRIBUTES) &&
+                       (rejected_mask & FILE_WRITE_ATTRIBUTES) &&
+                       (lp_map_readonly(SNUM(conn)) ||
+                       lp_map_archive(SNUM(conn)) ||
+                       lp_map_hidden(SNUM(conn)) ||
+                       lp_map_system(SNUM(conn)))) {
+               rejected_mask &= ~FILE_WRITE_ATTRIBUTES;
+
+               DEBUG(10,("smbd_check_access_rights: "
+                       "overrode "
+                       "FILE_WRITE_ATTRIBUTES "
+                       "on file %s\n",
+                       smb_fname_str_dbg(smb_fname)));
+       }
+
+       if (parent_override_delete(conn,
+                               smb_fname,
+                               access_mask,
+                               rejected_mask)) {
+               /* Were we trying to do an open
+                * for delete and didn't get DELETE
+                * access (only) ? Check if the
+                * directory allows DELETE_CHILD.
+                * See here:
+                * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx
+                * for details. */
+
+               rejected_mask &= ~DELETE_ACCESS;
+
+               DEBUG(10,("smbd_check_access_rights: "
+                       "overrode "
+                       "DELETE_ACCESS on "
+                       "file %s\n",
+                       smb_fname_str_dbg(smb_fname)));
+       }
+
+       if (rejected_mask != 0) {
+               return NT_STATUS_ACCESS_DENIED;
+       } else {
+               return NT_STATUS_OK;
+       }
 }
 
 static NTSTATUS check_parent_access(struct connection_struct *conn,
                                struct smb_filename *smb_fname,
                                uint32_t access_mask,
-                               char **pp_parent_dir,
-                               struct security_descriptor **pp_parent_sd)
+                               char **pp_parent_dir)
 {
        NTSTATUS status;
        char *parent_dir = NULL;
@@ -171,6 +204,19 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (pp_parent_dir) {
+               *pp_parent_dir = parent_dir;
+       }
+
+       if (get_current_uid(conn) == (uid_t)0) {
+               /* I'm sorry sir, I didn't know you were root... */
+               DEBUG(10,("check_parent_access: root override "
+                       "on %s. Granting 0x%x\n",
+                       smb_fname_str_dbg(smb_fname),
+                       (unsigned int)access_mask ));
+               return NT_STATUS_OK;
+       }
+
        status = SMB_VFS_GET_NT_ACL(conn,
                                parent_dir,
                                SECINFO_DACL,
@@ -184,11 +230,14 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
                return status;
        }
 
-       status = smb1_file_se_access_check(conn,
-                                       parent_sd,
-                                       get_current_nttok(conn),
-                                       access_mask,
-                                       &access_granted);
+       /*
+        * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of
+        * owner WRITE_DAC and READ_CONTROL.
+        */
+       status = se_access_check(parent_sd,
+                               get_current_nttok(conn),
+                               (access_mask & ~FILE_READ_ATTRIBUTES),
+                               &access_granted);
        if(!NT_STATUS_IS_OK(status)) {
                DEBUG(5,("check_parent_access: access check "
                        "on directory %s for "
@@ -201,12 +250,6 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
                return status;
        }
 
-       if (pp_parent_dir) {
-               *pp_parent_dir = parent_dir;
-       }
-       if (pp_parent_sd) {
-               *pp_parent_sd = parent_sd;
-       }
        return NT_STATUS_OK;
 }
 
@@ -565,6 +608,35 @@ static NTSTATUS open_file(files_struct *fsp,
                        return NT_STATUS_OBJECT_NAME_INVALID;
                }
 
+               /* Can we access this file ? */
+               if (!fsp->base_fsp) {
+                       /* Only do this check on non-stream open. */
+                       if (file_existed) {
+                               status = smbd_check_access_rights(conn,
+                                               smb_fname,
+                                               access_mask);
+                       } else if (local_flags & O_CREAT){
+                               status = check_parent_access(conn,
+                                               smb_fname,
+                                               SEC_DIR_ADD_FILE,
+                                               NULL);
+                       } else {
+                               /* File didn't exist and no O_CREAT. */
+                               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       }
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(10,("open_file: "
+                                       "%s on file "
+                                       "%s returned %s\n",
+                                       file_existed ?
+                                               "smbd_check_access_rights" :
+                                               "check_parent_access",
+                                       smb_fname_str_dbg(smb_fname),
+                                       nt_errstr(status) ));
+                               return status;
+                       }
+               }
+
                /* Actually do the open */
                status = fd_open(conn, fsp, local_flags, unx_mode);
                if (!NT_STATUS_IS_OK(status)) {
@@ -579,89 +651,34 @@ static NTSTATUS open_file(files_struct *fsp,
                }
 
        } else {
-               uint32_t access_granted = 0;
-
                fsp->fh->fd = -1; /* What we used to call a stat open. */
                if (!file_existed) {
                        /* File must exist for a stat open. */
                        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                }
 
-               status = smbd_check_open_rights(conn,
+               status = smbd_check_access_rights(conn,
                                smb_fname,
-                               access_mask,
-                               &access_granted);
-               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
-                       /*
-                        * On NT_STATUS_ACCESS_DENIED, access_granted
-                        * contains the denied bits.
-                        */
-
-                       if ((access_mask & FILE_WRITE_ATTRIBUTES) &&
-                                       (access_granted & FILE_WRITE_ATTRIBUTES) &&
-                                       (lp_map_readonly(SNUM(conn)) ||
-                                        lp_map_archive(SNUM(conn)) ||
-                                        lp_map_hidden(SNUM(conn)) ||
-                                        lp_map_system(SNUM(conn)))) {
-                               access_granted &= ~FILE_WRITE_ATTRIBUTES;
+                               access_mask);
 
-                               DEBUG(10,("open_file: "
-                                         "overrode "
-                                         "FILE_WRITE_"
-                                         "ATTRIBUTES "
-                                         "on file %s\n",
-                                         smb_fname_str_dbg(
-                                                 smb_fname)));
-                       }
-
-                       if (parent_override_delete(conn,
-                                               smb_fname,
-                                               access_mask,
-                                               access_granted)) {
-                               /* Were we trying to do a stat open
-                                * for delete and didn't get DELETE
-                                * access (only) ? Check if the
-                                * directory allows DELETE_CHILD.
-                                * See here:
-                                * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx
-                                * for details. */
-
-                               access_granted &= ~DELETE_ACCESS;
-
-                               DEBUG(10,("open_file: "
-                                         "overrode "
-                                         "DELETE_ACCESS on "
-                                         "file %s\n",
-                                         smb_fname_str_dbg(
-                                                 smb_fname)));
-                       }
-
-                       if (access_granted != 0) {
-                               DEBUG(10,("open_file: Access "
-                                         "denied (0x%x) on file "
-                                         "%s\n",
-                                         access_granted,
-                                         smb_fname_str_dbg(
-                                                 smb_fname)));
-                               return status;
-                       }
-
-               } else if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-                                   fsp->posix_open &&
-                                   S_ISLNK(smb_fname->st.st_ex_mode)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
+                               fsp->posix_open &&
+                               S_ISLNK(smb_fname->st.st_ex_mode)) {
                        /* This is a POSIX stat open for delete
                         * or rename on a symlink that points
                         * nowhere. Allow. */
                        DEBUG(10,("open_file: allowing POSIX "
                                  "open on bad symlink %s\n",
-                                 smb_fname_str_dbg(
-                                         smb_fname)));
-               } else if (!NT_STATUS_IS_OK(status)) {
+                                 smb_fname_str_dbg(smb_fname)));
+                       status = NT_STATUS_OK;
+               }
+
+               if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10,("open_file: "
-                                 "smbd_check_open_rights on file "
-                                 "%s returned %s\n",
-                                 smb_fname_str_dbg(smb_fname),
-                                 nt_errstr(status) ));
+                               "smbd_check_access_rights on file "
+                               "%s returned %s\n",
+                               smb_fname_str_dbg(smb_fname),
+                               nt_errstr(status) ));
                        return status;
                }
        }
@@ -894,8 +911,7 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                          "share entry with an open file\n");
        }
 
-       if (is_deferred_open_entry(share_entry) ||
-           is_unused_share_mode_entry(share_entry)) {
+       if (is_deferred_open_entry(share_entry)) {
                goto panic;
        }
 
@@ -1259,11 +1275,9 @@ static void defer_open(struct share_mode_lock *lck,
        for (i=0; i<lck->num_share_modes; i++) {
                struct share_mode_entry *e = &lck->share_modes[i];
 
-               if (!is_deferred_open_entry(e)) {
-                       continue;
-               }
-
-               if (procid_is_me(&e->pid) && (e->op_mid == req->mid)) {
+               if (is_deferred_open_entry(e) &&
+                   procid_is_me(&e->pid) &&
+                   (e->op_mid == req->mid)) {
                        DEBUG(0, ("Trying to defer an already deferred "
                                "request: mid=%llu, exiting\n",
                                (unsigned long long)req->mid));
@@ -1456,7 +1470,9 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
 
        /* Calculate MAXIMUM_ALLOWED_ACCESS if requested. */
        if (access_mask & MAXIMUM_ALLOWED_ACCESS) {
-               if (file_existed) {
+               if (get_current_uid(conn) == (uid_t)0) {
+                       access_mask  |= FILE_GENERIC_ALL;
+               } else if (file_existed) {
 
                        struct security_descriptor *sd;
                        uint32_t access_granted = 0;
@@ -1474,10 +1490,13 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
                                return NT_STATUS_ACCESS_DENIED;
                        }
 
-                       status = smb1_file_se_access_check(conn,
-                                       sd,
+                       /*
+                        * Never test FILE_READ_ATTRIBUTES. se_access_check()
+                        * also takes care of owner WRITE_DAC and READ_CONTROL.
+                        */
+                       status = se_access_check(sd,
                                        get_current_nttok(conn),
-                                       access_mask,
+                                       (access_mask & ~FILE_READ_ATTRIBUTES),
                                        &access_granted);
 
                        TALLOC_FREE(sd);
@@ -1490,7 +1509,7 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
                                return NT_STATUS_ACCESS_DENIED;
                        }
 
-                       access_mask = access_granted;
+                       access_mask = (access_granted | FILE_READ_ATTRIBUTES);
                } else {
                        access_mask = FILE_GENERIC_ALL;
                }
@@ -1526,10 +1545,10 @@ void remove_deferred_open_entry(struct file_id id, uint64_t mid,
                        NULL, NULL, NULL);
        if (lck == NULL) {
                DEBUG(0, ("could not get share mode lock\n"));
-       } else {
-               del_deferred_open_entry(lck, mid, pid);
-               TALLOC_FREE(lck);
+               return;
        }
+       del_deferred_open_entry(lck, mid, pid);
+       TALLOC_FREE(lck);
 }
 
 /****************************************************************
@@ -1704,11 +1723,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                }
        }
 
-       status = check_name(conn, smb_fname->base_name);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
        if (!posix_open) {
                new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
                if (file_existed) {
@@ -2060,8 +2074,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                        if (((can_access_mask & FILE_WRITE_DATA) &&
                                !CAN_WRITE(conn)) ||
-                           !can_access_file_data(conn, smb_fname,
-                                                 can_access_mask)) {
+                               !NT_STATUS_IS_OK(smbd_check_access_rights(conn,
+                                               smb_fname, can_access_mask))) {
                                can_access = False;
                        }
 
@@ -2548,8 +2562,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        status = check_parent_access(conn,
                                        smb_dname,
                                        access_mask,
-                                       &parent_dir,
-                                       NULL);
+                                       &parent_dir);
        if(!NT_STATUS_IS_OK(status)) {
                DEBUG(5,("mkdir_internal: check_parent_access "
                        "on directory %s for path %s returned %s\n",
@@ -2797,31 +2810,9 @@ static NTSTATUS open_directory(connection_struct *conn,
        }
 
        if (info == FILE_WAS_OPENED) {
-               uint32_t access_granted = 0;
-               status = smbd_check_open_rights(conn, smb_dname, access_mask,
-                                               &access_granted);
-
-               /* Were we trying to do a directory open
-                * for delete and didn't get DELETE
-                * access (only) ? Check if the
-                * directory allows DELETE_CHILD.
-                * See here:
-                * http://blogs.msdn.com/oldnewthing/archive/2004/06/04/148426.aspx
-                * for details. */
-
-               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
-                               parent_override_delete(conn,
-                                               smb_dname,
-                                               access_mask,
-                                               access_granted)) {
-                       DEBUG(10,("open_directory: overrode ACCESS_DENIED "
-                               "on directory %s\n",
-                               smb_fname_str_dbg(smb_dname)));
-                       status = NT_STATUS_OK;
-               }
-
+               status = smbd_check_access_rights(conn, smb_dname, access_mask);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(10, ("open_directory: smbd_check_open_rights on "
+                       DEBUG(10, ("open_directory: smbd_check_access_rights on "
                                "file %s failed with %s\n",
                                smb_fname_str_dbg(smb_dname),
                                nt_errstr(status)));
@@ -2993,7 +2984,6 @@ void msg_file_was_renamed(struct messaging_context *msg,
                          struct server_id server_id,
                          DATA_BLOB *data)
 {
-       struct smbd_server_connection *sconn;
        files_struct *fsp;
        char *frm = (char *)data->data;
        struct file_id id;
@@ -3003,12 +2993,9 @@ void msg_file_was_renamed(struct messaging_context *msg,
        struct smb_filename *smb_fname = NULL;
        size_t sp_len, bn_len;
        NTSTATUS status;
-
-       sconn = msg_ctx_to_sconn(msg);
-       if (sconn == NULL) {
-               DEBUG(1, ("could not find sconn\n"));
-               return;
-       }
+       struct smbd_server_connection *sconn =
+               talloc_get_type_abort(private_data,
+               struct smbd_server_connection);
 
        if (data->data == NULL
            || data->length < MSG_FILE_RENAMED_MIN_SIZE + 2) {
@@ -3187,6 +3174,109 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        return status;
 }
 
+/*********************************************************************
+ Create a default ACL by inheriting from the parent. If no inheritance
+ from the parent available, don't set anything. This will leave the actual
+ permissions the new file or directory already got from the filesystem
+ as the NT ACL when read.
+*********************************************************************/
+
+static NTSTATUS inherit_new_acl(files_struct *fsp)
+{
+       TALLOC_CTX *ctx = talloc_tos();
+       char *parent_name = NULL;
+       struct security_descriptor *parent_desc = NULL;
+       NTSTATUS status = NT_STATUS_OK;
+       struct security_descriptor *psd = NULL;
+       struct dom_sid *owner_sid = NULL;
+       struct dom_sid *group_sid = NULL;
+       uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL);
+       bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn));
+       bool inheritable_components = false;
+       size_t size = 0;
+
+       if (!parent_dirname(ctx, fsp->fsp_name->base_name, &parent_name, NULL)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = SMB_VFS_GET_NT_ACL(fsp->conn,
+                               parent_name,
+                               (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
+                               &parent_desc);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       inheritable_components = sd_has_inheritable_components(parent_desc,
+                                       fsp->is_directory);
+
+       if (!inheritable_components && !inherit_owner) {
+               /* Nothing to inherit and not setting owner. */
+               return NT_STATUS_OK;
+       }
+
+       /* Create an inherited descriptor from the parent. */
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10,("inherit_new_acl: parent acl for %s is:\n",
+                       fsp_str_dbg(fsp) ));
+               NDR_PRINT_DEBUG(security_descriptor, parent_desc);
+       }
+
+       /* Inherit from parent descriptor if "inherit owner" set. */
+       if (inherit_owner) {
+               owner_sid = parent_desc->owner_sid;
+               group_sid = parent_desc->group_sid;
+       }
+
+       if (owner_sid == NULL) {
+               owner_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+       }
+       if (group_sid == NULL) {
+               group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX];
+       }
+
+       status = se_create_child_secdesc(ctx,
+                       &psd,
+                       &size,
+                       parent_desc,
+                       owner_sid,
+                       group_sid,
+                       fsp->is_directory);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* If inheritable_components == false,
+          se_create_child_secdesc()
+          creates a security desriptor with a NULL dacl
+          entry, but with SEC_DESC_DACL_PRESENT. We need
+          to remove that flag. */
+
+       if (!inheritable_components) {
+               security_info_sent &= ~SECINFO_DACL;
+               psd->type &= ~SEC_DESC_DACL_PRESENT;
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               DEBUG(10,("inherit_new_acl: child acl for %s is:\n",
+                       fsp_str_dbg(fsp) ));
+               NDR_PRINT_DEBUG(security_descriptor, psd);
+       }
+
+       if (inherit_owner) {
+               /* We need to be root to force this. */
+               become_root();
+       }
+       status = SMB_VFS_FSET_NT_ACL(fsp,
+                       security_info_sent,
+                       psd);
+       if (inherit_owner) {
+               unbecome_root();
+       }
+       return status;
+}
+
 /*
  * Wrapper around open_file_ntcreate and open_directory
  */
@@ -3256,26 +3346,6 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
-       /* This is the correct thing to do (check every time) but can_delete
-        * is expensive (it may have to read the parent directory
-        * permissions). So for now we're not doing it unless we have a strong
-        * hint the client is really going to delete this file. If the client
-        * is forcing FILE_CREATE let the filesystem take care of the
-        * permissions. */
-
-       /* Setting FILE_SHARE_DELETE is the hint. */
-
-       if ((create_disposition != FILE_CREATE)
-           && (access_mask & DELETE_ACCESS)
-           && (!(can_delete_file_in_directory(conn, smb_fname) ||
-                can_access_file_acl(conn, smb_fname, DELETE_ACCESS)))) {
-               status = NT_STATUS_ACCESS_DENIED;
-               DEBUG(10,("create_file_unixpath: open file %s "
-                         "for delete ACCESS_DENIED\n",
-                         smb_fname_str_dbg(smb_fname)));
-               goto fail;
-       }
-
        if ((access_mask & SEC_FLAG_SYSTEM_SECURITY) &&
                        !security_token_has_privilege(get_current_nttok(conn),
                                        SEC_PRIV_SECURITY)) {
@@ -3444,45 +3514,6 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 
        fsp->base_fsp = base_fsp;
 
-       /*
-        * According to the MS documentation, the only time the security
-        * descriptor is applied to the opened file is iff we *created* the
-        * file; an existing file stays the same.
-        *
-        * Also, it seems (from observation) that you can open the file with
-        * any access mask but you can still write the sd. We need to override
-        * the granted access before we call set_sd
-        * Patch for bug #2242 from Tom Lackemann <cessnatomny@yahoo.com>.
-        */
-
-       if ((sd != NULL) && (info == FILE_WAS_CREATED)
-           && lp_nt_acl_support(SNUM(conn))) {
-
-               uint32_t sec_info_sent;
-               uint32_t saved_access_mask = fsp->access_mask;
-
-               sec_info_sent = get_sec_info(sd);
-
-               fsp->access_mask = FILE_GENERIC_ALL;
-
-               /* Convert all the generic bits. */
-               security_acl_map_generic(sd->dacl, &file_generic_mapping);
-               security_acl_map_generic(sd->sacl, &file_generic_mapping);
-
-               if (sec_info_sent & (SECINFO_OWNER|
-                                       SECINFO_GROUP|
-                                       SECINFO_DACL|
-                                       SECINFO_SACL)) {
-                       status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd);
-               }
-
-               fsp->access_mask = saved_access_mask;
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
-       }
-
        if ((ea_list != NULL) &&
            ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN))) {
                status = set_ea(conn, fsp, fsp->fsp_name, ea_list);
@@ -3518,6 +3549,54 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
+       if ((info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn)) &&
+                               fsp->base_fsp == NULL) {
+               if (sd != NULL) {
+                       /*
+                        * According to the MS documentation, the only time the security
+                        * descriptor is applied to the opened file is iff we *created* the
+                        * file; an existing file stays the same.
+                        *
+                        * Also, it seems (from observation) that you can open the file with
+                        * any access mask but you can still write the sd. We need to override
+                        * the granted access before we call set_sd
+                        * Patch for bug #2242 from Tom Lackemann <cessnatomny@yahoo.com>.
+                        */
+
+                       uint32_t sec_info_sent;
+                       uint32_t saved_access_mask = fsp->access_mask;
+
+                       sec_info_sent = get_sec_info(sd);
+
+                       fsp->access_mask = FILE_GENERIC_ALL;
+
+                       /* Convert all the generic bits. */
+                       security_acl_map_generic(sd->dacl, &file_generic_mapping);
+                       security_acl_map_generic(sd->sacl, &file_generic_mapping);
+
+                       if (sec_info_sent & (SECINFO_OWNER|
+                                               SECINFO_GROUP|
+                                               SECINFO_DACL|
+                                               SECINFO_SACL)) {
+                               status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd);
+                       }
+
+                       fsp->access_mask = saved_access_mask;
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               goto fail;
+                       }
+               } else if (lp_inherit_acls(SNUM(conn))) {
+                       /* Inherit from parent. Errors here are not fatal. */
+                       status = inherit_new_acl(fsp);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(10,("inherit_new_acl: failed for %s with %s\n",
+                                       fsp_str_dbg(fsp),
+                                       nt_errstr(status) ));
+                       }
+               }
+       }
+
        DEBUG(10, ("create_file_unixpath: info=%d\n", info));
 
        *result = fsp;
@@ -3762,13 +3841,6 @@ NTSTATUS create_file_default(connection_struct *conn,
                }
        }
 
-       /* All file access must go through check_name() */
-
-       status = check_name(conn, smb_fname->base_name);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-
        if (stream_name && is_ntfs_default_stream_smb_fname(smb_fname)) {
                int ret;
                smb_fname->stream_name = NULL;