[SCM] Samba Shared Repository - branch master updated
Jeremy Allison
jra at samba.org
Fri Sep 2 21:00:02 UTC 2022
The branch, master has been updated
via 8591d942437 smbXsrv_client: notify a different node to drop a connection by client guid.
via 21ef01e7b83 smbXsrv_client: correctly check in negotiate_request.length smbXsrv_client_connection_pass[ed]_*
via 0efcfaa49c3 s3:tests: add test_smbXsrv_client_cross_node.sh
via 3fd18a0d5b7 s3:tests: let test_smbXsrv_client_dead_rec.sh cleanup the correct files
from 2643b7b5746 Cleanup and bug fixes in vxfs vfs code.
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 8591d9424371e173b079d5c8a267ea4c2cb266ad
Author: Stefan Metzmacher <metze at samba.org>
Date: Tue Aug 30 20:45:50 2022 +0200
smbXsrv_client: notify a different node to drop a connection by client guid.
If a client disconnected all its interfaces and reconnects when
the come back, it will likely start from any ip address returned
dns, which means it can try to connect to a different ctdb node.
The old node may not have noticed the disconnect and still holds
the client_guid based smbd.
Up unil now the new node returned NT_STATUS_NOT_SUPPORTED to
the SMB2 Negotiate request, as messaging_send_iov[_from]() will
return -1/ENOSYS if a file descriptor os passed to a process on
a different node.
Now we tell the other node to teardown all client connections
belonging to the client-guid.
Note that this is not authenticated, but if an attacker can
capture the client-guid, he can also inject TCP resets anyway,
to get the same effect.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15159
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Autobuild-User(master): Jeremy Allison <jra at samba.org>
Autobuild-Date(master): Fri Sep 2 20:59:15 UTC 2022 on sn-devel-184
commit 21ef01e7b8368caa050ed82b9d787d1679220b2b
Author: Stefan Metzmacher <metze at samba.org>
Date: Tue Aug 30 16:56:12 2022 +0200
smbXsrv_client: correctly check in negotiate_request.length smbXsrv_client_connection_pass[ed]_*
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15159
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
commit 0efcfaa49c3d61f2c8116ebafd55b72d3277d0d8
Author: Stefan Metzmacher <metze at samba.org>
Date: Wed Aug 31 14:04:10 2022 +0200
s3:tests: add test_smbXsrv_client_cross_node.sh
This demonstrates that a client-guid connected to ctdb node 0
caused a connection with the same client-guid to be rejected by
ctdb node 1. Node 1 rejects the SMB2 Negotiate with
NT_STATUS_NOT_SUPPORTED, because passing the multi-channel connection
to a different node is not supported.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15159
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
commit 3fd18a0d5b77a9f78c595852c342d4c8c33fac61
Author: Stefan Metzmacher <metze at samba.org>
Date: Wed Aug 31 13:55:19 2022 +0200
s3:tests: let test_smbXsrv_client_dead_rec.sh cleanup the correct files
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15159
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
-----------------------------------------------------------------------
Summary of changes:
librpc/idl/messaging.idl | 1 +
source3/librpc/idl/smbXsrv.idl | 28 +++
.../script/tests/test_smbXsrv_client_cross_node.sh | 95 ++++++++
.../script/tests/test_smbXsrv_client_dead_rec.sh | 2 +-
source3/selftest/tests.py | 7 +
source3/smbd/smbXsrv_client.c | 266 +++++++++++++++++++--
6 files changed, 375 insertions(+), 24 deletions(-)
create mode 100755 source3/script/tests/test_smbXsrv_client_cross_node.sh
Changeset truncated at 500 lines:
diff --git a/librpc/idl/messaging.idl b/librpc/idl/messaging.idl
index d6929c799ad..5d217c03f5b 100644
--- a/librpc/idl/messaging.idl
+++ b/librpc/idl/messaging.idl
@@ -138,6 +138,7 @@ interface messaging
MSG_SMBXSRV_SESSION_CLOSE = 0x0600,
MSG_SMBXSRV_CONNECTION_PASS = 0x0601,
MSG_SMBXSRV_CONNECTION_PASSED = 0x0602,
+ MSG_SMBXSRV_CONNECTION_DROP = 0x0603,
/* source4 and NTVFS smb server messages */
MSG_BRL_RETRY = 0x0700,
diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl
index fc502009b3b..ec65a5c1a61 100644
--- a/source3/librpc/idl/smbXsrv.idl
+++ b/source3/librpc/idl/smbXsrv.idl
@@ -143,6 +143,7 @@ interface smbXsrv
boolean8 server_multi_channel_enabled;
hyper next_channel_id;
[ignore] struct tevent_req *connection_pass_subreq;
+ [ignore] struct tevent_req *connection_drop_subreq;
/*
* A List of pending breaks.
@@ -194,6 +195,33 @@ interface smbXsrv
[in] smbXsrv_connection_passB blob
);
+ /*
+ * smbXsrv_connection_drop is used in the MSG_SMBXSRV_CONNECTION_DROP
+ * message as reaction the record is deleted.
+ */
+ typedef struct {
+ GUID client_guid;
+ server_id src_server_id;
+ NTTIME xconn_connect_time;
+ server_id dst_server_id;
+ NTTIME client_connect_time;
+ } smbXsrv_connection_drop0;
+
+ typedef union {
+ [case(0)] smbXsrv_connection_drop0 *info0;
+ [default] hyper *dummy;
+ } smbXsrv_connection_dropU;
+
+ typedef [public] struct {
+ smbXsrv_version_values version;
+ [value(0)] uint32 reserved;
+ [switch_is(version)] smbXsrv_connection_dropU info;
+ } smbXsrv_connection_dropB;
+
+ void smbXsrv_connection_drop_decode(
+ [in] smbXsrv_connection_dropB blob
+ );
+
/* sessions */
typedef [public,bitmap8bit] bitmap {
diff --git a/source3/script/tests/test_smbXsrv_client_cross_node.sh b/source3/script/tests/test_smbXsrv_client_cross_node.sh
new file mode 100755
index 00000000000..ff826924b49
--- /dev/null
+++ b/source3/script/tests/test_smbXsrv_client_cross_node.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+#
+# Test smbd let cluster node 0 destroy the connection,
+# if the client with a specific client-guid connections to node 1
+#
+
+if [ $# -lt 4 ]; then
+ echo Usage: test_smbXsrv_client_cross_node.sh SERVERCONFFILE NODE0 NODE1 SHARENAME
+ exit 1
+fi
+
+CONF=$1
+NODE0=$2
+NODE1=$3
+SHARE=$4
+
+SMBCLIENT="$BINDIR/smbclient"
+SMBSTATUS="$BINDIR/smbstatus"
+
+incdir=$(dirname "$0")/../../../testprogs/blackbox
+. "$incdir"/subunit.sh
+
+failed=0
+
+test_smbclient()
+{
+ name="$1"
+ server="$2"
+ share="$3"
+ cmd="$4"
+ shift
+ shift
+ subunit_start_test "$name"
+ output=$($VALGRIND $SMBCLIENT //$server/$share -c "$cmd" "$@" 2>&1)
+ status=$?
+ if [ x$status = x0 ]; then
+ subunit_pass_test "$name"
+ else
+ echo "$output" | subunit_fail_test "$name"
+ fi
+ return $status
+}
+
+cd "$SELFTEST_TMPDIR" || exit 1
+
+# Create the smbclient communication pipes.
+rm -f smbclient-stdin smbclient-stdout smbclient-stderr
+mkfifo smbclient-stdin smbclient-stdout smbclient-stderr
+
+UID_WRAPPER_ROOT=1
+export UID_WRAPPER_ROOT
+
+smbstatus_num_sessions()
+{
+ UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$SMBSTATUS" "$CONF" --json | jq -M '.sessions | length'
+}
+
+testit_grep "step1: smbstatus 0 sessions" '^0$' smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+test_smbclient "smbclient against node0[${NODE0}]" "${NODE0}" "${SHARE}" "ls" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+ --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+ || failed=$(expr $failed + 1)
+
+testit_grep "step2: smbstatus 0 sessions" '^0$' smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+CLI_FORCE_INTERACTIVE=1
+export CLI_FORCE_INTERACTIVE
+
+testit "start backgroup smbclient against node0[${NODE0}]" true || failed=$(expr $failed + 1)
+
+# Connect a first time
+${SMBCLIENT} //"${NODE0}"/"${SHARE}" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+ --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+ <smbclient-stdin >smbclient-stdout 2>smbclient-stderr &
+CLIENT_PID=$!
+
+exec 100>smbclient-stdin 101<smbclient-stdout 102<smbclient-stderr
+
+testit "sleep 1 second" true || failed=$(expr $failed + 1)
+sleep 1
+
+testit_grep "step3: smbstatus 1 session" '^1$' smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+# Connect a second time
+unset CLI_FORCE_INTERACTIVE
+test_smbclient "smbclient against node1[${NODE1}]" "${NODE1}" "${SHARE}" "ls" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+ --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+ || failed=$(expr $failed + 1)
+
+kill $CLIENT_PID
+rm -f smbclient-stdin smbclient-stdout smbclient-stderr
+
+testit_grep "step24: smbstatus 0 sessions" '^0$' smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+testok "$0" "$failed"
diff --git a/source3/script/tests/test_smbXsrv_client_dead_rec.sh b/source3/script/tests/test_smbXsrv_client_dead_rec.sh
index a29350878bd..0a287370944 100755
--- a/source3/script/tests/test_smbXsrv_client_dead_rec.sh
+++ b/source3/script/tests/test_smbXsrv_client_dead_rec.sh
@@ -62,7 +62,7 @@ ${SMBCLIENT} //"${SERVER}"/"${SHARE}" -U"${USER}"%"${PASSWORD}" \
--option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
-c exit
-rm -f smbclient-stdin smbclient-stdout aio_outstanding_testfile
+rm -f smbclient-stdin smbclient-stdout smbclient-stderr
#
# Ensure the panic count didn't change.
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 3dc3105c057..4b192cf4ad5 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -1419,6 +1419,13 @@ plantestsuite("samba3.blackbox.smbXsrv_client_dead_rec", "fileserver:local",
'$SERVER_IP',
"tmp"])
+plantestsuite("samba3.blackbox.smbXsrv_client_cross_node", "clusteredmember:local",
+ [os.path.join(samba3srcdir,
+ "script/tests/test_smbXsrv_client_cross_node.sh"),
+ configuration,
+ 'ctdb0', 'ctdb1',
+ "tmp"])
+
env = 'fileserver'
plantestsuite("samba3.blackbox.virus_scanner", "%s:local" % (env),
[os.path.join(samba3srcdir,
diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c
index 079ca80ad12..d7a6fa35bf0 100644
--- a/source3/smbd/smbXsrv_client.c
+++ b/source3/smbd/smbXsrv_client.c
@@ -346,6 +346,55 @@ static NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req
return NT_STATUS_OK;
}
+static NTSTATUS smb2srv_client_connection_drop(struct smbd_smb2_request *smb2req,
+ struct smbXsrv_client_global0 *global)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ struct smbXsrv_connection_drop0 drop_info0;
+ struct smbXsrv_connection_dropB drop_blob;
+ struct iovec iov;
+
+ drop_info0 = (struct smbXsrv_connection_drop0) {
+ .client_guid = global->client_guid,
+ .src_server_id = smb2req->xconn->client->global->server_id,
+ .xconn_connect_time = smb2req->xconn->client->global->initial_connect_time,
+ .dst_server_id = global->server_id,
+ .client_connect_time = global->initial_connect_time,
+ };
+
+ ZERO_STRUCT(drop_blob);
+ drop_blob.version = smbXsrv_version_global_current();
+ drop_blob.info.info0 = &drop_info0;
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &drop_blob,
+ (ndr_push_flags_fn_t)ndr_push_smbXsrv_connection_dropB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ iov.iov_base = blob.data;
+ iov.iov_len = blob.length;
+
+ status = messaging_send_iov(smb2req->xconn->client->msg_ctx,
+ global->server_id,
+ MSG_SMBXSRV_CONNECTION_DROP,
+ &iov, 1,
+ NULL, 0);
+ data_blob_free(&blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
static NTSTATUS smbXsrv_client_global_store(struct smbXsrv_client_global0 *global)
{
struct smbXsrv_client_globalB global_blob;
@@ -552,15 +601,17 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req)
return;
}
- subreq = messaging_filtered_read_send(state,
- state->ev,
- client->msg_ctx,
- smb2srv_client_mc_negprot_filter,
- NULL);
- if (tevent_req_nomem(subreq, req)) {
- return;
+ if (procid_is_local(&global->server_id)) {
+ subreq = messaging_filtered_read_send(state,
+ state->ev,
+ client->msg_ctx,
+ smb2srv_client_mc_negprot_filter,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2srv_client_mc_negprot_done, req);
}
- tevent_req_set_callback(subreq, smb2srv_client_mc_negprot_done, req);
/*
* If the record changed, but we are not happy with the change yet,
@@ -593,11 +644,20 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req)
}
tevent_req_set_callback(subreq, smb2srv_client_mc_negprot_watched, req);
- status = smb2srv_client_connection_pass(state->smb2req,
- global);
- TALLOC_FREE(global);
- if (tevent_req_nterror(req, status)) {
- return;
+ if (procid_is_local(&global->server_id)) {
+ status = smb2srv_client_connection_pass(state->smb2req,
+ global);
+ TALLOC_FREE(global);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else {
+ status = smb2srv_client_connection_drop(state->smb2req,
+ global);
+ TALLOC_FREE(global);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
}
TALLOC_FREE(state->db_rec);
@@ -614,10 +674,6 @@ static bool smb2srv_client_mc_negprot_filter(struct messaging_rec *rec, void *pr
return false;
}
- if (rec->buf.length < SMB2_HDR_BODY) {
- return false;
- }
-
return true;
}
@@ -707,6 +763,14 @@ static void smb2srv_client_mc_negprot_done(struct tevent_req *subreq)
return;
}
+ if (passed_info0->negotiate_request.length != 0) {
+ DBG_ERR("negotiate_request.length[%zu]\n",
+ passed_info0->negotiate_request.length);
+ NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
tevent_req_nterror(req, NT_STATUS_MESSAGE_RETRIEVED);
}
@@ -788,6 +852,8 @@ static int smbXsrv_client_destructor(struct smbXsrv_client *client)
static bool smbXsrv_client_connection_pass_filter(struct messaging_rec *rec, void *private_data);
static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq);
+static bool smbXsrv_client_connection_drop_filter(struct messaging_rec *rec, void *private_data);
+static void smbXsrv_client_connection_drop_loop(struct tevent_req *subreq);
NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
struct tevent_context *ev_ctx,
@@ -870,6 +936,18 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
tevent_req_set_callback(subreq, smbXsrv_client_connection_pass_loop, client);
client->connection_pass_subreq = subreq;
+ subreq = messaging_filtered_read_send(client,
+ client->raw_ev_ctx,
+ client->msg_ctx,
+ smbXsrv_client_connection_drop_filter,
+ client);
+ if (subreq == NULL) {
+ TALLOC_FREE(client);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, smbXsrv_client_connection_drop_loop, client);
+ client->connection_drop_subreq = subreq;
+
*_client = client;
return NT_STATUS_OK;
}
@@ -931,12 +1009,6 @@ static bool smbXsrv_client_connection_pass_filter(struct messaging_rec *rec, voi
return false;
}
- if (rec->buf.length < SMB2_HDR_BODY) {
- return false;
- }
-
- /* TODO: verify client_guid...? */
-
return true;
}
@@ -1029,6 +1101,15 @@ static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq)
goto next;
}
+ if (pass_info0->negotiate_request.length < SMB2_HDR_BODY) {
+ DBG_WARNING("negotiate_request.length[%zu]\n",
+ pass_info0->negotiate_request.length);
+ if (DEBUGLVL(DBGLVL_WARNING)) {
+ NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+ }
+ goto next;
+ }
+
status = smb2srv_client_connection_passed(client, pass_info0);
if (!NT_STATUS_IS_OK(status)) {
const char *r = "smb2srv_client_connection_passed() failed";
@@ -1092,6 +1173,144 @@ next:
client->connection_pass_subreq = subreq;
}
+static bool smbXsrv_client_connection_drop_filter(struct messaging_rec *rec, void *private_data)
+{
+ if (rec->msg_type != MSG_SMBXSRV_CONNECTION_DROP) {
+ return false;
+ }
+
+ if (rec->num_fds != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static void smbXsrv_client_connection_drop_loop(struct tevent_req *subreq)
+{
+ struct smbXsrv_client *client =
+ tevent_req_callback_data(subreq,
+ struct smbXsrv_client);
+ int ret;
+ struct messaging_rec *rec = NULL;
+ struct smbXsrv_connection_dropB drop_blob;
+ enum ndr_err_code ndr_err;
+ struct smbXsrv_connection_drop0 *drop_info0 = NULL;
+ struct server_id_buf src_server_id_buf = {};
+ NTSTATUS status;
+
+ client->connection_drop_subreq = NULL;
+
+ ret = messaging_filtered_read_recv(subreq, talloc_tos(), &rec);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ goto next;
+ }
+
+ if (rec->num_fds != 0) {
+ DBG_ERR("MSG_SMBXSRV_CONNECTION_DROP: num_fds[%u]\n",
+ rec->num_fds);
+ goto next;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&rec->buf, rec, &drop_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_connection_dropB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_WARNING("ndr_pull_struct_blob - %s\n", nt_errstr(status));
+ goto next;
+ }
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ }
+
+ if (drop_blob.version != SMBXSRV_VERSION_0) {
+ DBG_ERR("ignore invalid version %u\n", drop_blob.version);
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ goto next;
+ }
+
+ drop_info0 = drop_blob.info.info0;
+ if (drop_info0 == NULL) {
+ DBG_ERR("ignore NULL info %u\n", drop_blob.version);
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ goto next;
+ }
+
+ if (!GUID_equal(&client->global->client_guid, &drop_info0->client_guid))
+ {
+ struct GUID_txt_buf buf1, buf2;
+
+ DBG_WARNING("client's client_guid [%s] != droped guid [%s]\n",
+ GUID_buf_string(&client->global->client_guid,
+ &buf1),
+ GUID_buf_string(&drop_info0->client_guid,
+ &buf2));
+ if (DEBUGLVL(DBGLVL_WARNING)) {
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ }
+ goto next;
+ }
+
+ if (client->global->initial_connect_time !=
+ drop_info0->client_connect_time)
+ {
+ DBG_WARNING("client's initial connect time [%s] (%llu) != "
+ "droped initial connect time [%s] (%llu)\n",
+ nt_time_string(talloc_tos(),
+ client->global->initial_connect_time),
+ (unsigned long long)client->global->initial_connect_time,
+ nt_time_string(talloc_tos(),
+ drop_info0->client_connect_time),
+ (unsigned long long)drop_info0->client_connect_time);
+ if (DEBUGLVL(DBGLVL_WARNING)) {
+ NDR_PRINT_DEBUG(smbXsrv_connection_dropB, &drop_blob);
+ }
+ goto next;
+ }
+
+ /*
+ * Disconnect all client connections, which means we will tear down all
+ * sessions, tcons and non-durable opens. At the end we will remove our
+ * smbXsrv_client_global.tdb record, which will wake up the watcher on
+ * the other node in order to let it take over the client.
+ *
+ * The client will have to reopen all sessions, tcons and durable opens.
+ */
+ smbd_server_disconnect_client(client,
+ server_id_str_buf(drop_info0->src_server_id, &src_server_id_buf));
+ return;
+
+next:
+ if (rec != NULL) {
--
Samba Shared Repository
More information about the samba-cvs
mailing list