1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-08 21:18:16 +03:00

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@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>

Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Fri Sep  2 20:59:15 UTC 2022 on sn-devel-184
This commit is contained in:
Stefan Metzmacher 2022-08-30 20:45:50 +02:00 committed by Jeremy Allison
parent 21ef01e7b8
commit 8591d94243
4 changed files with 255 additions and 14 deletions

View File

@ -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,

View File

@ -1 +0,0 @@
samba3.blackbox.smbXsrv_client_cross_node.smbclient.against.node1

View File

@ -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 {

View File

@ -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);
@ -792,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,
@ -874,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;
}
@ -1099,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) {
int sock_fd;
uint8_t fd_idx;
for (fd_idx = 0; fd_idx < rec->num_fds; fd_idx++) {
sock_fd = rec->fds[fd_idx];
close(sock_fd);
}
rec->num_fds = 0;
TALLOC_FREE(rec);
}
subreq = messaging_filtered_read_send(client,
client->raw_ev_ctx,
client->msg_ctx,
smbXsrv_client_connection_drop_filter,
client);
if (subreq == NULL) {
const char *r;
r = "messaging_read_send(MSG_SMBXSRV_CONNECTION_DROP failed";
exit_server_cleanly(r);
return;
}
tevent_req_set_callback(subreq, smbXsrv_client_connection_drop_loop, client);
client->connection_drop_subreq = subreq;
}
NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client)
{
struct smbXsrv_client_table *table = client->table;
@ -1117,6 +1329,7 @@ NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client)
}
TALLOC_FREE(client->connection_pass_subreq);
TALLOC_FREE(client->connection_drop_subreq);
client->global->db_rec = smbXsrv_client_global_fetch_locked(
table->global.db_ctx,