1
0
mirror of https://github.com/samba-team/samba.git synced 2025-09-12 13:44:19 +03:00

smb2_server: don't cancel pending request if at least one channel is still alive

In order to allow replays of requests on a channel failure, we should
not cancel pending requests, the strategie that seems to make windows
clients happy is to let the requests running and return
NT_STATUS_FILE_NOT_AVAILABLE as long as the original request is still
pending.

Here we introduce xconn->transport.shutdown_wait_queue, this is used
to keep the xconn alive for the lifetime of pending requests.

Now we only cancel pending requests if the disconnected connection
is the last channel for a session.

In that case smbXsrv_session_remove_channel() and
smb2srv_session_shutdown_send() will take care of it.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
Stefan Metzmacher
2021-02-24 17:44:12 +01:00
committed by Jeremy Allison
parent 01b675ab32
commit f0e5537834
4 changed files with 53 additions and 34 deletions

View File

@@ -1,12 +1,3 @@
# These are temporary in order to demonstrate the current bugs
^samba3.smb2.replay.dhv2-pending2n-vs-oplock-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2n-vs-lease-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2l-vs-oplock-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2l-vs-lease-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2o-vs-oplock-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2o-vs-lease-sane.nt4_dc
^samba3.smb2.replay.dhv2-pending2n-vs-oplock-sane.ad_dc
^samba3.smb2.replay.dhv2-pending2o-vs-oplock-sane.ad_dc
# These tests demonstrate the broken Windows behavior # These tests demonstrate the broken Windows behavior
# and check for ACCESS_DENIED instead of FILE_NOT_AVAILABLE # and check for ACCESS_DENIED instead of FILE_NOT_AVAILABLE
# See https://bugzilla.samba.org/show_bug.cgi?id=14449 # See https://bugzilla.samba.org/show_bug.cgi?id=14449

View File

@@ -368,6 +368,7 @@ struct smbXsrv_connection {
struct { struct {
NTSTATUS status; NTSTATUS status;
bool terminating; bool terminating;
struct tevent_queue *shutdown_wait_queue;
int sock; int sock;
struct tevent_fd *fde; struct tevent_fd *fde;

View File

@@ -1498,7 +1498,6 @@ size_t smbXsrv_client_valid_connections(struct smbXsrv_client *client)
} }
struct smbXsrv_connection_shutdown_state { struct smbXsrv_connection_shutdown_state {
struct tevent_queue *wait_queue;
struct smbXsrv_connection *xconn; struct smbXsrv_connection *xconn;
}; };
@@ -1521,6 +1520,7 @@ static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
*/ */
SMB_ASSERT(!NT_STATUS_IS_OK(xconn->transport.status)); SMB_ASSERT(!NT_STATUS_IS_OK(xconn->transport.status));
SMB_ASSERT(xconn->transport.terminating); SMB_ASSERT(xconn->transport.terminating);
SMB_ASSERT(xconn->transport.shutdown_wait_queue == NULL);
req = tevent_req_create(mem_ctx, &state, req = tevent_req_create(mem_ctx, &state,
struct smbXsrv_connection_shutdown_state); struct smbXsrv_connection_shutdown_state);
@@ -1531,45 +1531,48 @@ static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
state->xconn = xconn; state->xconn = xconn;
tevent_req_defer_callback(req, ev); tevent_req_defer_callback(req, ev);
status = smbXsrv_session_disconnect_xconn(xconn); xconn->transport.shutdown_wait_queue =
if (tevent_req_nterror(req, status)) { tevent_queue_create(state, "smbXsrv_connection_shutdown_queue");
return tevent_req_post(req, ev); if (tevent_req_nomem(xconn->transport.shutdown_wait_queue, req)) {
}
state->wait_queue = tevent_queue_create(state, "smbXsrv_connection_shutdown_queue");
if (tevent_req_nomem(state->wait_queue, req)) {
return tevent_req_post(req, ev); return tevent_req_post(req, ev);
} }
for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
/*
* The connection is gone so we
* don't need to take care of
* any crypto
*/
preq->session = NULL;
preq->do_signing = false;
preq->do_encryption = false;
preq->preauth = NULL;
if (preq->subreq != NULL) {
tevent_req_cancel(preq->subreq);
}
/* /*
* Now wait until the request is finished. * Now wait until the request is finished.
* *
* We don't set a callback, as we just want to block the * We don't set a callback, as we just want to block the
* wait queue and the talloc_free() of the request will * wait queue and the talloc_free() of the request will
* remove the item from the wait queue. * remove the item from the wait queue.
*
* Note that we don't cancel the requests here
* in order to keep the replay detection logic correct.
*
* However if we teardown the last channel of
* a connection, we'll call some logic via
* smbXsrv_session_disconnect_xconn()
* -> smbXsrv_session_disconnect_xconn_callback()
* -> smbXsrv_session_remove_channel()
* -> smb2srv_session_shutdown_send()
* will indeed cancel the request.
*/ */
subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); subreq = tevent_queue_wait_send(preq, ev,
xconn->transport.shutdown_wait_queue);
if (tevent_req_nomem(subreq, req)) { if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev); return tevent_req_post(req, ev);
} }
} }
len = tevent_queue_length(state->wait_queue); /*
* This may attach sessions with num_channels == 0
* to xconn->transport.shutdown_wait_queue.
*/
status = smbXsrv_session_disconnect_xconn(xconn);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
len = tevent_queue_length(xconn->transport.shutdown_wait_queue);
if (len == 0) { if (len == 0) {
tevent_req_done(req); tevent_req_done(req);
return tevent_req_post(req, ev); return tevent_req_post(req, ev);
@@ -1580,7 +1583,7 @@ static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
* this way we get notified when all pending requests are finished * this way we get notified when all pending requests are finished
* and send to the socket. * and send to the socket.
*/ */
subreq = tevent_queue_wait_send(state, ev, state->wait_queue); subreq = tevent_queue_wait_send(state, ev, xconn->transport.shutdown_wait_queue);
if (tevent_req_nomem(subreq, req)) { if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev); return tevent_req_post(req, ev);
} }

View File

@@ -1596,8 +1596,32 @@ NTSTATUS smbXsrv_session_remove_channel(struct smbXsrv_session *session,
global->num_channels--; global->num_channels--;
if (global->num_channels == 0) { if (global->num_channels == 0) {
struct smbXsrv_client *client = session->client; struct smbXsrv_client *client = session->client;
struct tevent_queue *xconn_wait_queue =
xconn->transport.shutdown_wait_queue;
struct tevent_req *subreq = NULL; struct tevent_req *subreq = NULL;
/*
* Let the connection wait until the session is
* destroyed.
*
* We don't set a callback, as we just want to block the
* wait queue and the talloc_free() of the session will
* remove the item from the wait queue in order
* to remove allow the connection to disapear.
*/
if (xconn_wait_queue != NULL) {
subreq = tevent_queue_wait_send(session,
client->raw_ev_ctx,
xconn_wait_queue);
if (subreq == NULL) {
status = NT_STATUS_NO_MEMORY;
DBG_ERR("tevent_queue_wait_send() session(%llu) failed: %s\n",
(unsigned long long)session->global->session_wire_id,
nt_errstr(status));
return status;
}
}
/* /*
* This is garanteed to set * This is garanteed to set
* session->status = NT_STATUS_USER_SESSION_DELETED * session->status = NT_STATUS_USER_SESSION_DELETED