smbXsrv_client: handle NAME_NOT_FOUND from smb2srv_client_connection_{pass,drop}()
authorStefan Metzmacher <metze@samba.org>
Wed, 12 Oct 2022 12:57:18 +0000 (14:57 +0200)
committerJule Anger <janger@samba.org>
Tue, 18 Oct 2022 13:32:11 +0000 (13:32 +0000)
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 <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
(cherry picked from commit 5d66d5b84f87267243dcd5223210906ce589af91)

source3/smbd/smbXsrv_client.c

index 0a958842d34104f5664c89029a217c97da3a19ac..f57bc724910d0ccf384aa0f9fd4cc4140cf3c256 100644 (file)
@@ -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;
                }