s3/smbd: fix handling of delete-on-close on directories
authorRalph Boehme <slow@samba.org>
Thu, 11 Jan 2018 16:52:06 +0000 (17:52 +0100)
committerRalph Boehme <slow@samba.org>
Sat, 3 Feb 2018 22:42:16 +0000 (23:42 +0100)
This implements a check to test the delete-on-close flag of a directory
for requests to create files in this directory.

Windows server implement this check, Samba doesn't as it has performance
implications.

This commit implements the check and a new option to control it. By
default the check is skipped, setting "check parent directory delete on
close = yes" enables it.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Ralph Böhme <slow@samba.org>
Autobuild-Date(master): Sat Feb  3 23:42:16 CET 2018 on sn-devel-144

docs-xml/smbdotconf/tuning/checkparentdirectorydeleteonclose.xml [new file with mode: 0644]
lib/param/loadparm.c
selftest/knownfail.d/samba3.base.delete [deleted file]
selftest/target/Samba3.pm
selftest/target/Samba4.pm
source3/param/loadparm.c
source3/smbd/open.c

diff --git a/docs-xml/smbdotconf/tuning/checkparentdirectorydeleteonclose.xml b/docs-xml/smbdotconf/tuning/checkparentdirectorydeleteonclose.xml
new file mode 100644 (file)
index 0000000..1de0609
--- /dev/null
@@ -0,0 +1,13 @@
+<samba:parameter name="check parent directory delete on close"
+                 context="S"
+                type="boolean"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+    <para>A Windows SMB server prevents the client from creating files in a
+    directory that has the delete-on-close flag set. By default Samba doesn't
+    perform this check as this check is a quite expensive operation in Samba.
+    </para>
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
index 7854f57a158243c1244e73235636110ed80407b8..efad4a10f032369cff456e84f9dd0f56a37785dd 100644 (file)
@@ -2998,6 +2998,8 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
 
        lpcfg_do_global_parameter(lp_ctx, "prefork children", "1");
 
+       lpcfg_do_global_parameter(lp_ctx, "check parent directory delete on close", "no");
+
        for (i = 0; parm_table[i].label; i++) {
                if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
                        lp_ctx->flags[i] |= FLAG_DEFAULT;
diff --git a/selftest/knownfail.d/samba3.base.delete b/selftest/knownfail.d/samba3.base.delete
deleted file mode 100644 (file)
index 6129ca7..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-samba3.base.delete.deltest20\(nt4_dc\)
-samba3.base.delete.deltest20\(ad_dc\)
index f2dcdd1489bac2ff4130f29013f24e0bf2017be9..ee7c3be9a9d04a4402b849bcd303a9e966e3457a 100755 (executable)
@@ -231,6 +231,7 @@ sub setup_nt4_dc($$)
        rpc_daemon:lsasd = fork
        rpc_daemon:fssd = fork
        fss: sequence timeout = 1
+       check parent directory delete on close = yes
 ";
 
        my $vars = $self->provision($path, "SAMBA-TEST",
index c161ee082a05b32c05f67da206cad04c6c5855cf..123bf6c3a91e7de3e7f1514007e93765db767eaf 100755 (executable)
@@ -1815,6 +1815,7 @@ sub provision_ad_dc($$$$$$)
        smbd:writetimeupdatedelay = 500000
        create mask = 755
        dos filemode = yes
+       check parent directory delete on close = yes
 
         dcerpc endpoint servers = -winreg -srvsvc
 
index 096c23f4fb3976cbf4bbb8730ab5967cca56e8ec..b1b502c117e47013aab437dc9038889eb21642a6 100644 (file)
@@ -244,6 +244,7 @@ static const struct loadparm_service _sDefault =
        .smb_encrypt = SMB_SIGNING_DEFAULT,
        .kernel_share_modes = true,
        .durable_handles = true,
+       .check_parent_directory_delete_on_close = false,
        .param_opt = NULL,
        .dummy = ""
 };
index e55c394109204ba1147a2fc116954fffbd324725..5817bdbe9ae238406aec80a4a8cdaacfd6e94fc1 100644 (file)
@@ -253,6 +253,11 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        struct security_descriptor *parent_sd = NULL;
        uint32_t access_granted = 0;
        struct smb_filename *parent_smb_fname = NULL;
+       struct share_mode_lock *lck = NULL;
+       struct file_id id = {0};
+       uint32_t name_hash;
+       bool delete_on_close_set;
+       int ret;
 
        if (!parent_dirname(talloc_tos(),
                                smb_fname->base_name,
@@ -320,7 +325,45 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                return status;
        }
 
-       return NT_STATUS_OK;
+       if (!(access_mask & (SEC_DIR_ADD_FILE | SEC_DIR_ADD_SUBDIR))) {
+               return NT_STATUS_OK;
+       }
+       if (!lp_check_parent_directory_delete_on_close(SNUM(conn))) {
+               return NT_STATUS_OK;
+       }
+
+       /* Check if the directory has delete-on-close set */
+       ret = SMB_VFS_STAT(conn, parent_smb_fname);
+       if (ret != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto out;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(conn, &parent_smb_fname->st);
+
+       status = file_name_hash(conn, parent_smb_fname->base_name, &name_hash);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       lck = get_existing_share_mode_lock(talloc_tos(), id);
+       if (lck == NULL) {
+               status = NT_STATUS_OK;
+               goto out;
+       }
+
+       delete_on_close_set = is_delete_on_close_set(lck, name_hash);
+       if (delete_on_close_set) {
+               status = NT_STATUS_DELETE_PENDING;
+               goto out;
+       }
+
+       status = NT_STATUS_OK;
+
+out:
+       TALLOC_FREE(lck);
+       TALLOC_FREE(parent_smb_fname);
+       return status;
 }
 
 /****************************************************************************