Add early return on stat open without O_CREAT if file doesn't exist.
[mat/samba.git] / source3 / smbd / open.c
index b897c8aa2565b9856f5ff78613262924ffa5cc65..6fb891830f833bdccba6f0f91b9d434b0dab72c2 100644 (file)
@@ -134,6 +134,63 @@ NTSTATUS smbd_check_open_rights(struct connection_struct *conn,
        return status;
 }
 
+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)
+{
+       NTSTATUS status;
+       char *parent_dir = NULL;
+       struct security_descriptor *parent_sd = NULL;
+       uint32_t access_granted = 0;
+
+       if (!parent_dirname(talloc_tos(),
+                               smb_fname->base_name,
+                               &parent_dir,
+                               NULL)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = SMB_VFS_GET_NT_ACL(conn,
+                               parent_dir,
+                               SECINFO_DACL,
+                               &parent_sd);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(5,("check_parent_access: SMB_VFS_GET_NT_ACL failed for "
+                       "%s with error %s\n",
+                       parent_dir,
+                       nt_errstr(status)));
+               return status;
+       }
+
+       status = smb1_file_se_access_check(conn,
+                                       parent_sd,
+                                       get_current_nttok(conn),
+                                       access_mask,
+                                       &access_granted);
+       if(!NT_STATUS_IS_OK(status)) {
+               DEBUG(5,("check_parent_access: access check "
+                       "on directory %s for "
+                       "path %s for mask 0x%x returned (0x%x) %s\n",
+                       parent_dir,
+                       smb_fname->base_name,
+                       access_mask,
+                       access_granted,
+                       nt_errstr(status) ));
+               return status;
+       }
+
+       if (pp_parent_dir) {
+               *pp_parent_dir = parent_dir;
+       }
+       if (pp_parent_sd) {
+               *pp_parent_sd = parent_sd;
+       }
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
@@ -503,86 +560,89 @@ 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) {
-                       uint32_t access_granted = 0;
+               if (!file_existed) {
+                       /* File must exist for a stat open. */
+                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               }
 
-                       status = smbd_check_open_rights(conn,
-                                       smb_fname,
-                                       access_mask,
-                                       &access_granted);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               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;
-
-                                               DEBUG(10,("open_file: "
-                                                         "overrode "
-                                                         "FILE_WRITE_"
-                                                         "ATTRIBUTES "
-                                                         "on file %s\n",
-                                                         smb_fname_str_dbg(
-                                                                 smb_fname)));
-                                       }
+               status = smbd_check_open_rights(conn,
+                               smb_fname,
+                               access_mask,
+                               &access_granted);
+               if (!NT_STATUS_IS_OK(status)) {
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+                               /*
+                                * On NT_STATUS_ACCESS_DENIED, access_granted
+                                * contains the denied bits.
+                                */
 
-                                       if ((access_mask & DELETE_ACCESS) &&
-                                           (access_granted & DELETE_ACCESS) &&
-                                           can_delete_file_in_directory(conn,
-                                               smb_fname)) {
-                                               /* 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_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;
 
-                                       if (access_granted != 0) {
-                                               DEBUG(10,("open_file: Access "
-                                                         "denied on file "
-                                                         "%s\n",
-                                                         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)) {
-                                       /* 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",
+                                       DEBUG(10,("open_file: "
+                                                 "overrode "
+                                                 "FILE_WRITE_"
+                                                 "ATTRIBUTES "
+                                                 "on file %s\n",
                                                  smb_fname_str_dbg(
                                                          smb_fname)));
-                               } else {
+                               }
+
+                               if ((access_mask & DELETE_ACCESS) &&
+                                   (access_granted & DELETE_ACCESS) &&
+                                   can_delete_file_in_directory(conn,
+                                       smb_fname)) {
+                                       /* 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: "
-                                                 "smbd_check_open_rights on file "
-                                                 "%s returned %s\n",
-                                                 smb_fname_str_dbg(smb_fname),
-                                                 nt_errstr(status) ));
+                                                 "overrode "
+                                                 "DELETE_ACCESS on "
+                                                 "file %s\n",
+                                                 smb_fname_str_dbg(
+                                                         smb_fname)));
+                               }
+
+                               if (access_granted != 0) {
+                                       DEBUG(10,("open_file: Access "
+                                                 "denied on file "
+                                                 "%s\n",
+                                                 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)) {
+                               /* 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 {
+                               DEBUG(10,("open_file: "
+                                         "smbd_check_open_rights on file "
+                                         "%s returned %s\n",
+                                         smb_fname_str_dbg(smb_fname),
+                                         nt_errstr(status) ));
+                               return status;
                        }
                }
        }
@@ -2437,13 +2497,14 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                               uint32 file_attributes)
 {
        mode_t mode;
-       char *parent_dir;
+       char *parent_dir = NULL;
        NTSTATUS status;
        bool posix_open = false;
        bool need_re_stat = false;
+       uint32_t access_mask = SEC_DIR_ADD_SUBDIR;
 
-       if(!CAN_WRITE(conn)) {
-               DEBUG(5,("mkdir_internal: failing create on read-only share "
+       if(access_mask & ~(conn->share_access)) {
+               DEBUG(5,("mkdir_internal: failing share access "
                         "%s\n", lp_servicename(SNUM(conn))));
                return NT_STATUS_ACCESS_DENIED;
        }
@@ -2465,6 +2526,20 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                mode = unix_mode(conn, FILE_ATTRIBUTE_DIRECTORY, smb_dname, parent_dir);
        }
 
+       status = check_parent_access(conn,
+                                       smb_dname,
+                                       access_mask,
+                                       &parent_dir,
+                                       NULL);
+       if(!NT_STATUS_IS_OK(status)) {
+               DEBUG(5,("mkdir_internal: check_parent_access "
+                       "on directory %s for path %s returned %s\n",
+                       parent_dir,
+                       smb_dname->base_name,
+                       nt_errstr(status) ));
+               return status;
+       }
+
        if (SMB_VFS_MKDIR(conn, smb_dname->base_name, mode) != 0) {
                return map_nt_error_from_unix(errno);
        }
@@ -2479,9 +2554,9 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        }
 
        if (!S_ISDIR(smb_dname->st.st_ex_mode)) {
-               DEBUG(0, ("Directory just '%s' created is not a directory\n",
+               DEBUG(0, ("Directory '%s' just created is not a directory !\n",
                          smb_fname_str_dbg(smb_dname)));
-               return NT_STATUS_ACCESS_DENIED;
+               return NT_STATUS_NOT_A_DIRECTORY;
        }
 
        if (lp_store_dos_attributes(SNUM(conn))) {
@@ -2631,6 +2706,15 @@ static NTSTATUS open_directory(connection_struct *conn,
                        /* If directory exists error. If directory doesn't
                         * exist create. */
 
+                       if (dir_existed) {
+                               status = NT_STATUS_OBJECT_NAME_COLLISION;
+                               DEBUG(2, ("open_directory: unable to create "
+                                         "%s. Error was %s\n",
+                                         smb_fname_str_dbg(smb_dname),
+                                         nt_errstr(status)));
+                               return status;
+                       }
+
                        status = mkdir_internal(conn, smb_dname,
                                                file_attributes);
 
@@ -2651,18 +2735,29 @@ static NTSTATUS open_directory(connection_struct *conn,
                         * exist create.
                         */
 
-                       status = mkdir_internal(conn, smb_dname,
+                       if (dir_existed) {
+                               status = NT_STATUS_OK;
+                               info = FILE_WAS_OPENED;
+                       } else {
+                               status = mkdir_internal(conn, smb_dname,
                                                file_attributes);
 
-                       if (NT_STATUS_IS_OK(status)) {
-                               info = FILE_WAS_CREATED;
+                               if (NT_STATUS_IS_OK(status)) {
+                                       info = FILE_WAS_CREATED;
+                               } else {
+                                       /* Cope with create race. */
+                                       if (!NT_STATUS_EQUAL(status,
+                                                       NT_STATUS_OBJECT_NAME_COLLISION)) {
+                                               DEBUG(2, ("open_directory: unable to create "
+                                                       "%s. Error was %s\n",
+                                                       smb_fname_str_dbg(smb_dname),
+                                                       nt_errstr(status)));
+                                               return status;
+                                       }
+                                       info = FILE_WAS_OPENED;
+                               }
                        }
 
-                       if (NT_STATUS_EQUAL(status,
-                                           NT_STATUS_OBJECT_NAME_COLLISION)) {
-                               info = FILE_WAS_OPENED;
-                               status = NT_STATUS_OK;
-                       }
                        break;
 
                case FILE_SUPERSEDE:
@@ -2972,8 +3067,8 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        TALLOC_CTX *frame = talloc_stackframe();
        NTSTATUS status;
 
-       status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
-                                   &num_streams, &stream_info);
+       status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
+                               &num_streams, &stream_info);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)
            || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
@@ -2983,7 +3078,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        }
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+               DEBUG(10, ("vfs_streaminfo failed: %s\n",
                           nt_errstr(status)));
                goto fail;
        }