From: Stefan Metzmacher Date: Thu, 25 Feb 2021 16:58:48 +0000 (+0100) Subject: smb2_server: fallback global session lookup if the session belongs to a different... X-Git-Tag: tevent-0.11.0~1484 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8ccd2391afcc50ca109e328a523b233fcb91a90;p=thirdparty%2Fsamba.git smb2_server: fallback global session lookup if the session belongs to a different client 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 Reported-by: Jones Syue Reviewed-by: Jeremy Allison --- diff --git a/selftest/knownfail.d/smb2.session b/selftest/knownfail.d/smb2.session index 3e18af20527..8fcecf00630 100644 --- a/selftest/knownfail.d/smb2.session +++ b/selftest/knownfail.d/smb2.session @@ -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 diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index fd505dea289..0cab0d7396c 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -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); diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 90a0b4860fd..d9de7f4ab06 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -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: diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 3b988252931..da33b53b7e9 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -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; } diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index 9bfdb8fbf04..734b2edfe2e 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -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) { /*