]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smb2_server: fallback global session lookup if the session belongs to a different...
authorStefan Metzmacher <metze@samba.org>
Thu, 25 Feb 2021 16:58:48 +0000 (17:58 +0100)
committerJeremy Allison <jra@samba.org>
Wed, 17 Mar 2021 00:49:32 +0000 (00:49 +0000)
The key is that we need to have the signing key in order to pass the
signing checks and give the correct session bind error status.

This should fix the MultipleChannel_Negative_SMB2002 testcase
of the Windows Protocol Test Suite (FileServer).

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

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reported-by: Jones Syue <jonessyue@qnap.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/knownfail.d/smb2.session
source3/smbd/globals.h
source3/smbd/smb2_server.c
source3/smbd/smb2_sesssetup.c
source3/smbd/smbXsrv_session.c

index 3e18af205273139e60d849b4f614f4d30e530afd..8fcecf006302fc38a89152d6bfbcd535af12cece 100644 (file)
@@ -1,5 +1,4 @@
 ^samba3.smb2.session.*.bind_invalid_auth
-^samba3.smb2.session.*.bind_negative_smb202
 ^samba3.smb2.session.*.bind_negative_smb210
 ^samba3.smb2.session.*.bind_negative_smb2to3
 ^samba3.smb2.session.*.bind_negative_smb3to2
index fd505dea289820f6c13238021eea40c71f70cc6a..0cab0d7396c6da8eceaffa6994176ed8adc6e708 100644 (file)
@@ -607,6 +607,10 @@ NTSTATUS smb2srv_session_lookup_conn(struct smbXsrv_connection *conn,
 NTSTATUS smb2srv_session_lookup_client(struct smbXsrv_client *client,
                                       uint64_t session_id, NTTIME now,
                                       struct smbXsrv_session **session);
+NTSTATUS smb2srv_session_lookup_global(struct smbXsrv_client *client,
+                                      uint64_t session_wire_id,
+                                      TALLOC_CTX *mem_ctx,
+                                      struct smbXsrv_session **session);
 NTSTATUS get_valid_smbXsrv_session(struct smbXsrv_client *client,
                                   uint64_t session_wire_id,
                                   struct smbXsrv_session **session);
index 90a0b4860fd6e8a442baf85d34d21102cea3caea..d9de7f4ab06b39fd5263be9fd67659cd53de290a 100644 (file)
@@ -437,6 +437,10 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *xconn,
 
                        status = smb2srv_session_lookup_conn(xconn, uid, now,
                                                             &s);
+                       if (s == NULL) {
+                               status = smb2srv_session_lookup_global(xconn->client,
+                                                                      uid, req, &s);
+                       }
                        if (s == NULL) {
                                DEBUG(1, ("invalid session[%llu] in "
                                          "SMB2_TRANSFORM header\n",
@@ -2525,6 +2529,28 @@ static NTSTATUS smbd_smb2_request_check_session(struct smbd_smb2_request *req)
                req->session = session;
                req->last_session_id = in_session_id;
        }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+               switch (in_opcode) {
+               case SMB2_OP_SESSSETUP:
+                       status = smb2srv_session_lookup_global(req->xconn->client,
+                                                              in_session_id,
+                                                              req,
+                                                              &session);
+                       if (NT_STATUS_IS_OK(status)) {
+                               /*
+                                * We fallback to a session of
+                                * another process in order to
+                                * get the signing correct.
+                                *
+                                * We don't set req->last_session_id here.
+                                */
+                               req->session = session;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
        if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
                switch (in_opcode) {
                case SMB2_OP_SESSSETUP:
index 3b98825293123e139cfab2f4bd781bd20c786fd8..da33b53b7e978dd4db5c44c3adfaead473430247 100644 (file)
@@ -794,6 +794,13 @@ auth:
 
                state->session = smb2req->session;
                status = state->session->status;
+               if (NT_STATUS_EQUAL(status, NT_STATUS_BAD_LOGON_SESSION_STATE)) {
+                       /*
+                        * This comes from smb2srv_session_lookup_global().
+                        */
+                       tevent_req_nterror(req, NT_STATUS_USER_SESSION_DELETED);
+                       return tevent_req_post(req, ev);
+               }
                if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
                        status = NT_STATUS_OK;
                }
index 9bfdb8fbf04ac3abd55483fa60f3d486579ba65e..734b2edfe2e8ac9e80768a8b00133bd6baaec78e 100644 (file)
@@ -1162,6 +1162,10 @@ static void smb2srv_session_close_previous_modified(struct tevent_req *subreq)
        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);
 }
@@ -2184,6 +2188,113 @@ NTSTATUS get_valid_smbXsrv_session(struct smbXsrv_client *client,
        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 intialize 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);
+       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;
+
+       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 = talloc_move(mem_ctx, &session);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
 NTSTATUS smb2srv_session_table_init(struct smbXsrv_connection *conn)
 {
        /*