Second part of fix for #6154, ensure we return max access
[ira/wip.git] / source3 / smbd / open.c
index f00361979d5ffa20921053b40f8b53735f427307..acd347520d1952d447a7fe2237b18d8486a59de6 100644 (file)
@@ -20,9 +20,9 @@
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
 
 extern const struct generic_mapping file_generic_mapping;
-extern bool global_client_failed_oplock_break;
 
 struct deferred_open_record {
        bool delayed_for_oplocks;
@@ -67,13 +67,24 @@ NTSTATUS smb1_file_se_access_check(const struct security_descriptor *sd,
 
 static NTSTATUS check_open_rights(struct connection_struct *conn,
                                const char *fname,
-                               uint32_t access_mask)
+                               uint32_t access_mask,
+                               uint32_t *access_granted)
 {
        /* Check if we have rights to open. */
        NTSTATUS status;
-       uint32_t access_granted = 0;
        struct security_descriptor *sd;
 
+       *access_granted = 0;
+
+       if (conn->server_info->utok.uid == 0 || conn->admin_user) {
+               /* I'm sorry sir, I didn't know you were root... */
+               *access_granted = access_mask;
+               if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+                       *access_granted |= FILE_GENERIC_ALL;
+               }
+               return NT_STATUS_OK;
+       }
+
        status = SMB_VFS_GET_NT_ACL(conn, fname,
                        (OWNER_SECURITY_INFORMATION |
                        GROUP_SECURITY_INFORMATION |
@@ -90,9 +101,17 @@ static NTSTATUS check_open_rights(struct connection_struct *conn,
        status = smb1_file_se_access_check(sd,
                                conn->server_info->ptok,
                                access_mask,
-                               &access_granted);
+                               access_granted);
 
        TALLOC_FREE(sd);
+
+       DEBUG(10,("check_open_rights: file %s requesting "
+               "0x%x returning 0x%x (%s)\n",
+               fname,
+               (unsigned int)access_mask,
+               (unsigned int)*access_granted,
+               nt_errstr(status) ));
+
        return status;
 }
 
@@ -122,6 +141,18 @@ static NTSTATUS fd_open(struct connection_struct *conn,
        fsp->fh->fd = SMB_VFS_OPEN(conn,fname,fsp,flags,mode);
        if (fsp->fh->fd == -1) {
                status = map_nt_error_from_unix(errno);
+               if (errno == EMFILE) {
+                       static time_t last_warned = 0L;
+
+                       if (time((time_t *) NULL) > last_warned) {
+                               DEBUG(0,("Too many open files, unable "
+                                       "to open more!  smbd's max "
+                                       "open files = %d\n",
+                                       lp_max_open_files()));
+                               last_warned = time((time_t *) NULL);
+                       }
+               }
+
        }
 
        DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
@@ -415,14 +446,49 @@ static NTSTATUS open_file(files_struct *fsp,
        } else {
                fsp->fh->fd = -1; /* What we used to call a stat open. */
                if (file_existed) {
+                       uint32_t access_granted = 0;
+
                        status = check_open_rights(conn,
                                        path,
-                                       access_mask);
+                                       access_mask,
+                                       &access_granted);
                        if (!NT_STATUS_IS_OK(status)) {
-                               DEBUG(10, ("open_file: Access denied on "
-                                       "file %s\n",
-                                       path));
-                               return status;
+                               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+                                       if ((access_mask & DELETE_ACCESS) &&
+                                                       (access_granted == DELETE_ACCESS) &&
+                                                       can_delete_file_in_directory(conn, path)) {
+                                               /* 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. */
+
+                                               DEBUG(10,("open_file: overrode ACCESS_DENIED "
+                                                       "on file %s\n",
+                                                       path ));
+                                       } else {
+                                               DEBUG(10, ("open_file: Access denied on "
+                                                       "file %s\n",
+                                                       path));
+                                               return status;
+                                       }
+                               } else if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
+                                                       fsp->posix_open &&
+                                                       S_ISLNK(psbuf->st_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",
+                                               path ));
+                               } else {
+                                       DEBUG(10, ("open_file: check_open_rights "
+                                               "on file %s returned %s\n",
+                                               path, nt_errstr(status) ));
+                                       return status;
+                               }
                        }
                }
        }
@@ -796,7 +862,6 @@ static bool delay_for_oplocks(struct share_mode_lock *lck,
                              int pass_number,
                              int oplock_request)
 {
-       extern uint32 global_client_caps;
        int i;
        struct share_mode_entry *exclusive = NULL;
        bool valid_entry = false;
@@ -1331,6 +1396,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        bool def_acl = False;
        bool posix_open = False;
        bool new_file_created = False;
+       bool clear_ads = false;
        struct file_id id;
        NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
        mode_t new_unx_mode = (mode_t)0;
@@ -1360,11 +1426,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                DEBUG(10, ("open_file_ntcreate: printer open fname=%s\n", fname));
 
-               return print_fsp_open(req, conn, fname, req->vuid, fsp);
+               return print_fsp_open(req, conn, fname, req->vuid, fsp, psbuf);
        }
 
-       if (!parent_dirname_talloc(talloc_tos(), fname, &parent_dir,
-                                  &newname)) {
+       if (!parent_dirname(talloc_tos(), fname, &parent_dir, &newname)) {
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -1384,7 +1449,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                   "create_disposition = 0x%x create_options=0x%x "
                   "unix mode=0%o oplock_request=%d\n",
                   fname, new_dos_attributes, access_mask, share_access,
-                  create_disposition, create_options, unx_mode,
+                  create_disposition, create_options, (unsigned int)unx_mode,
                   oplock_request));
 
        if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
@@ -1463,12 +1528,14 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        /* If file exists replace/overwrite. If file doesn't
                         * exist create. */
                        flags2 |= (O_CREAT | O_TRUNC);
+                       clear_ads = true;
                        break;
 
                case FILE_OVERWRITE_IF:
                        /* If file exists replace/overwrite. If file doesn't
                         * exist create. */
                        flags2 |= (O_CREAT | O_TRUNC);
+                       clear_ads = true;
                        break;
 
                case FILE_OPEN:
@@ -1493,6 +1560,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                        }
                        flags2 |= O_TRUNC;
+                       clear_ads = true;
                        break;
 
                case FILE_CREATE:
@@ -1927,6 +1995,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        SMB_ASSERT(lck != NULL);
 
+       /* Delete streams if create_disposition requires it */
+       if (file_existed && clear_ads && !is_ntfs_stream_name(fname)) {
+               status = delete_all_streams(conn, fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(lck);
+                       fd_close(fsp);
+                       return status;
+               }
+       }
+
        /* note that we ignore failure for the following. It is
            basically a hack for NFS, and NFS will never set one of
            these only read them. Nobody but Samba can ever set a deny
@@ -2190,8 +2268,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                return status;
        }
 
-       if (!parent_dirname_talloc(talloc_tos(), name, &parent_dir,
-                                  &dirname)) {
+       if (!parent_dirname(talloc_tos(), name, &parent_dir, &dirname)) {
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -2384,9 +2461,30 @@ static NTSTATUS open_directory(connection_struct *conn,
        }
 
        if (info == FILE_WAS_OPENED) {
+               uint32_t access_granted = 0;
                status = check_open_rights(conn,
                                        fname,
-                                       access_mask);
+                                       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) &&
+                               (access_mask & DELETE_ACCESS) &&
+                               (access_granted == DELETE_ACCESS) &&
+                               can_delete_file_in_directory(conn, fname))) {
+                       DEBUG(10,("open_directory: overrode ACCESS_DENIED "
+                               "on directory %s\n",
+                               fname ));
+                       status = NT_STATUS_OK;
+               }
+
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("open_directory: check_open_rights on "
                                "file %s failed with %s\n",
@@ -2539,8 +2637,8 @@ void msg_file_was_renamed(struct messaging_context *msg,
         }
 
        /* Unpack the message. */
-       pull_file_id_16(frm, &id);
-       sharepath = &frm[16];
+       pull_file_id_24(frm, &id);
+       sharepath = &frm[24];
        newname = sharepath + strlen(sharepath) + 1;
        sp_len = strlen(sharepath);
 
@@ -2815,8 +2913,11 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
            && (create_disposition != FILE_CREATE)
            && (share_access & FILE_SHARE_DELETE)
            && (access_mask & DELETE_ACCESS)
-           && (!can_delete_file_in_directory(conn, fname))) {
+           && (!(can_delete_file_in_directory(conn, fname) ||
+                can_access_file_acl(conn, fname, DELETE_ACCESS)))) {
                status = NT_STATUS_ACCESS_DENIED;
+               DEBUG(10,("create_file_unixpath: open file %s "
+                       "for delete ACCESS_DENIED\n", fname ));
                goto fail;
        }