diff options
author | pg9182 <96569817+pg9182@users.noreply.github.com> | 2023-03-06 12:02:53 -0500 |
---|---|---|
committer | pg9182 <96569817+pg9182@users.noreply.github.com> | 2023-04-17 14:50:10 -0400 |
commit | 87bd14cbe83c0eca42a6c15f4712415627941df2 (patch) | |
tree | 303e089a46e174b5db1766e915254d87c90b746d /NorthstarDLL/server | |
parent | 45819c0ef2881610ca26261792995a58e6f68631 (diff) | |
download | NorthstarLauncher-87bd14cbe83c0eca42a6c15f4712415627941df2.tar.gz NorthstarLauncher-87bd14cbe83c0eca42a6c15f4712415627941df2.zip |
Replace HTTP auth server with Atlas connectionless packet
Diffstat (limited to 'NorthstarDLL/server')
-rw-r--r-- | NorthstarDLL/server/auth/serverauthentication.cpp | 103 | ||||
-rw-r--r-- | NorthstarDLL/server/auth/serverauthentication.h | 9 | ||||
-rw-r--r-- | NorthstarDLL/server/servernethooks.cpp | 40 | ||||
-rw-r--r-- | NorthstarDLL/server/serverpresence.cpp | 9 | ||||
-rw-r--r-- | NorthstarDLL/server/serverpresence.h | 3 |
5 files changed, 41 insertions, 123 deletions
diff --git a/NorthstarDLL/server/auth/serverauthentication.cpp b/NorthstarDLL/server/auth/serverauthentication.cpp index 4d9eb19f..8f62e1dd 100644 --- a/NorthstarDLL/server/auth/serverauthentication.cpp +++ b/NorthstarDLL/server/auth/serverauthentication.cpp @@ -14,104 +14,30 @@ #include "client/r2client.h" #include "server/r2server.h" -#include "httplib.h" - #include <fstream> #include <filesystem> +#include <string> #include <thread> AUTOHOOK_INIT() -const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; - // global vars ServerAuthenticationManager* g_pServerAuthentication; CBaseServer__RejectConnectionType CBaseServer__RejectConnection; -void ServerAuthenticationManager::StartPlayerAuthServer() +void ServerAuthenticationManager::AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata) { - if (m_bRunningPlayerAuthThread) - { - spdlog::warn("ServerAuthenticationManager::StartPlayerAuthServer was called while m_bRunningPlayerAuthThread is true"); - return; - } - - g_pServerPresence->SetAuthPort(Cvar_ns_player_auth_port->GetInt()); // set auth port for presence - m_bRunningPlayerAuthThread = true; - - // listen is a blocking call so thread this - std::thread serverThread( - [this] - { - // this is just a super basic way to verify that servers have ports open, masterserver will try to read this before ensuring - // server is legit - m_PlayerAuthServer.Get( - "/verify", - [](const httplib::Request& request, httplib::Response& response) - { response.set_content(AUTHSERVER_VERIFY_STRING, "text/plain"); }); - - m_PlayerAuthServer.Post( - "/authenticate_incoming_player", - [this](const httplib::Request& request, httplib::Response& response) - { - if (!request.has_param("id") || !request.has_param("authToken") || request.body.size() >= R2::PERSISTENCE_MAX_SIZE || - !request.has_param("serverAuthToken") || - strcmp(g_pMasterServerManager->m_sOwnServerAuthToken, request.get_param_value("serverAuthToken").c_str())) - { - response.set_content("{\"success\":false}", "application/json"); - return; - } - - uint64_t uid; - try - { - uid = std::strtoull(request.get_param_value("id").c_str(), nullptr, 10); - } - catch (std::exception const& ex) - { - response.set_content("{\"success\":false}", "application/json"); - return; - } - if (!g_pBanSystem->IsUIDAllowed(uid)) - { - response.set_content("{\"success\":false,\"reject\":\"Banned from this server.\"}", "application/json"); - return; - } - - RemoteAuthData newAuthData {}; - strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), request.get_param_value("id").c_str(), sizeof(newAuthData.uid) - 1); - strncpy_s( - newAuthData.username, - sizeof(newAuthData.username), - request.get_param_value("username").c_str(), - sizeof(newAuthData.username) - 1); - - newAuthData.pdataSize = request.body.size(); - newAuthData.pdata = new char[newAuthData.pdataSize]; - memcpy(newAuthData.pdata, request.body.c_str(), newAuthData.pdataSize); - - std::lock_guard<std::mutex> guard(m_AuthDataMutex); - m_RemoteAuthenticationData.insert(std::make_pair(request.get_param_value("authToken"), newAuthData)); - - response.set_content("{\"success\":true}", "application/json"); - }); - - m_PlayerAuthServer.listen("0.0.0.0", Cvar_ns_player_auth_port->GetInt()); - }); - - serverThread.detach(); -} + std::string uidS = std::to_string(uid); -void ServerAuthenticationManager::StopPlayerAuthServer() -{ - if (!m_bRunningPlayerAuthThread) - { - spdlog::warn("ServerAuthenticationManager::StopPlayerAuthServer was called while m_bRunningPlayerAuthThread is false"); - return; - } + RemoteAuthData newAuthData {}; + strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), uidS.c_str(), uidS.length()); + strncpy_s(newAuthData.username, sizeof(newAuthData.username), username.c_str(), username.length()); + newAuthData.pdata = new char[pdata.length()]; + newAuthData.pdataSize = pdata.length(); + memcpy(newAuthData.pdata, pdata.c_str(), newAuthData.pdataSize); - m_bRunningPlayerAuthThread = false; - m_PlayerAuthServer.stop(); + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + m_RemoteAuthenticationData[token] = newAuthData; } void ServerAuthenticationManager::AddPlayer(R2::CBaseClient* pPlayer, const char* pToken) @@ -323,11 +249,11 @@ bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, if (!bFakePlayer) { if (!g_pServerAuthentication->VerifyPlayerName(pNextPlayerToken, pName, pVerifiedName)) - pAuthenticationFailure = "Invalid name."; + pAuthenticationFailure = "Invalid Name."; else if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid)) - pAuthenticationFailure = "Banned from this server."; + pAuthenticationFailure = "Banned From server."; else if (!g_pServerAuthentication->CheckAuthentication(self, iNextPlayerUid, pNextPlayerToken)) - pAuthenticationFailure = "Authentication failed."; + pAuthenticationFailure = "Authentication Failed."; } else // need to copy name for bots still strncpy_s(pVerifiedName, pName, 63); @@ -423,7 +349,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), ( g_pServerAuthentication = new ServerAuthenticationManager; - g_pServerAuthentication->Cvar_ns_player_auth_port = new ConVar("ns_player_auth_port", "8081", FCVAR_GAMEDLL, ""); g_pServerAuthentication->Cvar_ns_erase_auth_info = new ConVar("ns_erase_auth_info", "1", FCVAR_GAMEDLL, "Whether auth info should be erased from this server on disconnect or crash"); g_pServerAuthentication->Cvar_ns_auth_allow_insecure = diff --git a/NorthstarDLL/server/auth/serverauthentication.h b/NorthstarDLL/server/auth/serverauthentication.h index 2ca07a8a..dd0e13af 100644 --- a/NorthstarDLL/server/auth/serverauthentication.h +++ b/NorthstarDLL/server/auth/serverauthentication.h @@ -1,6 +1,5 @@ #pragma once #include "core/convar/convar.h" -#include "httplib.h" #include "engine/r2engine.h" #include <unordered_map> #include <string> @@ -27,11 +26,7 @@ extern CBaseServer__RejectConnectionType CBaseServer__RejectConnection; class ServerAuthenticationManager { - private: - httplib::Server m_PlayerAuthServer; - public: - ConVar* Cvar_ns_player_auth_port; ConVar* Cvar_ns_erase_auth_info; ConVar* Cvar_ns_auth_allow_insecure; ConVar* Cvar_ns_auth_allow_insecure_write; @@ -41,14 +36,12 @@ class ServerAuthenticationManager std::unordered_map<R2::CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData; bool m_bAllowDuplicateAccounts = false; - bool m_bRunningPlayerAuthThread = false; bool m_bNeedLocalAuthForNewgame = false; bool m_bForceResetLocalPlayerPersistence = false; bool m_bStartingLocalSPGame = false; public: - void StartPlayerAuthServer(); - void StopPlayerAuthServer(); + void AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata); void AddPlayer(R2::CBaseClient* pPlayer, const char* pAuthToken); void RemovePlayer(R2::CBaseClient* pPlayer); diff --git a/NorthstarDLL/server/servernethooks.cpp b/NorthstarDLL/server/servernethooks.cpp index c367c1e1..905d3251 100644 --- a/NorthstarDLL/server/servernethooks.cpp +++ b/NorthstarDLL/server/servernethooks.cpp @@ -12,8 +12,8 @@ AUTOHOOK_INIT() static ConVar* Cvar_net_debug_atlas_packet; static ConVar* Cvar_net_debug_atlas_packet_insecure; -#define HMACSHA256_LEN (256 / 8) -BCRYPT_ALG_HANDLE HMACSHA256; +static BCRYPT_ALG_HANDLE HMACSHA256; +constexpr size_t HMACSHA256_LEN = 256 / 8; static bool InitHMACSHA256() { @@ -42,13 +42,10 @@ static bool InitHMACSHA256() return true; } -// note: all Atlas connectionless packets should be idempotent so multiple attempts can be made to mitigate packet loss -// note: all long-running Atlas connectionless packet handlers should be started in a new thread (with copies of the data) to avoid blocking -// networking - +// compare the HMAC-SHA256(data, key) against sig (note: all strings are treated as raw binary data) static bool VerifyHMACSHA256(std::string key, std::string sig, std::string data) { - bool result = false; + uint8_t invalid = 1; char hash[HMACSHA256_LEN]; NTSTATUS status; @@ -72,18 +69,21 @@ static bool VerifyHMACSHA256(std::string key, std::string sig, std::string data) goto cleanup; } - if (std::string(hash, sizeof(hash)) == sig) + // constant-time compare + if (sig.length() == sizeof(hash)) { - result = true; - goto cleanup; + invalid = 0; + for (size_t i = 0; i < sizeof(hash); i++) + invalid |= (uint8_t)(sig[i]) ^ (uint8_t)(hash[i]); } cleanup: if (h) BCryptDestroyHash(h); - return result; + return !invalid; } +// v1 HMACSHA256-signed masterserver request (HMAC-SHA256(JSONData, MasterServerToken) + JSONData) static void ProcessAtlasConnectionlessPacketSigreq1(R2::netpacket_t* packet, bool dbg, std::string pType, std::string pData) { if (pData.length() < HMACSHA256_LEN) @@ -129,7 +129,7 @@ static void ProcessAtlasConnectionlessPacketSigreq1(R2::netpacket_t* packet, boo if (dbg) spdlog::info("got Atlas connectionless packet (size={} type={} data={})", packet->size, pType, pData); - std::thread t([pData] { g_pMasterServerManager->ProcessConnectionlessPacketSigreq1(pData); }); + std::thread t(&MasterServerManager::ProcessConnectionlessPacketSigreq1, g_pMasterServerManager, pData); t.detach(); return; @@ -152,6 +152,10 @@ static void ProcessAtlasConnectionlessPacket(R2::netpacket_t* packet) } } + // note: all Atlas connectionless packets should be idempotent so multiple attempts can be made to mitigate packet loss + // note: all long-running Atlas connectionless packet handlers should be started in a new thread (with copies of the data) to avoid + // blocking networking + // v1 HMACSHA256-signed masterserver request if (pType == "sigreq1") { @@ -166,13 +170,22 @@ static void ProcessAtlasConnectionlessPacket(R2::netpacket_t* packet) AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, R2::netpacket_t* packet)) { + // packet->data consists of 0xFFFFFFFF (int32 -1) to indicate packets aren't split, followed by a header consisting of a single + // character, which is used to uniquely identify the packet kind. Most kinds follow this with a null-terminated string payload + // then an arbitrary amoount of data. + + // T (no rate limits since we authenticate packets before doing anything expensive) if (4 < packet->size && packet->data[4] == 'T') { ProcessAtlasConnectionlessPacket(packet); return false; } + + // check rate limits for the original unconnected packets if (!g_pServerLimits->CheckConnectionlessPacketLimits(packet)) return false; + + // A, H, I, N return ProcessConnectionlessPacket(a1, packet); } @@ -202,7 +215,4 @@ ON_DLL_LOAD_RELIESON("engine.dll", ServerNetHooks, ConVar, (CModule module)) "0", FCVAR_NONE, "Whether to disable signature verification for Atlas connectionless packets (DANGEROUS: this allows anyone to impersonate Atlas)"); - - if (Cvar_net_debug_atlas_packet_insecure->GetBool()) - spdlog::warn("DANGEROUS: Atlas connectionless packet signature verification disabled; anyone will be able to impersonate Atlas"); } diff --git a/NorthstarDLL/server/serverpresence.cpp b/NorthstarDLL/server/serverpresence.cpp index e0b29cb6..945f5810 100644 --- a/NorthstarDLL/server/serverpresence.cpp +++ b/NorthstarDLL/server/serverpresence.cpp @@ -106,9 +106,8 @@ void ServerPresenceManager::AddPresenceReporter(ServerPresenceReporter* reporter void ServerPresenceManager::CreatePresence() { // reset presence fields that rely on runtime server state - // these being: port/auth port, map/playlist name, and playercount/maxplayers + // these being: port, map/playlist name, and playercount/maxplayers m_ServerPresence.m_iPort = 0; - m_ServerPresence.m_iAuthPort = 0; m_ServerPresence.m_iPlayerCount = 0; // this should actually be 0 at this point, so shouldn't need updating later m_ServerPresence.m_iMaxPlayers = 0; @@ -170,12 +169,6 @@ void ServerPresenceManager::SetPort(const int iPort) m_ServerPresence.m_iPort = iPort; } -void ServerPresenceManager::SetAuthPort(const int iAuthPort) -{ - // update authport - m_ServerPresence.m_iAuthPort = iAuthPort; -} - void ServerPresenceManager::SetName(const std::string sServerNameUnicode) { // update name diff --git a/NorthstarDLL/server/serverpresence.h b/NorthstarDLL/server/serverpresence.h index 3df2b68d..3aabecde 100644 --- a/NorthstarDLL/server/serverpresence.h +++ b/NorthstarDLL/server/serverpresence.h @@ -5,7 +5,6 @@ struct ServerPresence { public: int m_iPort; - int m_iAuthPort; std::string m_sServerId; @@ -28,7 +27,6 @@ struct ServerPresence ServerPresence(const ServerPresence* obj) { m_iPort = obj->m_iPort; - m_iAuthPort = obj->m_iAuthPort; m_sServerId = obj->m_sServerId; @@ -83,7 +81,6 @@ class ServerPresenceManager void RunFrame(double flCurrentTime); void SetPort(const int iPort); - void SetAuthPort(const int iPort); void SetName(const std::string sServerNameUnicode); void SetDescription(const std::string sServerDescUnicode); |