Fix bug #6876 - Delete of an object whose parent folder does not have delete rights...
authorJeremy Allison <jra@samba.org>
Wed, 13 Jan 2010 00:04:44 +0000 (16:04 -0800)
committerJeremy Allison <jra@samba.org>
Wed, 13 Jan 2010 00:04:44 +0000 (16:04 -0800)
Final fix for the vfs_acl_xattr and vfs_acl_tdb code.
Ensure we can delete a file even if the underlying POSIX
permissions don't allow it, if the Windows permissions do.

Jeremy.

source3/include/smb.h
source3/locking/locking.c
source3/modules/vfs_acl_common.c
source3/modules/vfs_acl_tdb.c
source3/modules/vfs_acl_xattr.c
source3/smbd/close.c
source3/smbd/posix_acls.c

index b23ea647ecd68551fd51e7db92d20863a2348c4c..bc7a90d5492911bcf418e894dbd81a24b35c69de 100644 (file)
@@ -451,6 +451,7 @@ typedef struct files_struct {
        bool aio_write_behind;
        bool lockdb_clean;
        bool initial_delete_on_close; /* Only set at NTCreateX if file was created. */
+       bool delete_on_close;
        bool posix_open;
        struct smb_filename *fsp_name;
 
index 26018f90db9a1d8f733b1dcc5e995c36d60f527c..095d0b17b9d388ed359efdcedcd2655788d9af9a 100644 (file)
@@ -1459,6 +1459,9 @@ bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const UNIX_USE
        }
 
        TALLOC_FREE(lck);
+
+       fsp->delete_on_close = delete_on_close;
+
        return True;
 }
 
index 1eec44808377621c41637d27be91112fab634fec..aeb9ce37eaee4aea718470dd80add9f261f27e8a 100644 (file)
@@ -760,6 +760,108 @@ static SMB_STRUCT_DIR *opendir_acl_common(vfs_handle_struct *handle,
        return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
 }
 
+static int acl_common_remove_object(vfs_handle_struct *handle,
+                                       const char *path,
+                                       bool is_directory)
+{
+       connection_struct *conn = handle->conn;
+       struct file_id id;
+       files_struct *fsp = NULL;
+       int ret = 0;
+       char *parent_dir = NULL;
+       const char *final_component = NULL;
+       struct smb_filename local_fname;
+       int saved_errno = 0;
+
+       if (!parent_dirname(talloc_tos(), path,
+                       &parent_dir, &final_component)) {
+               saved_errno = ENOMEM;
+               goto out;
+       }
+
+       DEBUG(10,("acl_common_remove_object: removing %s %s/%s\n",
+               is_directory ? "directory" : "file",
+               parent_dir, final_component ));
+
+       /* cd into the parent dir to pin it. */
+       ret = SMB_VFS_CHDIR(conn, parent_dir);
+       if (ret == -1) {
+               saved_errno = errno;
+               goto out;
+       }
+
+       ZERO_STRUCT(local_fname);
+       local_fname.base_name = CONST_DISCARD(char *,final_component);
+
+       /* Must use lstat here. */
+       ret = SMB_VFS_LSTAT(conn, &local_fname);
+       if (ret == -1) {
+               saved_errno = errno;
+               goto out;
+       }
+
+       /* Ensure we have this file open with DELETE access. */
+       id = vfs_file_id_from_sbuf(conn, &local_fname.st);
+       for (fsp = file_find_di_first(id); fsp; file_find_di_next(fsp)) {
+               if (fsp->access_mask & DELETE_ACCESS &&
+                               fsp->delete_on_close) {
+                       /* We did open this for delete,
+                        * allow the delete as root.
+                        */
+                       break;
+               }
+       }
+
+       if (!fsp) {
+               DEBUG(10,("acl_common_remove_object: %s %s/%s "
+                       "not an open file\n",
+                       is_directory ? "directory" : "file",
+                       parent_dir, final_component ));
+               saved_errno = EACCES;
+               goto out;
+       }
+
+       if (is_directory) {
+               ret = SMB_VFS_NEXT_RMDIR(handle, final_component);
+       } else {
+               ret = SMB_VFS_NEXT_UNLINK(handle, &local_fname);
+       }
+       if (ret == -1) {
+               saved_errno = errno;
+       }
+
+  out:
+
+       TALLOC_FREE(parent_dir);
+
+       vfs_ChDir(conn, conn->connectpath);
+       if (saved_errno) {
+               errno = saved_errno;
+       }
+       return ret;
+}
+
+static int rmdir_acl_common(struct vfs_handle_struct *handle,
+                               const char *path)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_RMDIR(handle, path);
+       if (!(ret == -1 && (errno == EACCES || errno == EPERM))) {
+               DEBUG(10,("rmdir_acl_common: unlink of %s failed %s\n",
+                       path,
+                       strerror(errno) ));
+               return ret;
+       }
+
+       become_root();
+       ret = acl_common_remove_object(handle,
+                                       path,
+                                       true);
+       unbecome_root();
+       return ret;
+}
+
 static NTSTATUS create_file_acl_common(struct vfs_handle_struct *handle,
                                struct smb_request *req,
                                uint16_t root_dir_fid,
@@ -857,3 +959,28 @@ static NTSTATUS create_file_acl_common(struct vfs_handle_struct *handle,
        /* NOTREACHED */
        return status;
 }
+
+static int unlink_acl_common(struct vfs_handle_struct *handle,
+                       const struct smb_filename *smb_fname)
+{
+       int ret;
+
+       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       if (!(ret == -1 && (errno == EACCES || errno == EPERM))) {
+               DEBUG(10,("unlink_acl_common: unlink of %s failed %s\n",
+                       smb_fname->base_name,
+                       strerror(errno) ));
+               return ret;
+       }
+       /* Don't do anything fancy for streams. */
+       if (smb_fname->stream_name) {
+               return ret;
+       }
+
+       become_root();
+       ret = acl_common_remove_object(handle,
+                                       smb_fname->base_name,
+                                       false);
+       unbecome_root();
+       return ret;
+}
index a1088ab63cf4471e72e7cb321d845421dc180474..2afe69d764581ba37747af0eadf311c7dfafa271 100644 (file)
@@ -265,7 +265,7 @@ static int unlink_acl_tdb(vfs_handle_struct *handle,
                goto out;
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
+       ret = unlink_acl_common(handle, smb_fname_tmp);
 
        if (ret == -1) {
                goto out;
@@ -413,6 +413,7 @@ static struct vfs_fn_pointers vfs_acl_tdb_fns = {
        .connect_fn = connect_acl_tdb,
        .opendir = opendir_acl_common,
        .mkdir = mkdir_acl_common,
+       .rmdir = rmdir_acl_common,
        .open = open_acl_common,
        .create_file = create_file_acl_common,
        .unlink = unlink_acl_tdb,
index 625ef91e8fdc4d125037115b94a48dd5fe3567a1..18f2d42784b6bf2f9de7b7f815e0e865439a4327 100644 (file)
@@ -199,8 +199,10 @@ static struct vfs_fn_pointers vfs_acl_xattr_fns = {
        .connect_fn = connect_acl_xattr,
        .opendir = opendir_acl_common,
        .mkdir = mkdir_acl_common,
+       .rmdir = rmdir_acl_common,
        .open = open_acl_common,
        .create_file = create_file_acl_common,
+       .unlink = unlink_acl_common,
        .fget_nt_acl = fget_nt_acl_common,
        .get_nt_acl = get_nt_acl_common,
        .fset_nt_acl = fset_nt_acl_common,
index 05c3c709a1e7a31611d48ee9438dc843c08e1dad..e81a2fdff6369bc7841fb69615fb48c637f87290 100644 (file)
@@ -336,6 +336,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                        become_user(conn, fsp->vuid);
                        became_user = True;
                }
+               fsp->delete_on_close = true;
                set_delete_on_close_lck(lck, True, &current_user.ut);
                if (became_user) {
                        unbecome_user();
@@ -481,6 +482,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         * the delete on close flag. JRA.
         */
 
+       fsp->delete_on_close = false;
        set_delete_on_close_lck(lck, False, NULL);
 
  done:
@@ -958,6 +960,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
                }
                send_stat_cache_delete_message(fsp->fsp_name->base_name);
                set_delete_on_close_lck(lck, True, &current_user.ut);
+               fsp->delete_on_close = true;
                if (became_user) {
                        unbecome_user();
                }
index 8d66bf105948c3b8207f4b5a3d9e4edb404b9e6c..7342420a8998c3a7a64b5239926e225be18b451b 100644 (file)
@@ -1107,9 +1107,10 @@ uint32_t map_canon_ace_perms(int snum,
                        nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
                        nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
                }
-               if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
-                       nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER);
-               }
+       }
+
+       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",