auth/credentials: don't ignore "client use kerberos" and --use-kerberos for machine...
[samba.git] / source3 / smbd / smbXsrv_session.c
index 381ce407c70836bcba31e422a4e70a5a9323125b..36f39c68a14fa7b7d109e1defd4fa7867765ac0c 100644 (file)
 */
 
 #include "includes.h"
+#include "smbXsrv_session.h"
 #include "system/filesys.h"
 #include <tevent.h>
+#include "lib/util/server_id.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "dbwrap/dbwrap.h"
@@ -37,6 +39,8 @@
 #include "librpc/gen_ndr/ndr_smbXsrv.h"
 #include "serverid.h"
 #include "lib/util/tevent_ntstatus.h"
+#include "lib/global_contexts.h"
+#include "source3/include/util_tdb.h"
 
 struct smbXsrv_session_table {
        struct {
@@ -66,16 +70,14 @@ NTSTATUS smbXsrv_session_global_init(struct messaging_context *msg_ctx)
        /*
         * This contains secret information like session keys!
         */
-       global_path = lock_path("smbXsrv_session_global.tdb");
+       global_path = lock_path(talloc_tos(), "smbXsrv_session_global.tdb");
        if (global_path == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
        backend = db_open(NULL, global_path,
-                         0, /* hash_size */
-                         TDB_DEFAULT |
-                         TDB_CLEAR_IF_FIRST |
-                         TDB_INCOMPATIBLE_HASH,
+                         SMBD_VOLATILE_TDB_HASH_SIZE,
+                         SMBD_VOLATILE_TDB_FLAGS,
                          O_RDWR | O_CREAT, 0600,
                          DBWRAP_LOCK_ORDER_1,
                          DBWRAP_FLAG_NONE);
@@ -88,7 +90,7 @@ NTSTATUS smbXsrv_session_global_init(struct messaging_context *msg_ctx)
                return status;
        }
 
-       db_ctx = db_open_watched(NULL, backend, server_messaging_context());
+       db_ctx = db_open_watched(NULL, &backend, global_messaging_context());
        if (db_ctx == NULL) {
                TALLOC_FREE(backend);
                return NT_STATUS_NO_MEMORY;
@@ -183,7 +185,7 @@ static struct db_record *smbXsrv_session_global_fetch_locked(
 
        if (rec == NULL) {
                DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
-                         hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
+                         tdb_data_dbg(key));
        }
 
        return rec;
@@ -204,7 +206,7 @@ static struct db_record *smbXsrv_session_local_fetch_locked(
 
        if (rec == NULL) {
                DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id,
-                         hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
+                         tdb_data_dbg(key));
        }
 
        return rec;
@@ -257,7 +259,9 @@ static NTSTATUS smbXsrv_session_table_init(struct smbXsrv_connection *conn,
 
        table->global.db_ctx = smbXsrv_session_global_db_ctx;
 
-       subreq = messaging_read_send(table, client->ev_ctx, client->msg_ctx,
+       subreq = messaging_read_send(table,
+                                    client->raw_ev_ctx,
+                                    client->msg_ctx,
                                     MSG_SMBXSRV_SESSION_CLOSE);
        if (subreq == NULL) {
                TALLOC_FREE(table);
@@ -297,28 +301,24 @@ static void smbXsrv_session_close_loop(struct tevent_req *subreq)
                        (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_session_closeB);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
-               DEBUG(1,("smbXsrv_session_close_loop: "
-                        "ndr_pull_struct_blob - %s\n",
-                        nt_errstr(status)));
+               DBG_WARNING("ndr_pull_struct_blob - %s\n", nt_errstr(status));
                goto next;
        }
 
-       DEBUG(10,("smbXsrv_session_close_loop: MSG_SMBXSRV_SESSION_CLOSE\n"));
-       if (DEBUGLVL(10)) {
+       DBG_DEBUG("MSG_SMBXSRV_SESSION_CLOSE\n");
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
                NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
        }
 
        if (close_blob.version != SMBXSRV_VERSION_0) {
-               DEBUG(0,("smbXsrv_session_close_loop: "
-                        "ignore invalid version %u\n", close_blob.version));
+               DBG_ERR("ignore invalid version %u\n", close_blob.version);
                NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                goto next;
        }
 
        close_info0 = close_blob.info.info0;
        if (close_info0 == NULL) {
-               DEBUG(0,("smbXsrv_session_close_loop: "
-                        "ignore NULL info %u\n", close_blob.version));
+               DBG_ERR("ignore NULL info %u\n", close_blob.version);
                NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                goto next;
        }
@@ -327,10 +327,9 @@ static void smbXsrv_session_close_loop(struct tevent_req *subreq)
                                               close_info0->old_session_wire_id,
                                               now, &session);
        if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
-               DEBUG(4,("smbXsrv_session_close_loop: "
-                        "old_session_wire_id %llu not found\n",
-                        (unsigned long long)close_info0->old_session_wire_id));
-               if (DEBUGLVL(4)) {
+               DBG_INFO("old_session_wire_id %" PRIu64 " not found\n",
+                        close_info0->old_session_wire_id);
+               if (DEBUGLVL(DBGLVL_INFO)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                }
                goto next;
@@ -338,52 +337,52 @@ static void smbXsrv_session_close_loop(struct tevent_req *subreq)
        if (!NT_STATUS_IS_OK(status) &&
            !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
            !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
-               DEBUG(1,("smbXsrv_session_close_loop: "
-                        "old_session_wire_id %llu - %s\n",
-                        (unsigned long long)close_info0->old_session_wire_id,
-                        nt_errstr(status)));
-               if (DEBUGLVL(1)) {
+               DBG_WARNING("old_session_wire_id %" PRIu64 " - %s\n",
+                           close_info0->old_session_wire_id,
+                           nt_errstr(status));
+               if (DEBUGLVL(DBGLVL_WARNING)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                }
                goto next;
        }
 
        if (session->global->session_global_id != close_info0->old_session_global_id) {
-               DEBUG(1,("smbXsrv_session_close_loop: "
-                        "old_session_wire_id %llu - global %u != %u\n",
-                        (unsigned long long)close_info0->old_session_wire_id,
-                        session->global->session_global_id,
-                        close_info0->old_session_global_id));
-               if (DEBUGLVL(1)) {
+               DBG_WARNING("old_session_wire_id %" PRIu64 " - "
+                           "global %" PRIu32 " != %" PRIu32 "\n",
+                           close_info0->old_session_wire_id,
+                           session->global->session_global_id,
+                           close_info0->old_session_global_id);
+               if (DEBUGLVL(DBGLVL_WARNING)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                }
                goto next;
        }
 
        if (session->global->creation_time != close_info0->old_creation_time) {
-               DEBUG(1,("smbXsrv_session_close_loop: "
-                        "old_session_wire_id %llu - "
-                        "creation %s (%llu) != %s (%llu)\n",
-                        (unsigned long long)close_info0->old_session_wire_id,
-                        nt_time_string(rec, session->global->creation_time),
-                        (unsigned long long)session->global->creation_time,
-                        nt_time_string(rec, close_info0->old_creation_time),
-                        (unsigned long long)close_info0->old_creation_time));
-               if (DEBUGLVL(1)) {
+               DBG_WARNING("old_session_wire_id %" PRIu64 " - "
+                           "creation %s (%" PRIu64 ") != %s (%" PRIu64 ")\n",
+                           close_info0->old_session_wire_id,
+                           nt_time_string(rec,
+                                          session->global->creation_time),
+                           session->global->creation_time,
+                           nt_time_string(rec,
+                                          close_info0->old_creation_time),
+                           close_info0->old_creation_time);
+               if (DEBUGLVL(DBGLVL_WARNING)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                }
                goto next;
        }
 
-       subreq = smb2srv_session_shutdown_send(session, client->ev_ctx,
+       subreq = smb2srv_session_shutdown_send(session, client->raw_ev_ctx,
                                               session, NULL);
        if (subreq == NULL) {
                status = NT_STATUS_NO_MEMORY;
-               DEBUG(0, ("smbXsrv_session_close_loop: "
-                         "smb2srv_session_shutdown_send(%llu) failed: %s\n",
-                         (unsigned long long)session->global->session_wire_id,
-                         nt_errstr(status)));
-               if (DEBUGLVL(1)) {
+               DBG_ERR("smb2srv_session_shutdown_send(%" PRIu64
+                       ") failed: %s\n",
+                       session->global->session_wire_id,
+                       nt_errstr(status));
+               if (DEBUGLVL(DBGLVL_WARNING)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob);
                }
                goto next;
@@ -395,7 +394,9 @@ static void smbXsrv_session_close_loop(struct tevent_req *subreq)
 next:
        TALLOC_FREE(rec);
 
-       subreq = messaging_read_send(table, client->ev_ctx, client->msg_ctx,
+       subreq = messaging_read_send(table,
+                                    client->raw_ev_ctx,
+                                    client->msg_ctx,
                                     MSG_SMBXSRV_SESSION_CLOSE);
        if (subreq == NULL) {
                const char *r;
@@ -416,18 +417,17 @@ static void smbXsrv_session_close_shutdown_done(struct tevent_req *subreq)
        status = smb2srv_session_shutdown_recv(subreq);
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("smbXsrv_session_close_loop: "
-                         "smb2srv_session_shutdown_recv(%llu) failed: %s\n",
-                         (unsigned long long)session->global->session_wire_id,
-                         nt_errstr(status)));
+               DBG_ERR("smb2srv_session_shutdown_recv(%" PRIu64
+                       ") failed: %s\n",
+                       session->global->session_wire_id,
+                       nt_errstr(status));
        }
 
        status = smbXsrv_session_logoff(session);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("smbXsrv_session_close_loop: "
-                         "smbXsrv_session_logoff(%llu) failed: %s\n",
-                         (unsigned long long)session->global->session_wire_id,
-                         nt_errstr(status)));
+               DBG_ERR("smbXsrv_session_logoff(%" PRIu64 ") failed: %s\n",
+                       session->global->session_wire_id,
+                       nt_errstr(status));
        }
 
        TALLOC_FREE(session);
@@ -703,7 +703,8 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
                                        bool *is_free,
                                        bool *was_free,
                                        TALLOC_CTX *mem_ctx,
-                                       struct smbXsrv_session_global0 **_g);
+                                       struct smbXsrv_session_global0 **_g,
+                                       uint32_t *pseqnum);
 
 static NTSTATUS smbXsrv_session_global_allocate(struct db_context *db,
                                        TALLOC_CTX *mem_ctx,
@@ -755,7 +756,7 @@ static NTSTATUS smbXsrv_session_global_allocate(struct db_context *db,
                smbXsrv_session_global_verify_record(global->db_rec,
                                                     &is_free,
                                                     &was_free,
-                                                    NULL, NULL);
+                                                    NULL, NULL, NULL);
 
                if (!is_free) {
                        TALLOC_FREE(global->db_rec);
@@ -795,7 +796,8 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
                                        bool *is_free,
                                        bool *was_free,
                                        TALLOC_CTX *mem_ctx,
-                                       struct smbXsrv_session_global0 **_g)
+                                       struct smbXsrv_session_global0 **_g,
+                                       uint32_t *pseqnum)
 {
        TDB_DATA key;
        TDB_DATA val;
@@ -814,6 +816,9 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
        if (_g) {
                *_g = NULL;
        }
+       if (pseqnum) {
+               *pseqnum = 0;
+       }
 
        key = dbwrap_record_get_key(db_rec);
 
@@ -833,10 +838,9 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
                        (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_session_globalB);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
-               DEBUG(1,("smbXsrv_session_global_verify_record: "
-                        "key '%s' ndr_pull_struct_blob - %s\n",
-                        hex_encode_talloc(frame, key.dptr, key.dsize),
-                        nt_errstr(status)));
+               DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
+                           tdb_data_dbg(key),
+                           nt_errstr(status));
                TALLOC_FREE(frame);
                *is_free = true;
                if (was_free) {
@@ -845,16 +849,15 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
                return;
        }
 
-       DEBUG(10,("smbXsrv_session_global_verify_record\n"));
-       if (DEBUGLVL(10)) {
+       DBG_DEBUG("smbXsrv_session_global_verify_record\n");
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
                NDR_PRINT_DEBUG(smbXsrv_session_globalB, &global_blob);
        }
 
        if (global_blob.version != SMBXSRV_VERSION_0) {
-               DEBUG(0,("smbXsrv_session_global_verify_record: "
-                        "key '%s' use unsupported version %u\n",
-                        hex_encode_talloc(frame, key.dptr, key.dsize),
-                        global_blob.version));
+               DBG_ERR("key '%s' use unsupported version %u\n",
+                       tdb_data_dbg(key),
+                       global_blob.version);
                NDR_PRINT_DEBUG(smbXsrv_session_globalB, &global_blob);
                TALLOC_FREE(frame);
                *is_free = true;
@@ -866,15 +869,31 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
 
        global = global_blob.info.info0;
 
+#define __BLOB_KEEP_SECRET(__blob) do { \
+       if ((__blob).length != 0) { \
+               talloc_keep_secret((__blob).data); \
+       } \
+} while(0)
+       {
+               uint32_t i;
+               __BLOB_KEEP_SECRET(global->application_key_blob);
+               __BLOB_KEEP_SECRET(global->signing_key_blob);
+               __BLOB_KEEP_SECRET(global->encryption_key_blob);
+               __BLOB_KEEP_SECRET(global->decryption_key_blob);
+               for (i = 0; i < global->num_channels; i++) {
+                       __BLOB_KEEP_SECRET(global->channels[i].signing_key_blob);
+               }
+       }
+#undef __BLOB_KEEP_SECRET
+
        exists = serverid_exists(&global->channels[0].server_id);
        if (!exists) {
                struct server_id_buf idbuf;
-               DEBUG(2,("smbXsrv_session_global_verify_record: "
-                        "key '%s' server_id %s does not exist.\n",
-                        hex_encode_talloc(frame, key.dptr, key.dsize),
-                        server_id_str_buf(global->channels[0].server_id,
-                                          &idbuf)));
-               if (DEBUGLVL(2)) {
+               DBG_NOTICE("key '%s' server_id %s does not exist.\n",
+                          tdb_data_dbg(key),
+                          server_id_str_buf(global->channels[0].server_id,
+                                            &idbuf));
+               if (DEBUGLVL(DBGLVL_NOTICE)) {
                        NDR_PRINT_DEBUG(smbXsrv_session_globalB, &global_blob);
                }
                TALLOC_FREE(frame);
@@ -886,6 +905,9 @@ static void smbXsrv_session_global_verify_record(struct db_record *db_rec,
        if (_g) {
                *_g = talloc_move(mem_ctx, &global);
        }
+       if (pseqnum) {
+               *pseqnum = global_blob.seqnum;
+       }
        TALLOC_FREE(frame);
 }
 
@@ -904,45 +926,46 @@ static NTSTATUS smbXsrv_session_global_store(struct smbXsrv_session_global0 *glo
         * store the information in the old format.
         */
 
-       if (global->db_rec == NULL) {
-               return NT_STATUS_INTERNAL_ERROR;
-       }
-
        key = dbwrap_record_get_key(global->db_rec);
        val = dbwrap_record_get_value(global->db_rec);
 
-       ZERO_STRUCT(global_blob);
-       global_blob.version = smbXsrv_version_global_current();
+       global_blob = (struct smbXsrv_session_globalB){
+               .version = smbXsrv_version_global_current(),
+               .info.info0 = global,
+       };
+
        if (val.dsize >= 8) {
                global_blob.seqnum = IVAL(val.dptr, 4);
        }
        global_blob.seqnum += 1;
-       global_blob.info.info0 = global;
 
-       ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
-                       (ndr_push_flags_fn_t)ndr_push_smbXsrv_session_globalB);
+       ndr_err = ndr_push_struct_blob(
+               &blob,
+               talloc_tos(),
+               &global_blob,
+               (ndr_push_flags_fn_t)ndr_push_smbXsrv_session_globalB);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
-               DEBUG(1,("smbXsrv_session_global_store: key '%s' ndr_push - %s\n",
-                        hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
-                        nt_errstr(status)));
+               DBG_WARNING("key '%s' ndr_push - %s\n",
+                           tdb_data_dbg(key),
+                           nt_errstr(status));
                TALLOC_FREE(global->db_rec);
                return status;
        }
 
        val = make_tdb_data(blob.data, blob.length);
        status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
+       TALLOC_FREE(blob.data);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1,("smbXsrv_session_global_store: key '%s' store - %s\n",
-                        hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
-                        nt_errstr(status)));
+               DBG_WARNING("key '%s' store - %s\n",
+                           tdb_data_dbg(key),
+                           nt_errstr(status));
                TALLOC_FREE(global->db_rec);
                return status;
        }
 
-       if (DEBUGLVL(10)) {
-               DEBUG(10,("smbXsrv_session_global_store: key '%s' stored\n",
-                        hex_encode_talloc(global->db_rec, key.dptr, key.dsize)));
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
+               DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
                NDR_PRINT_DEBUG(smbXsrv_session_globalB, &global_blob);
        }
 
@@ -955,10 +978,28 @@ struct smb2srv_session_close_previous_state {
        struct tevent_context *ev;
        struct smbXsrv_connection *connection;
        struct dom_sid *current_sid;
+       uint64_t previous_session_id;
        uint64_t current_session_id;
        struct db_record *db_rec;
+       uint64_t watch_instance;
+       uint32_t last_seqnum;
 };
 
+static void smb2srv_session_close_previous_cleanup(struct tevent_req *req,
+                                                  enum tevent_req_state req_state)
+{
+       struct smb2srv_session_close_previous_state *state =
+               tevent_req_data(req,
+               struct smb2srv_session_close_previous_state);
+
+       if (state->db_rec != NULL) {
+               dbwrap_watched_watch_remove_instance(state->db_rec,
+                                                    state->watch_instance);
+               state->watch_instance = 0;
+               TALLOC_FREE(state->db_rec);
+       }
+}
+
 static void smb2srv_session_close_previous_check(struct tevent_req *req);
 static void smb2srv_session_close_previous_modified(struct tevent_req *subreq);
 
@@ -969,8 +1010,8 @@ struct tevent_req *smb2srv_session_close_previous_send(TALLOC_CTX *mem_ctx,
                                        uint64_t previous_session_id,
                                        uint64_t current_session_id)
 {
-       struct tevent_req *req;
-       struct smb2srv_session_close_previous_state *state;
+       struct tevent_req *req = NULL;
+       struct smb2srv_session_close_previous_state *state = NULL;
        uint32_t global_id = previous_session_id & UINT32_MAX;
        uint64_t global_zeros = previous_session_id & 0xFFFFFFFF00000000LLU;
        struct smbXsrv_session_table *table = conn->client->session_table;
@@ -983,8 +1024,11 @@ struct tevent_req *smb2srv_session_close_previous_send(TALLOC_CTX *mem_ctx,
        }
        state->ev = ev;
        state->connection = conn;
+       state->previous_session_id = previous_session_id;
        state->current_session_id = current_session_id;
 
+       tevent_req_set_cleanup_fn(req, smb2srv_session_close_previous_cleanup);
+
        if (global_zeros != 0) {
                tevent_req_done(req);
                return tevent_req_post(req, ev);
@@ -996,14 +1040,11 @@ struct tevent_req *smb2srv_session_close_previous_send(TALLOC_CTX *mem_ctx,
        }
        current_token = session_info->security_token;
 
-       if (current_token->num_sids > PRIMARY_USER_SID_INDEX) {
-               state->current_sid = &current_token->sids[PRIMARY_USER_SID_INDEX];
-       }
-
-       if (state->current_sid == NULL) {
+       if (current_token->num_sids <= PRIMARY_USER_SID_INDEX) {
                tevent_req_done(req);
                return tevent_req_post(req, ev);
        }
+       state->current_sid = &current_token->sids[PRIMARY_USER_SID_INDEX];
 
        if (!security_token_has_nt_authenticated_users(current_token)) {
                /* TODO */
@@ -1043,21 +1084,21 @@ static void smb2srv_session_close_previous_check(struct tevent_req *req)
        struct tevent_req *subreq = NULL;
        NTSTATUS status;
        bool is_free = false;
+       uint32_t seqnum = 0;
 
        smbXsrv_session_global_verify_record(state->db_rec,
                                             &is_free,
                                             NULL,
                                             state,
-                                            &global);
+                                            &global,
+                                            &seqnum);
 
        if (is_free) {
-               TALLOC_FREE(state->db_rec);
                tevent_req_done(req);
                return;
        }
 
        if (global->auth_session_info == NULL) {
-               TALLOC_FREE(state->db_rec);
                tevent_req_done(req);
                return;
        }
@@ -1065,53 +1106,75 @@ static void smb2srv_session_close_previous_check(struct tevent_req *req)
        previous_token = global->auth_session_info->security_token;
 
        if (!security_token_is_sid(previous_token, state->current_sid)) {
-               TALLOC_FREE(state->db_rec);
                tevent_req_done(req);
                return;
        }
 
+       /*
+        * If the record changed, but we are not happy with the change yet,
+        * we better remove ourself from the waiter list
+        * (most likely the first position)
+        * and re-add us at the end of the list.
+        *
+        * This gives other waiters a change
+        * to make progress.
+        *
+        * Otherwise we'll keep our waiter instance alive,
+        * keep waiting (most likely at first position).
+        * It means the order of watchers stays fair.
+        */
+       if (state->last_seqnum != seqnum) {
+               state->last_seqnum = seqnum;
+               dbwrap_watched_watch_remove_instance(state->db_rec,
+                                                    state->watch_instance);
+               state->watch_instance =
+                       dbwrap_watched_watch_add_instance(state->db_rec);
+       }
+
        subreq = dbwrap_watched_watch_send(state, state->ev, state->db_rec,
+                                          state->watch_instance,
                                           (struct server_id){0});
        if (tevent_req_nomem(subreq, req)) {
-               TALLOC_FREE(state->db_rec);
                return;
        }
        tevent_req_set_callback(subreq,
                                smb2srv_session_close_previous_modified,
                                req);
 
-       close_info0.old_session_global_id = global->session_global_id;
-       close_info0.old_session_wire_id = global->session_wire_id;
-       close_info0.old_creation_time = global->creation_time;
-       close_info0.new_session_wire_id = state->current_session_id;
+       close_info0 = (struct smbXsrv_session_close0){
+               .old_session_global_id = global->session_global_id,
+               .old_session_wire_id = global->session_wire_id,
+               .old_creation_time = global->creation_time,
+               .new_session_wire_id = state->current_session_id,
+       };
 
-       ZERO_STRUCT(close_blob);
-       close_blob.version = smbXsrv_version_global_current();
-       close_blob.info.info0 = &close_info0;
+       close_blob = (struct smbXsrv_session_closeB){
+               .version = smbXsrv_version_global_current(),
+               .info.info0 = &close_info0,
+       };
 
        ndr_err = ndr_push_struct_blob(&blob, state, &close_blob,
                        (ndr_push_flags_fn_t)ndr_push_smbXsrv_session_closeB);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               TALLOC_FREE(state->db_rec);
                status = ndr_map_error2ntstatus(ndr_err);
-               DEBUG(1,("smb2srv_session_close_previous_check: "
-                        "old_session[%llu] new_session[%llu] ndr_push - %s\n",
-                        (unsigned long long)close_info0.old_session_wire_id,
-                        (unsigned long long)close_info0.new_session_wire_id,
-                        nt_errstr(status)));
+               DBG_WARNING("old_session[%" PRIu64 "] "
+                           "new_session[%" PRIu64 "] ndr_push - %s\n",
+                           close_info0.old_session_wire_id,
+                           close_info0.new_session_wire_id,
+                           nt_errstr(status));
                tevent_req_nterror(req, status);
                return;
        }
 
-       status = messaging_send(conn->msg_ctx,
+       status = messaging_send(conn->client->msg_ctx,
                                global->channels[0].server_id,
                                MSG_SMBXSRV_SESSION_CLOSE, &blob);
-       TALLOC_FREE(state->db_rec);
+       TALLOC_FREE(global);
        if (tevent_req_nterror(req, status)) {
                return;
        }
 
-       TALLOC_FREE(global);
+       TALLOC_FREE(state->db_rec);
        return;
 }
 
@@ -1123,15 +1186,28 @@ static void smb2srv_session_close_previous_modified(struct tevent_req *subreq)
        struct smb2srv_session_close_previous_state *state =
                tevent_req_data(req,
                struct smb2srv_session_close_previous_state);
+       uint32_t global_id;
        NTSTATUS status;
+       uint64_t instance = 0;
 
-       status = dbwrap_watched_watch_recv(subreq, state, &state->db_rec, NULL,
-                                          NULL);
+       status = dbwrap_watched_watch_recv(subreq, &instance, NULL, NULL);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
        }
 
+       state->watch_instance = instance;
+
+       global_id = state->previous_session_id & UINT32_MAX;
+
+       state->db_rec = smbXsrv_session_global_fetch_locked(
+               state->connection->client->session_table->global.db_ctx,
+               global_id, state /* TALLOC_CTX */);
+       if (state->db_rec == NULL) {
+               tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+               return;
+       }
+
        smb2srv_session_close_previous_check(req);
 }
 
@@ -1184,11 +1260,13 @@ static int smbXsrv_session_destructor(struct smbXsrv_session *session)
 {
        NTSTATUS status;
 
+       DBG_DEBUG("destructing session(%" PRIu64 ")\n",
+                 session->global->session_wire_id);
+
        status = smbXsrv_session_clear_and_logoff(session);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("smbXsrv_session_destructor: "
-                         "smbXsrv_session_logoff() failed: %s\n",
-                         nt_errstr(status)));
+               DBG_ERR("smbXsrv_session_logoff() failed: %s\n",
+                       nt_errstr(status));
        }
 
        TALLOC_FREE(session->global);
@@ -1221,6 +1299,7 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
        session->idle_time = now;
        session->status = NT_STATUS_MORE_PROCESSING_REQUIRED;
        session->client = conn->client;
+       session->homes_snum = -1;
 
        status = smbXsrv_session_global_allocate(table->global.db_ctx,
                                                 session,
@@ -1235,6 +1314,7 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
                uint64_t id = global->session_global_id;
 
                global->connection_dialect = conn->smb2.server.dialect;
+               global->client_guid = conn->smb2.client.guid;
 
                global->session_wire_id = id;
 
@@ -1279,7 +1359,7 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
        global->creation_time = now;
        global->expiration_time = GENSEC_EXPIRE_TIME_INFINITY;
 
-       status = smbXsrv_session_add_channel(session, conn, &channel);
+       status = smbXsrv_session_add_channel(session, conn, now, &channel);
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(session);
                return status;
@@ -1299,23 +1379,21 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
 
        status = smbXsrv_session_global_store(global);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("smbXsrv_session_create: "
-                        "global_id (0x%08x) store failed - %s\n",
-                        session->global->session_global_id,
-                        nt_errstr(status)));
+               DBG_ERR("global_id (0x%08x) store failed - %s\n",
+                       session->global->session_global_id,
+                       nt_errstr(status));
                TALLOC_FREE(session);
                return status;
        }
 
-       if (DEBUGLVL(10)) {
-               struct smbXsrv_sessionB session_blob;
-
-               ZERO_STRUCT(session_blob);
-               session_blob.version = SMBXSRV_VERSION_0;
-               session_blob.info.info0 = session;
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
+               struct smbXsrv_sessionB session_blob = {
+                       .version = SMBXSRV_VERSION_0,
+                       .info.info0 = session,
+               };
 
-               DEBUG(10,("smbXsrv_session_create: global_id (0x%08x) stored\n",
-                        session->global->session_global_id));
+               DBG_DEBUG("global_id (0x%08x) stored\n",
+                         session->global->session_global_id);
                NDR_PRINT_DEBUG(smbXsrv_sessionB, &session_blob);
        }
 
@@ -1325,6 +1403,7 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn,
 
 NTSTATUS smbXsrv_session_add_channel(struct smbXsrv_session *session,
                                     struct smbXsrv_connection *conn,
+                                    NTTIME now,
                                     struct smbXsrv_channel_global0 **_c)
 {
        struct smbXsrv_session_global0 *global = session->global;
@@ -1332,7 +1411,7 @@ NTSTATUS smbXsrv_session_add_channel(struct smbXsrv_session *session,
 
        if (global->num_channels > 31) {
                /*
-                * Windows 2012 and 2012R2 allow up to 32 channels
+                * Windows allow up to 32 channels
                 */
                return NT_STATUS_INSUFFICIENT_RESOURCES;
        }
@@ -1347,9 +1426,14 @@ NTSTATUS smbXsrv_session_add_channel(struct smbXsrv_session *session,
        global->channels = c;
 
        c = &global->channels[global->num_channels];
-       ZERO_STRUCTP(c);
 
-       c->server_id = messaging_server_id(conn->msg_ctx);
+       *c = (struct smbXsrv_channel_global0){
+               .server_id = messaging_server_id(conn->client->msg_ctx),
+               .channel_id = conn->channel_id,
+               .creation_time = now,
+               .connection = conn,
+       };
+
        c->local_address = tsocket_address_string(conn->local_address,
                                                  global->channels);
        if (c->local_address == NULL) {
@@ -1365,7 +1449,6 @@ NTSTATUS smbXsrv_session_add_channel(struct smbXsrv_session *session,
        if (c->remote_name == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
-       c->connection = conn;
 
        global->num_channels += 1;
 
@@ -1379,9 +1462,16 @@ NTSTATUS smbXsrv_session_update(struct smbXsrv_session *session)
        NTSTATUS status;
 
        if (session->global->db_rec != NULL) {
-               DEBUG(0, ("smbXsrv_session_update(0x%08x): "
+               DBG_ERR("smbXsrv_session_update(0x%08x): "
                          "Called with db_rec != NULL'\n",
-                         session->global->session_global_id));
+                         session->global->session_global_id);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       if (table == NULL) {
+               DBG_ERR("smbXsrv_session_update(0x%08x): "
+                         "Called with table == NULL'\n",
+                         session->global->session_global_id);
                return NT_STATUS_INTERNAL_ERROR;
        }
 
@@ -1395,22 +1485,20 @@ NTSTATUS smbXsrv_session_update(struct smbXsrv_session *session)
 
        status = smbXsrv_session_global_store(session->global);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("smbXsrv_session_update: "
-                        "global_id (0x%08x) store failed - %s\n",
-                        session->global->session_global_id,
-                        nt_errstr(status)));
+               DBG_ERR("global_id (0x%08x) store failed - %s\n",
+                       session->global->session_global_id,
+                       nt_errstr(status));
                return status;
        }
 
-       if (DEBUGLVL(10)) {
-               struct smbXsrv_sessionB session_blob;
+       if (DEBUGLVL(DBGLVL_DEBUG)) {
+               struct smbXsrv_sessionB session_blob = {
+                       .version = SMBXSRV_VERSION_0,
+                       .info.info0 = session,
+               };
 
-               ZERO_STRUCT(session_blob);
-               session_blob.version = SMBXSRV_VERSION_0;
-               session_blob.info.info0 = session;
-
-               DEBUG(10,("smbXsrv_session_update: global_id (0x%08x) stored\n",
-                         session->global->session_global_id));
+               DBG_DEBUG("global_id (0x%08x) stored\n",
+                         session->global->session_global_id);
                NDR_PRINT_DEBUG(smbXsrv_sessionB, &session_blob);
        }
 
@@ -1426,10 +1514,16 @@ NTSTATUS smbXsrv_session_find_channel(const struct smbXsrv_session *session,
        for (i=0; i < session->global->num_channels; i++) {
                struct smbXsrv_channel_global0 *c = &session->global->channels[i];
 
-               if (c->connection == conn) {
-                       *_c = c;
-                       return NT_STATUS_OK;
+               if (c->channel_id != conn->channel_id) {
+                       continue;
                }
+
+               if (c->connection != conn) {
+                       continue;
+               }
+
+               *_c = c;
+               return NT_STATUS_OK;
        }
 
        return NT_STATUS_USER_SESSION_DELETED;
@@ -1443,6 +1537,10 @@ NTSTATUS smbXsrv_session_find_auth(const struct smbXsrv_session *session,
        struct smbXsrv_session_auth0 *a;
 
        for (a = session->pending_auth; a != NULL; a = a->next) {
+               if (a->channel_id != conn->channel_id) {
+                       continue;
+               }
+
                if (a->connection == conn) {
                        if (now != 0) {
                                a->idle_time = now;
@@ -1481,18 +1579,22 @@ NTSTATUS smbXsrv_session_create_auth(struct smbXsrv_session *session,
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       a = talloc_zero(session, struct smbXsrv_session_auth0);
+       a = talloc(session, struct smbXsrv_session_auth0);
        if (a == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
-       a->session = session;
-       a->connection = conn;
-       a->in_flags = in_flags;
-       a->in_security_mode = in_security_mode;
-       a->creation_time = now;
-       a->idle_time = now;
 
-       if (conn->protocol >= PROTOCOL_SMB3_10) {
+       *a = (struct smbXsrv_session_auth0){
+               .session = session,
+               .connection = conn,
+               .in_flags = in_flags,
+               .in_security_mode = in_security_mode,
+               .creation_time = now,
+               .idle_time = now,
+               .channel_id = conn->channel_id,
+       };
+
+       if (conn->protocol >= PROTOCOL_SMB3_11) {
                a->preauth = talloc(a, struct smbXsrv_preauth);
                if (a->preauth == NULL) {
                        TALLOC_FREE(session);
@@ -1508,6 +1610,129 @@ NTSTATUS smbXsrv_session_create_auth(struct smbXsrv_session *session,
        return NT_STATUS_OK;
 }
 
+static void smbXsrv_session_remove_channel_done(struct tevent_req *subreq);
+
+NTSTATUS smbXsrv_session_remove_channel(struct smbXsrv_session *session,
+                                       struct smbXsrv_connection *xconn)
+{
+       struct smbXsrv_session_auth0 *a = NULL;
+       struct smbXsrv_channel_global0 *c = NULL;
+       NTSTATUS status;
+       bool need_update = false;
+
+       status = smbXsrv_session_find_auth(session, xconn, 0, &a);
+       if (!NT_STATUS_IS_OK(status)) {
+               a = NULL;
+       }
+       status = smbXsrv_session_find_channel(session, xconn, &c);
+       if (!NT_STATUS_IS_OK(status)) {
+               c = NULL;
+       }
+
+       if (a != NULL) {
+               smbXsrv_session_auth0_destructor(a);
+               a->connection = NULL;
+               need_update = true;
+       }
+
+       if (c != NULL) {
+               struct smbXsrv_session_global0 *global = session->global;
+               ptrdiff_t n;
+
+               n = (c - global->channels);
+               if (n >= global->num_channels || n < 0) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+               ARRAY_DEL_ELEMENT(global->channels, n, global->num_channels);
+               global->num_channels--;
+               if (global->num_channels == 0) {
+                       struct smbXsrv_client *client = session->client;
+                       struct tevent_queue *xconn_wait_queue =
+                               xconn->transport.shutdown_wait_queue;
+                       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 disappear.
+                        */
+                       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(%" PRIu64
+                                               ") failed: %s\n",
+                                               session->global
+                                                       ->session_wire_id,
+                                               nt_errstr(status));
+                                       return status;
+                               }
+                       }
+
+                       /*
+                        * This is guaranteed to set
+                        * session->status = NT_STATUS_USER_SESSION_DELETED
+                        * even if NULL is returned.
+                        */
+                       subreq = smb2srv_session_shutdown_send(session,
+                                                              client->raw_ev_ctx,
+                                                              session,
+                                                              NULL);
+                       if (subreq == NULL) {
+                               status = NT_STATUS_NO_MEMORY;
+                               DBG_ERR("smb2srv_session_shutdown_send("
+                                       "%" PRIu64 " failed: %s\n",
+                                       session->global->session_wire_id,
+                                       nt_errstr(status));
+                               return status;
+                       }
+                       tevent_req_set_callback(subreq,
+                                               smbXsrv_session_remove_channel_done,
+                                               session);
+               }
+               need_update = true;
+       }
+
+       if (!need_update) {
+               return NT_STATUS_OK;
+       }
+
+       return smbXsrv_session_update(session);
+}
+
+static void smbXsrv_session_remove_channel_done(struct tevent_req *subreq)
+{
+       struct smbXsrv_session *session =
+               tevent_req_callback_data(subreq,
+               struct smbXsrv_session);
+       NTSTATUS status;
+
+       status = smb2srv_session_shutdown_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("smb2srv_session_shutdown_recv(%" PRIu64
+                       ") failed: %s\n",
+                       session->global->session_wire_id,
+                       nt_errstr(status));
+       }
+
+       status = smbXsrv_session_logoff(session);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("smbXsrv_session_logoff(%" PRIu64 ") failed: %s\n",
+                       session->global->session_wire_id,
+                       nt_errstr(status));
+       }
+
+       TALLOC_FREE(session);
+}
+
 struct smb2srv_session_shutdown_state {
        struct tevent_queue *wait_queue;
 };
@@ -1519,9 +1744,9 @@ struct tevent_req *smb2srv_session_shutdown_send(TALLOC_CTX *mem_ctx,
                                        struct smbXsrv_session *session,
                                        struct smbd_smb2_request *current_req)
 {
-       struct tevent_req *req;
-       struct smb2srv_session_shutdown_state *state;
-       struct tevent_req *subreq;
+       struct tevent_req *req = NULL;
+       struct smb2srv_session_shutdown_state *state = NULL;
+       struct tevent_req *subreq = NULL;
        struct smbXsrv_connection *xconn = NULL;
        size_t len = 0;
 
@@ -1554,28 +1779,7 @@ struct tevent_req *smb2srv_session_shutdown_send(TALLOC_CTX *mem_ctx,
                                continue;
                        }
 
-                       if (!NT_STATUS_IS_OK(xconn->transport.status)) {
-                               preq->session = NULL;
-                               /*
-                                * If we no longer have a session we can't
-                                * sign or encrypt replies.
-                                */
-                               preq->do_signing = false;
-                               preq->do_encryption = false;
-                               preq->preauth = NULL;
-
-                               if (preq->subreq != NULL) {
-                                       tevent_req_cancel(preq->subreq);
-                               }
-                               continue;
-                       }
-
-                       /*
-                        * Never cancel anything in a compound
-                        * request. Way too hard to deal with
-                        * the result.
-                        */
-                       if (!preq->compound_related && preq->subreq != NULL) {
+                       if (preq->subreq != NULL) {
                                tevent_req_cancel(preq->subreq);
                        }
 
@@ -1650,6 +1854,30 @@ NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session)
        session->client = NULL;
        session->status = NT_STATUS_USER_SESSION_DELETED;
 
+       /*
+        * For SMB2 this is a bit redundant as files are also close
+        * below via smb2srv_tcon_disconnect_all() -> ... ->
+        * smbXsrv_tcon_disconnect() -> close_cnum() ->
+        * file_close_conn().
+        */
+       file_close_user(sconn, session->global->session_wire_id);
+
+       if (session->tcon_table != NULL) {
+               /*
+                * Note: We only have a tcon_table for SMB2.
+                */
+               status = smb2srv_tcon_disconnect_all(session);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_ERR("smbXsrv_session_logoff(0x%08x): "
+                                 "smb2srv_tcon_disconnect_all() failed: %s\n",
+                                 session->global->session_global_id,
+                                 nt_errstr(status));
+                       error = status;
+               }
+       }
+
+       invalidate_vuid(sconn, session->global->session_wire_id);
+
        global_rec = session->global->db_rec;
        session->global->db_rec = NULL;
        if (global_rec == NULL) {
@@ -1667,12 +1895,11 @@ NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session)
                if (!NT_STATUS_IS_OK(status)) {
                        TDB_DATA key = dbwrap_record_get_key(global_rec);
 
-                       DEBUG(0, ("smbXsrv_session_logoff(0x%08x): "
+                       DBG_ERR("smbXsrv_session_logoff(0x%08x): "
                                  "failed to delete global key '%s': %s\n",
                                  session->global->session_global_id,
-                                 hex_encode_talloc(global_rec, key.dptr,
-                                                   key.dsize),
-                                 nt_errstr(status)));
+                                 tdb_data_dbg(key),
+                                 nt_errstr(status));
                        error = status;
                }
        }
@@ -1694,12 +1921,11 @@ NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session)
                if (!NT_STATUS_IS_OK(status)) {
                        TDB_DATA key = dbwrap_record_get_key(local_rec);
 
-                       DEBUG(0, ("smbXsrv_session_logoff(0x%08x): "
+                       DBG_ERR("smbXsrv_session_logoff(0x%08x): "
                                  "failed to delete local key '%s': %s\n",
                                  session->global->session_global_id,
-                                 hex_encode_talloc(local_rec, key.dptr,
-                                                   key.dsize),
-                                 nt_errstr(status)));
+                                 tdb_data_dbg(key),
+                                 nt_errstr(status));
                        error = status;
                }
                table->local.num_sessions -= 1;
@@ -1709,29 +1935,6 @@ NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session)
        }
        session->db_rec = NULL;
 
-       if (session->compat) {
-               file_close_user(sconn, session->compat->vuid);
-       }
-
-       if (session->tcon_table != NULL) {
-               /*
-                * Note: We only have a tcon_table for SMB2.
-                */
-               status = smb2srv_tcon_disconnect_all(session);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0, ("smbXsrv_session_logoff(0x%08x): "
-                                 "smb2srv_tcon_disconnect_all() failed: %s\n",
-                                 session->global->session_global_id,
-                                 nt_errstr(status)));
-                       error = status;
-               }
-       }
-
-       if (session->compat) {
-               invalidate_vuid(sconn, session->compat->vuid);
-               session->compat = NULL;
-       }
-
        return error;
 }
 
@@ -1743,36 +1946,31 @@ struct smbXsrv_session_logoff_all_state {
 static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec,
                                               void *private_data);
 
-NTSTATUS smbXsrv_session_logoff_all(struct smbXsrv_connection *conn)
+NTSTATUS smbXsrv_session_logoff_all(struct smbXsrv_client *client)
 {
-       struct smbXsrv_session_table *table = conn->client->session_table;
-       struct smbXsrv_session_logoff_all_state state;
+       struct smbXsrv_session_table *table = client->session_table;
+       struct smbXsrv_session_logoff_all_state state = {};
        NTSTATUS status;
        int count = 0;
 
        if (table == NULL) {
-               DEBUG(10, ("smbXsrv_session_logoff_all: "
-                          "empty session_table, nothing to do.\n"));
+               DBG_DEBUG("empty session_table, nothing to do.\n");
                return NT_STATUS_OK;
        }
 
-       ZERO_STRUCT(state);
-
        status = dbwrap_traverse(table->local.db_ctx,
                                 smbXsrv_session_logoff_all_callback,
                                 &state, &count);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("smbXsrv_session_logoff_all: "
-                         "dbwrap_traverse() failed: %s\n",
-                         nt_errstr(status)));
+               DBG_ERR("dbwrap_traverse() failed: %s\n", nt_errstr(status));
                return status;
        }
 
        if (!NT_STATUS_IS_OK(state.first_status)) {
-               DEBUG(0, ("smbXsrv_session_logoff_all: "
-                         "count[%d] errors[%d] first[%s]\n",
-                         count, state.errors,
-                         nt_errstr(state.first_status)));
+               DBG_ERR("count[%d] errors[%d] first[%s]\n",
+                       count,
+                       state.errors,
+                       nt_errstr(state.first_status));
                return state.first_status;
        }
 
@@ -1803,8 +2001,8 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec,
        session = talloc_get_type_abort(ptr, struct smbXsrv_session);
 
        session->db_rec = local_rec;
-
        status = smbXsrv_session_clear_and_logoff(session);
+       session->db_rec = NULL;
        if (!NT_STATUS_IS_OK(status)) {
                if (NT_STATUS_IS_OK(state->first_status)) {
                        state->first_status = status;
@@ -1816,6 +2014,160 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec,
        return 0;
 }
 
+struct smbXsrv_session_local_trav_state {
+       NTSTATUS status;
+       int (*caller_cb)(struct smbXsrv_session *session,
+                        void *caller_data);
+       void *caller_data;
+};
+
+static int smbXsrv_session_local_traverse_cb(struct db_record *local_rec,
+                                            void *private_data);
+
+NTSTATUS smbXsrv_session_local_traverse(
+       struct smbXsrv_client *client,
+       int (*caller_cb)(struct smbXsrv_session *session,
+                        void *caller_data),
+       void *caller_data)
+{
+       struct smbXsrv_session_table *table = client->session_table;
+       struct smbXsrv_session_local_trav_state state;
+       NTSTATUS status;
+       int count = 0;
+
+       state = (struct smbXsrv_session_local_trav_state) {
+               .status = NT_STATUS_OK,
+               .caller_cb = caller_cb,
+               .caller_data = caller_data,
+       };
+
+       if (table == NULL) {
+               DBG_DEBUG("empty session_table, nothing to do.\n");
+               return NT_STATUS_OK;
+       }
+
+       status = dbwrap_traverse(table->local.db_ctx,
+                                smbXsrv_session_local_traverse_cb,
+                                &state,
+                                &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("dbwrap_traverse() failed: %s\n", nt_errstr(status));
+               return status;
+       }
+       if (!NT_STATUS_IS_OK(state.status)) {
+               DBG_ERR("count[%d] status[%s]\n",
+                       count, nt_errstr(state.status));
+               return state.status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static int smbXsrv_session_local_traverse_cb(struct db_record *local_rec,
+                                            void *private_data)
+{
+       struct smbXsrv_session_local_trav_state *state =
+               (struct smbXsrv_session_local_trav_state *)private_data;
+       TDB_DATA val;
+       void *ptr = NULL;
+       struct smbXsrv_session *session = NULL;
+       int ret;
+
+       val = dbwrap_record_get_value(local_rec);
+       if (val.dsize != sizeof(ptr)) {
+               state->status = NT_STATUS_INTERNAL_ERROR;
+               return -1;
+       }
+
+       memcpy(&ptr, val.dptr, val.dsize);
+       session = talloc_get_type_abort(ptr, struct smbXsrv_session);
+
+       session->db_rec = local_rec;
+       ret = state->caller_cb(session, state->caller_data);
+       session->db_rec = NULL;
+
+       return ret;
+}
+
+struct smbXsrv_session_disconnect_xconn_state {
+       struct smbXsrv_connection *xconn;
+       NTSTATUS first_status;
+       int errors;
+};
+
+static int smbXsrv_session_disconnect_xconn_callback(struct db_record *local_rec,
+                                              void *private_data);
+
+NTSTATUS smbXsrv_session_disconnect_xconn(struct smbXsrv_connection *xconn)
+{
+       struct smbXsrv_client *client = xconn->client;
+       struct smbXsrv_session_table *table = client->session_table;
+       struct smbXsrv_session_disconnect_xconn_state state = {
+               .xconn = xconn,
+       };
+       NTSTATUS status;
+       int count = 0;
+
+       if (table == NULL) {
+               DBG_ERR("empty session_table, nothing to do.\n");
+               return NT_STATUS_OK;
+       }
+
+       status = dbwrap_traverse(table->local.db_ctx,
+                                smbXsrv_session_disconnect_xconn_callback,
+                                &state, &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("dbwrap_traverse() failed: %s\n",
+                       nt_errstr(status));
+               return status;
+       }
+
+       if (!NT_STATUS_IS_OK(state.first_status)) {
+               DBG_ERR("count[%d] errors[%d] first[%s]\n",
+                       count, state.errors,
+                       nt_errstr(state.first_status));
+               return state.first_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static int smbXsrv_session_disconnect_xconn_callback(struct db_record *local_rec,
+                                              void *private_data)
+{
+       struct smbXsrv_session_disconnect_xconn_state *state =
+               (struct smbXsrv_session_disconnect_xconn_state *)private_data;
+       TDB_DATA val;
+       void *ptr = NULL;
+       struct smbXsrv_session *session = NULL;
+       NTSTATUS status;
+
+       val = dbwrap_record_get_value(local_rec);
+       if (val.dsize != sizeof(ptr)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               if (NT_STATUS_IS_OK(state->first_status)) {
+                       state->first_status = status;
+               }
+               state->errors++;
+               return 0;
+       }
+
+       memcpy(&ptr, val.dptr, val.dsize);
+       session = talloc_get_type_abort(ptr, struct smbXsrv_session);
+
+       session->db_rec = local_rec;
+       status = smbXsrv_session_remove_channel(session, state->xconn);
+       session->db_rec = NULL;
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_IS_OK(state->first_status)) {
+                       state->first_status = status;
+               }
+               state->errors++;
+       }
+
+       return 0;
+}
+
 NTSTATUS smb1srv_session_table_init(struct smbXsrv_connection *conn)
 {
        /*
@@ -1836,6 +2188,213 @@ NTSTATUS smb1srv_session_lookup(struct smbXsrv_connection *conn,
                                            session);
 }
 
+NTSTATUS smbXsrv_session_info_lookup(struct smbXsrv_client *client,
+                                    uint64_t session_wire_id,
+                                    struct auth_session_info **si)
+{
+       struct smbXsrv_session_table *table = client->session_table;
+       uint8_t key_buf[SMBXSRV_SESSION_LOCAL_TDB_KEY_SIZE];
+       struct smbXsrv_session_local_fetch_state state = {
+               .status = NT_STATUS_INTERNAL_ERROR,
+       };
+       TDB_DATA key;
+       NTSTATUS status;
+
+       if (session_wire_id == 0) {
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table == NULL) {
+               /* this might happen before the end of negprot */
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table->local.db_ctx == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       key = smbXsrv_session_local_id_to_key(session_wire_id, key_buf);
+
+       status = dbwrap_parse_record(table->local.db_ctx, key,
+                                    smbXsrv_session_local_fetch_parser,
+                                    &state);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       if (!NT_STATUS_IS_OK(state.status)) {
+               return state.status;
+       }
+       if (state.session->global->auth_session_info == NULL) {
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       *si = state.session->global->auth_session_info;
+       return NT_STATUS_OK;
+}
+
+/*
+ * In memory of get_valid_user_struct()
+ *
+ * This function is similar to smbXsrv_session_local_lookup() and it's wrappers,
+ * but it doesn't implement the state checks of
+ * those. get_valid_smbXsrv_session() is NOT meant to be called to validate the
+ * session wire-id of incoming SMB requests, it MUST only be used in later
+ * internal processing where the session wire-id has already been validated.
+ */
+NTSTATUS get_valid_smbXsrv_session(struct smbXsrv_client *client,
+                                  uint64_t session_wire_id,
+                                  struct smbXsrv_session **session)
+{
+       struct smbXsrv_session_table *table = client->session_table;
+       uint8_t key_buf[SMBXSRV_SESSION_LOCAL_TDB_KEY_SIZE];
+       struct smbXsrv_session_local_fetch_state state = {
+               .status = NT_STATUS_INTERNAL_ERROR,
+       };
+       TDB_DATA key;
+       NTSTATUS status;
+
+       if (session_wire_id == 0) {
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table == NULL) {
+               /* this might happen before the end of negprot */
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table->local.db_ctx == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       key = smbXsrv_session_local_id_to_key(session_wire_id, key_buf);
+
+       status = dbwrap_parse_record(table->local.db_ctx, key,
+                                    smbXsrv_session_local_fetch_parser,
+                                    &state);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       if (!NT_STATUS_IS_OK(state.status)) {
+               return state.status;
+       }
+       if (state.session->global->auth_session_info == NULL) {
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       *session = state.session;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS smb2srv_session_lookup_global(struct smbXsrv_client *client,
+                                      uint64_t session_wire_id,
+                                      TALLOC_CTX *mem_ctx,
+                                      struct smbXsrv_session **_session)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct smbXsrv_session_table *table = client->session_table;
+       uint32_t global_id = session_wire_id & UINT32_MAX;
+       uint64_t global_zeros = session_wire_id & 0xFFFFFFFF00000000LLU;
+       struct smbXsrv_session *session = NULL;
+       struct db_record *global_rec = NULL;
+       bool is_free = false;
+       NTSTATUS status;
+
+       if (global_id == 0) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+       if (global_zeros != 0) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table == NULL) {
+               /* this might happen before the end of negprot */
+               TALLOC_FREE(frame);
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       if (table->global.db_ctx == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       session = talloc_zero(mem_ctx, struct smbXsrv_session);
+       if (session == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_steal(frame, session);
+
+       session->client = client;
+       session->status = NT_STATUS_BAD_LOGON_SESSION_STATE;
+       session->local_id = global_id;
+
+       /*
+        * This means smb2_get_new_nonce() will return
+        * NT_STATUS_ENCRYPTION_FAILED.
+        *
+        * But we initialize some random parts just in case...
+        */
+       session->nonce_high_max = session->nonce_high = 0;
+       generate_nonce_buffer((uint8_t *)&session->nonce_high_random,
+                             sizeof(session->nonce_high_random));
+       generate_nonce_buffer((uint8_t *)&session->nonce_low,
+                             sizeof(session->nonce_low));
+
+       global_rec = smbXsrv_session_global_fetch_locked(table->global.db_ctx,
+                                                        global_id,
+                                                        frame);
+       if (global_rec == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       smbXsrv_session_global_verify_record(global_rec,
+                                            &is_free,
+                                            NULL,
+                                            session,
+                                            &session->global,
+                                            NULL);
+       if (is_free) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_USER_SESSION_DELETED;
+       }
+
+       /*
+        * We don't have channels on this session
+        * and only the main signing key
+        */
+       session->global->num_channels = 0;
+       status = smb2_signing_key_sign_create(session->global,
+                                             session->global->signing_algo,
+                                             NULL, /* no master key */
+                                             NULL, /* derivations */
+                                             &session->global->signing_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       session->global->signing_key->blob = session->global->signing_key_blob;
+       session->global->signing_flags = 0;
+
+       status = smb2_signing_key_cipher_create(session->global,
+                                               session->global->encryption_cipher,
+                                               NULL, /* no master key */
+                                               NULL, /* derivations */
+                                               &session->global->decryption_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       session->global->decryption_key->blob = session->global->decryption_key_blob;
+       session->global->encryption_flags = 0;
+
+       *_session = talloc_move(mem_ctx, &session);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
 NTSTATUS smb2srv_session_table_init(struct smbXsrv_connection *conn)
 {
        /*
@@ -1900,18 +2459,25 @@ static int smbXsrv_session_global_traverse_fn(struct db_record *rec, void *data)
        ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
                        (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_session_globalB);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               DEBUG(1,("Invalid record in smbXsrv_session_global.tdb:"
+               DBG_WARNING("Invalid record in smbXsrv_session_global.tdb:"
                         "key '%s' ndr_pull_struct_blob - %s\n",
-                        hex_encode_talloc(frame, key.dptr, key.dsize),
-                        ndr_errstr(ndr_err)));
+                        tdb_data_dbg(key),
+                        ndr_errstr(ndr_err));
                goto done;
        }
 
        if (global_blob.version != SMBXSRV_VERSION_0) {
-               DEBUG(1,("Invalid record in smbXsrv_session_global.tdb:"
-                        "key '%s' unsuported version - %d\n",
-                        hex_encode_talloc(frame, key.dptr, key.dsize),
-                        (int)global_blob.version));
+               DBG_WARNING("Invalid record in smbXsrv_session_global.tdb:"
+                        "key '%s' unsupported version - %d\n",
+                        tdb_data_dbg(key),
+                        (int)global_blob.version);
+               goto done;
+       }
+
+       if (global_blob.info.info0 == NULL) {
+               DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
+                        "key '%s' info0 NULL pointer\n",
+                        tdb_data_dbg(key));
                goto done;
        }
 
@@ -1938,8 +2504,8 @@ NTSTATUS smbXsrv_session_global_traverse(
        status = smbXsrv_session_global_init(NULL);
        if (!NT_STATUS_IS_OK(status)) {
                unbecome_root();
-               DEBUG(0, ("Failed to initialize session_global: %s\n",
-                         nt_errstr(status)));
+               DBG_ERR("Failed to initialize session_global: %s\n",
+                         nt_errstr(status));
                return status;
        }