diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index 6094a72f3..0ed766c9f 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -67,7 +67,8 @@ int32 AccountManagement::CreateLoginServerAccount( uint32 created_account_id = 0; if (login_account_id > 0) { created_account_id = server.db->CreateLoginDataWithID(username, hash, source_loginserver, login_account_id); - } else { + } + else { created_account_id = server.db->CreateLoginAccount(username, hash, source_loginserver, email); } @@ -251,6 +252,59 @@ bool AccountManagement::UpdateLoginserverUserCredentials( return true; } +/** + * @param in_account_username + * @param in_account_password + */ +bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( + const std::string &in_account_username, + const std::string &in_account_password +) +{ + auto mode = server.options.GetEncryptionMode(); + auto hash = eqcrypt_hash(in_account_username, in_account_password, mode); + + bool updated_account = server.db->UpdateLoginWorldAdminAccountPasswordByUsername( + in_account_username, + hash + ); + + LogInfo( + "[{}] account_name [{}] status [{}]", + __func__, + in_account_username, + (updated_account ? "success" : "failed") + ); + + return updated_account; +} + +/** + * @param in_account_id + * @param in_account_password_hash + */ +bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordById( + uint32 in_account_id, + const std::string &in_account_password_hash +) +{ + bool updated_account = server.db->UpdateLoginWorldAdminAccountPassword(in_account_id, in_account_password_hash); + + LogInfo( + "[{}] account_name [{}] status [{}]", + __func__, + in_account_id, + (updated_account ? "success" : "failed") + ); + + return updated_account; +} + +/** + * @param in_account_username + * @param in_account_password + * @return + */ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( const std::string &in_account_username, const std::string &in_account_password @@ -362,4 +416,4 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( ); return res.get(); -} +} \ No newline at end of file diff --git a/loginserver/account_management.h b/loginserver/account_management.h index d88cdfdab..052f2619f 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -88,6 +88,26 @@ public: const std::string &in_account_username, const std::string &in_account_password ); + + /** + * @param in_account_username + * @param in_account_password + * @return + */ + static bool UpdateLoginserverWorldAdminAccountPasswordByName( + const std::string &in_account_username, + const std::string &in_account_password + ); + + /** + * @param in_account_id + * @param in_account_password_hash + * @return + */ + static bool UpdateLoginserverWorldAdminAccountPasswordById( + uint32 in_account_id, + const std::string &in_account_password_hash + ); }; diff --git a/loginserver/client.cpp b/loginserver/client.cpp index a1adfc934..4b116fc1c 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -499,83 +499,66 @@ bool Client::VerifyLoginHash( const std::string &password_hash ) { - auto mode = server.options.GetEncryptionMode(); - if (eqcrypt_verify_hash(account_username, account_password, password_hash, mode)) { + auto encryption_mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash(account_username, account_password, password_hash, encryption_mode)) { return true; } else { if (server.options.IsUpdatingInsecurePasswords()) { - if (mode < EncryptionModeArgon2) { - mode = EncryptionModeArgon2; + if (encryption_mode < EncryptionModeArgon2) { + encryption_mode = EncryptionModeArgon2; } - if (password_hash.length() == 32) { //md5 is insecure + uint32 insecure_source_encryption_mode = 0; + if (password_hash.length() == CryptoHash::md5_hash_length) { for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { - if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - LogDebug( - "user [{0}] loginserver [{1}] mode [{2}]", - account_username, - source_loginserver, - mode - ); - server.db->UpdateLoginserverAccountPasswordHash( - account_username, - source_loginserver, - eqcrypt_hash( - account_username, - account_password, - mode - )); - return true; + if (i != encryption_mode && + eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + insecure_source_encryption_mode = i; } } } - else if (password_hash.length() == 40) { //sha1 is insecure + else if (password_hash.length() == CryptoHash::sha1_hash_length && insecure_source_encryption_mode == 0) { for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { - if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - LogDebug( - "user [{0}] loginserver [{1}] mode [{2}]", - account_username, - source_loginserver, - mode - ); - - server.db->UpdateLoginserverAccountPasswordHash( - account_username, - source_loginserver, - eqcrypt_hash( - account_username, - account_password, - mode - )); - return true; + if (i != encryption_mode && + eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + insecure_source_encryption_mode = i; } } } - else if (password_hash.length() == 128) { //sha2-512 is insecure + else if (password_hash.length() == CryptoHash::sha512_hash_length && insecure_source_encryption_mode == 0) { for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { - if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { - LogDebug( - "user [{0}] loginserver [{1}] mode [{2}]", - account_username, - source_loginserver, - mode - ); - - server.db->UpdateLoginserverAccountPasswordHash( - account_username, - source_loginserver, - eqcrypt_hash( - account_username, - account_password, - mode - )); - return true; + if (i != encryption_mode && + eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { + insecure_source_encryption_mode = i; } } } - //argon2 is still secure - //scrypt is still secure + + if (insecure_source_encryption_mode > 0) { + LogInfo( + "[{}] Updated insecure password user [{}] loginserver [{}] from mode [{}] ({}) to mode [{}] ({})", + __func__, + account_username, + source_loginserver, + GetEncryptionByModeId(insecure_source_encryption_mode), + insecure_source_encryption_mode, + GetEncryptionByModeId(encryption_mode), + encryption_mode + ); + + server.db->UpdateLoginserverAccountPasswordHash( + account_username, + source_loginserver, + eqcrypt_hash( + account_username, + account_password, + encryption_mode + ) + ); + + return true; + } } } diff --git a/loginserver/database.cpp b/loginserver/database.cpp index 660641e29..35f49e0eb 100644 --- a/loginserver/database.cpp +++ b/loginserver/database.cpp @@ -395,15 +395,15 @@ Database::DbWorldRegistration Database::GetWorldRegistration( world_registration.server_list_type = std::stoi(row[3]); world_registration.is_server_trusted = std::stoi(row[2]) > 0; world_registration.server_list_description = row[4]; + world_registration.server_admin_id = std::stoi(row[5]); - int db_account_id = std::stoi(row[5]); - if (db_account_id <= 0) { + if (world_registration.server_admin_id <= 0) { return world_registration; } auto world_registration_query = fmt::format( "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", - db_account_id + world_registration.server_admin_id ); auto world_registration_results = QueryDatabase(world_registration_query); @@ -473,6 +473,47 @@ void Database::UpdateWorldRegistration(unsigned int id, std::string long_name, s QueryDatabase(query); } +/** + * @param id + * @param admin_account_password_hash + */ +bool Database::UpdateLoginWorldAdminAccountPassword( + unsigned int id, + const std::string &admin_account_password_hash +) +{ + auto results = QueryDatabase( + fmt::format( + "UPDATE login_server_admins SET account_password = '{}' WHERE id = {}", + EscapeString(admin_account_password_hash), + id + ) + ); + + return results.Success(); +} + +/** + * + * @param admin_account_username + * @param admin_account_password_hash + */ +bool Database::UpdateLoginWorldAdminAccountPasswordByUsername( + const std::string &admin_account_username, + const std::string &admin_account_password_hash +) +{ + auto results = QueryDatabase( + fmt::format( + "UPDATE login_server_admins SET account_password = '{}' WHERE account_name = '{}'", + EscapeString(admin_account_password_hash), + EscapeString(admin_account_username) + ) + ); + + return results.Success(); +} + /** * @param server_long_name * @param server_short_name diff --git a/loginserver/database.h b/loginserver/database.h index ef1d7bfe0..8ffcb187b 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -149,6 +149,7 @@ public: std::string server_list_description; std::string server_admin_account_name; std::string server_admin_account_password; + uint32 server_admin_id; }; /** @@ -269,6 +270,24 @@ public: const std::string &source_loginserver = "local" ); + /** + * @param id + * @param admin_account_password_hash + */ + bool UpdateLoginWorldAdminAccountPassword( + unsigned int id, + const std::string& admin_account_password_hash + ); + + /** + * @param admin_account_username + * @param admin_account_password_hash + */ + bool UpdateLoginWorldAdminAccountPasswordByUsername( + const std::string &admin_account_username, + const std::string &admin_account_password_hash + ); + /** * @param name * @param password diff --git a/loginserver/encryption.h b/loginserver/encryption.h index 000a3d0b4..7db61197e 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -23,29 +23,34 @@ #include #include "../common/types.h" -enum EncryptionMode -{ - EncryptionModeMD5 = 1, - EncryptionModeMD5PassUser = 2, - EncryptionModeMD5UserPass = 3, - EncryptionModeMD5Triple = 4, - EncryptionModeSHA = 5, - EncryptionModeSHAPassUser = 6, - EncryptionModeSHAUserPass = 7, - EncryptionModeSHATriple = 8, - EncryptionModeSHA512 = 9, +enum EncryptionMode { + EncryptionModeMD5 = 1, + EncryptionModeMD5PassUser = 2, + EncryptionModeMD5UserPass = 3, + EncryptionModeMD5Triple = 4, + EncryptionModeSHA = 5, + EncryptionModeSHAPassUser = 6, + EncryptionModeSHAUserPass = 7, + EncryptionModeSHATriple = 8, + EncryptionModeSHA512 = 9, EncryptionModeSHA512PassUser = 10, EncryptionModeSHA512UserPass = 11, - EncryptionModeSHA512Triple = 12, - EncryptionModeArgon2 = 13, - EncryptionModeSCrypt = 14 + EncryptionModeSHA512Triple = 12, + EncryptionModeArgon2 = 13, + EncryptionModeSCrypt = 14 }; +namespace CryptoHash { + const int md5_hash_length = 32; + const int sha1_hash_length = 40; + const int sha512_hash_length = 128; +} + /** * @param mode * @return */ std::string GetEncryptionByModeId(uint32 mode); -const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc); +const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc); std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode); bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode); diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index d297d519d..ae487d676 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -57,6 +57,7 @@ namespace LoginserverCommandHandler { function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken; function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; + function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } @@ -269,4 +270,32 @@ namespace LoginserverCommandHandler { LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Update world admin account password"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + + AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName( + cmd("--username").str(), + cmd("--password").str() + ); + } } diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index c7abc50a6..2f890f956 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -33,6 +33,7 @@ namespace LoginserverCommandHandler { void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); + void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 42f4f742f..765c108da 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -97,7 +97,7 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe { if (server.options.IsWorldTraceOn()) { LogDebug( - "Application packet received from server: {0}, (size {1})", + "Application packet received from server: [{0}], (size {1})", opcode, packet.Length() ); @@ -164,7 +164,7 @@ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet if (server.options.IsWorldTraceOn()) { LogDebug( "World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]", - this->GetServerLongName(), + GetServerLongName(), ls_status->status, ls_status->num_players, ls_status->num_zones @@ -182,7 +182,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne { if (server.options.IsWorldTraceOn()) { LogDebug( - "Application packet received from server: {0}, (size {1})", + "Application packet received from server: [{0}], (size {1})", opcode, packet.Length() ); @@ -272,7 +272,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne per->Message ); - LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); + LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); } if (server.options.IsDumpOutPacketsOn()) { @@ -284,7 +284,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne } else { LogError( - "Received User-To-World Response for {0} but could not find the client referenced!", + "Received User-To-World Response for [{0}] but could not find the client referenced!", user_to_world_response->lsaccountid ); } @@ -333,7 +333,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac ); if (client) { - LogDebug("Found client with user id of {0} and account name of {1}", + LogDebug("Found client with user id of [{0}] and account name of {1}", user_to_world_response->lsaccountid, client->GetAccountName().c_str() ); @@ -353,7 +353,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac client->GetPlayServerID() ); - LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); + LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); if (user_to_world_response->response > 0) { per->Allowed = 1; @@ -391,13 +391,13 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac if (server.options.IsTraceOn()) { LogDebug( - "Sending play response with following data, allowed {0}, sequence {1}, server number {2}, message {3}", + "Sending play response with following data, allowed [{0}], sequence {1}, server number {2}, message {3}", per->Allowed, per->Sequence, per->ServerNumber, per->Message ); - LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); + LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); } if (server.options.IsDumpOutPacketsOn()) { @@ -409,7 +409,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac } else { LogError( - "Received User-To-World Response for {0} but could not find the client referenced!.", + "Received User-To-World Response for [{0}] but could not find the client referenced!.", user_to_world_response->lsaccountid ); } @@ -423,7 +423,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet { if (server.options.IsWorldTraceOn()) { LogDebug( - "Application packet received from server: {0}, (size {1})", + "Application packet received from server: [{0}], (size {1})", opcode, packet.Length() ); @@ -481,12 +481,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info return; } - if (!this->HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { + if (!HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { LogError("WorldServer::Handle_NewLSInfo failed validation rules"); return; } - this->SetAccountPassword(new_world_server_info_packet->account_password) + SetAccountPassword(new_world_server_info_packet->account_password) ->SetLongName(new_world_server_info_packet->server_long_name) ->SetShortName(new_world_server_info_packet->server_short_name) ->SetLocalIp(new_world_server_info_packet->local_ip_address) @@ -505,7 +505,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info } else { if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { - LogInfo("World tried to login but there already exists a server that has that name"); + LogInfo("World tried to login but there already exists a server that has that name, destroying [{}]", + long_name); server.server_manager->DestroyServerByName(long_name, short_name, this); } } @@ -528,15 +529,11 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info GetServerShortName() ); - /** - * Validate password hash - */ - auto mode = server.options.GetEncryptionMode(); - if (eqcrypt_verify_hash( + if (WorldServer::ValidateWorldServerAdminLogin( + login_server_admin.id, GetAccountName(), GetAccountPassword(), - login_server_admin.account_password, - mode + login_server_admin.account_password )) { LogDebug( "WorldServer::Handle_NewLSInfo | Authenticating world admin... [{0}] ({1}) success! World ({2})", @@ -546,7 +543,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info ); world_server_admin_id = login_server_admin.id; - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); } } } @@ -558,19 +555,19 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info ); if (!server.options.IsUnregisteredAllowed()) { - if (!this->HandleNewLoginserverRegisteredOnly(world_registration)) { + if (!HandleNewLoginserverRegisteredOnly(world_registration)) { LogError( "WorldServer::HandleNewLoginserverRegisteredOnly checks failed with server [{0}]", - this->GetServerLongName() + GetServerLongName() ); return; } } else { - if (!this->HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) { + if (!HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) { LogError( "WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed with server [{0}]", - this->GetServerLongName() + GetServerLongName() ); return; } @@ -710,10 +707,10 @@ bool WorldServer::HandleNewLoginserverInfoValidation( if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) { if (strlen(new_world_server_info_packet->local_ip_address) == 0) { LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); - this->SetLocalIp("127.0.0.1"); + SetLocalIp("127.0.0.1"); } else { - this->SetLocalIp(new_world_server_info_packet->local_ip_address); + SetLocalIp(new_world_server_info_packet->local_ip_address); } } else { @@ -723,19 +720,19 @@ bool WorldServer::HandleNewLoginserverInfoValidation( if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) { if (strlen(new_world_server_info_packet->remote_ip_address) == 0) { - this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + SetRemoteIp(GetConnection()->Handle()->RemoteIP()); LogWarning( - "Remote address was null, defaulting to stream address {0}", + "Remote address was null, defaulting to stream address [{0}]", remote_ip_address ); } else { - this->SetRemoteIp(new_world_server_info_packet->remote_ip_address); + SetRemoteIp(new_world_server_info_packet->remote_ip_address); } } else { - this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); + SetRemoteIp(GetConnection()->Handle()->RemoteIP()); LogWarning( "Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]", @@ -754,7 +751,7 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( Database::DbWorldRegistration &world_registration ) { - if (!this->GetAccountName().empty() && !this->GetAccountPassword().empty()) { + if (!GetAccountName().empty() && !GetAccountPassword().empty()) { if (world_registration.loaded) { bool does_world_server_not_require_authentication = ( world_registration.server_admin_account_name.empty() || @@ -762,39 +759,38 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( ); bool does_world_server_pass_authentication_check = ( - world_registration.server_admin_account_name == this->GetAccountName() && - eqcrypt_verify_hash( + world_registration.server_admin_account_name == GetAccountName() && + WorldServer::ValidateWorldServerAdminLogin( + world_registration.server_admin_id, GetAccountName(), GetAccountPassword(), - world_registration.server_admin_account_password, - server.options.GetEncryptionMode() + world_registration.server_admin_account_password ) ); - this - ->SetServerDescription(world_registration.server_description) + SetServerDescription(world_registration.server_description) ->SetServerId(world_registration.server_id) ->SetIsServerTrusted(world_registration.is_server_trusted) ->SetServerListTypeId(world_registration.server_list_type); if (does_world_server_not_require_authentication) { - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); LogInfo( - "Server long_name {0} short_name [{1}] successfully logged into account that had no user/password requirement", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] successfully logged into account that had no user/password requirement", + GetServerLongName(), + GetServerShortName() ); } else if (does_world_server_pass_authentication_check) { - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); LogInfo( - "Server long_name {0} short_name [{1}] successfully logged in", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] successfully logged in", + GetServerLongName(), + GetServerShortName() ); if (IsServerTrusted()) { @@ -805,10 +801,10 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( } else { LogInfo( - "Server long_name {0} short_name [{1}] attempted to log in but account and password did not " + "Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not " "match the entry in the database, and only registered servers are allowed", - this->GetServerLongName(), - this->GetServerShortName() + GetServerLongName(), + GetServerShortName() ); return false; @@ -816,9 +812,9 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( } else { LogInfo( - "Server long_name {0} short_name [{1}] attempted to log in but database couldn't find an entry and only registered servers are allowed", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] attempted to log in but database couldn't find an entry and only registered servers are allowed", + GetServerLongName(), + GetServerShortName() ); return false; @@ -826,9 +822,9 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( } else { LogInfo( - "Server long_name {0} short_name [{1}] did not attempt to log in but only registered servers are allowed", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] did not attempt to log in but only registered servers are allowed", + GetServerLongName(), + GetServerShortName() ); return false; @@ -846,38 +842,37 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( ) { if (world_registration.loaded) { - this - ->SetServerDescription(world_registration.server_description) + SetServerDescription(world_registration.server_description) ->SetServerId(world_registration.server_id) ->SetIsServerTrusted(world_registration.is_server_trusted) ->SetServerListTypeId(world_registration.server_list_type); bool does_world_server_pass_authentication_check = ( - world_registration.server_admin_account_name == this->GetAccountName() && - eqcrypt_verify_hash( + world_registration.server_admin_account_name == GetAccountName() && + WorldServer::ValidateWorldServerAdminLogin( + world_registration.server_admin_id, GetAccountName(), GetAccountPassword(), - world_registration.server_admin_account_password, - server.options.GetEncryptionMode() + world_registration.server_admin_account_password ) ); bool does_world_server_have_non_empty_credentials = ( - !this->GetAccountName().empty() && - !this->GetAccountPassword().empty() + !GetAccountName().empty() && + !GetAccountPassword().empty() ); if (does_world_server_have_non_empty_credentials) { if (does_world_server_pass_authentication_check) { - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); LogInfo( - "Server long_name {0} short_name [{1}] successfully logged in", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] successfully logged in", + GetServerLongName(), + GetServerShortName() ); - if (this->IsServerTrusted()) { + if (IsServerTrusted()) { LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); EQ::Net::DynamicPacket outapp; connection->Send(ServerOP_LSAccountUpdate, outapp); @@ -889,9 +884,9 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( * this is the first of two cases where we should deny access even if unregistered is allowed */ LogInfo( - "Server long_name {0} short_name [{1}] attempted to log in but account and password did not match the entry in the database.", - this->GetServerLongName(), - this->GetServerShortName() + "Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database.", + GetServerLongName(), + GetServerShortName() ); } } @@ -900,19 +895,19 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( /** * this is the second of two cases where we should deny access even if unregistered is allowed */ - if (!this->GetAccountName().empty() || !this->GetAccountPassword().empty()) { + if (!GetAccountName().empty() || !GetAccountPassword().empty()) { LogInfo( "Server [{0}] [{1}] did not login but this server required a password to login", - this->GetServerLongName(), - this->GetServerShortName() + GetServerLongName(), + GetServerShortName() ); } else { - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); LogInfo( "Server [{0}] [{1}] did not login but unregistered servers are allowed", - this->GetServerLongName(), - this->GetServerShortName() + GetServerLongName(), + GetServerShortName() ); } } @@ -920,11 +915,11 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( else { LogInfo( "Server [{0}] ({1}) is not registered but unregistered servers are allowed", - this->GetServerLongName(), - this->GetServerShortName() + GetServerLongName(), + GetServerShortName() ); - this->SetIsServerAuthorized(true); + SetIsServerAuthorized(true); if (world_registration.loaded) { return true; @@ -934,12 +929,11 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( uint32 server_admin_id = 0; if (login_server_admin.loaded) { - auto mode = server.options.GetEncryptionMode(); - if (eqcrypt_verify_hash( + if (WorldServer::ValidateWorldServerAdminLogin( + login_server_admin.id, GetAccountName(), GetAccountPassword(), - login_server_admin.account_password, - mode + login_server_admin.account_password )) { server_admin_id = login_server_admin.id; } @@ -962,6 +956,88 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( return true; } +/** + * @param world_admin_id + * @param world_admin_username + * @param world_admin_password + * @param world_admin_password_hash + * @return + */ +bool WorldServer::ValidateWorldServerAdminLogin( + int world_admin_id, + const std::string &world_admin_username, + const std::string &world_admin_password, + const std::string &world_admin_password_hash +) +{ + auto encryption_mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, encryption_mode)) { + return true; + } + else { + if (server.options.IsUpdatingInsecurePasswords()) { + if (encryption_mode < EncryptionModeArgon2) { + encryption_mode = EncryptionModeArgon2; + } + + uint32 insecure_source_encryption_mode = 0; + if (world_admin_password_hash.length() == CryptoHash::md5_hash_length) { + for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { + if (i != encryption_mode && + eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { + LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); + insecure_source_encryption_mode = i; + } + } + } + else if (world_admin_password_hash.length() == CryptoHash::sha1_hash_length && + insecure_source_encryption_mode == 0) { + for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { + if (i != encryption_mode && + eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { + LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); + insecure_source_encryption_mode = i; + } + } + } + else if (world_admin_password_hash.length() == CryptoHash::sha512_hash_length && + insecure_source_encryption_mode == 0) { + for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { + if (i != encryption_mode && + eqcrypt_verify_hash(world_admin_username, world_admin_password, world_admin_password_hash, i)) { + LogDebug("[{}] Checking for [{}] world admin", __func__, GetEncryptionByModeId(i)); + insecure_source_encryption_mode = i; + } + } + } + + if (insecure_source_encryption_mode > 0) { + LogInfo( + "[{}] Updated insecure world_admin_username [{}] from mode [{}] ({}) to mode [{}] ({})", + __func__, + world_admin_username, + GetEncryptionByModeId(insecure_source_encryption_mode), + insecure_source_encryption_mode, + GetEncryptionByModeId(encryption_mode), + encryption_mode + ); + + std::string new_password_hash = eqcrypt_hash( + world_admin_username, + world_admin_password, + encryption_mode + ); + + server.db->UpdateLoginWorldAdminAccountPassword(world_admin_id, new_password_hash); + + return true; + } + } + } + + return false; +} + /** * @param in_server_list_id * @return diff --git a/loginserver/world_server.h b/loginserver/world_server.h index 60697bc3d..04db63924 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -33,8 +33,7 @@ /** * World server class, controls the connected server processing. */ -class WorldServer -{ +class WorldServer { public: WorldServer(std::shared_ptr worldserver_connection); @@ -58,7 +57,11 @@ public: * @return */ unsigned int GetServerId() const { return server_id; } - WorldServer * SetServerId(unsigned int id) { server_id = id; return this; } + WorldServer *SetServerId(unsigned int id) + { + server_id = id; + return this; + } /** * @return @@ -80,7 +83,7 @@ public: * @return */ unsigned int GetServerListID() const { return server_list_type_id; } - WorldServer * SetServerListTypeId(unsigned int in_server_list_id); + WorldServer *SetServerListTypeId(unsigned int in_server_list_id); int GetStatus() const { return server_status; } unsigned int GetZonesBooted() const { return zones_booted; } @@ -91,7 +94,7 @@ public: * * @param new_world_server_info_packet */ - void Handle_NewLSInfo(ServerNewLSInfo_Struct* new_world_server_info_packet); + void Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info_packet); /** * Takes the status struct we received from world and processes it @@ -111,24 +114,44 @@ public: * @param account_id * @param loginserver_name */ - void SendClientAuth(std::string ip, std::string account, std::string key, unsigned int account_id, const std::string &loginserver_name); + void SendClientAuth( + std::string ip, + std::string account, + std::string key, + unsigned int account_id, + const std::string &loginserver_name + ); - WorldServer * SetZonesBooted(unsigned int in_zones_booted); - WorldServer * SetPlayersOnline(unsigned int in_players_online); - WorldServer * SetServerStatus(int in_server_status); - WorldServer * SetServerProcessType(unsigned int in_server_process_type); - WorldServer * SetLongName(const std::string &in_long_name); - WorldServer * SetShortName(const std::string &in_short_name); - WorldServer * SetAccountName(const std::string &in_account_name); - WorldServer * SetAccountPassword(const std::string &in_account_password); - WorldServer * SetRemoteIp(const std::string &in_remote_ip); - WorldServer * SetLocalIp(const std::string &in_local_ip); - WorldServer * SetProtocol(const std::string &in_protocol); - WorldServer * SetVersion(const std::string &in_version); - WorldServer * SetServerDescription(const std::string &in_server_description); - WorldServer * SetIsServerAuthorized(bool in_is_server_authorized); - WorldServer * SetIsServerLoggedIn(bool in_is_server_logged_in); - WorldServer * SetIsServerTrusted(bool in_is_server_trusted); + /** + * @param world_admin_id + * @param world_admin_username + * @param world_admin_password + * @param world_admin_password_hash + * @return + */ + static bool ValidateWorldServerAdminLogin( + int world_admin_id, + const std::string &world_admin_username, + const std::string &world_admin_password, + const std::string &world_admin_password_hash + ); + + WorldServer *SetZonesBooted(unsigned int in_zones_booted); + WorldServer *SetPlayersOnline(unsigned int in_players_online); + WorldServer *SetServerStatus(int in_server_status); + WorldServer *SetServerProcessType(unsigned int in_server_process_type); + WorldServer *SetLongName(const std::string &in_long_name); + WorldServer *SetShortName(const std::string &in_short_name); + WorldServer *SetAccountName(const std::string &in_account_name); + WorldServer *SetAccountPassword(const std::string &in_account_password); + WorldServer *SetRemoteIp(const std::string &in_remote_ip); + WorldServer *SetLocalIp(const std::string &in_local_ip); + WorldServer *SetProtocol(const std::string &in_protocol); + WorldServer *SetVersion(const std::string &in_version); + WorldServer *SetServerDescription(const std::string &in_server_description); + WorldServer *SetIsServerAuthorized(bool in_is_server_authorized); + WorldServer *SetIsServerLoggedIn(bool in_is_server_logged_in); + WorldServer *SetIsServerTrusted(bool in_is_server_trusted); bool IsServerAuthorized() const; bool IsServerLoggedIn() const; @@ -162,24 +185,26 @@ private: void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet); std::shared_ptr connection; + unsigned int zones_booted; unsigned int players_online; - int server_status; + int server_status; unsigned int server_id; unsigned int server_list_type_id; unsigned int server_process_type; - std::string server_description; - std::string long_name; - std::string short_name; - std::string account_name; - std::string account_password; - std::string remote_ip_address; - std::string local_ip; - std::string protocol; - std::string version; - bool is_server_authorized; - bool is_server_logged_in; - bool is_server_trusted; + std::string server_description; + std::string long_name; + std::string short_name; + std::string account_name; + std::string account_password; + std::string remote_ip_address; + std::string local_ip; + std::string protocol; + std::string version; + + bool is_server_authorized; + bool is_server_logged_in; + bool is_server_trusted; /** * Keepalive