diff options
author | Jeremy Allison <jra@samba.org> | 2010-01-12 16:04:44 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2010-01-12 16:04:44 -0800 |
commit | 47c1d9b39f292772e8d8f7a737ddff6c8bdfdeae (patch) | |
tree | a032c194ae17b0634df31def0b125fc4db894f2e | |
parent | 2f30aea3324f32f9b8555e961256fc1280da2871 (diff) | |
download | samba-47c1d9b39f292772e8d8f7a737ddff6c8bdfdeae.tar.gz samba-47c1d9b39f292772e8d8f7a737ddff6c8bdfdeae.tar.bz2 samba-47c1d9b39f292772e8d8f7a737ddff6c8bdfdeae.zip |
Fix bug #6876 - Delete of an object whose parent folder does not have delete rights fails even if the delete right is set on the object.
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.
-rw-r--r-- | source3/include/smb.h | 1 | ||||
-rw-r--r-- | source3/locking/locking.c | 3 | ||||
-rw-r--r-- | source3/modules/vfs_acl_common.c | 127 | ||||
-rw-r--r-- | source3/modules/vfs_acl_tdb.c | 3 | ||||
-rw-r--r-- | source3/modules/vfs_acl_xattr.c | 2 | ||||
-rw-r--r-- | source3/smbd/close.c | 3 | ||||
-rw-r--r-- | source3/smbd/posix_acls.c | 7 |
7 files changed, 142 insertions, 4 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h index b23ea647ec..bc7a90d549 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -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; diff --git a/source3/locking/locking.c b/source3/locking/locking.c index 26018f90db..095d0b17b9 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -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; } diff --git a/source3/modules/vfs_acl_common.c b/source3/modules/vfs_acl_common.c index 1eec448083..aeb9ce37ea 100644 --- a/source3/modules/vfs_acl_common.c +++ b/source3/modules/vfs_acl_common.c @@ -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; +} diff --git a/source3/modules/vfs_acl_tdb.c b/source3/modules/vfs_acl_tdb.c index a1088ab63c..2afe69d764 100644 --- a/source3/modules/vfs_acl_tdb.c +++ b/source3/modules/vfs_acl_tdb.c @@ -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, diff --git a/source3/modules/vfs_acl_xattr.c b/source3/modules/vfs_acl_xattr.c index 625ef91e8f..18f2d42784 100644 --- a/source3/modules/vfs_acl_xattr.c +++ b/source3/modules/vfs_acl_xattr.c @@ -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, diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 05c3c709a1..e81a2fdff6 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -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, ¤t_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, ¤t_user.ut); + fsp->delete_on_close = true; if (became_user) { unbecome_user(); } diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index 8d66bf1059..7342420a89 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -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", |