aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/server
diff options
context:
space:
mode:
authorpg9182 <96569817+pg9182@users.noreply.github.com>2023-03-06 12:02:53 -0500
committerpg9182 <96569817+pg9182@users.noreply.github.com>2023-04-17 14:50:10 -0400
commit87bd14cbe83c0eca42a6c15f4712415627941df2 (patch)
tree303e089a46e174b5db1766e915254d87c90b746d /NorthstarDLL/server
parent45819c0ef2881610ca26261792995a58e6f68631 (diff)
downloadNorthstarLauncher-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.cpp103
-rw-r--r--NorthstarDLL/server/auth/serverauthentication.h9
-rw-r--r--NorthstarDLL/server/servernethooks.cpp40
-rw-r--r--NorthstarDLL/server/serverpresence.cpp9
-rw-r--r--NorthstarDLL/server/serverpresence.h3
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);