fix: use generated bcrypt password for internal master connections (#1720)

* add password hashing for master server

* use define
This commit is contained in:
David Markowitz 2025-01-10 01:45:20 -08:00 committed by GitHub
parent 136133dde2
commit 8abc545bd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 73 additions and 32 deletions

View File

@ -70,12 +70,15 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1500;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0));
@ -89,7 +92,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal, masterPassword);
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();

View File

@ -92,10 +92,12 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
std::string ourIP = "localhost";
@ -104,7 +106,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal, masterPassword);
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);

View File

@ -9,10 +9,11 @@ public:
struct MasterInfo {
std::string ip;
uint32_t port{};
std::string password{};
};
// Set the master server ip and port.
virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0;
virtual void SetMasterInfo(const MasterInfo& info) = 0;
// Get the master server info.
virtual std::optional<MasterInfo> GetMasterInfo() = 0;

View File

@ -96,7 +96,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@ -1,14 +1,14 @@
#include "MySQLDatabase.h"
void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void MySQLDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways.
ExecuteDelete("TRUNCATE TABLE servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
}
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
auto result = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (!result->next()) {
return std::nullopt;
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
toReturn.ip = result->getString("ip").c_str();
toReturn.port = result->getInt("port");
toReturn.password = result->getString("master_password").c_str();
return toReturn;
}

View File

@ -94,7 +94,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@ -1,14 +1,14 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void SQLiteDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways.
ExecuteDelete("DELETE FROM servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022 ?)", info.ip, info.port, info.password);
}
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
auto [_, result] = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (result.eof()) {
return std::nullopt;
@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
toReturn.ip = result.getStringField("ip");
toReturn.port = result.getIntField("port");
toReturn.password = result.getStringField("master_password");
return toReturn;
}

View File

@ -236,7 +236,7 @@ void TestSQLDatabase::InsertNewAccount(const std::string_view username, const st
}
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
}

View File

@ -73,7 +73,7 @@ class TestSQLDatabase : public GameDatabase {
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@ -62,6 +62,14 @@ std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr;
SystemAddress chatServerMasterPeerSysAddr;
int GenerateBCryptPassword(const std::string& password, const int workFactor, char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) {
int32_t bcryptState = ::bcrypt_gensalt(workFactor, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
return 0;
}
int main(int argc, char** argv) {
constexpr uint32_t masterFramerate = mediumFramerate;
constexpr uint32_t masterFrameDelta = mediumFrameDelta;
@ -94,7 +102,7 @@ int main(int argc, char** argv) {
std::string(folder) +
") folder from your download to the binary directory or re-run cmake.";
LOG("%s", msg.c_str());
// toss an error box up for windows users running the download
// toss an error box up for windows users running the download
#ifdef DARKFLAME_PLATFORM_WIN32
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
#endif
@ -238,10 +246,7 @@ int main(int argc, char** argv) {
// Regenerate hash based on new password
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE));
@ -279,10 +284,7 @@ int main(int argc, char** argv) {
//Generate new hash for bcrypt
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
//Create account
try {
@ -318,15 +320,24 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal);
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
const auto& cfgPassword = Game::config->GetValue("master_password");
GenerateBCryptPassword(!cfgPassword.empty() ? cfgPassword : "3.25DARKFLAME1", 13, salt, hash);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal, hash);
std::string master_server_ip = "localhost";
const auto masterServerIPString = Game::config->GetValue("master_ip");
if (!masterServerIPString.empty()) master_server_ip = masterServerIPString;
if (master_server_ip == "") master_server_ip = Game::server->GetIP();
IServers::MasterInfo info;
info.ip = master_server_ip;
info.port = Game::server->GetPort();
info.password = hash;
Database::Get()->SetMasterIp(master_server_ip, Game::server->GetPort());
Database::Get()->SetMasterInfo(info);
//Create additional objects here:
PersistentIDManager::Initialize();

View File

@ -3,4 +3,3 @@
#include "RakPeer.h"
#define NET_PASSWORD_EXTERNAL "3.25 ND1"
#define NET_PASSWORD_INTERNAL "3.25 DARKFLAME1"

View File

@ -40,7 +40,21 @@ public:
}
} ReceiveDownloadCompleteCB;
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
dServer::dServer(
const std::string& ip,
int port,
int instanceID,
int maxConnections,
bool isInternal,
bool useEncryption,
Logger* logger,
const std::string masterIP,
int masterPort,
ServerType serverType,
dConfig* config,
Game::signal_t* lastSignal,
const std::string& masterPassword,
unsigned int zoneID) {
mIP = ip;
mPort = port;
mZoneID = zoneID;
@ -56,6 +70,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
mReplicaManager = nullptr;
mServerType = serverType;
mConfig = config;
mMasterPassword = masterPassword;
mShouldShutdown = lastSignal;
//Attempt to start our server here:
mIsOkay = Startup();
@ -203,11 +218,11 @@ bool dServer::Startup() {
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
if (mIsInternal) {
mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15);
mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size());
} else {
UpdateBandwidthLimit();
UpdateMaximumMtuSize();
mPeer->SetIncomingPassword("3.25 ND1", 8);
mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
}
mPeer->SetMaximumIncomingConnections(mMaxConnections);
@ -257,7 +272,7 @@ void dServer::SetupForMasterConnection() {
bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, mMasterPassword.c_str(), mMasterPassword.size());
}
void dServer::UpdateReplica() {

View File

@ -46,6 +46,7 @@ public:
ServerType serverType,
dConfig* config,
Game::signal_t* shouldShutdown,
const std::string& masterPassword,
unsigned int zoneID = 0);
~dServer();
@ -121,4 +122,5 @@ protected:
std::string mMasterIP;
int mMasterPort;
std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now();
std::string mMasterPassword;
};

View File

@ -202,11 +202,13 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP = "localhost";
uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
UserManager::Instance()->Initialize();
@ -214,7 +216,7 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, masterPassword, zoneID);
//Connect to the chat server:
uint32_t chatPort = 1501;
@ -223,7 +225,7 @@ int main(int argc, char** argv) {
auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), 0);
Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
Game::chatServer->Startup(1, 30, &chatSock, 1);
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
//Set up other things:
Game::randomEngine = std::mt19937(time(0));
@ -371,7 +373,7 @@ int main(int argc, char** argv) {
if (framesSinceChatDisconnect >= chatReconnectionTime) {
framesSinceChatDisconnect = 0;
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
}
} else framesSinceChatDisconnect = 0;

View File

@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@ -9,3 +9,5 @@ world_port_start=3000
# 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1
master_password=3.25DARKFLAME1