From 5d66d5b84f87267243dcd5223210906ce589af91 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Oct 2022 14:57:18 +0200 Subject: [PATCH] smbXsrv_client: handle NAME_NOT_FOUND from smb2srv_client_connection_{pass,drop}() If we get NT_STATUS_OBJECT_NOT_FOUND from smb2srv_client_connection_{pass,drop}() we should just keep the connection and overwrite the stale record in smbXsrv_client_global.tdb. It's basically a race with serverid_exists() and a process that doesn't cleanly teardown. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15200 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- source3/smbd/smbXsrv_client.c | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c index 2d9938606f3..292690efb17 100644 --- a/source3/smbd/smbXsrv_client.c +++ b/source3/smbd/smbXsrv_client.c @@ -189,6 +189,7 @@ static void smbXsrv_client_global_verify_record(struct db_record *db_rec, bool *is_free, bool *was_free, TALLOC_CTX *mem_ctx, + const struct server_id *dead_server_id, struct smbXsrv_client_global0 **_g, uint32_t *pseqnum) { @@ -198,6 +199,7 @@ static void smbXsrv_client_global_verify_record(struct db_record *db_rec, struct smbXsrv_client_globalB global_blob; enum ndr_err_code ndr_err; struct smbXsrv_client_global0 *global = NULL; + bool dead = false; bool exists; TALLOC_CTX *frame = talloc_stackframe(); @@ -254,6 +256,22 @@ static void smbXsrv_client_global_verify_record(struct db_record *db_rec, global = global_blob.info.info0; + dead = server_id_equal(dead_server_id, &global->server_id); + if (dead) { + struct server_id_buf tmp; + + DBG_NOTICE("key '%s' server_id %s is already dead.\n", + hex_encode_talloc(frame, key.dptr, key.dsize), + server_id_str_buf(global->server_id, &tmp)); + if (DEBUGLVL(DBGLVL_NOTICE)) { + NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob); + } + TALLOC_FREE(frame); + dbwrap_record_delete(db_rec); + *is_free = true; + return; + } + exists = serverid_exists(&global->server_id); if (!exists) { struct server_id_buf tmp; @@ -534,6 +552,7 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req) struct tevent_req *subreq = NULL; NTSTATUS status; uint32_t seqnum = 0; + struct server_id last_server_id = { .pid = 0, }; TALLOC_FREE(state->filter_subreq); SMB_ASSERT(state->db_rec == NULL); @@ -545,10 +564,14 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req) return; } +verify_again: + TALLOC_FREE(global); + smbXsrv_client_global_verify_record(state->db_rec, &is_free, NULL, state, + &last_server_id, &global, &seqnum); if (is_free) { @@ -602,6 +625,16 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req) return; } + /* + * If last_server_id is set, we expect + * smbXsrv_client_global_verify_record() + * to detect the already dead global->server_id + * as state->db_rec is still locked and its + * value didn't change. + */ + SMB_ASSERT(last_server_id.pid == 0); + last_server_id = global->server_id; + if (procid_is_local(&global->server_id)) { subreq = messaging_filtered_read_send(state, state->ev, @@ -618,12 +651,28 @@ static void smb2srv_client_mc_negprot_next(struct tevent_req *req) if (procid_is_local(&global->server_id)) { status = smb2srv_client_connection_pass(state->smb2req, global); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + /* + * We remembered last_server_id = global->server_id + * above, so we'll treat it as dead in the + * next round to smbXsrv_client_global_verify_record(). + */ + goto verify_again; + } if (tevent_req_nterror(req, status)) { return; } } else { status = smb2srv_client_connection_drop(state->smb2req, global); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + /* + * We remembered last_server_id = global->server_id + * above, so we'll treat it as dead in the + * next round to smbXsrv_client_global_verify_record(). + */ + goto verify_again; + } if (tevent_req_nterror(req, status)) { return; } -- 2.11.4.GIT