summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--VERSION2
-rw-r--r--ctdb/server/ctdb_recoverd.c1
-rw-r--r--selftest/knownfail_mit_krb5_pre_1_18 (renamed from selftest/knownfail.d/kinit_mit)0
-rwxr-xr-xselftest/selftest.pl1
-rw-r--r--selftest/skip2
-rw-r--r--selftest/wscript3
-rwxr-xr-xsource3/libads/ldap.c1
-rw-r--r--source3/librpc/crypto/gse.c4
-rw-r--r--source3/modules/offload_token.c10
-rw-r--r--source3/modules/test_vfs_full_audit.c49
-rw-r--r--source3/modules/vfs_aio_pthread.c109
-rw-r--r--source3/modules/vfs_full_audit.c1
-rw-r--r--source3/modules/wscript_build5
-rwxr-xr-xsource3/script/tests/test_force_close_share.sh100
-rwxr-xr-xsource3/selftest/tests.py13
-rw-r--r--source3/smbd/aio.c13
-rw-r--r--source3/smbd/close.c80
-rw-r--r--source3/smbd/conn_idle.c179
-rw-r--r--source3/smbd/fileio.c17
-rw-r--r--source3/smbd/files.c31
-rw-r--r--source3/smbd/proto.h2
-rw-r--r--source3/smbd/reply.c650
-rw-r--r--source3/smbd/service.c67
-rw-r--r--source3/smbd/smb2_flush.c7
-rw-r--r--source3/smbd/trans2.c32
-rw-r--r--source4/kdc/mit-kdb/kdb_samba.c2
-rw-r--r--source4/kdc/mit-kdb/kdb_samba.h21
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_policies.c40
-rw-r--r--source4/torture/smb2/smb2.c1
-rw-r--r--source4/torture/smb2/timestamps.c775
-rwxr-xr-xtestprogs/blackbox/test_kinit_mit.sh4
-rwxr-xr-xtestprogs/blackbox/test_net_ads.sh17
-rw-r--r--wscript_configure_system_mitkrb53
33 files changed, 1991 insertions, 251 deletions
diff --git a/VERSION b/VERSION
index a8cdfb7be6b..e651d0ad94b 100644
--- a/VERSION
+++ b/VERSION
@@ -99,7 +99,7 @@ SAMBA_VERSION_RC_RELEASE=
# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
# -> "3.0.0-SVN-build-199" #
########################################################
-SAMBA_VERSION_IS_GIT_SNAPSHOT=no
+SAMBA_VERSION_IS_GIT_SNAPSHOT=yes
########################################################
# This is for specifying a release nickname #
diff --git a/ctdb/server/ctdb_recoverd.c b/ctdb/server/ctdb_recoverd.c
index ee083e92fb1..3f5d43c1e87 100644
--- a/ctdb/server/ctdb_recoverd.c
+++ b/ctdb/server/ctdb_recoverd.c
@@ -2078,6 +2078,7 @@ static int verify_local_ip_allocation(struct ctdb_context *ctdb,
/* Return early if disabled... */
if (ctdb_config.failover_disabled ||
ctdb_op_is_disabled(rec->takeover_run)) {
+ talloc_free(mem_ctx);
return 0;
}
diff --git a/selftest/knownfail.d/kinit_mit b/selftest/knownfail_mit_krb5_pre_1_18
index ef1a3d5aa91..ef1a3d5aa91 100644
--- a/selftest/knownfail.d/kinit_mit
+++ b/selftest/knownfail_mit_krb5_pre_1_18
diff --git a/selftest/selftest.pl b/selftest/selftest.pl
index b6094fef3b9..b166b28e0cb 100755
--- a/selftest/selftest.pl
+++ b/selftest/selftest.pl
@@ -451,6 +451,7 @@ my $testenv_default = "none";
if ($opt_mitkrb5 == 1) {
$ENV{MITKRB5} = $opt_mitkrb5;
+ $ENV{KRB5RCACHETYPE} = "none";
}
# After this many seconds, the server will self-terminate. All tests
diff --git a/selftest/skip b/selftest/skip
index 11bf29599fa..549ba202021 100644
--- a/selftest/skip
+++ b/selftest/skip
@@ -75,6 +75,8 @@
^samba3.smb2.durable-open-disconnect # Not a test, but a way to create a disconnected durable
^samba3.smb2.scan # No tests
^samba3.smb2.oplock.levelii501 # No test yet
+^samba3.smb2.timestamp_resolution # See the comment on the test
+^samba4.smb2.timestamp_resolution
^samba3.rpc.samr.passwords.lockout\(ad_dc\) # No point running this version, it just waits 12 times longer the samba4 version of this test, covering the same code
^samba4.base.iometer
^samba4.base.casetable
diff --git a/selftest/wscript b/selftest/wscript
index 4d03eb76842..501a5df5824 100644
--- a/selftest/wscript
+++ b/selftest/wscript
@@ -142,6 +142,9 @@ def cmd_testonly(opt):
'--flapping=${srcdir}/selftest/flapping '
'--flapping=${srcdir}/selftest/flapping.d')
+ if CONFIG_GET(opt, 'HAVE_MIT_KRB5_PRE_1_18'):
+ env.FILTER_XFAIL += ' --expected-failures=${srcdir}/selftest/knownfail_mit_krb5_pre_1_18'
+
if Options.options.FAIL_IMMEDIATELY:
env.FILTER_XFAIL += ' --fail-immediately'
diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c
index a630c5a0345..f0fcf9fcd56 100755
--- a/source3/libads/ldap.c
+++ b/source3/libads/ldap.c
@@ -1373,6 +1373,7 @@ char *ads_parent_dn(const char *dn)
"userAccountControl",
"DnsHostName",
"ServicePrincipalName",
+ "userPrincipalName",
"unicodePwd",
/* Additional attributes Samba checks */
diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 6675f4dc597..1cf111bd974 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -244,10 +244,6 @@ static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
err_out:
- if (gse_ctx->k5ctx) {
- krb5_free_context(gse_ctx->k5ctx);
- }
-
TALLOC_FREE(gse_ctx);
return status;
}
diff --git a/source3/modules/offload_token.c b/source3/modules/offload_token.c
index 3fb84dabdff..03bb3309f38 100644
--- a/source3/modules/offload_token.c
+++ b/source3/modules/offload_token.c
@@ -280,6 +280,16 @@ NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
return NT_STATUS_ACCESS_DENIED;
}
+ if (src_fsp->closing) {
+ DBG_INFO("copy chunk src handle with closing in progress.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dst_fsp->closing) {
+ DBG_INFO("copy chunk dst handle with closing in progress.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
if (src_fsp->is_directory) {
DBG_INFO("copy chunk no read on src directory handle (%s).\n",
smb_fname_str_dbg(src_fsp->fsp_name));
diff --git a/source3/modules/test_vfs_full_audit.c b/source3/modules/test_vfs_full_audit.c
new file mode 100644
index 00000000000..4a12e466ff3
--- /dev/null
+++ b/source3/modules/test_vfs_full_audit.c
@@ -0,0 +1,49 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Unit test for entries in vfs_full_audit arrays.
+ *
+ * Copyright (C) Jeremy Allison 2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Needed for static build to complete... */
+#include "includes.h"
+#include "smbd/smbd.h"
+NTSTATUS vfs_full_audit_init(TALLOC_CTX *ctx);
+
+#include "vfs_full_audit.c"
+#include <cmocka.h>
+
+static void test_full_audit_array(void **state)
+{
+ unsigned i;
+
+ for (i=0; i<SMB_VFS_OP_LAST; i++) {
+ assert_non_null(vfs_op_names[i].name);
+ assert_int_equal(vfs_op_names[i].type, i);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_full_audit_array),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source3/modules/vfs_aio_pthread.c b/source3/modules/vfs_aio_pthread.c
index d13ce2fdc63..1ccf89a6d8c 100644
--- a/source3/modules/vfs_aio_pthread.c
+++ b/source3/modules/vfs_aio_pthread.c
@@ -51,6 +51,7 @@ struct aio_open_private_data {
const char *fname;
char *dname;
connection_struct *conn;
+ struct smbXsrv_connection *xconn;
const struct security_unix_token *ux_tok;
uint64_t initial_allocation_size;
/* Returns. */
@@ -62,6 +63,7 @@ struct aio_open_private_data {
static struct aio_open_private_data *open_pd_list;
static void aio_open_do(struct aio_open_private_data *opd);
+static void opd_free(struct aio_open_private_data *opd);
/************************************************************************
Find the open private data by mid.
@@ -90,10 +92,40 @@ static void aio_open_handle_completion(struct tevent_req *subreq)
tevent_req_callback_data(subreq,
struct aio_open_private_data);
int ret;
- struct smbXsrv_connection *xconn;
ret = pthreadpool_tevent_job_recv(subreq);
TALLOC_FREE(subreq);
+
+ /*
+ * We're no longer in flight. Remove the
+ * destructor used to preserve opd so
+ * a talloc_free actually removes it.
+ */
+ talloc_set_destructor(opd, NULL);
+
+ if (opd->conn == NULL) {
+ /*
+ * We were shutdown closed in flight. No one
+ * wants the result, and state has been reparented
+ * to the NULL context, so just free it so we
+ * don't leak memory.
+ */
+ DBG_NOTICE("aio open request for %s/%s abandoned in flight\n",
+ opd->dname,
+ opd->fname);
+ if (opd->ret_fd != -1) {
+ close(opd->ret_fd);
+ opd->ret_fd = -1;
+ }
+ /*
+ * Find outstanding event and reschedule so the client
+ * gets an error message return from the open.
+ */
+ schedule_deferred_open_message_smb(opd->xconn, opd->mid);
+ opd_free(opd);
+ return;
+ }
+
if (ret != 0) {
bool ok;
@@ -127,15 +159,8 @@ static void aio_open_handle_completion(struct tevent_req *subreq)
opd->in_progress = false;
- /*
- * TODO: In future we need a proper algorithm
- * to find the correct connection for a fsp.
- * For now we only have one connection, so this is correct...
- */
- xconn = opd->conn->sconn->client->connections;
-
/* Find outstanding event and reschedule. */
- if (!schedule_deferred_open_message_smb(xconn, opd->mid)) {
+ if (!schedule_deferred_open_message_smb(opd->xconn, opd->mid)) {
/*
* Outstanding event didn't exist or was
* cancelled. Free up the fd and throw
@@ -145,7 +170,7 @@ static void aio_open_handle_completion(struct tevent_req *subreq)
close(opd->ret_fd);
opd->ret_fd = -1;
}
- TALLOC_FREE(opd);
+ opd_free(opd);
}
}
@@ -207,27 +232,28 @@ static void aio_open_do(struct aio_open_private_data *opd)
}
/************************************************************************
- Open private data destructor.
+ Open private data teardown.
***********************************************************************/
-static int opd_destructor(struct aio_open_private_data *opd)
+static void opd_free(struct aio_open_private_data *opd)
{
if (opd->dir_fd != -1) {
close(opd->dir_fd);
}
DLIST_REMOVE(open_pd_list, opd);
- return 0;
+ TALLOC_FREE(opd);
}
/************************************************************************
Create and initialize a private data struct for async open.
***********************************************************************/
-static struct aio_open_private_data *create_private_open_data(const files_struct *fsp,
+static struct aio_open_private_data *create_private_open_data(TALLOC_CTX *ctx,
+ const files_struct *fsp,
int flags,
mode_t mode)
{
- struct aio_open_private_data *opd = talloc_zero(NULL,
+ struct aio_open_private_data *opd = talloc_zero(ctx,
struct aio_open_private_data);
const char *fname = NULL;
@@ -244,13 +270,19 @@ static struct aio_open_private_data *create_private_open_data(const files_struct
.mid = fsp->mid,
.in_progress = true,
.conn = fsp->conn,
+ /*
+ * TODO: In future we need a proper algorithm
+ * to find the correct connection for a fsp.
+ * For now we only have one connection, so this is correct...
+ */
+ .xconn = fsp->conn->sconn->client->connections,
.initial_allocation_size = fsp->initial_allocation_size,
};
/* Copy our current credentials. */
opd->ux_tok = copy_unix_token(opd, get_current_utok(fsp->conn));
if (opd->ux_tok == NULL) {
- TALLOC_FREE(opd);
+ opd_free(opd);
return NULL;
}
@@ -262,12 +294,12 @@ static struct aio_open_private_data *create_private_open_data(const files_struct
fsp->fsp_name->base_name,
&opd->dname,
&fname) == false) {
- TALLOC_FREE(opd);
+ opd_free(opd);
return NULL;
}
opd->fname = talloc_strdup(opd, fname);
if (opd->fname == NULL) {
- TALLOC_FREE(opd);
+ opd_free(opd);
return NULL;
}
@@ -277,15 +309,30 @@ static struct aio_open_private_data *create_private_open_data(const files_struct
opd->dir_fd = open(opd->dname, O_RDONLY);
#endif
if (opd->dir_fd == -1) {
- TALLOC_FREE(opd);
+ opd_free(opd);
return NULL;
}
- talloc_set_destructor(opd, opd_destructor);
DLIST_ADD_END(open_pd_list, opd);
return opd;
}
+static int opd_inflight_destructor(struct aio_open_private_data *opd)
+{
+ /*
+ * Setting conn to NULL allows us to
+ * discover the connection was torn
+ * down which kills the fsp that owns
+ * opd.
+ */
+ DBG_NOTICE("aio open request for %s/%s cancelled\n",
+ opd->dname,
+ opd->fname);
+ opd->conn = NULL;
+ /* Don't let opd go away. */
+ return -1;
+}
+
/*****************************************************************
Setup an async open.
*****************************************************************/
@@ -297,7 +344,18 @@ static int open_async(const files_struct *fsp,
struct aio_open_private_data *opd = NULL;
struct tevent_req *subreq = NULL;
- opd = create_private_open_data(fsp, flags, mode);
+ /*
+ * Allocate off fsp->conn, not NULL or fsp. As we're going
+ * async fsp will get talloc_free'd when we return
+ * EINPROGRESS/NT_STATUS_MORE_PROCESSING_REQUIRED. A new fsp
+ * pointer gets allocated on every re-run of the
+ * open code path. Allocating on fsp->conn instead
+ * of NULL allows use to get notified via destructor
+ * if the conn is force-closed or we shutdown.
+ * opd is always safely freed in all codepath so no
+ * memory leaks.
+ */
+ opd = create_private_open_data(fsp->conn, fsp, flags, mode);
if (opd == NULL) {
DEBUG(10, ("open_async: Could not create private data.\n"));
return -1;
@@ -308,6 +366,7 @@ static int open_async(const files_struct *fsp,
fsp->conn->sconn->pool,
aio_open_worker, opd);
if (subreq == NULL) {
+ opd_free(opd);
return -1;
}
tevent_req_set_callback(subreq, aio_open_handle_completion, opd);
@@ -317,6 +376,12 @@ static int open_async(const files_struct *fsp,
opd->dname,
opd->fname));
+ /*
+ * Add a destructor to protect us from connection
+ * teardown whilst the open thread is in flight.
+ */
+ talloc_set_destructor(opd, opd_inflight_destructor);
+
/* Cause the calling code to reschedule us. */
errno = EINPROGRESS; /* Maps to NT_STATUS_MORE_PROCESSING_REQUIRED. */
return -1;
@@ -364,7 +429,7 @@ static bool find_completed_open(files_struct *fsp,
smb_fname_str_dbg(fsp->fsp_name)));
/* Now we can free the opd. */
- TALLOC_FREE(opd);
+ opd_free(opd);
return true;
}
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index 5c8267dea9f..b0237cdacba 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -295,6 +295,7 @@ static struct {
{ SMB_VFS_OP_FALLOCATE,"fallocate" },
{ SMB_VFS_OP_LOCK, "lock" },
{ SMB_VFS_OP_KERNEL_FLOCK, "kernel_flock" },
+ { SMB_VFS_OP_FCNTL, "fcntl" },
{ SMB_VFS_OP_LINUX_SETLEASE, "linux_setlease" },
{ SMB_VFS_OP_GETLOCK, "getlock" },
{ SMB_VFS_OP_SYMLINKAT, "symlinkat" },
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index 41d8568e43a..d4c87209106 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -91,6 +91,11 @@ bld.SAMBA3_MODULE('vfs_full_audit',
internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_full_audit'),
enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_full_audit'))
+bld.SAMBA3_BINARY('test_vfs_full_audit',
+ source='test_vfs_full_audit.c',
+ deps='smbd_base cmocka',
+ for_selftest=True)
+
bld.SAMBA3_MODULE('vfs_fake_perms',
subsystem='vfs',
source='vfs_fake_perms.c',
diff --git a/source3/script/tests/test_force_close_share.sh b/source3/script/tests/test_force_close_share.sh
new file mode 100755
index 00000000000..da78b5a752e
--- /dev/null
+++ b/source3/script/tests/test_force_close_share.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+#
+# Test smbcontrol close-share command.
+#
+# Copyright (C) 2020 Volker Lendecke
+# Copyright (C) 2020 Jeremy Allison
+#
+# Note this is designed to be run against
+# the aio_delay_inject share which is preconfigured
+# with 2 second delays on pread/pwrite.
+
+if [ $# -lt 5 ]; then
+ echo Usage: test_share_force_close.sh \
+ SERVERCONFFILE SMBCLIENT SMBCONTROL IP aio_delay_inject_sharename
+exit 1
+fi
+
+CONF=$1
+SMBCLIENT=$2
+SMBCONTROL=$3
+SERVER=$4
+SHARE=$5
+
+incdir=$(dirname $0)/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+failed=0
+
+# Create the smbclient communication pipes.
+rm -f smbclient-stdin smbclient-stdout smbclient-stderr
+mkfifo smbclient-stdin smbclient-stdout smbclient-stderr
+
+# Create a large-ish testfile
+rm testfile
+head -c 20MB /dev/zero >testfile
+
+CLI_FORCE_INTERACTIVE=1; export CLI_FORCE_INTERACTIVE
+
+${SMBCLIENT} //${SERVER}/${SHARE} ${CONF} -U${USER}%${PASSWORD} \
+ < smbclient-stdin > smbclient-stdout 2>smbclient-stderr &
+CLIENT_PID=$!
+
+sleep 1
+
+exec 100>smbclient-stdin 101<smbclient-stdout 102<smbclient-stderr
+
+# consume the smbclient startup messages
+head -n 1 <&101
+head -n 1 <&102
+
+# Ensure we're putting a fresh file.
+echo "del testfile" >&100
+echo "put testfile" >&100
+
+sleep 1
+
+# Close the aio_delay_inject share whilst we have outstanding writes.
+
+testit "smbcontrol" ${SMBCONTROL} ${CONF} smbd close-share ${SHARE} ||
+ failed=$(expr $failed + 1)
+
+sleep 1
+
+# If we get one or more NT_STATUS_NETWORK_NAME_DELETED
+# or NT_STATUS_INVALID_HANDLE on stderr from the writes we
+# know the server stayed up and didn't crash when the
+# close-share removed the share.
+#
+# BUG: https://bugzilla.samba.org/show_bug.cgi?id=14301
+#
+COUNT=$(head -n 2 <&102 |
+ grep -e NT_STATUS_NETWORK_NAME_DELETED -e NT_STATUS_INVALID_HANDLE |
+ wc -l)
+
+testit "Verify close-share did cancel the file put" \
+ test $COUNT -ge 1 || failed=$(expr $failed + 1)
+
+kill ${CLIENT_PID}
+
+# Rerun smbclient to remove the testfile on the server.
+rm -f smbclient-stdin smbclient-stdout smbclient-stderr testfile
+mkfifo smbclient-stdin smbclient-stdout
+
+${SMBCLIENT} //${SERVER}/${SHARE} ${CONF} -U${USER}%${PASSWORD} \
+ < smbclient-stdin > smbclient-stdout &
+CLIENT_PID=$!
+
+sleep 1
+
+exec 100>smbclient-stdin 101<smbclient-stdout
+
+echo "del testfile" >&100
+
+sleep 1
+
+kill ${CLIENT_PID}
+
+rm -f smbclient-stdin smbclient-stdout testfile
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index dddcb03230c..68ca1eac6e8 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -466,6 +466,10 @@ plantestsuite("samba3.test_nfs4_acl", "none",
[os.path.join(bindir(), "test_nfs4_acls"),
"$SMB_CONF_PATH"])
+plantestsuite("samba3.test_vfs_full_audit", "none",
+ [os.path.join(bindir(), "test_vfs_full_audit"),
+ "$SMB_CONF_PATH"])
+
plantestsuite(
"samba3.resolvconf", "none",
[os.path.join(samba3srcdir, "script/tests/test_resolvconf.sh")])
@@ -802,6 +806,15 @@ plantestsuite("samba3.blackbox.close-denied-share", "simpleserver:local",
'$SERVER_IP',
"tmp"])
+plantestsuite("samba3.blackbox.force-close-share", "simpleserver:local",
+ [os.path.join(samba3srcdir,
+ "script/tests/test_force_close_share.sh"),
+ configuration,
+ os.path.join(bindir(), "smbclient"),
+ os.path.join(bindir(), "smbcontrol"),
+ '$SERVER_IP',
+ "aio_delay_inject"])
+
plantestsuite("samba3.blackbox.open-eintr", "simpleserver:local",
[os.path.join(samba3srcdir,
"script/tests/test_open_eintr.sh"),
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c
index 0f824f5aa1f..cf35f3297ec 100644
--- a/source3/smbd/aio.c
+++ b/source3/smbd/aio.c
@@ -103,6 +103,7 @@ static int aio_del_req_from_fsp(struct aio_req_fsp_link *lnk)
if (fsp->num_aio_requests == 0) {
tevent_wait_done(fsp->deferred_close);
+ TALLOC_FREE(fsp->aio_requests);
}
return 0;
}
@@ -121,9 +122,19 @@ bool aio_add_req_to_fsp(files_struct *fsp, struct tevent_req *req)
if (array_len <= fsp->num_aio_requests) {
struct tevent_req **tmp;
+ if (fsp->num_aio_requests + 10 < 10) {
+ /* Integer wrap. */
+ TALLOC_FREE(lnk);
+ return false;
+ }
+
+ /*
+ * Allocate in blocks of 10 so we don't allocate
+ * on every aio request.
+ */
tmp = talloc_realloc(
fsp, fsp->aio_requests, struct tevent_req *,
- fsp->num_aio_requests+1);
+ fsp->num_aio_requests+10);
if (tmp == NULL) {
TALLOC_FREE(lnk);
return false;
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index f45371e656c..d5af62a277c 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -31,6 +31,7 @@
#include "auth.h"
#include "messages.h"
#include "../librpc/gen_ndr/open_files.h"
+#include "lib/util/tevent_ntstatus.h"
/****************************************************************************
Run a file if it is a magic script.
@@ -635,6 +636,20 @@ static NTSTATUS ntstatus_keeperror(NTSTATUS s1, NTSTATUS s2)
return s2;
}
+static void assert_no_pending_aio(struct files_struct *fsp,
+ enum file_close_type close_type)
+{
+ unsigned num_requests = fsp->num_aio_requests;
+
+ if (num_requests == 0) {
+ return;
+ }
+
+ DBG_ERR("fsp->num_aio_requests=%u\n", num_requests);
+ smb_panic("can not close with outstanding aio requests");
+ return;
+}
+
/****************************************************************************
Close a file.
@@ -651,45 +666,7 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
connection_struct *conn = fsp->conn;
bool is_durable = false;
- if (fsp->num_aio_requests != 0) {
-
- if (close_type != SHUTDOWN_CLOSE) {
- /*
- * reply_close and the smb2 close must have
- * taken care of this. No other callers of
- * close_file should ever have created async
- * I/O.
- *
- * We need to panic here because if we close()
- * the fd while we have outstanding async I/O
- * requests, in the worst case we could end up
- * writing to the wrong file.
- */
- DEBUG(0, ("fsp->num_aio_requests=%u\n",
- fsp->num_aio_requests));
- smb_panic("can not close with outstanding aio "
- "requests");
- }
-
- /*
- * For shutdown close, just drop the async requests
- * including a potential close request pending for
- * this fsp. Drop the close request first, the
- * destructor for the aio_requests would execute it.
- */
- TALLOC_FREE(fsp->deferred_close);
-
- while (fsp->num_aio_requests != 0) {
- /*
- * The destructor of the req will remove
- * itself from the fsp.
- * Don't use TALLOC_FREE here, this will overwrite
- * what the destructor just wrote into
- * aio_requests[0].
- */
- talloc_free(fsp->aio_requests[0]);
- }
- }
+ assert_no_pending_aio(fsp, close_type);
while (talloc_array_length(fsp->blocked_smb1_lock_reqs) != 0) {
smbd_smb1_brl_finish_by_req(
@@ -1134,30 +1111,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
notify_status = NT_STATUS_OK;
}
- if (fsp->num_aio_requests != 0) {
- if (close_type != SHUTDOWN_CLOSE) {
- /*
- * We panic here because if we close() the fd while we
- * have outstanding async I/O requests, an async IO
- * request might use the fd. For directories the fd is
- * read-only, so this is not as bad as with files, but
- * still, better safe then sorry.
- */
- DBG_ERR("fsp->num_aio_requests=%u\n",
- fsp->num_aio_requests);
- smb_panic("close with outstanding aio requests");
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- while (fsp->num_aio_requests != 0) {
- /*
- * The destructor of the req will remove itself from the
- * fsp. Don't use TALLOC_FREE here, this will overwrite
- * what the destructor just wrote into aio_requests[0].
- */
- talloc_free(fsp->aio_requests[0]);
- }
- }
+ assert_no_pending_aio(fsp, close_type);
/*
* NT can set delete_on_close of the last open
diff --git a/source3/smbd/conn_idle.c b/source3/smbd/conn_idle.c
index cd12e3f1266..cf5a417bff7 100644
--- a/source3/smbd/conn_idle.c
+++ b/source3/smbd/conn_idle.c
@@ -23,6 +23,7 @@
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "rpc_server/rpc_pipes.h"
+#include "lib/util/tevent_ntstatus.h"
/****************************************************************************
Update last used timestamps.
@@ -76,61 +77,201 @@ bool conn_idle_all(struct smbd_server_connection *sconn, time_t t)
}
/****************************************************************************
- Forcibly unmount a share.
+ Forcibly unmount a share - async
All instances of the parameter 'sharename' share are unmounted.
The special sharename '*' forces unmount of all shares.
****************************************************************************/
+static struct tevent_req *conn_force_tdis_send(connection_struct *conn);
+static void conn_force_tdis_done(struct tevent_req *req);
+
void conn_force_tdis(
struct smbd_server_connection *sconn,
bool (*check_fn)(struct connection_struct *conn,
void *private_data),
void *private_data)
{
- connection_struct *conn, *next;
+ connection_struct *conn;
/* SMB1 and SMB 2*/
- for (conn = sconn->connections; conn; conn = next) {
+ for (conn = sconn->connections; conn; conn = conn->next) {
struct smbXsrv_tcon *tcon;
bool do_close = false;
- NTSTATUS status;
- uint64_t vuid = UID_FIELD_INVALID;
-
- next = conn->next;
+ struct tevent_req *req;
if (conn->tcon == NULL) {
continue;
}
tcon = conn->tcon;
+ if (!NT_STATUS_IS_OK(tcon->status)) {
+ /* In the process of already being disconnected. */
+ continue;
+ }
+
do_close = check_fn(conn, private_data);
if (!do_close) {
continue;
}
+ req = conn_force_tdis_send(conn);
+ if (req == NULL) {
+ DBG_WARNING("talloc_fail forcing async close of "
+ "share '%s'\n",
+ tcon->global->share_name);
+ continue;
+ }
+
DBG_WARNING("Forcing close of "
"share '%s' (wire_id=0x%08x)\n",
tcon->global->share_name,
tcon->global->tcon_wire_id);
- if (sconn->using_smb2) {
- vuid = conn->vuid;
+ tevent_req_set_callback(req, conn_force_tdis_done, conn);
+ }
+}
+
+struct conn_force_tdis_state {
+ struct tevent_queue *wait_queue;
+};
+
+static void conn_force_tdis_wait_done(struct tevent_req *subreq);
+
+static struct tevent_req *conn_force_tdis_send(connection_struct *conn)
+{
+ struct tevent_req *req;
+ struct conn_force_tdis_state *state;
+ struct tevent_req *subreq;
+ files_struct *fsp;
+
+ /* Create this off the NULL context. We must clean up on return. */
+ req = tevent_req_create(NULL, &state,
+ struct conn_force_tdis_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->wait_queue = tevent_queue_create(state,
+ "conn_force_tdis_wait_queue");
+ if (tevent_req_nomem(state->wait_queue, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ /*
+ * Make sure that no new request will be able to use this tcon.
+ * This ensures that once all outstanding fsp->aio_requests
+ * on this tcon are done, we are safe to close it.
+ */
+ conn->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
+
+ for (fsp = conn->sconn->files; fsp; fsp = fsp->next) {
+ if (fsp->conn != conn) {
+ continue;
}
+ /*
+ * Flag the file as close in progress.
+ * This will prevent any more IO being
+ * done on it. Not strictly needed, but
+ * doesn't hurt to flag it as closing.
+ */
+ fsp->closing = true;
- conn = NULL;
- status = smbXsrv_tcon_disconnect(tcon, vuid);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("conn_force_tdis: "
- "smbXsrv_tcon_disconnect() of share '%s' "
- "(wire_id=0x%08x) failed: %s\n",
- tcon->global->share_name,
- tcon->global->tcon_wire_id,
- nt_errstr(status)));
+ if (fsp->num_aio_requests > 0) {
+ /*
+ * Now wait until all aio requests on this fsp are
+ * finished.
+ *
+ * We don't set a callback, as we just want to block the
+ * wait queue and the talloc_free() of fsp->aio_request
+ * will remove the item from the wait queue.
+ */
+ subreq = tevent_queue_wait_send(fsp->aio_requests,
+ conn->sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
}
+ }
+ /*
+ * Now we add our own waiter to the end of the queue,
+ * this way we get notified when all pending requests are finished
+ * and reply to the outstanding SMB1 request.
+ */
+ subreq = tevent_queue_wait_send(state,
+ conn->sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ tevent_req_set_callback(subreq, conn_force_tdis_wait_done, req);
+ return req;
+}
+
+static void conn_force_tdis_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+
+ tevent_queue_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+static NTSTATUS conn_force_tdis_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void conn_force_tdis_done(struct tevent_req *req)
+{
+ connection_struct *conn = tevent_req_callback_data(
+ req, connection_struct);
+ NTSTATUS status;
+ uint64_t vuid = UID_FIELD_INVALID;
+ struct smbXsrv_tcon *tcon = conn->tcon;
+ struct smbd_server_connection *sconn = conn->sconn;
+
+ status = conn_force_tdis_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("conn_force_tdis_recv of share '%s' "
+ "(wire_id=0x%08x) failed: %s\n",
+ tcon->global->share_name,
+ tcon->global->tcon_wire_id,
+ nt_errstr(status));
+ return;
+ }
- TALLOC_FREE(tcon);
+ if (conn->sconn->using_smb2) {
+ vuid = conn->vuid;
}
+ DBG_WARNING("Closing "
+ "share '%s' (wire_id=0x%08x)\n",
+ tcon->global->share_name,
+ tcon->global->tcon_wire_id);
+
+ conn = NULL;
+ status = smbXsrv_tcon_disconnect(tcon, vuid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("smbXsrv_tcon_disconnect() of share '%s' "
+ "(wire_id=0x%08x) failed: %s\n",
+ tcon->global->share_name,
+ tcon->global->tcon_wire_id,
+ nt_errstr(status));
+ return;
+ }
+
+ TALLOC_FREE(tcon);
+
+ /*
+ * As we've been awoken, we may have changed
+ * uid in the meantime. Ensure we're still root.
+ */
change_to_root_user();
reload_services(sconn, conn_snum_used, true);
}
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index 029965282f1..31d5b7510b7 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -104,15 +104,7 @@ void fsp_flush_write_time_update(struct files_struct *fsp)
DEBUG(5, ("Update write time on %s\n", fsp_str_dbg(fsp)));
- /* change the write time in the open file db. */
- (void)set_write_time(fsp->file_id, timespec_current());
-
- /* And notify. */
- notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
- FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name->base_name);
-
- /* Remove the timed event handler. */
- TALLOC_FREE(fsp->update_write_time_event);
+ trigger_write_time_update_immediate(fsp);
}
static void update_write_time_handler(struct tevent_context *ctx,
@@ -214,17 +206,14 @@ void mark_file_modified(files_struct *fsp)
{
int dosmode;
+ trigger_write_time_update(fsp);
+
if (fsp->modified) {
return;
}
fsp->modified = true;
- if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
- return;
- }
- trigger_write_time_update(fsp);
-
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
return;
}
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 99b2f343685..a982c0a5980 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -189,23 +189,6 @@ void file_close_conn(connection_struct *conn)
}
/****************************************************************************
- Close all open files for a pid and a vuid.
-****************************************************************************/
-
-void file_close_pid(struct smbd_server_connection *sconn, uint16_t smbpid,
- uint64_t vuid)
-{
- files_struct *fsp, *next;
-
- for (fsp=sconn->files;fsp;fsp=next) {
- next = fsp->next;
- if ((fsp->file_pid == smbpid) && (fsp->vuid == vuid)) {
- close_file(NULL, fsp, SHUTDOWN_CLOSE);
- }
- }
-}
-
-/****************************************************************************
Initialise file structures.
****************************************************************************/
@@ -598,6 +581,9 @@ files_struct *file_fsp(struct smb_request *req, uint16_t fid)
if (req->chain_fsp->deferred_close) {
return NULL;
}
+ if (req->chain_fsp->closing) {
+ return NULL;
+ }
return req->chain_fsp;
}
@@ -622,6 +608,10 @@ files_struct *file_fsp(struct smb_request *req, uint16_t fid)
return NULL;
}
+ if (fsp->closing) {
+ return NULL;
+ }
+
req->chain_fsp = fsp;
return fsp;
}
@@ -669,6 +659,10 @@ struct files_struct *file_fsp_get(struct smbd_smb2_request *smb2req,
return NULL;
}
+ if (fsp->closing) {
+ return NULL;
+ }
+
return fsp;
}
@@ -682,6 +676,9 @@ struct files_struct *file_fsp_smb2(struct smbd_smb2_request *smb2req,
if (smb2req->compat_chain_fsp->deferred_close) {
return NULL;
}
+ if (smb2req->compat_chain_fsp->closing) {
+ return NULL;
+ }
return smb2req->compat_chain_fsp;
}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 96d574023a5..911fe6dad3c 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -379,8 +379,6 @@ void fsp_set_gen_id(files_struct *fsp);
NTSTATUS file_new(struct smb_request *req, connection_struct *conn,
files_struct **result);
void file_close_conn(connection_struct *conn);
-void file_close_pid(struct smbd_server_connection *sconn, uint16_t smbpid,
- uint64_t vuid);
bool file_init_global(void);
bool file_init(struct smbd_server_connection *sconn);
void file_close_user(struct smbd_server_connection *sconn, uint64_t vuid);
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 40cd7483750..54f3d330c95 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -42,6 +42,7 @@
#include "smbprofile.h"
#include "../lib/tsocket/tsocket.h"
#include "lib/tevent_wait.h"
+#include "lib/util/tevent_ntstatus.h"
#include "libcli/smb/smb_signing.h"
#include "lib/util/sys_rw_data.h"
#include "librpc/gen_ndr/open_files.h"
@@ -2611,40 +2612,196 @@ void reply_open_and_X(struct smb_request *req)
Reply to a SMBulogoffX.
****************************************************************************/
-void reply_ulogoffX(struct smb_request *req)
+static struct tevent_req *reply_ulogoffX_send(struct smb_request *smb1req,
+ struct smbXsrv_session *session);
+static void reply_ulogoffX_done(struct tevent_req *req);
+
+void reply_ulogoffX(struct smb_request *smb1req)
{
struct timeval now = timeval_current();
struct smbXsrv_session *session = NULL;
+ struct tevent_req *req;
NTSTATUS status;
- START_PROFILE(SMBulogoffX);
+ /*
+ * Don't setup the profile charge here, take
+ * it in reply_ulogoffX_done(). Not strictly correct
+ * but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
- status = smb1srv_session_lookup(req->xconn,
- req->vuid,
+ status = smb1srv_session_lookup(smb1req->xconn,
+ smb1req->vuid,
timeval_to_nttime(&now),
&session);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(3,("ulogoff, vuser id %llu does not map to user.\n",
- (unsigned long long)req->vuid));
+ /* Not going async, profile here. */
+ START_PROFILE(SMBulogoffX);
+ DBG_WARNING("ulogoff, vuser id %llu does not map to user.\n",
+ (unsigned long long)smb1req->vuid);
- req->vuid = UID_FIELD_INVALID;
- reply_force_doserror(req, ERRSRV, ERRbaduid);
+ smb1req->vuid = UID_FIELD_INVALID;
+ reply_force_doserror(smb1req, ERRSRV, ERRbaduid);
+ END_PROFILE(SMBulogoffX);
+ return;
+ }
+
+ req = reply_ulogoffX_send(smb1req, session);
+ if (req == NULL) {
+ /* Not going async, profile here. */
+ START_PROFILE(SMBulogoffX);
+ reply_force_doserror(smb1req, ERRDOS, ERRnomem);
END_PROFILE(SMBulogoffX);
return;
}
+ /* We're async. This will complete later. */
+ tevent_req_set_callback(req, reply_ulogoffX_done, smb1req);
+ return;
+}
+
+struct reply_ulogoffX_state {
+ struct tevent_queue *wait_queue;
+ struct smbXsrv_session *session;
+};
+
+static void reply_ulogoffX_wait_done(struct tevent_req *subreq);
+
+/****************************************************************************
+ Async SMB1 ulogoffX.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_ulogoffX_send(struct smb_request *smb1req,
+ struct smbXsrv_session *session)
+{
+ struct tevent_req *req;
+ struct reply_ulogoffX_state *state;
+ struct tevent_req *subreq;
+ files_struct *fsp;
+ struct smbd_server_connection *sconn = session->client->sconn;
+ uint64_t vuid = session->global->session_wire_id;
+
+ req = tevent_req_create(smb1req, &state,
+ struct reply_ulogoffX_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->wait_queue = tevent_queue_create(state,
+ "reply_ulogoffX_wait_queue");
+ if (tevent_req_nomem(state->wait_queue, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ state->session = session;
+
/*
- * TODO: cancel all outstanding requests on the session
+ * Make sure that no new request will be able to use this session.
+ * This ensures that once all outstanding fsp->aio_requests
+ * on this session are done, we are safe to close it.
*/
- status = smbXsrv_session_logoff(session);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("reply_ulogoff: "
- "smbXsrv_session_logoff() failed: %s\n",
- nt_errstr(status)));
+ session->status = NT_STATUS_USER_SESSION_DELETED;
+
+ for (fsp = sconn->files; fsp; fsp = fsp->next) {
+ if (fsp->vuid != vuid) {
+ continue;
+ }
/*
- * If we hit this case, there is something completely
- * wrong, so we better disconnect the transport connection.
+ * Flag the file as close in progress.
+ * This will prevent any more IO being
+ * done on it.
*/
+ fsp->closing = true;
+
+ if (fsp->num_aio_requests > 0) {
+ /*
+ * Now wait until all aio requests on this fsp are
+ * finished.
+ *
+ * We don't set a callback, as we just want to block the
+ * wait queue and the talloc_free() of fsp->aio_request
+ * will remove the item from the wait queue.
+ */
+ subreq = tevent_queue_wait_send(fsp->aio_requests,
+ sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ }
+ }
+
+ /*
+ * Now we add our own waiter to the end of the queue,
+ * this way we get notified when all pending requests are finished
+ * and reply to the outstanding SMB1 request.
+ */
+ subreq = tevent_queue_wait_send(state,
+ sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ /*
+ * We're really going async - move the SMB1 request from
+ * a talloc stackframe above us to the sconn talloc-context.
+ * We need this to stick around until the wait_done
+ * callback is invoked.
+ */
+ smb1req = talloc_move(sconn, &smb1req);
+
+ tevent_req_set_callback(subreq, reply_ulogoffX_wait_done, req);
+
+ return req;
+}
+
+static void reply_ulogoffX_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+
+ tevent_queue_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+static NTSTATUS reply_ulogoffX_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_ulogoffX_done(struct tevent_req *req)
+{
+ struct smb_request *smb1req = tevent_req_callback_data(
+ req, struct smb_request);
+ struct reply_ulogoffX_state *state = tevent_req_data(req,
+ struct reply_ulogoffX_state);
+ struct smbXsrv_session *session = state->session;
+ NTSTATUS status;
+
+ /*
+ * Take the profile charge here. Not strictly
+ * correct but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
+ START_PROFILE(SMBulogoffX);
+
+ status = reply_ulogoffX_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb1req);
+ END_PROFILE(SMBulogoffX);
+ exit_server(__location__ ": reply_ulogoffX_recv failed");
+ return;
+ }
+
+ status = smbXsrv_session_logoff(session);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb1req);
END_PROFILE(SMBulogoffX);
exit_server(__location__ ": smbXsrv_session_logoff failed");
return;
@@ -2652,15 +2809,21 @@ void reply_ulogoffX(struct smb_request *req)
TALLOC_FREE(session);
- reply_outbuf(req, 2, 0);
- SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
- SSVAL(req->outbuf, smb_vwv1, 0); /* no andx offset */
+ reply_outbuf(smb1req, 2, 0);
+ SSVAL(smb1req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
+ SSVAL(smb1req->outbuf, smb_vwv1, 0); /* no andx offset */
- DEBUG(3, ("ulogoffX vuid=%llu\n",
- (unsigned long long)req->vuid));
+ DBG_NOTICE("ulogoffX vuid=%llu\n",
+ (unsigned long long)smb1req->vuid);
+ smb1req->vuid = UID_FIELD_INVALID;
+ /*
+ * The following call is needed to push the
+ * reply data back out the socket after async
+ * return. Plus it frees smb1req.
+ */
+ smb_request_done(smb1req);
END_PROFILE(SMBulogoffX);
- req->vuid = UID_FIELD_INVALID;
}
/****************************************************************************
@@ -5538,6 +5701,10 @@ static struct files_struct *file_sync_one_fn(struct files_struct *fsp,
}
sync_file(conn, fsp, True /* write through */);
+ if (fsp->modified) {
+ trigger_write_time_update_immediate(fsp);
+ }
+
return NULL;
}
@@ -5576,6 +5743,9 @@ void reply_flush(struct smb_request *req)
END_PROFILE(SMBflush);
return;
}
+ if (fsp->modified) {
+ trigger_write_time_update_immediate(fsp);
+ }
}
reply_outbuf(req, 0, 0);
@@ -5590,16 +5760,228 @@ void reply_flush(struct smb_request *req)
conn POINTER CAN BE NULL HERE !
****************************************************************************/
-void reply_exit(struct smb_request *req)
+static struct tevent_req *reply_exit_send(struct smb_request *smb1req);
+static void reply_exit_done(struct tevent_req *req);
+
+void reply_exit(struct smb_request *smb1req)
+{
+ struct tevent_req *req;
+
+ /*
+ * Don't setup the profile charge here, take
+ * it in reply_exit_done(). Not strictly correct
+ * but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
+ req = reply_exit_send(smb1req);
+ if (req == NULL) {
+ /* Not going async, profile here. */
+ START_PROFILE(SMBexit);
+ reply_force_doserror(smb1req, ERRDOS, ERRnomem);
+ END_PROFILE(SMBexit);
+ return;
+ }
+
+ /* We're async. This will complete later. */
+ tevent_req_set_callback(req, reply_exit_done, smb1req);
+ return;
+}
+
+struct reply_exit_state {
+ struct tevent_queue *wait_queue;
+};
+
+static void reply_exit_wait_done(struct tevent_req *subreq);
+
+/****************************************************************************
+ Async SMB1 exit.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_exit_send(struct smb_request *smb1req)
+{
+ struct tevent_req *req;
+ struct reply_exit_state *state;
+ struct tevent_req *subreq;
+ files_struct *fsp;
+ struct smbd_server_connection *sconn = smb1req->sconn;
+
+ req = tevent_req_create(smb1req, &state,
+ struct reply_exit_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->wait_queue = tevent_queue_create(state,
+ "reply_exit_wait_queue");
+ if (tevent_req_nomem(state->wait_queue, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ for (fsp = sconn->files; fsp; fsp = fsp->next) {
+ if (fsp->file_pid != smb1req->smbpid) {
+ continue;
+ }
+ if (fsp->vuid != smb1req->vuid) {
+ continue;
+ }
+ /*
+ * Flag the file as close in progress.
+ * This will prevent any more IO being
+ * done on it.
+ */
+ fsp->closing = true;
+
+ if (fsp->num_aio_requests > 0) {
+ /*
+ * Now wait until all aio requests on this fsp are
+ * finished.
+ *
+ * We don't set a callback, as we just want to block the
+ * wait queue and the talloc_free() of fsp->aio_request
+ * will remove the item from the wait queue.
+ */
+ subreq = tevent_queue_wait_send(fsp->aio_requests,
+ sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ }
+ }
+
+ /*
+ * Now we add our own waiter to the end of the queue,
+ * this way we get notified when all pending requests are finished
+ * and reply to the outstanding SMB1 request.
+ */
+ subreq = tevent_queue_wait_send(state,
+ sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ /*
+ * We're really going async - move the SMB1 request from
+ * a talloc stackframe above us to the conn talloc-context.
+ * We need this to stick around until the wait_done
+ * callback is invoked.
+ */
+ smb1req = talloc_move(sconn, &smb1req);
+
+ tevent_req_set_callback(subreq, reply_exit_wait_done, req);
+
+ return req;
+}
+
+static void reply_exit_wait_done(struct tevent_req *subreq)
{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+
+ tevent_queue_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+static NTSTATUS reply_exit_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_exit_done(struct tevent_req *req)
+{
+ struct smb_request *smb1req = tevent_req_callback_data(
+ req, struct smb_request);
+ struct smbd_server_connection *sconn = smb1req->sconn;
+ struct smbXsrv_connection *xconn = smb1req->xconn;
+ NTTIME now = timeval_to_nttime(&smb1req->request_time);
+ struct smbXsrv_session *session = NULL;
+ files_struct *fsp, *next;
+ NTSTATUS status;
+
+ /*
+ * Take the profile charge here. Not strictly
+ * correct but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
START_PROFILE(SMBexit);
- file_close_pid(req->sconn, req->smbpid, req->vuid);
+ status = reply_exit_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb1req);
+ END_PROFILE(SMBexit);
+ exit_server(__location__ ": reply_exit_recv failed");
+ return;
+ }
- reply_outbuf(req, 0, 0);
+ /*
+ * Ensure the session is still valid.
+ */
+ status = smb1srv_session_lookup(xconn,
+ smb1req->vuid,
+ now,
+ &session);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+ smb_request_done(smb1req);
+ END_PROFILE(SMBexit);
+ }
+
+ /*
+ * Ensure the vuid is still valid - no one
+ * called reply_ulogoffX() in the meantime.
+ * reply_exit() doesn't have AS_USER set, so
+ * use set_current_user_info() directly.
+ * This is the same logic as in switch_message().
+ */
+ if (session->global->auth_session_info != NULL) {
+ set_current_user_info(
+ session->global->auth_session_info->unix_info->sanitized_username,
+ session->global->auth_session_info->unix_info->unix_name,
+ session->global->auth_session_info->info->domain_name);
+ }
+
+ /* No more aio - do the actual closes. */
+ for (fsp = sconn->files; fsp; fsp = next) {
+ bool ok;
+ next = fsp->next;
+
+ if (fsp->file_pid != smb1req->smbpid) {
+ continue;
+ }
+ if (fsp->vuid != smb1req->vuid) {
+ continue;
+ }
+ if (!fsp->closing) {
+ continue;
+ }
- DEBUG(3,("exit\n"));
+ /*
+ * reply_exit() has the DO_CHDIR flag set.
+ */
+ ok = chdir_current_service(fsp->conn);
+ if (!ok) {
+ reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+ smb_request_done(smb1req);
+ END_PROFILE(SMBexit);
+ }
+ close_file(NULL, fsp, SHUTDOWN_CLOSE);
+ }
+ reply_outbuf(smb1req, 0, 0);
+ /*
+ * The following call is needed to push the
+ * reply data back out the socket after async
+ * return. Plus it frees smb1req.
+ */
+ smb_request_done(smb1req);
+ DBG_INFO("reply_exit complete\n");
END_PROFILE(SMBexit);
return;
}
@@ -5660,6 +6042,13 @@ void reply_close(struct smb_request *req)
fsp->num_aio_requests));
/*
+ * Flag the file as close in progress.
+ * This will prevent any more IO being
+ * done on it.
+ */
+ fsp->closing = true;
+
+ /*
* We depend on the aio_extra destructor to take care of this
* close request once fsp->num_aio_request drops to 0.
*/
@@ -6018,46 +6407,211 @@ void reply_unlock(struct smb_request *req)
conn POINTER CAN BE NULL HERE !
****************************************************************************/
-void reply_tdis(struct smb_request *req)
+static struct tevent_req *reply_tdis_send(struct smb_request *smb1req);
+static void reply_tdis_done(struct tevent_req *req);
+
+void reply_tdis(struct smb_request *smb1req)
{
- NTSTATUS status;
- connection_struct *conn = req->conn;
- struct smbXsrv_tcon *tcon;
+ connection_struct *conn = smb1req->conn;
+ struct tevent_req *req;
- START_PROFILE(SMBtdis);
+ /*
+ * Don't setup the profile charge here, take
+ * it in reply_tdis_done(). Not strictly correct
+ * but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
- if (!conn) {
- DEBUG(4,("Invalid connection in tdis\n"));
- reply_force_doserror(req, ERRSRV, ERRinvnid);
+ if (conn == NULL) {
+ /* Not going async, profile here. */
+ START_PROFILE(SMBtdis);
+ DBG_INFO("Invalid connection in tdis\n");
+ reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+ END_PROFILE(SMBtdis);
+ return;
+ }
+
+ req = reply_tdis_send(smb1req);
+ if (req == NULL) {
+ /* Not going async, profile here. */
+ START_PROFILE(SMBtdis);
+ reply_force_doserror(smb1req, ERRDOS, ERRnomem);
END_PROFILE(SMBtdis);
return;
}
+ /* We're async. This will complete later. */
+ tevent_req_set_callback(req, reply_tdis_done, smb1req);
+ return;
+}
+
+struct reply_tdis_state {
+ struct tevent_queue *wait_queue;
+};
+
+static void reply_tdis_wait_done(struct tevent_req *subreq);
+
+/****************************************************************************
+ Async SMB1 tdis.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_tdis_send(struct smb_request *smb1req)
+{
+ struct tevent_req *req;
+ struct reply_tdis_state *state;
+ struct tevent_req *subreq;
+ connection_struct *conn = smb1req->conn;
+ files_struct *fsp;
- tcon = conn->tcon;
- req->conn = NULL;
+ req = tevent_req_create(smb1req, &state,
+ struct reply_tdis_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->wait_queue = tevent_queue_create(state, "reply_tdis_wait_queue");
+ if (tevent_req_nomem(state->wait_queue, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
/*
- * TODO: cancel all outstanding requests on the tcon
+ * Make sure that no new request will be able to use this tcon.
+ * This ensures that once all outstanding fsp->aio_requests
+ * on this tcon are done, we are safe to close it.
*/
- status = smbXsrv_tcon_disconnect(tcon, req->vuid);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("reply_tdis: "
- "smbXsrv_tcon_disconnect() failed: %s\n",
- nt_errstr(status)));
+ conn->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
+
+ for (fsp = conn->sconn->files; fsp; fsp = fsp->next) {
+ if (fsp->conn != conn) {
+ continue;
+ }
/*
- * If we hit this case, there is something completely
- * wrong, so we better disconnect the transport connection.
+ * Flag the file as close in progress.
+ * This will prevent any more IO being
+ * done on it. Not strictly needed, but
+ * doesn't hurt to flag it as closing.
*/
+ fsp->closing = true;
+
+ if (fsp->num_aio_requests > 0) {
+ /*
+ * Now wait until all aio requests on this fsp are
+ * finished.
+ *
+ * We don't set a callback, as we just want to block the
+ * wait queue and the talloc_free() of fsp->aio_request
+ * will remove the item from the wait queue.
+ */
+ subreq = tevent_queue_wait_send(fsp->aio_requests,
+ conn->sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ }
+ }
+
+ /*
+ * Now we add our own waiter to the end of the queue,
+ * this way we get notified when all pending requests are finished
+ * and reply to the outstanding SMB1 request.
+ */
+ subreq = tevent_queue_wait_send(state,
+ conn->sconn->ev_ctx,
+ state->wait_queue);
+ if (tevent_req_nomem(subreq, req)) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ /*
+ * We're really going async - move the SMB1 request from
+ * a talloc stackframe above us to the sconn talloc-context.
+ * We need this to stick around until the wait_done
+ * callback is invoked.
+ */
+ smb1req = talloc_move(smb1req->sconn, &smb1req);
+
+ tevent_req_set_callback(subreq, reply_tdis_wait_done, req);
+
+ return req;
+}
+
+static void reply_tdis_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+
+ tevent_queue_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+static NTSTATUS reply_tdis_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_tdis_done(struct tevent_req *req)
+{
+ struct smb_request *smb1req = tevent_req_callback_data(
+ req, struct smb_request);
+ NTSTATUS status;
+ struct smbXsrv_tcon *tcon = smb1req->conn->tcon;
+ bool ok;
+
+ /*
+ * Take the profile charge here. Not strictly
+ * correct but better than the other SMB1 async
+ * code that double-charges at the moment.
+ */
+ START_PROFILE(SMBtdis);
+
+ status = reply_tdis_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb1req);
+ END_PROFILE(SMBtdis);
+ exit_server(__location__ ": reply_tdis_recv failed");
+ return;
+ }
+
+ /*
+ * As we've been awoken, we may have changed
+ * directory in the meantime.
+ * reply_tdis() has the DO_CHDIR flag set.
+ */
+ ok = chdir_current_service(smb1req->conn);
+ if (!ok) {
+ reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+ smb_request_done(smb1req);
+ END_PROFILE(SMBtdis);
+ }
+
+ status = smbXsrv_tcon_disconnect(tcon,
+ smb1req->vuid);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb1req);
END_PROFILE(SMBtdis);
exit_server(__location__ ": smbXsrv_tcon_disconnect failed");
return;
}
+ /* smbXsrv_tcon_disconnect frees smb1req->conn. */
+ smb1req->conn = NULL;
+
TALLOC_FREE(tcon);
- reply_outbuf(req, 0, 0);
+ reply_outbuf(smb1req, 0, 0);
+ /*
+ * The following call is needed to push the
+ * reply data back out the socket after async
+ * return. Plus it frees smb1req.
+ */
+ smb_request_done(smb1req);
END_PROFILE(SMBtdis);
- return;
}
/****************************************************************************
@@ -8857,6 +9411,10 @@ void reply_setattrE(struct smb_request *req)
goto out;
}
+ if (fsp->modified) {
+ trigger_write_time_update_immediate(fsp);
+ }
+
DEBUG( 3, ( "reply_setattrE %s actime=%u modtime=%u "
" createtime=%u\n",
fsp_fnum_dbg(fsp),
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index 1abc23ad422..03125a30dad 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -146,55 +146,46 @@ bool chdir_current_service(connection_struct *conn)
const struct smb_filename origpath_fname = {
.base_name = conn->origpath,
};
+ int saved_errno = 0;
+ char *utok_str = NULL;
int ret;
conn->lastused_count++;
ret = vfs_ChDir(conn, &connectpath_fname);
- if (ret != 0) {
- int saved_errno = errno;
-
- if (saved_errno == EACCES) {
- char *str = utok_string(
- talloc_tos(),
- conn->session_info->unix_token);
- DBG_WARNING("vfs_ChDir(%s) got "
- "permission denied, current "
- "token: %s\n",
- conn->connectpath, str);
- TALLOC_FREE(str);
- } else {
- DBG_ERR("vfs_ChDir(%s) failed: "
- "%s!\n",
- conn->connectpath,
- strerror(saved_errno));
- }
+ if (ret == 0) {
+ return true;
+ }
+ saved_errno = errno;
+
+ utok_str = utok_string(talloc_tos(),
+ conn->session_info->unix_token);
+ if (utok_str == NULL) {
+ errno = saved_errno;
return false;
}
+ DBG_ERR("vfs_ChDir(%s) failed: %s. Current token: %s\n",
+ conn->connectpath,
+ strerror(saved_errno),
+ utok_str);
+
ret = vfs_ChDir(conn, &origpath_fname);
- if (ret != 0) {
- int saved_errno = errno;
-
- if (saved_errno == EACCES) {
- char *str = utok_string(
- talloc_tos(),
- conn->session_info->unix_token);
- DBG_WARNING("vfs_ChDir(%s) got "
- "permission denied, current "
- "token: %s\n",
- conn->origpath, str);
- TALLOC_FREE(str);
- } else {
- DBG_ERR("vfs_ChDir(%s) failed: "
- "%s!\n",
- conn->origpath,
- strerror(saved_errno));
- }
- return false;
+ if (ret == 0) {
+ TALLOC_FREE(utok_str);
+ return true;
}
+ saved_errno = errno;
- return true;
+ DBG_ERR("vfs_ChDir(%s) failed: %s. Current token: %s\n",
+ conn->origpath,
+ strerror(saved_errno),
+ utok_str);
+
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ }
+ return false;
}
/****************************************************************************
diff --git a/source3/smbd/smb2_flush.c b/source3/smbd/smb2_flush.c
index 86d5bbc58f0..08539e95807 100644
--- a/source3/smbd/smb2_flush.c
+++ b/source3/smbd/smb2_flush.c
@@ -112,6 +112,7 @@ static void smbd_smb2_request_flush_done(struct tevent_req *subreq)
struct smbd_smb2_flush_state {
struct smbd_smb2_request *smb2req;
+ struct files_struct *fsp;
};
static void smbd_smb2_flush_done(struct tevent_req *subreq);
@@ -132,6 +133,7 @@ static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
return NULL;
}
state->smb2req = smb2req;
+ state->fsp = fsp;
DEBUG(10,("smbd_smb2_flush: %s - %s\n",
fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
@@ -207,6 +209,8 @@ static void smbd_smb2_flush_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
+ struct smbd_smb2_flush_state *state = tevent_req_data(
+ req, struct smbd_smb2_flush_state);
int ret;
struct vfs_aio_state vfs_aio_state;
@@ -216,6 +220,9 @@ static void smbd_smb2_flush_done(struct tevent_req *subreq)
tevent_req_nterror(req, map_nt_error_from_unix(vfs_aio_state.error));
return;
}
+ if (state->fsp->modified) {
+ trigger_write_time_update_immediate(state->fsp);
+ }
tevent_req_done(req);
}
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 2cf669f4b4d..339dd36b6af 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -6675,6 +6675,13 @@ static NTSTATUS smb_set_file_size(connection_struct *conn,
get_file_size_stat(psbuf));
if (size == get_file_size_stat(psbuf)) {
+ if (fsp == NULL) {
+ return NT_STATUS_OK;
+ }
+ if (!fsp->modified) {
+ return NT_STATUS_OK;
+ }
+ trigger_write_time_update_immediate(fsp);
return NT_STATUS_OK;
}
@@ -7817,8 +7824,15 @@ static NTSTATUS smb_set_file_basic_info(connection_struct *conn,
DEBUG(10, ("smb_set_file_basic_info: file %s\n",
smb_fname_str_dbg(smb_fname)));
- return smb_set_file_time(conn, fsp, smb_fname, &ft,
- true);
+ status = smb_set_file_time(conn, fsp, smb_fname, &ft, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (fsp != NULL && fsp->modified) {
+ trigger_write_time_update_immediate(fsp);
+ }
+ return NT_STATUS_OK;
}
/****************************************************************************
@@ -7855,11 +7869,15 @@ static NTSTATUS smb_set_info_standard(connection_struct *conn,
return status;
}
- return smb_set_file_time(conn,
- fsp,
- smb_fname,
- &ft,
- true);
+ status = smb_set_file_time(conn, fsp, smb_fname, &ft, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (fsp != NULL && fsp->modified) {
+ trigger_write_time_update_immediate(fsp);
+ }
+ return NT_STATUS_OK;
}
/****************************************************************************
diff --git a/source4/kdc/mit-kdb/kdb_samba.c b/source4/kdc/mit-kdb/kdb_samba.c
index c5157d6ed1b..02bbdca9f54 100644
--- a/source4/kdc/mit-kdb/kdb_samba.c
+++ b/source4/kdc/mit-kdb/kdb_samba.c
@@ -139,7 +139,7 @@ static void kdb_samba_db_free_principal_e_data(krb5_context context,
kdb_vftabl kdb_function_table = {
.maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
- .min_ver = 1,
+ .min_ver = KRB5_KDB_DAL_MAJOR_VERSION == 6 ? 1 : 0,
.init_library = kdb_samba_init_library,
.fini_library = kdb_samba_fini_library,
diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h
index 22ef9085b6a..ad4f6e27573 100644
--- a/source4/kdc/mit-kdb/kdb_samba.h
+++ b/source4/kdc/mit-kdb/kdb_samba.h
@@ -114,6 +114,7 @@ krb5_error_code kdb_samba_dbekd_encrypt_key_data(krb5_context context,
/* from kdb_samba_policies.c */
+#if KRB5_KDB_API_VERSION < 10
krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
unsigned int flags,
krb5_const_principal client_princ,
@@ -127,6 +128,26 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
krb5_timestamp authtime,
krb5_authdata **tgt_auth_data,
krb5_authdata ***signed_auth_data);
+#else
+krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_db_entry *local_krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *local_krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ void *authdata_info,
+ krb5_data ***auth_indicators,
+ krb5_authdata ***signed_auth_data);
+#endif
krb5_error_code kdb_samba_db_check_policy_as(krb5_context context,
krb5_kdc_req *kdcreq,
diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c
index fc80329f221..9197551ed61 100644
--- a/source4/kdc/mit-kdb/kdb_samba_policies.c
+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c
@@ -287,6 +287,7 @@ done:
return code;
}
+#if KRB5_KDB_API_VERSION < 10
krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
unsigned int flags,
krb5_const_principal client_princ,
@@ -301,18 +302,41 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
krb5_authdata **tgt_auth_data,
krb5_authdata ***signed_auth_data)
{
- krb5_const_principal ks_client_princ;
+#else
+krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_db_entry *local_krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *local_krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ void *authdata_info,
+ krb5_data ***auth_indicators,
+ krb5_authdata ***signed_auth_data)
+{
+#endif
krb5_authdata **authdata = NULL;
krb5_boolean is_as_req;
krb5_error_code code;
krb5_pac pac = NULL;
krb5_data pac_data;
- /* Prefer canonicalised name from client entry */
- if (client != NULL) {
- ks_client_princ = client->princ;
- } else {
- ks_client_princ = client_princ;
+#if KRB5_KDB_API_VERSION >= 10
+ krbtgt = krbtgt == NULL ? local_krbtgt : krbtgt;
+ krbtgt_key = krbtgt_key == NULL ? local_krbtgt_key : krbtgt_key;
+#endif
+
+ /* FIXME: We don't support S4U yet */
+ if (flags & KRB5_KDB_FLAGS_S4U) {
+ return KRB5_KDB_DBTYPE_NOSUP;
}
is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
@@ -327,7 +351,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
if (!is_as_req) {
code = ks_verify_pac(context,
flags,
- ks_client_princ,
+ client_princ,
client,
server,
krbtgt,
@@ -354,7 +378,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
goto done;
}
- code = krb5_pac_sign(context, pac, authtime, ks_client_princ,
+ code = krb5_pac_sign(context, pac, authtime, client_princ,
server_key, krbtgt_key, &pac_data);
if (code != 0) {
DBG_ERR("krb5_pac_sign failed: %d\n", code);
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
index c258c15ff91..41b9ffbc356 100644
--- a/source4/torture/smb2/smb2.c
+++ b/source4/torture/smb2/smb2.c
@@ -198,6 +198,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
torture_suite_add_suite(suite, torture_smb2_multichannel_init(suite));
torture_suite_add_suite(suite, torture_smb2_samba3misc_init(suite));
torture_suite_add_suite(suite, torture_smb2_timestamps_init(suite));
+ torture_suite_add_suite(suite, torture_smb2_timestamp_resolution_init(suite));
torture_suite_add_1smb2_test(suite, "openattr", torture_smb2_openattrtest);
torture_suite_add_1smb2_test(suite, "winattr", torture_smb2_winattrtest);
torture_suite_add_suite(suite, torture_smb2_readwrite_init(suite));
diff --git a/source4/torture/smb2/timestamps.c b/source4/torture/smb2/timestamps.c
index 9655e5bc164..89b64886e4d 100644
--- a/source4/torture/smb2/timestamps.c
+++ b/source4/torture/smb2/timestamps.c
@@ -27,6 +27,7 @@
#include "torture/smb2/proto.h"
#define BASEDIR "smb2-timestamps"
+#define FNAME "testfile.dat"
static bool test_time_t(struct torture_context *tctx,
struct smb2_tree *tree,
@@ -235,11 +236,11 @@ done:
return ret;
}
-static bool test_time_t_100000000000(struct torture_context *tctx,
+static bool test_time_t_15032385535(struct torture_context *tctx,
struct smb2_tree *tree)
{
- return test_time_t(tctx, tree, "test_time_t_100000000000.txt",
- 100000000000 /* >> INT32_MAX */);
+ return test_time_t(tctx, tree, "test_time_t_15032385535.txt",
+ 15032385535 /* >> INT32_MAX, limit on ext */);
}
static bool test_time_t_10000000000(struct torture_context *tctx,
@@ -287,6 +288,618 @@ static bool test_time_t_1968(struct torture_context *tctx,
-63158400 /* 1968 */);
}
+static bool test_delayed_write_vs_seteof(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create cr;
+ struct smb2_handle h1 = {{0}};
+ struct smb2_handle h2 = {{0}};
+ NTTIME create_time;
+ NTTIME set_time;
+ union smb_fileinfo finfo;
+ union smb_setfileinfo setinfo;
+ struct smb2_close c;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Open file-handle 1\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = BASEDIR "\\" FNAME,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h1 = cr.out.file.handle;
+ create_time = cr.out.create_time;
+ sleep(1);
+
+ torture_comment(tctx, "Write to file-handle 1\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+
+ torture_comment(tctx, "Check writetime hasn't been updated\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ torture_assert_nttime_equal(tctx,
+ finfo.all_info.out.write_time,
+ create_time,
+ "Writetime != set_time (wrong!)\n");
+
+ torture_comment(tctx, "Setinfo EOF on file-handle 1,"
+ " should flush pending writetime update\n");
+
+ setinfo = (union smb_setfileinfo) {
+ .generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION,
+ };
+ setinfo.end_of_file_info.in.file.handle = h1;
+ setinfo.end_of_file_info.in.size = 1; /* same size! */
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Check writetime has been updated "
+ "by the setinfo EOF\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+ if (!(finfo.all_info.out.write_time > create_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "setinfo EOF hasn't updated writetime\n");
+ }
+
+ torture_comment(tctx, "Open file-handle 2\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE,
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = BASEDIR "\\" FNAME,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h2 = cr.out.file.handle;
+
+ torture_comment(tctx, "Set write time on file-handle 2\n");
+
+ setinfo = (union smb_setfileinfo) {
+ .generic.level = RAW_FILEINFO_BASIC_INFORMATION,
+ };
+ setinfo.generic.in.file.handle = h2;
+ unix_to_nt_time(&set_time, time(NULL) + 86400);
+ setinfo.basic_info.in.write_time = set_time;
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ status = smb2_util_close(tree, h2);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h2);
+
+ torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
+
+ c = (struct smb2_close) {
+ .in.file.handle = h1,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h1);
+
+ torture_assert_nttime_equal(tctx,
+ c.out.write_time,
+ set_time,
+ "Writetime != set_time (wrong!)\n");
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ if (!smb2_util_handle_empty(h2)) {
+ smb2_util_close(tree, h2);
+ }
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
+static bool test_delayed_write_vs_flush(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create cr;
+ struct smb2_handle h1 = {{0}};
+ union smb_fileinfo finfo;
+ struct smb2_flush f;
+ struct smb2_close c;
+ NTTIME create_time;
+ NTTIME flush_time;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Open file-handle 1\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = BASEDIR "\\" FNAME,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h1 = cr.out.file.handle;
+ create_time = cr.out.create_time;
+ sleep(1);
+
+ torture_comment(tctx, "Write to file-handle 1\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+
+ torture_comment(tctx, "Check writetime hasn't been updated\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ torture_assert_nttime_equal(tctx,
+ finfo.all_info.out.write_time,
+ create_time,
+ "Writetime != create_time (wrong!)\n");
+
+ torture_comment(tctx, "Flush file, "
+ "should flush pending writetime update\n");
+
+ f = (struct smb2_flush) {
+ .in.file.handle = h1,
+ };
+
+ status = smb2_flush(tree, &f);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "flush failed\n");
+
+ torture_comment(tctx, "Check writetime has been updated "
+ "by the setinfo EOF\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ flush_time = finfo.all_info.out.write_time;
+ if (!(flush_time > create_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "flush hasn't updated writetime\n");
+ }
+
+ torture_comment(tctx, "Close file-handle 1, write-time should not be updated\n");
+
+ c = (struct smb2_close) {
+ .in.file.handle = h1,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h1);
+
+ torture_assert_nttime_equal(tctx,
+ c.out.write_time,
+ flush_time,
+ "writetime != flushtime (wrong!)\n");
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
+static bool test_delayed_write_vs_setbasic_do(struct torture_context *tctx,
+ struct smb2_tree *tree,
+ union smb_setfileinfo *setinfo,
+ bool expect_update)
+{
+ char *path = NULL;
+ struct smb2_create cr;
+ struct smb2_handle h1 = {{0}};
+ NTTIME create_time;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ bool ret = true;
+
+ torture_comment(tctx, "Create testfile\n");
+
+ path = talloc_asprintf(tree, BASEDIR "\\" FNAME ".%" PRIu32,
+ generate_random());
+ torture_assert_not_null_goto(tctx, path, ret, done, "OOM\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.create_disposition = NTCREATEX_DISP_CREATE,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = path,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h1 = cr.out.file.handle;
+ create_time = cr.out.create_time;
+
+ torture_comment(tctx, "Write to file\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+
+ torture_comment(tctx, "Get timestamps\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ torture_assert_nttime_equal(tctx,
+ finfo.all_info.out.write_time,
+ create_time,
+ "Writetime != create_time (wrong!)\n");
+
+ torture_comment(tctx, "Set timestamps\n");
+
+ setinfo->end_of_file_info.in.file.handle = h1;
+ status = smb2_setinfo_file(tree, setinfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Check timestamps\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ if (expect_update) {
+ if (!(finfo.all_info.out.write_time > create_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done, "setinfo basicinfo "
+ "hasn't updated writetime\n");
+ }
+ } else {
+ if (finfo.all_info.out.write_time != create_time) {
+ ret = false;
+ torture_fail_goto(tctx, done, "setinfo basicinfo "
+ "hasn't updated writetime\n");
+ }
+ }
+
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h1);
+
+ status = smb2_util_unlink(tree, path);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+done:
+ TALLOC_FREE(path);
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ return ret;
+}
+
+static bool test_delayed_write_vs_setbasic(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_handle h1 = {{0}};
+ union smb_setfileinfo setinfo;
+ time_t t = time(NULL) - 86400;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ /*
+ * Yes, this is correct, tested against Windows 2016: even if all
+ * timestamp fields are 0, a pending write time is flushed.
+ */
+ torture_comment(tctx, "Test: setting all-0 timestamps flushes?\n");
+
+ setinfo = (union smb_setfileinfo) {
+ .generic.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ };
+ ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
+ if (ret != true) {
+ goto done;
+ }
+
+ torture_comment(tctx, "Test: setting create_time flushes?\n");
+ unix_to_nt_time(&setinfo.basic_info.in.create_time, t);
+ ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
+ if (ret != true) {
+ goto done;
+ }
+
+ torture_comment(tctx, "Test: setting access_time flushes?\n");
+ unix_to_nt_time(&setinfo.basic_info.in.access_time, t);
+ ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
+ if (ret != true) {
+ goto done;
+ }
+
+ torture_comment(tctx, "Test: setting change_time flushes?\n");
+ unix_to_nt_time(&setinfo.basic_info.in.change_time, t);
+ ret = test_delayed_write_vs_setbasic_do(tctx, tree, &setinfo, true);
+ if (ret != true) {
+ goto done;
+ }
+
+done:
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
+static bool test_delayed_1write(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create cr;
+ struct smb2_handle h1 = {{0}};
+ union smb_fileinfo finfo;
+ struct smb2_close c;
+ NTTIME create_time;
+ NTTIME write_time;
+ NTTIME close_time;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Open file-handle 1\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = BASEDIR "\\" FNAME,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h1 = cr.out.file.handle;
+ create_time = cr.out.create_time;
+ sleep(1);
+
+ torture_comment(tctx, "Write to file-handle 1\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+ sleep(3);
+
+ torture_comment(tctx, "Check writetime has been updated\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+ write_time = finfo.all_info.out.write_time;
+
+ if (!(write_time > create_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done,
+ "Write-time not updated (wrong!)\n");
+ }
+
+ torture_comment(tctx, "Close file-handle 1\n");
+ sleep(1);
+
+ c = (struct smb2_close) {
+ .in.file.handle = h1,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h1);
+ close_time = c.out.write_time;
+
+ torture_assert_nttime_equal(tctx, close_time, write_time,
+ "Writetime != close_time (wrong!)\n");
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
+static bool test_delayed_2write(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_create cr;
+ struct smb2_handle h1 = {{0}};
+ union smb_fileinfo finfo;
+ struct smb2_close c;
+ NTTIME create_time;
+ NTTIME write_time;
+ NTTIME write_time2;
+ struct timespec now;
+ NTTIME send_close_time;
+ NTTIME close_time;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Open file\n");
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED,
+ .in.create_disposition = NTCREATEX_DISP_OPEN_IF,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.fname = BASEDIR "\\" FNAME,
+ };
+ status = smb2_create(tree, tctx, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h1 = cr.out.file.handle;
+ create_time = cr.out.create_time;
+ sleep(1);
+
+ torture_comment(tctx, "Write to file\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+ sleep(3);
+
+ torture_comment(tctx, "Check writetime has been updated\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+ write_time = finfo.all_info.out.write_time;
+
+ if (!(write_time > create_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done,
+ "Write-time not updated (wrong!)\n");
+ }
+
+ torture_comment(tctx, "Write a second time\n");
+
+ status = smb2_util_write(tree, h1, "s", 0, 1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+ sleep(3);
+
+ torture_comment(tctx, "Check writetime has NOT been updated\n");
+
+ finfo = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h1,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+ write_time2 = finfo.all_info.out.write_time;
+
+ torture_assert_nttime_equal(tctx, write_time2, write_time,
+ "second write updated write-time (wrong!)\n");
+
+ torture_comment(tctx, "Close file-handle 1\n");
+ sleep(2);
+
+ now = timespec_current();
+ send_close_time = full_timespec_to_nt_time(&now);
+
+ c = (struct smb2_close) {
+ .in.file.handle = h1,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h1);
+ close_time = c.out.write_time;
+
+ if (!(close_time > send_close_time)) {
+ ret = false;
+ torture_fail_goto(tctx, done,
+ "Write-time not updated (wrong!)\n");
+ }
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree, h1);
+ }
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
/*
basic testing of SMB2 timestamps
*/
@@ -294,7 +907,7 @@ struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx)
{
struct torture_suite *suite = torture_suite_create(ctx, "timestamps");
- torture_suite_add_1smb2_test(suite, "time_t_100000000000", test_time_t_100000000000);
+ torture_suite_add_1smb2_test(suite, "time_t_15032385535", test_time_t_15032385535);
torture_suite_add_1smb2_test(suite, "time_t_10000000000", test_time_t_10000000000);
torture_suite_add_1smb2_test(suite, "time_t_4294967295", test_time_t_4294967295);
torture_suite_add_1smb2_test(suite, "time_t_1", test_time_t_1);
@@ -303,6 +916,160 @@ struct torture_suite *torture_smb2_timestamps_init(TALLOC_CTX *ctx)
torture_suite_add_1smb2_test(suite, "time_t_-2", test_time_t_minus_2);
torture_suite_add_1smb2_test(suite, "time_t_1968", test_time_t_1968);
+ /*
+ * Testing of delayed write-time udpates
+ */
+ torture_suite_add_1smb2_test(suite, "delayed-write-vs-seteof", test_delayed_write_vs_seteof);
+ torture_suite_add_1smb2_test(suite, "delayed-write-vs-flush", test_delayed_write_vs_flush);
+ torture_suite_add_1smb2_test(suite, "delayed-write-vs-setbasic", test_delayed_write_vs_setbasic);
+ torture_suite_add_1smb2_test(suite, "delayed-1write", test_delayed_1write);
+ torture_suite_add_1smb2_test(suite, "delayed-2write", test_delayed_2write);
+
+ suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
+
+ return suite;
+}
+
+/*
+ * This test shows that Windows has a timestamp resolution of ~15ms. When so
+ * when a smaller amount of time than that has passed it's not necessarily
+ * detectable on a Windows 2019 and newer who implement immediate timestamp
+ * updates.
+ *
+ * Note that this test relies on a low latency SMB connection. Even with a low
+ * latency connection of eg 1m there's a chance of 1/15 that the first part of
+ * the test expecting no timestamp change fails as the writetime is updated.
+ *
+ * Due to this timing dependency this test is skipped in Samba CI, but it is
+ * preserved here for future SMB2 timestamps behaviour archealogists.
+ *
+ * See also: https://lists.samba.org/archive/cifs-protocol/2019-December/003358.html
+ */
+static bool test_timestamp_resolution1(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ union smb_fileinfo finfo1;
+ const char *fname = BASEDIR "\\" FNAME;
+ struct smb2_create cr;
+ struct smb2_handle h = {{0}};
+ struct smb2_close cl;
+ NTSTATUS status;
+ bool ret = true;
+
+ smb2_deltree(tree, BASEDIR);
+ status = torture_smb2_testdir(tree, BASEDIR, &h);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ status = smb2_util_close(tree, h );
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+
+ torture_comment(tctx, "Write without delay, expect no "
+ "write-time change\n");
+
+ smb2_generic_create(&cr, NULL, false, fname,
+ NTCREATEX_DISP_CREATE,
+ smb2_util_oplock_level(""), 0, 0);
+ status = smb2_create(tree, tree, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h = cr.out.file.handle;
+
+ finfo1 = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ status = smb2_util_write(tree, h, "123456789", 0, 9);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+
+ cl = (struct smb2_close) {
+ .in.file.handle = h,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &cl);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h);
+
+ torture_comment(tctx, "Initial: %s\nClose: %s\n",
+ nt_time_string(tctx, finfo1.basic_info.out.write_time),
+ nt_time_string(tctx, cl.out.write_time));
+
+ torture_assert_u64_equal_goto(tctx,
+ finfo1.basic_info.out.write_time,
+ cl.out.write_time,
+ ret, done,
+ "Write time changed (wrong!)\n");
+
+ torture_comment(tctx, "Write with 20 ms delay, expect "
+ "write-time change\n");
+
+ smb2_generic_create(&cr, NULL, false, fname,
+ NTCREATEX_DISP_OPEN,
+ smb2_util_oplock_level(""), 0, 0);
+ status = smb2_create(tree, tree, &cr);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "create failed\n");
+ h = cr.out.file.handle;
+
+ finfo1 = (union smb_fileinfo) {
+ .generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION,
+ .generic.in.file.handle = h,
+ };
+ status = smb2_getinfo_file(tree, tree, &finfo1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "getinfo failed\n");
+
+ smb_msleep(20);
+
+ status = smb2_util_write(tree, h, "123456789", 0, 9);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "write failed\n");
+
+ cl = (struct smb2_close) {
+ .in.file.handle = h,
+ .in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION,
+ };
+
+ status = smb2_close(tree, &cl);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "close failed\n");
+ ZERO_STRUCT(h);
+
+ torture_comment(tctx, "Initial: %s\nClose: %s\n",
+ nt_time_string(tctx, finfo1.basic_info.out.write_time),
+ nt_time_string(tctx, cl.out.write_time));
+
+ torture_assert_u64_not_equal_goto(
+ tctx,
+ finfo1.basic_info.out.write_time,
+ cl.out.write_time,
+ ret, done,
+ "Write time did not change (wrong!)\n");
+
+done:
+ if (!smb2_util_handle_empty(h)) {
+ smb2_util_close(tree, h);
+ }
+ smb2_deltree(tree, BASEDIR);
+ return ret;
+}
+
+/*
+ basic testing of SMB2 timestamps
+*/
+struct torture_suite *torture_smb2_timestamp_resolution_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "timestamp_resolution");
+
+ torture_suite_add_1smb2_test(suite, "resolution1", test_timestamp_resolution1);
+
suite->description = talloc_strdup(suite, "SMB2 timestamp tests");
return suite;
diff --git a/testprogs/blackbox/test_kinit_mit.sh b/testprogs/blackbox/test_kinit_mit.sh
index d28caecd603..61029a5e04c 100755
--- a/testprogs/blackbox/test_kinit_mit.sh
+++ b/testprogs/blackbox/test_kinit_mit.sh
@@ -134,10 +134,6 @@ testit "enable user with kerberos cache" $VALGRIND $PYTHON $samba_enableaccount
### Test kinit with canonicalization
###########################################################
-# This is currently not working due to an upstream bug in MIT Kerberos. The
-# test will ensure that we get notified when we can turn on canonicalization
-# in ads_krb5_chg_password().
-# https://bugzilla.samba.org/show_bug.cgi?id=14155
upperusername=$(echo $USERNAME | tr '[a-z]' '[A-Z]')
testit "kinit with canonicalize" $samba_texpect $PREFIX/tmpkinitscript $samba_kinit -C $upperusername@$REALM -S kadmin/changepw@$REALM || failed=`expr $failed + 1`
diff --git a/testprogs/blackbox/test_net_ads.sh b/testprogs/blackbox/test_net_ads.sh
index 8bcff006b8e..95c0cf76f90 100755
--- a/testprogs/blackbox/test_net_ads.sh
+++ b/testprogs/blackbox/test_net_ads.sh
@@ -237,6 +237,23 @@ testit "leave+createcomputer" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_P
testit "Remove OU=Servers" $VALGRIND $ldbdel -U$DC_USERNAME%$DC_PASSWORD -H ldap://$SERVER "OU=Servers,$base_dn"
+#
+# Test createupn option of 'net ads join'
+#
+testit "join+createupn" $VALGRIND $net_tool ads join -U$DC_USERNAME%$DC_PASSWORD createupn="host/test-$HOSTNAME@$REALM" || failed=`expr $failed + 1`
+
+testit_grep "checkupn" "userPrincipalName: host/test-$HOSTNAME@$REALM" $ldbsearch -U$DC_USERNAME%$DC_PASSWORD -H ldap://$SERVER.$REALM -s base -b "CN=$HOSTNAME,CN=Computers,$base_dn" || failed=`expr $failed + 1`
+
+dedicated_keytab_file="$PREFIX_ABS/test_net_create_dedicated_krb5.keytab"
+
+testit "create_keytab" $VALGRIND $net_tool ads keytab create --option="kerberosmethod=dedicatedkeytab" --option="dedicatedkeytabfile=$dedicated_keytab_file" || failed=`expr $failed + 1`
+
+testit_grep "checkupn+keytab" "host/test-$HOSTNAME@$REALM" $net_tool ads keytab list --option="kerberosmethod=dedicatedkeytab" --option="dedicatedkeytabfile=$dedicated_keytab_file" || failed=`expr $failed + 1`
+
+rm -f $dedicated_keytab_file
+
+testit "leave+createupn" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=`expr $failed + 1`
+
rm -rf $BASEDIR/$WORKDIR
exit $failed
diff --git a/wscript_configure_system_mitkrb5 b/wscript_configure_system_mitkrb5
index b05ac3f3e50..23587797119 100644
--- a/wscript_configure_system_mitkrb5
+++ b/wscript_configure_system_mitkrb5
@@ -77,6 +77,9 @@ if conf.env.KRB5_CONFIG:
else:
Logs.info('MIT Kerberos %s detected, MIT krb5 build can proceed' % (krb5_version))
+ if parse_version(krb5_version) < parse_version('1.18'):
+ conf.DEFINE('HAVE_MIT_KRB5_PRE_1_18', 1)
+
conf.CHECK_CFG(args="--cflags --libs", package="com_err", uselib_store="com_err")
conf.CHECK_FUNCS_IN('_et_list', 'com_err')
conf.CHECK_HEADERS('com_err.h', lib='com_err')