smbXsrv_client: move the connection passing to smb2srv_client_mc_negprot_send/recv
authorStefan Metzmacher <metze@samba.org>
Mon, 6 Jul 2020 15:27:05 +0000 (17:27 +0200)
committerJeremy Allison <jra@samba.org>
Sat, 6 Mar 2021 03:30:06 +0000 (03:30 +0000)
We need a full request/response pair in order to avoid races in
the multichannel connection passing.

smb2srv_client_mc_negprot_send/recv locks the
db record for the given client_guid.

If there's no entry found, we add ourself and
return NT_STATUS_OK.

If there's an existing process for that client guid
we start messaging_filtered_read_send()
dbwrap_watched_watch_send() before calling
smb2srv_client_connection_pass().

Then we release the lock and wait for either
MSG_SMBXSRV_CONNECTION_PASSED to arrive or
retry if dbwrap_watched_watch_recv signaled
a change in the database.

If we got MSG_SMBXSRV_CONNECTION_PASSED we'll
return NT_STATUS_MESSAGE_RETRIEVED in order to
signal that the other process will take care of
the connection and we terminate the current process.

All that is done completely async, which means that
the IDLE_CLOSED_TIMEOUT (60 seconds) may trigger
deadtime_fn(), which will send itself a MSG_SHUTDOWN.
So the process that accepted the tcp connection
exists if there was no MSG_SMBXSRV_CONNECTION_PASSED
within 60 seconds.

However the fd may still exists in the kernel (and
the new connection may still be handed to the other
process. If that process somehow exists before
there's no way to prevent a connection termination
for the client.

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

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): Sat Mar  6 03:30:06 UTC 2021 on sn-devel-184

librpc/idl/messaging.idl
source3/librpc/idl/smbXsrv.idl
source3/smbd/globals.h
source3/smbd/smb2_negprot.c
source3/smbd/smbXsrv_client.c

index 8160c76149cc0fa5b5b535eeb1ab00b4aa4eadd2..06a72520f8ba0442b5aca55bae2e7d835f3a5208 100644 (file)
@@ -136,6 +136,7 @@ interface messaging
                /* smbXsrv messages */
                MSG_SMBXSRV_SESSION_CLOSE       = 0x0600,
                MSG_SMBXSRV_CONNECTION_PASS     = 0x0601,
+               MSG_SMBXSRV_CONNECTION_PASSED   = 0x0602,
 
                /* source4 and NTVFS smb server messages */
                MSG_BRL_RETRY                   = 0x0700,
index 78fc3644d2fcd3ee712f2160799b2f98162448d4..09e3ca351f38c42ca50c309a81fe42dc2794b051 100644 (file)
@@ -167,11 +167,15 @@ interface smbXsrv
 
        /*
         * smbXsrv_connection_pass is used in the MSG_SMBXSRV_CONNECTION_PASS
-        * message
+        * message and echo'ed as MSG_SMBXSRV_CONNECTION_PASSED message with
+        * negotiate_request.length = 0.
         */
        typedef struct {
-               NTTIME                                  initial_connect_time;
                GUID                                    client_guid;
+               server_id                               src_server_id;
+               NTTIME                                  xconn_connect_time;
+               server_id                               dst_server_id;
+               NTTIME                                  client_connect_time;
                DATA_BLOB                               negotiate_request;
        } smbXsrv_connection_pass0;
 
index dd3d58f8509c1c9b7a5d0dfcd44cf19a6d94cbbe..d1a2c6204bad12d2297465877e077fb2e868e2df 100644 (file)
@@ -550,14 +550,11 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
                               struct messaging_context *msg_ctx,
                               NTTIME now,
                               struct smbXsrv_client **_client);
-NTSTATUS smbXsrv_client_update(struct smbXsrv_client *client);
 NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client);
-NTSTATUS smb2srv_client_lookup_global(struct smbXsrv_client *client,
-                                     struct GUID client_guid,
-                                     TALLOC_CTX *mem_ctx,
-                                     struct smbXsrv_client_global0 **_pass);
-NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
-                                       struct smbXsrv_client_global0 *global);
+struct tevent_req *smb2srv_client_mc_negprot_send(TALLOC_CTX *mem_ctx,
+                                                 struct tevent_context *ev,
+                                                 struct smbd_smb2_request *smb2req);
+NTSTATUS smb2srv_client_mc_negprot_recv(struct tevent_req *req);
 
 NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
                                        enum protocol_types protocol);
index 99303f1b07baee2058ba5091a991e7d56b95bb42..0cd093d1eba3ff0ff50b1e9ee17963de0336fe7d 100644 (file)
@@ -127,10 +127,19 @@ enum protocol_types smbd_smb2_protocol_dialect_match(const uint8_t *indyn,
        return PROTOCOL_NONE;
 }
 
+struct smbd_smb2_request_process_negprot_state {
+       struct smbd_smb2_request *req;
+       DATA_BLOB outbody;
+       DATA_BLOB outdyn;
+};
+
+static void smbd_smb2_request_process_negprot_mc_done(struct tevent_req *subreq);
+
 NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 {
+       struct smbd_smb2_request_process_negprot_state *state = NULL;
        struct smbXsrv_connection *xconn = req->xconn;
-       struct smbXsrv_client_global0 *global0 = NULL;
+       struct tevent_req *subreq = NULL;
        NTSTATUS status;
        const uint8_t *inbody;
        const uint8_t *indyn = NULL;
@@ -675,38 +684,83 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
                return smbd_smb2_request_done(req, outbody, &outdyn);
        }
 
-       status = smb2srv_client_lookup_global(xconn->client,
-                                             xconn->smb2.client.guid,
-                                             req, &global0);
-       /*
-        * TODO: check for races...
-        */
-       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECTID_NOT_FOUND)) {
+       state = talloc_zero(req, struct smbd_smb2_request_process_negprot_state);
+       if (state == NULL) {
+               return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+       }
+       *state = (struct smbd_smb2_request_process_negprot_state) {
+               .req = req,
+               .outbody = outbody,
+               .outdyn = outdyn,
+       };
+
+       subreq = smb2srv_client_mc_negprot_send(state,
+                                               req->xconn->client->raw_ev_ctx,
+                                               req);
+       if (subreq == NULL) {
+               return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+       }
+       tevent_req_set_callback(subreq,
+                               smbd_smb2_request_process_negprot_mc_done,
+                               state);
+       return NT_STATUS_OK;
+}
+
+static void smbd_smb2_request_process_negprot_mc_done(struct tevent_req *subreq)
+{
+       struct smbd_smb2_request_process_negprot_state *state =
+               tevent_req_callback_data(subreq,
+               struct smbd_smb2_request_process_negprot_state);
+       struct smbd_smb2_request *req = state->req;
+       struct smbXsrv_connection *xconn = req->xconn;
+       NTSTATUS status;
+
+       status = smb2srv_client_mc_negprot_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_MESSAGE_RETRIEVED)) {
                /*
-                * This stores the new client information in
-                * smbXsrv_client_global.tdb
+                * The connection was passed to another process
                 */
-               xconn->client->global->client_guid =
-                       xconn->smb2.client.guid;
-               status = smbXsrv_client_update(xconn->client);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
+               smbd_server_connection_terminate(xconn,
+                                                "passed connection");
+               /*
+                * smbd_server_connection_terminate() should not return!
+                */
+               smb_panic(__location__);
+               return;
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               status = smbd_smb2_request_error(req, status);
+               if (NT_STATUS_IS_OK(status)) {
+                       return;
                }
 
-               xconn->smb2.client.guid_verified = true;
-       } else if (NT_STATUS_IS_OK(status)) {
-               status = smb2srv_client_connection_pass(req,
-                                                       global0);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return smbd_smb2_request_error(req, status);
-               }
+               /*
+                * The connection was passed to another process
+                */
+               smbd_server_connection_terminate(xconn, nt_errstr(status));
+               /*
+                * smbd_server_connection_terminate() should not return!
+                */
+               smb_panic(__location__);
+               return;
+       }
 
-               smbd_server_connection_terminate(xconn,
-                                                "passed connection");
-               return NT_STATUS_OBJECTID_EXISTS;
-       } else {
-               return smbd_smb2_request_error(req, status);
+       /*
+        * We're the first connection...
+        */
+       status = smbd_smb2_request_done(req, state->outbody, &state->outdyn);
+       if (NT_STATUS_IS_OK(status)) {
+               return;
        }
 
-       return smbd_smb2_request_done(req, outbody, &outdyn);
+       /*
+        * The connection was passed to another process
+        */
+       smbd_server_connection_terminate(xconn, nt_errstr(status));
+       /*
+        * smbd_server_connection_terminate() should not return!
+        */
+       smb_panic(__location__);
+       return;
 }
index 93e2509041df35022fb5b867b67d6b5cfbb917bf..277ae1bab251294d14cd7b8f47704ee829f2265f 100644 (file)
@@ -38,6 +38,7 @@
 #include "serverid.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "lib/util/iov_buf.h"
+#include "lib/global_contexts.h"
 
 struct smbXsrv_client_table {
        struct {
@@ -54,6 +55,7 @@ static struct db_context *smbXsrv_client_global_db_ctx = NULL;
 NTSTATUS smbXsrv_client_global_init(void)
 {
        const char *global_path = NULL;
+       struct db_context *backend = NULL;
        struct db_context *db_ctx = NULL;
 
        if (smbXsrv_client_global_db_ctx != NULL) {
@@ -68,15 +70,15 @@ NTSTATUS smbXsrv_client_global_init(void)
                return NT_STATUS_NO_MEMORY;
        }
 
-       db_ctx = db_open(NULL, global_path,
-                        0, /* hash_size */
-                        TDB_DEFAULT |
-                        TDB_CLEAR_IF_FIRST |
-                        TDB_INCOMPATIBLE_HASH,
-                        O_RDWR | O_CREAT, 0600,
-                        DBWRAP_LOCK_ORDER_1,
-                        DBWRAP_FLAG_NONE);
-       if (db_ctx == NULL) {
+       backend = db_open(NULL, global_path,
+                         0, /* hash_size */
+                         TDB_DEFAULT |
+                         TDB_CLEAR_IF_FIRST |
+                         TDB_INCOMPATIBLE_HASH,
+                         O_RDWR | O_CREAT, 0600,
+                         DBWRAP_LOCK_ORDER_1,
+                         DBWRAP_FLAG_NONE);
+       if (backend == NULL) {
                NTSTATUS status;
 
                status = map_nt_error_from_unix_common(errno);
@@ -84,6 +86,12 @@ NTSTATUS smbXsrv_client_global_init(void)
                return status;
        }
 
+       db_ctx = db_open_watched(NULL, &backend, global_messaging_context());
+       if (db_ctx == NULL) {
+               TALLOC_FREE(backend);
+               return NT_STATUS_NO_MEMORY;
+       }
+
        smbXsrv_client_global_db_ctx = db_ctx;
 
        return NT_STATUS_OK;
@@ -265,47 +273,8 @@ static void smbXsrv_client_global_verify_record(struct db_record *db_rec,
        TALLOC_FREE(frame);
 }
 
-NTSTATUS smb2srv_client_lookup_global(struct smbXsrv_client *client,
-                                     struct GUID client_guid,
-                                     TALLOC_CTX *mem_ctx,
-                                     struct smbXsrv_client_global0 **_global)
-{
-       struct smbXsrv_client_table *table = client->table;
-       struct smbXsrv_client_global0 *global = NULL;
-       bool is_free = false;
-       struct db_record *db_rec;
-
-       db_rec = smbXsrv_client_global_fetch_locked(table->global.db_ctx,
-                                                   &client_guid,
-                                                   talloc_tos());
-       if (db_rec == NULL) {
-               return NT_STATUS_INTERNAL_DB_ERROR;
-       }
-
-       smbXsrv_client_global_verify_record(db_rec,
-                                           &is_free,
-                                           NULL,
-                                           mem_ctx,
-                                           &global);
-       TALLOC_FREE(db_rec);
-
-       if (is_free) {
-               return NT_STATUS_OBJECTID_NOT_FOUND;
-       }
-
-       if (global == NULL) {
-               /*
-                * most likely ndr_pull_struct_blob() failed
-                */
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       }
-
-       *_global = global;
-       return NT_STATUS_OK;
-}
-
-NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
-                                       struct smbXsrv_client_global0 *global)
+static NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
+                                              struct smbXsrv_client_global0 *global)
 {
        DATA_BLOB blob;
        enum ndr_err_code ndr_err;
@@ -315,8 +284,13 @@ NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
        ssize_t reqlen;
        struct iovec iov;
 
-       pass_info0.initial_connect_time = global->initial_connect_time;
-       pass_info0.client_guid = global->client_guid;
+       pass_info0 = (struct smbXsrv_connection_pass0) {
+               .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,
+       };
 
        reqlen = iov_buflen(smb2req->in.vector, smb2req->in.vector_count);
        if (reqlen == -1) {
@@ -436,6 +410,285 @@ static NTSTATUS smbXsrv_client_global_store(struct smbXsrv_client_global0 *globa
        return NT_STATUS_OK;
 }
 
+struct smb2srv_client_mc_negprot_state {
+       struct tevent_context *ev;
+       struct smbd_smb2_request *smb2req;
+       struct db_record *db_rec;
+};
+
+static void smb2srv_client_mc_negprot_cleanup(struct tevent_req *req,
+                                             enum tevent_req_state req_state)
+{
+       struct smb2srv_client_mc_negprot_state *state =
+               tevent_req_data(req,
+               struct smb2srv_client_mc_negprot_state);
+
+       TALLOC_FREE(state->db_rec);
+}
+
+static void smb2srv_client_mc_negprot_next(struct tevent_req *req);
+static bool smb2srv_client_mc_negprot_filter(struct messaging_rec *rec, void *private_data);
+static void smb2srv_client_mc_negprot_done(struct tevent_req *subreq);
+static void smb2srv_client_mc_negprot_watched(struct tevent_req *subreq);
+
+struct tevent_req *smb2srv_client_mc_negprot_send(TALLOC_CTX *mem_ctx,
+                                                 struct tevent_context *ev,
+                                                 struct smbd_smb2_request *smb2req)
+{
+       struct tevent_req *req = NULL;
+       struct smb2srv_client_mc_negprot_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb2srv_client_mc_negprot_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->smb2req = smb2req;
+
+       tevent_req_set_cleanup_fn(req, smb2srv_client_mc_negprot_cleanup);
+
+       smb2srv_client_mc_negprot_next(req);
+
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       return req;
+}
+
+static void smb2srv_client_mc_negprot_next(struct tevent_req *req)
+{
+       struct smb2srv_client_mc_negprot_state *state =
+               tevent_req_data(req,
+               struct smb2srv_client_mc_negprot_state);
+       struct smbXsrv_connection *xconn = state->smb2req->xconn;
+       struct smbXsrv_client *client = xconn->client;
+       struct smbXsrv_client_table *table = client->table;
+       struct GUID client_guid = xconn->smb2.client.guid;
+       struct smbXsrv_client_global0 *global = NULL;
+       bool is_free = false;
+       struct tevent_req *subreq = NULL;
+       NTSTATUS status;
+
+       SMB_ASSERT(state->db_rec == NULL);
+       state->db_rec = smbXsrv_client_global_fetch_locked(table->global.db_ctx,
+                                                          &client_guid,
+                                                          state);
+       if (state->db_rec == NULL) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_DB_ERROR);
+               return;
+       }
+
+       smbXsrv_client_global_verify_record(state->db_rec,
+                                           &is_free,
+                                           NULL,
+                                           state,
+                                           &global);
+       if (is_free) {
+               /*
+                * This stores the new client information in
+                * smbXsrv_client_global.tdb
+                */
+               client->global->client_guid = xconn->smb2.client.guid;
+
+               client->global->db_rec = state->db_rec;
+               state->db_rec = NULL;
+               status = smbXsrv_client_global_store(client->global);
+               SMB_ASSERT(client->global->db_rec == NULL);
+               if (!NT_STATUS_IS_OK(status)) {
+                       struct GUID_txt_buf buf;
+                       DBG_ERR("client_guid[%s] store failed - %s\n",
+                               GUID_buf_string(&client->global->client_guid,
+                                               &buf),
+                               nt_errstr(status));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               if (DEBUGLVL(DBGLVL_DEBUG)) {
+                       struct smbXsrv_clientB client_blob = {
+                               .version = SMBXSRV_VERSION_0,
+                               .info.info0 = client,
+                       };
+                       struct GUID_txt_buf buf;
+
+                       DBG_DEBUG("client_guid[%s] stored\n",
+                                 GUID_buf_string(&client->global->client_guid,
+                                                 &buf));
+                       NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob);
+               }
+
+               xconn->smb2.client.guid_verified = true;
+               tevent_req_done(req);
+               return;
+       }
+
+       if (global == NULL) {
+               /*
+                * most likely ndr_pull_struct_blob() failed
+                */
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_DB_CORRUPTION);
+               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;
+       }
+       tevent_req_set_callback(subreq, smb2srv_client_mc_negprot_done, req);
+
+       subreq = dbwrap_watched_watch_send(state,
+                                          state->ev,
+                                          state->db_rec,
+                                          global->server_id);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       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;
+       }
+
+       TALLOC_FREE(state->db_rec);
+       return;
+}
+
+static bool smb2srv_client_mc_negprot_filter(struct messaging_rec *rec, void *private_data)
+{
+       if (rec->msg_type != MSG_SMBXSRV_CONNECTION_PASSED) {
+               return false;
+       }
+
+       if (rec->num_fds != 0) {
+               return false;
+       }
+
+       if (rec->buf.length < SMB2_HDR_BODY) {
+               return false;
+       }
+
+       return true;
+}
+
+static void smb2srv_client_mc_negprot_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct smb2srv_client_mc_negprot_state *state =
+               tevent_req_data(req,
+               struct smb2srv_client_mc_negprot_state);
+       struct smbXsrv_connection *xconn = state->smb2req->xconn;
+       struct smbXsrv_client *client = xconn->client;
+       struct messaging_rec *rec = NULL;
+       struct smbXsrv_connection_passB passed_blob;
+       enum ndr_err_code ndr_err;
+       struct smbXsrv_connection_pass0 *passed_info0 = NULL;
+       NTSTATUS status;
+       int ret;
+
+       ret = messaging_filtered_read_recv(subreq, state, &rec);
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(ret);
+               DBG_ERR("messaging_filtered_read_recv() - %s\n",
+                       nt_errstr(status));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       DBG_DEBUG("MSG_SMBXSRV_CONNECTION_PASSED: received...\n");
+
+       ndr_err = ndr_pull_struct_blob(&rec->buf, rec, &passed_blob,
+                       (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_connection_passB);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               status = ndr_map_error2ntstatus(ndr_err);
+               DBG_ERR("ndr_pull_struct_blob - %s\n", nt_errstr(status));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+       }
+
+       if (passed_blob.version != SMBXSRV_VERSION_0) {
+               DBG_ERR("ignore invalid version %u\n", passed_blob.version);
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       passed_info0 = passed_blob.info.info0;
+       if (passed_info0 == NULL) {
+               DBG_ERR("ignore NULL info %u\n", passed_blob.version);
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (!GUID_equal(&xconn->smb2.client.guid, &passed_info0->client_guid)) {
+               struct GUID_txt_buf buf1, buf2;
+
+               DBG_ERR("client's client_guid [%s] != passed guid [%s]\n",
+                       GUID_buf_string(&xconn->smb2.client.guid,
+                                       &buf1),
+                       GUID_buf_string(&passed_info0->client_guid,
+                                       &buf2));
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (client->global->initial_connect_time !=
+           passed_info0->xconn_connect_time)
+       {
+               DBG_ERR("client's initial connect time [%s] (%llu) != "
+                       "passed xconn 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(),
+                                      passed_info0->xconn_connect_time),
+                       (unsigned long long)passed_info0->xconn_connect_time);
+               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);
+}
+
+static void smb2srv_client_mc_negprot_watched(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       NTSTATUS status;
+
+       status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       smb2srv_client_mc_negprot_next(req);
+}
+
+NTSTATUS smb2srv_client_mc_negprot_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
 static NTSTATUS smbXsrv_client_global_remove(struct smbXsrv_client_global0 *global)
 {
        TDB_DATA key;
@@ -573,6 +826,53 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS smb2srv_client_connection_passed(struct smbXsrv_client *client,
+                               const struct smbXsrv_connection_pass0 *recv_info0)
+{
+       DATA_BLOB blob;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+       struct smbXsrv_connection_pass0 passed_info0;
+       struct smbXsrv_connection_passB passed_blob;
+       struct iovec iov;
+
+       /*
+        * We echo back the message with a cleared negotiate_request
+        */
+       passed_info0 = *recv_info0;
+       passed_info0.negotiate_request = data_blob_null;
+
+       ZERO_STRUCT(passed_blob);
+       passed_blob.version = smbXsrv_version_global_current();
+       passed_blob.info.info0 = &passed_info0;
+
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &passed_blob);
+       }
+
+       ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &passed_blob,
+                       (ndr_push_flags_fn_t)ndr_push_smbXsrv_connection_passB);
+       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(client->msg_ctx,
+                                   recv_info0->src_server_id,
+                                   MSG_SMBXSRV_CONNECTION_PASSED,
+                                   &iov, 1,
+                                   NULL, 0);
+       data_blob_free(&blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static bool smbXsrv_client_connection_pass_filter(struct messaging_rec *rec, void *private_data)
 {
        if (rec->msg_type != MSG_SMBXSRV_CONNECTION_PASS) {
@@ -665,7 +965,7 @@ static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq)
        }
 
        if (client->global->initial_connect_time !=
-           pass_info0->initial_connect_time)
+           pass_info0->client_connect_time)
        {
                DBG_WARNING("client's initial connect time [%s] (%llu) != "
                        "passed initial connect time [%s] (%llu)\n",
@@ -673,22 +973,30 @@ static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq)
                                       client->global->initial_connect_time),
                        (unsigned long long)client->global->initial_connect_time,
                        nt_time_string(talloc_tos(),
-                                      pass_info0->initial_connect_time),
-                       (unsigned long long)pass_info0->initial_connect_time);
+                                      pass_info0->client_connect_time),
+                       (unsigned long long)pass_info0->client_connect_time);
                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";
+               DBG_ERR("%s => %s\n", r, nt_errstr(status));
+               NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+               exit_server_cleanly(r);
+               return;
+       }
+
        status = smbd_add_connection(client,
                                     sock_fd,
-                                    pass_info0->initial_connect_time,
+                                    pass_info0->xconn_connect_time,
                                     &xconn);
        if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
                rec->num_fds = 0;
-               DLIST_REMOVE(client->connections, xconn);
-               TALLOC_FREE(xconn);
+               smbd_server_connection_terminate(xconn, nt_errstr(status));
        }
        if (!NT_STATUS_IS_OK(status)) {
                DBG_ERR("smbd_add_connection => %s\n", nt_errstr(status));
@@ -736,53 +1044,6 @@ next:
        client->connection_pass_subreq = subreq;
 }
 
-NTSTATUS smbXsrv_client_update(struct smbXsrv_client *client)
-{
-       struct smbXsrv_client_table *table = client->table;
-       NTSTATUS status;
-
-       if (client->global->db_rec != NULL) {
-               struct GUID_txt_buf buf;
-               DBG_ERR("guid [%s]: Called with db_rec != NULL'\n",
-                       GUID_buf_string(&client->global->client_guid,
-                                       &buf));
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
-       client->global->db_rec = smbXsrv_client_global_fetch_locked(
-                                       table->global.db_ctx,
-                                       &client->global->client_guid,
-                                       client->global /* TALLOC_CTX */);
-       if (client->global->db_rec == NULL) {
-               return NT_STATUS_INTERNAL_DB_ERROR;
-       }
-
-       status = smbXsrv_client_global_store(client->global);
-       if (!NT_STATUS_IS_OK(status)) {
-               struct GUID_txt_buf buf;
-               DBG_ERR("client_guid[%s] store failed - %s\n",
-                       GUID_buf_string(&client->global->client_guid,
-                                       &buf),
-                       nt_errstr(status));
-               return status;
-       }
-
-       if (DEBUGLVL(DBGLVL_DEBUG)) {
-               struct smbXsrv_clientB client_blob = {
-                       .version = SMBXSRV_VERSION_0,
-                       .info.info0 = client,
-               };
-               struct GUID_txt_buf buf;
-
-               DBG_DEBUG("client_guid[%s] stored\n",
-                         GUID_buf_string(&client->global->client_guid,
-                                         &buf));
-               NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob);
-       }
-
-       return NT_STATUS_OK;
-}
-
 NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client)
 {
        struct smbXsrv_client_table *table = client->table;