diff options
author | Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> | 2023-12-27 00:32:01 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 01:32:01 +0100 |
commit | f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287 (patch) | |
tree | 90f2c6a4885dbd181799e2325cf33588697674e1 /primedev/server/auth | |
parent | bb8ed59f6891b1196c5f5bbe7346cd171c8215fa (diff) | |
download | NorthstarLauncher-f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287.tar.gz NorthstarLauncher-f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287.zip |
Folder restructuring from primedev (#624)v1.21.2-rc3v1.21.2
Copies of over the primedev folder structure for easier cherry-picking of further changes
Co-authored-by: F1F7Y <filip.bartos07@proton.me>
Diffstat (limited to 'primedev/server/auth')
-rw-r--r-- | primedev/server/auth/bansystem.cpp | 224 | ||||
-rw-r--r-- | primedev/server/auth/bansystem.h | 19 | ||||
-rw-r--r-- | primedev/server/auth/serverauthentication.cpp | 380 | ||||
-rw-r--r-- | primedev/server/auth/serverauthentication.h | 58 |
4 files changed, 681 insertions, 0 deletions
diff --git a/primedev/server/auth/bansystem.cpp b/primedev/server/auth/bansystem.cpp new file mode 100644 index 00000000..a45cde93 --- /dev/null +++ b/primedev/server/auth/bansystem.cpp @@ -0,0 +1,224 @@ +#include "bansystem.h" +#include "serverauthentication.h" +#include "core/convar/concommand.h" +#include "server/r2server.h" +#include "engine/r2engine.h" +#include "client/r2client.h" +#include "config/profile.h" + +#include <filesystem> + +const char* BANLIST_PATH_SUFFIX = "/banlist.txt"; +const char BANLIST_COMMENT_CHAR = '#'; + +ServerBanSystem* g_pBanSystem; + +void ServerBanSystem::OpenBanlist() +{ + std::ifstream banlistStream(GetNorthstarPrefix() + "/banlist.txt"); + + if (!banlistStream.fail()) + { + std::string line; + while (std::getline(banlistStream, line)) + { + // ignore line if first char is # or line is empty + if (line == "" || line.front() == BANLIST_COMMENT_CHAR) + continue; + + // remove tabs which shouldnt be there but maybe someone did the funny + line.erase(std::remove(line.begin(), line.end(), '\t'), line.end()); + // remove spaces to allow for spaces before uids + line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); + + // check if line is empty to allow for newlines in the file + if (line == "") + continue; + + // for inline comments like: 123123123 #banned for unfunny + std::string uid = line.substr(0, line.find(BANLIST_COMMENT_CHAR)); + + m_vBannedUids.push_back(strtoull(uid.c_str(), nullptr, 10)); + } + + banlistStream.close(); + } + + // open write stream for banlist // dont do this to allow for all time access + // m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app); +} + +void ServerBanSystem::ReloadBanlist() +{ + std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt"); + + if (!fsBanlist.fail()) + { + std::string line; + // since we wanna use this as the reload func we need to clear the list + m_vBannedUids.clear(); + while (std::getline(fsBanlist, line)) + m_vBannedUids.push_back(strtoull(line.c_str(), nullptr, 10)); + + fsBanlist.close(); + } +} + +void ServerBanSystem::ClearBanlist() +{ + m_vBannedUids.clear(); + + // reopen the file, don't provide std::ofstream::app so it clears on open + m_sBanlistStream.close(); + m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary); + m_sBanlistStream.close(); +} + +void ServerBanSystem::BanUID(uint64_t uid) +{ + // checking if last char is \n to make sure uids arent getting fucked + std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt"); + std::string content((std::istreambuf_iterator<char>(fsBanlist)), (std::istreambuf_iterator<char>())); + fsBanlist.close(); + + m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app); + if (content.back() != '\n') + m_sBanlistStream << std::endl; + + m_vBannedUids.push_back(uid); + m_sBanlistStream << std::to_string(uid) << std::endl; + m_sBanlistStream.close(); + spdlog::info("{} was banned", uid); +} + +void ServerBanSystem::UnbanUID(uint64_t uid) +{ + auto findResult = std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid); + if (findResult == m_vBannedUids.end()) + return; + + m_vBannedUids.erase(findResult); + + std::vector<std::string> banlistText; + std::ifstream fs_readBanlist(GetNorthstarPrefix() + "/banlist.txt"); + + if (!fs_readBanlist.fail()) + { + std::string line; + while (std::getline(fs_readBanlist, line)) + { + // support for comments and newlines added in https://github.com/R2Northstar/NorthstarLauncher/pull/227 + + std::string modLine = line; // copy the line into a free var that we can fuck with, line will be the original + + // remove tabs which shouldnt be there but maybe someone did the funny + modLine.erase(std::remove(modLine.begin(), modLine.end(), '\t'), modLine.end()); + // remove spaces to allow for spaces before uids + modLine.erase(std::remove(modLine.begin(), modLine.end(), ' '), modLine.end()); + + // ignore line if first char is # or empty line, just add it + if (line.front() == BANLIST_COMMENT_CHAR || modLine == "") + { + banlistText.push_back(line); + continue; + } + + // for inline comments like: 123123123 #banned for unfunny + std::string lineUid = line.substr(0, line.find(BANLIST_COMMENT_CHAR)); + // have to erase spaces or else inline comments will fuck up the uid finding + lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), '\t'), lineUid.end()); + lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), ' '), lineUid.end()); + + // if the uid in the line is the uid we wanna unban + if (std::to_string(uid) == lineUid) + { + // comment the uid out + line.insert(0, "# "); + + // add a comment with unban date + // not necessary but i feel like this makes it better + std::time_t t = std::time(0); + std::tm* now = std::localtime(&t); + + std::ostringstream unbanComment; + + //{y}/{m}/{d} {h}:{m} + unbanComment << " # unban date: "; + unbanComment << now->tm_year + 1900 << "-"; // this lib is so fucking awful + unbanComment << std::setw(2) << std::setfill('0') << now->tm_mon + 1 << "-"; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_mday << " "; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_hour << ":"; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_min; + + line.append(unbanComment.str()); + } + + banlistText.push_back(line); + } + + fs_readBanlist.close(); + } + + // open write stream for banlist // without append so we clear the file + if (m_sBanlistStream.is_open()) + m_sBanlistStream.close(); + m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary); + + for (std::string updatedLine : banlistText) + m_sBanlistStream << updatedLine << std::endl; + + m_sBanlistStream.close(); + spdlog::info("{} was unbanned", uid); +} + +bool ServerBanSystem::IsUIDAllowed(uint64_t uid) +{ + uint64_t localPlayerUserID = strtoull(g_pLocalPlayerUserID, nullptr, 10); + if (localPlayerUserID == uid) + return true; + + ReloadBanlist(); // Reload to have up to date list on join + return std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid) == m_vBannedUids.end(); +} + +void ConCommand_ban(const CCommand& args) +{ + if (args.ArgC() < 2) + return; + + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) + { + CBaseClient* player = &g_pClientArray[i]; + + if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1))) + { + g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10)); + CBaseClient__Disconnect(player, 1, "Banned from server"); + break; + } + } +} + +void ConCommand_unban(const CCommand& args) +{ + if (args.ArgC() < 2) + return; + + // assumedly the player being unbanned here wasn't already connected, so don't need to iterate over players or anything + g_pBanSystem->UnbanUID(strtoull(args.Arg(1), nullptr, 10)); +} + +void ConCommand_clearbanlist(const CCommand& args) +{ + g_pBanSystem->ClearBanlist(); +} + +ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (CModule module)) +{ + g_pBanSystem = new ServerBanSystem; + g_pBanSystem->OpenBanlist(); + + RegisterConCommand("ban", ConCommand_ban, "bans a given player by uid or name", FCVAR_GAMEDLL); + RegisterConCommand("unban", ConCommand_unban, "unbans a given player by uid", FCVAR_GAMEDLL); + RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_GAMEDLL); +} diff --git a/primedev/server/auth/bansystem.h b/primedev/server/auth/bansystem.h new file mode 100644 index 00000000..d6ac5a4f --- /dev/null +++ b/primedev/server/auth/bansystem.h @@ -0,0 +1,19 @@ +#pragma once +#include <fstream> + +class ServerBanSystem +{ +private: + std::ofstream m_sBanlistStream; + std::vector<uint64_t> m_vBannedUids; + +public: + void OpenBanlist(); + void ReloadBanlist(); + void ClearBanlist(); + void BanUID(uint64_t uid); + void UnbanUID(uint64_t uid); + bool IsUIDAllowed(uint64_t uid); +}; + +extern ServerBanSystem* g_pBanSystem; diff --git a/primedev/server/auth/serverauthentication.cpp b/primedev/server/auth/serverauthentication.cpp new file mode 100644 index 00000000..0d46426f --- /dev/null +++ b/primedev/server/auth/serverauthentication.cpp @@ -0,0 +1,380 @@ +#include "serverauthentication.h" +#include "shared/exploit_fixes/ns_limits.h" +#include "core/convar/cvar.h" +#include "core/convar/convar.h" +#include "masterserver/masterserver.h" +#include "server/serverpresence.h" +#include "engine/hoststate.h" +#include "bansystem.h" +#include "core/convar/concommand.h" +#include "dedicated/dedicated.h" +#include "config/profile.h" +#include "core/tier0.h" +#include "engine/r2engine.h" +#include "client/r2client.h" +#include "server/r2server.h" + +#include <fstream> +#include <filesystem> +#include <string> +#include <thread> + +AUTOHOOK_INIT() + +// global vars +ServerAuthenticationManager* g_pServerAuthentication; +CBaseServer__RejectConnectionType CBaseServer__RejectConnection; + +void ServerAuthenticationManager::AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata) +{ + std::string uidS = std::to_string(uid); + + 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); + + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + m_RemoteAuthenticationData[token] = newAuthData; +} + +void ServerAuthenticationManager::AddPlayer(CBaseClient* pPlayer, const char* pToken) +{ + PlayerAuthenticationData additionalData; + + auto remoteAuthData = m_RemoteAuthenticationData.find(pToken); + if (remoteAuthData != m_RemoteAuthenticationData.end()) + additionalData.pdataSize = remoteAuthData->second.pdataSize; + else + additionalData.pdataSize = PERSISTENCE_MAX_SIZE; + + additionalData.usingLocalPdata = pPlayer->m_iPersistenceReady == ePersistenceReady::READY_INSECURE; + + m_PlayerAuthenticationData.insert(std::make_pair(pPlayer, additionalData)); +} + +void ServerAuthenticationManager::RemovePlayer(CBaseClient* pPlayer) +{ + if (m_PlayerAuthenticationData.count(pPlayer)) + m_PlayerAuthenticationData.erase(pPlayer); +} + +bool ServerAuthenticationManager::VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64]) +{ + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + + // always use name from masterserver if available + // use of strncpy_s here should verify that this is always nullterminated within valid buffer size + auto authData = m_RemoteAuthenticationData.find(pAuthToken); + if (authData != m_RemoteAuthenticationData.end() && *authData->second.username) + strncpy_s(pOutVerifiedName, 64, authData->second.username, 63); + else + strncpy_s(pOutVerifiedName, 64, pName, 63); + + // now, check that whatever name we have is actually valid + // first, make sure it's >1 char + if (!*pOutVerifiedName) + return false; + + // next, make sure it's within a valid range of ascii characters + for (int i = 0; pOutVerifiedName[i]; i++) + { + if (pOutVerifiedName[i] < 32 || pOutVerifiedName[i] > 126) + return false; + } + + return true; +} + +bool ServerAuthenticationManager::IsDuplicateAccount(CBaseClient* pPlayer, const char* pPlayerUid) +{ + if (m_bAllowDuplicateAccounts) + return false; + + bool bHasUidPlayer = false; + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) + if (&g_pClientArray[i] != pPlayer && !strcmp(pPlayerUid, g_pClientArray[i].m_UID)) + return true; + + return false; +} + +bool ServerAuthenticationManager::CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) +{ + std::string sUid = std::to_string(iUid); + + // check whether this player's authentication is valid but don't actually write anything to the player, we'll do that later + // if we don't need auth this is valid + if (Cvar_ns_auth_allow_insecure->GetBool()) + return true; + + // local server that doesn't need auth (probably sp) and local player + if (m_bStartingLocalSPGame && !strcmp(sUid.c_str(), g_pLocalPlayerUserID)) + return true; + + // don't allow duplicate accounts + if (IsDuplicateAccount(pPlayer, sUid.c_str())) + return false; + + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + auto authData = m_RemoteAuthenticationData.find(pAuthToken); + if (authData != m_RemoteAuthenticationData.end() && !strcmp(sUid.c_str(), authData->second.uid)) + return true; + + return false; +} + +void ServerAuthenticationManager::AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) +{ + // for bot players, generate a new uid + if (pPlayer->m_bFakePlayer) + iUid = 0; // is this a good way of doing things :clueless: + + std::string sUid = std::to_string(iUid); + + // copy uuid + strcpy(pPlayer->m_UID, sUid.c_str()); + + std::lock_guard<std::mutex> guard(m_AuthDataMutex); + auto authData = m_RemoteAuthenticationData.find(pAuthToken); + if (authData != m_RemoteAuthenticationData.end()) + { + // if we're resetting let script handle the reset with InitPersistentData() on connect + if (!m_bForceResetLocalPlayerPersistence || strcmp(sUid.c_str(), g_pLocalPlayerUserID)) + { + // copy pdata into buffer + memcpy(pPlayer->m_PersistenceBuffer, authData->second.pdata, authData->second.pdataSize); + } + + // set persistent data as ready + pPlayer->m_iPersistenceReady = ePersistenceReady::READY_REMOTE; + } + // we probably allow insecure at this point, but make sure not to write anyway if not insecure + else if (Cvar_ns_auth_allow_insecure->GetBool() || pPlayer->m_bFakePlayer) + { + // set persistent data as ready + // note: actual placeholder persistent data is populated in script with InitPersistentData() + pPlayer->m_iPersistenceReady = ePersistenceReady::READY_INSECURE; + } +} + +bool ServerAuthenticationManager::RemovePlayerAuthData(CBaseClient* pPlayer) +{ + if (!Cvar_ns_erase_auth_info->GetBool()) // keep auth data forever + return false; + + // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect + if (m_bNeedLocalAuthForNewgame && !strcmp(pPlayer->m_UID, g_pLocalPlayerUserID)) + return false; + + // we don't have our auth token at this point, so lookup authdata by uid + for (auto& auth : m_RemoteAuthenticationData) + { + if (!strcmp(pPlayer->m_UID, 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<std::mutex> guard(m_AuthDataMutex); + + delete[] auth.second.pdata; + m_RemoteAuthenticationData.erase(auth.first); + return true; + } + } + + return false; +} + +void ServerAuthenticationManager::WritePersistentData(CBaseClient* pPlayer) +{ + if (pPlayer->m_iPersistenceReady == ePersistenceReady::READY_REMOTE) + { + g_pMasterServerManager->WritePlayerPersistentData( + pPlayer->m_UID, (const char*)pPlayer->m_PersistenceBuffer, m_PlayerAuthenticationData[pPlayer].pdataSize); + } + else if (Cvar_ns_auth_allow_insecure_write->GetBool()) + { + // todo: write pdata to disk here + } +} + +// auth hooks + +// store these in vars so we can use them in CBaseClient::Connect +// this is fine because ptrs won't decay by the time we use this, just don't use it outside of calls from cbaseclient::connectclient +char* pNextPlayerToken; +uint64_t iNextPlayerUid; + +// clang-format off +AUTOHOOK(CBaseServer__ConnectClient, engine.dll + 0x114430, +void*,, ( + void* self, + void* addr, + void* a3, + uint32_t a4, + uint32_t a5, + int32_t a6, + void* a7, + char* playerName, + char* serverFilter, + void* a10, + char a11, + void* a12, + char a13, + char a14, + int64_t uid, + uint32_t a16, + uint32_t a17)) +// clang-format on +{ + // auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here + pNextPlayerToken = serverFilter; + iNextPlayerUid = uid; + + return CBaseServer__ConnectClient(self, addr, a3, a4, a5, a6, a7, playerName, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17); +} + +ConVar* Cvar_ns_allowuserclantags; + +// clang-format off +AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740, +bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7)) +// clang-format on +{ + const char* pAuthenticationFailure = nullptr; + char pVerifiedName[64]; + + if (!bFakePlayer) + { + if (!g_pServerAuthentication->VerifyPlayerName(pNextPlayerToken, pName, pVerifiedName)) + pAuthenticationFailure = "Invalid Name."; + else if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid)) + pAuthenticationFailure = "Banned From server."; + else if (!g_pServerAuthentication->CheckAuthentication(self, iNextPlayerUid, pNextPlayerToken)) + pAuthenticationFailure = "Authentication Failed."; + } + else // need to copy name for bots still + strncpy_s(pVerifiedName, pName, 63); + + if (pAuthenticationFailure) + { + spdlog::info("{}'s (uid {}) connection was rejected: \"{}\"", pName, iNextPlayerUid, pAuthenticationFailure); + + strncpy_s(pDisconnectReason, 256, pAuthenticationFailure, 255); + return false; + } + + // try to actually connect the player + if (!CBaseClient__Connect(self, pVerifiedName, pNetChannel, bFakePlayer, a5, pDisconnectReason, a7)) + return false; + + // we already know this player's authentication data is legit, actually write it to them now + g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken); + + g_pServerAuthentication->AddPlayer(self, pNextPlayerToken); + g_pServerLimits->AddPlayer(self); + + return true; +} + +// clang-format off +AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80, +void,, (CBaseClient* self)) +// clang-format on +{ + // if we're authed, write our persistent data + // RemovePlayerAuthData returns true if it removed successfully, i.e. on first call only, and we only want to write on >= second call + // (since this func is called on map loads) + if (self->m_iPersistenceReady >= ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self)) + { + g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = false; + g_pServerAuthentication->WritePersistentData(self); + g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); + } + + CBaseClient__ActivatePlayer(self); +} + +// clang-format off +AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0, +void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)) +// clang-format on +{ + // have to manually format message because can't pass varargs to original func + char buf[1024]; + + va_list va; + va_start(va, pReason); + vsprintf(buf, pReason, va); + va_end(va); + + // this reason is used while connecting to a local server, hacky, but just ignore it + if (strcmp(pReason, "Connection closing")) + { + spdlog::info("Player {} disconnected: \"{}\"", self->m_Name, buf); + + // dcing, write persistent data + if (g_pServerAuthentication->m_PlayerAuthenticationData[self].needPersistenceWriteOnLeave) + g_pServerAuthentication->WritePersistentData(self); + + memset(self->m_PersistenceBuffer, 0, g_pServerAuthentication->m_PlayerAuthenticationData[self].pdataSize); + g_pServerAuthentication->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case + + g_pServerAuthentication->RemovePlayer(self); + g_pServerLimits->RemovePlayer(self); + } + + g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); + + _CBaseClient__Disconnect(self, unknownButAlways1, buf); +} + +void ConCommand_ns_resetpersistence(const CCommand& args) +{ + if (*g_pServerState == server_state_t::ss_active) + { + spdlog::error("ns_resetpersistence must be entered from the main menu"); + return; + } + + spdlog::info("resetting persistence on next lobby load..."); + g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = true; +} + +ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (CModule module)) +{ + AUTOHOOK_DISPATCH() + + g_pServerAuthentication = new ServerAuthenticationManager; + + 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 = + new ConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect"); + g_pServerAuthentication->Cvar_ns_auth_allow_insecure_write = new ConVar( + "ns_auth_allow_insecure_write", + "0", + FCVAR_GAMEDLL, + "Whether the pdata of unauthenticated clients will be written to disk when changed"); + + RegisterConCommand( + "ns_resetpersistence", ConCommand_ns_resetpersistence, "resets your pdata when you next enter the lobby", FCVAR_NONE); + + // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token + module.Offset(0x114655).Patch("EB"); + + // patch to disable fairfight marking players as cheaters and kicking them + module.Offset(0x101012).Patch("E9 90 00"); + + CBaseServer__RejectConnection = module.Offset(0x1182E0).RCast<CBaseServer__RejectConnectionType>(); + + if (CommandLine()->CheckParm("-allowdupeaccounts")) + { + // patch to allow same of multiple account + module.Offset(0x114510).Patch("EB"); + + g_pServerAuthentication->m_bAllowDuplicateAccounts = true; + } +} diff --git a/primedev/server/auth/serverauthentication.h b/primedev/server/auth/serverauthentication.h new file mode 100644 index 00000000..996d20e1 --- /dev/null +++ b/primedev/server/auth/serverauthentication.h @@ -0,0 +1,58 @@ +#pragma once +#include "core/convar/convar.h" +#include "engine/r2engine.h" +#include <unordered_map> +#include <string> + +struct RemoteAuthData +{ + char uid[33]; + char username[64]; + + // pdata + char* pdata; + size_t pdataSize; +}; + +struct PlayerAuthenticationData +{ + bool usingLocalPdata; + size_t pdataSize; + bool needPersistenceWriteOnLeave = true; +}; + +typedef int64_t (*CBaseServer__RejectConnectionType)(void* a1, unsigned int a2, void* a3, const char* a4, ...); +extern CBaseServer__RejectConnectionType CBaseServer__RejectConnection; + +class ServerAuthenticationManager +{ +public: + ConVar* Cvar_ns_erase_auth_info; + ConVar* Cvar_ns_auth_allow_insecure; + ConVar* Cvar_ns_auth_allow_insecure_write; + + std::mutex m_AuthDataMutex; + std::unordered_map<std::string, RemoteAuthData> m_RemoteAuthenticationData; + std::unordered_map<CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData; + + bool m_bAllowDuplicateAccounts = false; + bool m_bNeedLocalAuthForNewgame = false; + bool m_bForceResetLocalPlayerPersistence = false; + bool m_bStartingLocalSPGame = false; + +public: + void AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata); + + void AddPlayer(CBaseClient* pPlayer, const char* pAuthToken); + void RemovePlayer(CBaseClient* pPlayer); + + bool VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64]); + bool IsDuplicateAccount(CBaseClient* pPlayer, const char* pUid); + bool CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); + + void AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); + bool RemovePlayerAuthData(CBaseClient* pPlayer); + void WritePersistentData(CBaseClient* pPlayer); +}; + +extern ServerAuthenticationManager* g_pServerAuthentication; |