summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2010-01-12 16:04:44 -0800
committerJeremy Allison <jra@samba.org>2010-01-12 16:04:44 -0800
commit47c1d9b39f292772e8d8f7a737ddff6c8bdfdeae (patch)
treea032c194ae17b0634df31def0b125fc4db894f2e
parent2f30aea3324f32f9b8555e961256fc1280da2871 (diff)
downloadsamba-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.h1
-rw-r--r--source3/locking/locking.c3
-rw-r--r--source3/modules/vfs_acl_common.c127
-rw-r--r--source3/modules/vfs_acl_tdb.c3
-rw-r--r--source3/modules/vfs_acl_xattr.c2
-rw-r--r--source3/smbd/close.c3
-rw-r--r--source3/smbd/posix_acls.c7
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, &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();
}
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",