From f425377e5b15dc97ce8caa484b3e282ec5df529c Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Fri, 13 Aug 2021 20:02:28 +0100 Subject: add local http server for auth --- NorthstarDedicatedTest/convar.h | 2 +- NorthstarDedicatedTest/gameutils.cpp | 7 ++- NorthstarDedicatedTest/gameutils.h | 4 ++ NorthstarDedicatedTest/masterserver.cpp | 24 +++++---- NorthstarDedicatedTest/masterserver.h | 8 +-- NorthstarDedicatedTest/scriptserverbrowser.cpp | 4 +- NorthstarDedicatedTest/serverauthentication.cpp | 67 +++++++++++++++++++++---- NorthstarDedicatedTest/serverauthentication.h | 12 +++-- NorthstarDedicatedTest/sourceconsole.cpp | 3 ++ 9 files changed, 100 insertions(+), 31 deletions(-) (limited to 'NorthstarDedicatedTest') diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h index f2e35188..a9da3274 100644 --- a/NorthstarDedicatedTest/convar.h +++ b/NorthstarDedicatedTest/convar.h @@ -93,7 +93,7 @@ public: void* unknown[10]; void(*UnregisterConCommand) (ICvar* cvar, ConCommand* pCommandBase); void* unknown2[28]; - ConVar*(*FindVar)(const char* var_name); + ConVar*(*FindVar)(const char* var_name); // offset for this is currently very wrong }; VTable* m_vtable; diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index 1cf919fb..fc6b2dc7 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -15,6 +15,9 @@ ConVar* Cvar_hostport; // playlist stuff GetCurrentPlaylistType GetCurrentPlaylistName; +// uid +char* g_LocalPlayerUserID; + void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) { Cbuf_GetCurrentPlayer = (Cbuf_GetCurrentPlayerType)((char*)baseAddress + 0x120630); @@ -22,7 +25,9 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) g_GameCHostStateSingleton = (CHostState*)((char*)baseAddress + 0x7CF180); - Cvar_hostport = (ConVar*)((char*)baseAddress + 0x1FA6070); + Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070); GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640); + + g_LocalPlayerUserID = (char*)baseAddress + 0x13F8E688; } diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index 042e9bd5..ff987467 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -84,4 +84,8 @@ extern ConVar* Cvar_hostport; typedef const char*(*GetCurrentPlaylistType)(); extern GetCurrentPlaylistType GetCurrentPlaylistName; +// uid + +extern char* g_LocalPlayerUserID; + void InitialiseEngineGameUtilFunctions(HMODULE baseAddress); \ No newline at end of file diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index b8d1cd73..f5ba55f0 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -4,6 +4,7 @@ #include "gameutils.h" #include "hookutils.h" #include "httplib.h" +#include "serverauthentication.h" #include "rapidjson/document.h" #include "rapidjson/error/en.h" @@ -170,7 +171,7 @@ void MasterServerManager::RequestServerList() requestThread.detach(); } -void MasterServerManager::AuthenticateWithServer(char* serverId, char* password) +void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, char* serverId, char* password) { // dont wait, just stop if we're trying to do 2 auth requests at once if (m_authenticatingWithGameServer) @@ -180,14 +181,14 @@ void MasterServerManager::AuthenticateWithServer(char* serverId, char* password) m_scriptAuthenticatingWithGameServer = true; m_successfullyAuthenticatedWithGameServer = false; - std::thread requestThread([this, serverId, password]() + std::thread requestThread([this, uid, playerToken, serverId, password]() { httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue); http.set_connection_timeout(20); spdlog::info("Attempting authentication with server of id \"{}\"", serverId); - if (auto result = http.Post(fmt::format("/client/auth_with_server?server={}&password={}", serverId, password).c_str())) + if (auto result = http.Post(fmt::format("/client/auth_with_server?id={}&playerToken={}&server={}&password={}", uid, playerToken, serverId, password).c_str())) { m_successfullyConnected = true; @@ -239,6 +240,7 @@ void MasterServerManager::AuthenticateWithServer(char* serverId, char* password) spdlog::error("Failed authenticating with server: error {}", result.error()); m_successfullyConnected = false; m_successfullyAuthenticatedWithGameServer = false; + m_scriptAuthenticatingWithGameServer = false; } REQUEST_END_CLEANUP: @@ -249,7 +251,7 @@ void MasterServerManager::AuthenticateWithServer(char* serverId, char* password) requestThread.detach(); } -void MasterServerManager::AddSelfToServerList(int port, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password) +void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password) { if (!Cvar_ns_report_server_to_masterserver->m_nValue) return; @@ -257,7 +259,7 @@ void MasterServerManager::AddSelfToServerList(int port, char* name, char* descri if (!Cvar_ns_report_sp_server_to_masterserver->m_nValue && !strncmp(map, "sp_", 3)) return; - std::thread requestThread([this, port, name, description, map, playlist, maxPlayers, password] { + std::thread requestThread([this, port, authPort, name, description, map, playlist, maxPlayers, password] { httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue); http.set_connection_timeout(20); @@ -265,9 +267,9 @@ void MasterServerManager::AddSelfToServerList(int port, char* name, char* descri std::string request; if (*password) - request = fmt::format("/server/add_server?port={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", port, name, description, map, playlist, maxPlayers, password); + request = fmt::format("/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", port, authPort, name, description, map, playlist, maxPlayers, password); else - request = fmt::format("/server/add_server?port={}&name={}&description={}&map={}&playlist={}&maxPlayers={}", port, name, description, map, playlist, maxPlayers); + request = fmt::format("/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password=", port, authPort, name, description, map, playlist, maxPlayers); if (auto result = http.Post(request.c_str())) { @@ -419,7 +421,9 @@ void CHostState__State_NewGameHook(CHostState* hostState) // not 100% we should do this here, but whatever Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); - g_MasterServerManager->AddSelfToServerList(Cvar_hostport->m_nValue, Cvar_ns_server_name->m_pszString, Cvar_ns_server_desc->m_pszString, hostState->m_levelName, (char*)GetCurrentPlaylistName(), 16, Cvar_ns_server_password->m_pszString); + g_MasterServerManager->AddSelfToServerList(Cvar_hostport->m_nValue, Cvar_ns_player_auth_port->m_nValue, Cvar_ns_server_name->m_pszString, Cvar_ns_server_desc->m_pszString, hostState->m_levelName, (char*)GetCurrentPlaylistName(), 16, Cvar_ns_server_password->m_pszString); + g_ServerAuthenticationManager->StartPlayerAuthServer(); + CHostState__State_NewGame(hostState); } @@ -438,12 +442,14 @@ void CHostState__State_ChangeLevelSPHook(CHostState* hostState) void CHostState__State_GameShutdownHook(CHostState* hostState) { g_MasterServerManager->RemoveSelfFromServerList(); + g_ServerAuthenticationManager->StopPlayerAuthServer(); + CHostState__State_GameShutdown(hostState); } void InitialiseSharedMasterServer(HMODULE baseAddress) { - Cvar_ns_masterserver_hostname = RegisterConVar("ns_masterserver_hostname", "localhost", FCVAR_NONE, ""); + Cvar_ns_masterserver_hostname = RegisterConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, ""); Cvar_ns_masterserver_port = RegisterConVar("ns_masterserver_port", "8080", FCVAR_NONE, ""); Cvar_ns_server_name = RegisterConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, ""); diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h index 9b8e8a89..17c77a02 100644 --- a/NorthstarDedicatedTest/masterserver.h +++ b/NorthstarDedicatedTest/masterserver.h @@ -55,13 +55,15 @@ public: public: void ClearServerList(); void RequestServerList(); - void AuthenticateWithServer(char* serverId, char* password); - void AddSelfToServerList(int port, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password); + void AuthenticateWithServer(char* uid, char* playerToken, char* serverId, char* password); + void AddSelfToServerList(int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password); void UpdateServerMapAndPlaylist(char* map, char* playlist); void UpdateServerPlayerCount(int playerCount); + void WritePlayerPersistentData(char* playerId, char* pdata); void RemoveSelfFromServerList(); }; void InitialiseSharedMasterServer(HMODULE baseAddress); -extern MasterServerManager* g_MasterServerManager; \ No newline at end of file +extern MasterServerManager* g_MasterServerManager; +extern ConVar* Cvar_ns_masterserver_hostname; \ No newline at end of file diff --git a/NorthstarDedicatedTest/scriptserverbrowser.cpp b/NorthstarDedicatedTest/scriptserverbrowser.cpp index c5446031..5db5b719 100644 --- a/NorthstarDedicatedTest/scriptserverbrowser.cpp +++ b/NorthstarDedicatedTest/scriptserverbrowser.cpp @@ -176,7 +176,7 @@ SQInteger SQ_TryAuthWithServer(void* sqvm) } // do auth - g_MasterServerManager->AuthenticateWithServer(g_MasterServerManager->m_remoteServers[serverIndex].id, (char*)password); + g_MasterServerManager->AuthenticateWithServer(g_LocalPlayerUserID, (char*)"", g_MasterServerManager->m_remoteServers[serverIndex].id, (char*)password); return 0; } @@ -210,8 +210,6 @@ SQInteger SQ_ConnectToAuthedServer(void* sqvm) // i'm honestly not entirely sure how silentconnect works regarding ports and encryption so using connect for now Cbuf_AddText(Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", info.authToken).c_str(), cmd_source_t::kCommandSrcCode); Cbuf_AddText(Cbuf_GetCurrentPlayer(), fmt::format("connect {}.{}.{}.{}:{}", info.ip.S_un.S_un_b.s_b1, info.ip.S_un.S_un_b.s_b2, info.ip.S_un.S_un_b.s_b3, info.ip.S_un.S_un_b.s_b4, info.port).c_str(), cmd_source_t::kCommandSrcCode); - - spdlog::info(fmt::format("connect {}.{}.{}.{}:{}", info.ip.S_un.S_un_b.s_b1, info.ip.S_un.S_un_b.s_b2, info.ip.S_un.S_un_b.s_b3, info.ip.S_un.S_un_b.s_b4, info.port)); g_MasterServerManager->m_hasPendingConnectionInfo = false; return 0; diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index 964c6b16..9df23502 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -8,6 +8,8 @@ #include #include +const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; + // hook types typedef void*(*CBaseServer__ConnectClientType)(void* server, void* a2, void* a3, uint32_t a4, uint32_t a5, int32_t a6, void* a7, void* a8, char* serverFilter, void* a10, char a11, void* a12, char a13, char a14, int64_t uid, uint32_t a16, uint32_t a17); @@ -35,39 +37,77 @@ ConVar* CVar_sv_quota_stringcmdspersecond; void ServerAuthenticationManager::StartPlayerAuthServer() { + if (m_runningPlayerAuthThread) + { + spdlog::warn("ServerAuthenticationManager::StartPlayerAuthServer was called while m_runningPlayerAuthThread is true"); + return; + } + m_runningPlayerAuthThread = true; + // listen is a blocking call so thread this std::thread serverThread([this] { - while (m_runningPlayerAuthThread) - { + // 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.remote_addr != Cvar_ns_masterserver_hostname->m_pszString) + { + response.set_content("{\"success\":false}", "application/json"); + return; + } + + AuthData newAuthData; + strncpy(newAuthData.uid, request.get_param_value("id").c_str(), sizeof(newAuthData.uid)); + newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0; + + newAuthData.pdataSize = request.body.size(); + newAuthData.pdata = new char[newAuthData.pdataSize]; + memcpy(newAuthData.pdata, request.body.c_str(), newAuthData.pdataSize); + + std::lock_guard guard(m_authDataMutex); + m_authData.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->m_nValue); + }); + serverThread.detach(); } -void ServerAuthenticationManager::AddPlayerAuthData(char* authToken, char* uid, char* pdata, size_t pdataSize) +void ServerAuthenticationManager::StopPlayerAuthServer() { - + if (!m_runningPlayerAuthThread) + { + spdlog::warn("ServerAuthenticationManager::StopPlayerAuthServer was called while m_runningPlayerAuthThread is false"); + return; + } + + m_runningPlayerAuthThread = false; + m_playerAuthServer.stop(); } bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid, char* authToken) { std::string strUid = std::to_string(uid); - if (!m_authData.empty() && m_authData.count(authToken)) + std::lock_guard guard(m_authDataMutex); + if (!m_authData.empty() && m_authData.count(std::string(authToken))) { // use stored auth data - AuthData* authData = m_authData[authToken]; - if (strcmp(strUid.c_str(), authData->uid)) // connecting client's uid is different from auth's uid + AuthData authData = m_authData[authToken]; + if (strcmp(strUid.c_str(), authData.uid)) // connecting client's uid is different from auth's uid return false; // uuid strcpy((char*)player + 0xF500, strUid.c_str()); // copy pdata into buffer - memcpy((char*)player + 0x4FA, authData->pdata, authData->pdataSize); + memcpy((char*)player + 0x4FA, authData.pdata, authData.pdataSize); // set persistent data as ready, we use 0x4 internally to mark the client as using remote persistence *((char*)player + 0x4a0) = (char)0x4; @@ -112,10 +152,13 @@ bool ServerAuthenticationManager::RemovePlayerAuthData(void* player) // we don't have our auth token at this point, so lookup authdata by uid for (auto& auth : m_authData) { - if (!strcmp((char*)player + 0xF500, auth.second->uid)) + if (!strcmp((char*)player + 0xF500, auth.second.uid)) { // pretty sure this is fine, since we don't iterate after the erase // i think if we iterated after it'd be undefined behaviour tho + std::lock_guard guard(m_authDataMutex); + + delete[] auth.second.pdata; m_authData.erase(auth.first); return true; } @@ -196,6 +239,7 @@ void CBaseClient__DisconnectHook(void* self, uint32_t unknownButAlways1, const c // dcing, write persistent data g_ServerAuthenticationManager->WritePersistentData(self); + g_ServerAuthenticationManager->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case g_MasterServerManager->UpdateServerPlayerCount(playerCount = std::max(playerCount - 1, 0)); @@ -217,6 +261,7 @@ void InitialiseServerAuthentication(HMODULE baseAddress) CVar_ns_auth_allow_insecure_write = RegisterConVar("ns_auth_allow_insecure_write", "0", FCVAR_GAMEDLL, "Whether the pdata of unauthenticated clients will be written to disk when changed"); // literally just stolen from a fix valve used in csgo CVar_sv_quota_stringcmdspersecond = RegisterConVar("sv_quota_stringcmdspersecond", "40", FCVAR_NONE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands"); + Cvar_ns_player_auth_port = RegisterConVar("Cvar_ns_player_auth_port", "8081", FCVAR_GAMEDLL, ""); HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x114430, &CBaseServer__ConnectClientHook, reinterpret_cast(&CBaseServer__ConnectClient)); diff --git a/NorthstarDedicatedTest/serverauthentication.h b/NorthstarDedicatedTest/serverauthentication.h index e9aae4f0..b26ce2b2 100644 --- a/NorthstarDedicatedTest/serverauthentication.h +++ b/NorthstarDedicatedTest/serverauthentication.h @@ -1,10 +1,12 @@ #pragma once +#include "convar.h" +#include "httplib.h" #include #include struct AuthData { - char* uid; + char uid[33]; // pdata char* pdata; @@ -13,13 +15,17 @@ struct AuthData class ServerAuthenticationManager { +private: + httplib::Server m_playerAuthServer; + public: - std::unordered_map m_authData; + std::mutex m_authDataMutex; + std::unordered_map m_authData; bool m_runningPlayerAuthThread = false; public: void StartPlayerAuthServer(); - void AddPlayerAuthData(char* authToken, char* uid, char* pdata, size_t pdataSize); + void StopPlayerAuthServer(); bool AuthenticatePlayer(void* player, int64_t uid, char* authToken); bool RemovePlayerAuthData(void* player); void WritePersistentData(void* player); diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp index 88d038df..d5c7e347 100644 --- a/NorthstarDedicatedTest/sourceconsole.cpp +++ b/NorthstarDedicatedTest/sourceconsole.cpp @@ -16,6 +16,9 @@ void ConCommand_toggleconsole(const CCommand& arg) void ConCommand_help(const CCommand& arg) { + if (arg.ArgC() < 2) + return; + // todo: this should basically just call FindConVar once we have that working, then just print convar.GetHelpString } -- cgit v1.2.3