From 8abc545bd14b131bf960db48b0212852ba604f92 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 10 Jan 2025 01:45:20 -0800 Subject: [PATCH] fix: use generated bcrypt password for internal master connections (#1720) * add password hashing for master server * use define --- dAuthServer/AuthServer.cpp | 5 ++- dChatServer/ChatServer.cpp | 4 ++- dDatabase/GameDatabase/ITables/IServers.h | 3 +- dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 2 +- .../GameDatabase/MySQL/Tables/Servers.cpp | 7 ++-- .../GameDatabase/SQLite/SQLiteDatabase.h | 2 +- .../GameDatabase/SQLite/Tables/Servers.cpp | 7 ++-- .../GameDatabase/TestSQL/TestSQLDatabase.cpp | 2 +- .../GameDatabase/TestSQL/TestSQLDatabase.h | 2 +- dMasterServer/MasterServer.cpp | 33 ++++++++++++------- dNet/dNetCommon.h | 1 - dNet/dServer.cpp | 23 ++++++++++--- dNet/dServer.h | 2 ++ dWorldServer/WorldServer.cpp | 8 +++-- migrations/dlu/mysql/18_master_password.sql | 1 + migrations/dlu/sqlite/1_master_password.sql | 1 + resources/masterconfig.ini | 2 ++ 17 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 migrations/dlu/mysql/18_master_password.sql create mode 100644 migrations/dlu/sqlite/1_master_password.sql diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index 362dd431..eefbb13e 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -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(); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index dcfb4038..6a15b188 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -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(Game::config->GetValue("dont_generate_dcf")).value_or(false); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF); diff --git a/dDatabase/GameDatabase/ITables/IServers.h b/dDatabase/GameDatabase/ITables/IServers.h index ee74bbb8..d0641d9a 100644 --- a/dDatabase/GameDatabase/ITables/IServers.h +++ b/dDatabase/GameDatabase/ITables/IServers.h @@ -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 GetMasterInfo() = 0; diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 08168141..342f7b8a 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -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 GetCurrentPersistentId() override; void InsertDefaultPersistentId() override; void UpdatePersistentId(const uint32_t id) override; diff --git a/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp index 4411ad21..28846d63 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp @@ -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 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 MySQLDatabase::GetMasterInfo() { toReturn.ip = result->getString("ip").c_str(); toReturn.port = result->getInt("port"); + toReturn.password = result->getString("master_password").c_str(); return toReturn; } diff --git a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h index a09c72c9..d78bd213 100644 --- a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h +++ b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h @@ -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 GetCurrentPersistentId() override; void InsertDefaultPersistentId() override; void UpdatePersistentId(const uint32_t id) override; diff --git a/dDatabase/GameDatabase/SQLite/Tables/Servers.cpp b/dDatabase/GameDatabase/SQLite/Tables/Servers.cpp index 8c136a30..1af9d43b 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/Servers.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/Servers.cpp @@ -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 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 SQLiteDatabase::GetMasterInfo() { toReturn.ip = result.getStringField("ip"); toReturn.port = result.getIntField("port"); + toReturn.password = result.getStringField("master_password"); return toReturn; } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp index 0263a6e3..e0377a41 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp @@ -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) { } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h index 9d4b184f..75d65a4e 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -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 GetCurrentPersistentId() override; void InsertDefaultPersistentId() override; void UpdatePersistentId(const uint32_t id) override; diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 89ecef05..6e19172b 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -62,6 +62,14 @@ std::map 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(); diff --git a/dNet/dNetCommon.h b/dNet/dNetCommon.h index 73a90bc0..70768ad3 100644 --- a/dNet/dNetCommon.h +++ b/dNet/dNetCommon.h @@ -3,4 +3,3 @@ #include "RakPeer.h" #define NET_PASSWORD_EXTERNAL "3.25 ND1" -#define NET_PASSWORD_INTERNAL "3.25 DARKFLAME1" diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index c7e4b226..b743e1f4 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -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() { diff --git a/dNet/dServer.h b/dNet/dServer.h index a577d191..c25fa1f4 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -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; }; diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 6a596960..0bf467de 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -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(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(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; diff --git a/migrations/dlu/mysql/18_master_password.sql b/migrations/dlu/mysql/18_master_password.sql new file mode 100644 index 00000000..8e180d34 --- /dev/null +++ b/migrations/dlu/mysql/18_master_password.sql @@ -0,0 +1 @@ +ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1'); diff --git a/migrations/dlu/sqlite/1_master_password.sql b/migrations/dlu/sqlite/1_master_password.sql new file mode 100644 index 00000000..8e180d34 --- /dev/null +++ b/migrations/dlu/sqlite/1_master_password.sql @@ -0,0 +1 @@ +ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1'); diff --git a/resources/masterconfig.ini b/resources/masterconfig.ini index 1f14cf8d..302adc9d 100644 --- a/resources/masterconfig.ini +++ b/resources/masterconfig.ini @@ -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