Auto convert insecure world server admin passwords during the world authentication process, add cli support for updating world admin account

This commit is contained in:
Akkadius 2019-09-30 01:22:40 -05:00
parent 0005df31f7
commit f3c85dc585
10 changed files with 452 additions and 199 deletions

View File

@ -67,7 +67,8 @@ int32 AccountManagement::CreateLoginServerAccount(
uint32 created_account_id = 0; uint32 created_account_id = 0;
if (login_account_id > 0) { if (login_account_id > 0) {
created_account_id = server.db->CreateLoginDataWithID(username, hash, source_loginserver, login_account_id); 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); created_account_id = server.db->CreateLoginAccount(username, hash, source_loginserver, email);
} }
@ -251,6 +252,59 @@ bool AccountManagement::UpdateLoginserverUserCredentials(
return true; 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( uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
const std::string &in_account_username, const std::string &in_account_username,
const std::string &in_account_password const std::string &in_account_password

View File

@ -88,6 +88,26 @@ public:
const std::string &in_account_username, const std::string &in_account_username,
const std::string &in_account_password 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
);
}; };

View File

@ -499,67 +499,52 @@ bool Client::VerifyLoginHash(
const std::string &password_hash const std::string &password_hash
) )
{ {
auto mode = server.options.GetEncryptionMode(); auto encryption_mode = server.options.GetEncryptionMode();
if (eqcrypt_verify_hash(account_username, account_password, password_hash, mode)) { if (eqcrypt_verify_hash(account_username, account_password, password_hash, encryption_mode)) {
return true; return true;
} }
else { else {
if (server.options.IsUpdatingInsecurePasswords()) { if (server.options.IsUpdatingInsecurePasswords()) {
if (mode < EncryptionModeArgon2) { if (encryption_mode < EncryptionModeArgon2) {
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) { for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) {
if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { if (i != encryption_mode &&
LogDebug( eqcrypt_verify_hash(account_username, account_password, password_hash, i)) {
"user [{0}] loginserver [{1}] mode [{2}]", insecure_source_encryption_mode = i;
account_username,
source_loginserver,
mode
);
server.db->UpdateLoginserverAccountPasswordHash(
account_username,
source_loginserver,
eqcrypt_hash(
account_username,
account_password,
mode
));
return true;
} }
} }
} }
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) { for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) {
if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { if (i != encryption_mode &&
LogDebug( eqcrypt_verify_hash(account_username, account_password, password_hash, i)) {
"user [{0}] loginserver [{1}] mode [{2}]", insecure_source_encryption_mode = i;
account_username,
source_loginserver,
mode
);
server.db->UpdateLoginserverAccountPasswordHash(
account_username,
source_loginserver,
eqcrypt_hash(
account_username,
account_password,
mode
));
return true;
} }
} }
} }
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) { for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) {
if (i != mode && eqcrypt_verify_hash(account_username, account_password, password_hash, i)) { if (i != encryption_mode &&
LogDebug( eqcrypt_verify_hash(account_username, account_password, password_hash, i)) {
"user [{0}] loginserver [{1}] mode [{2}]", insecure_source_encryption_mode = i;
}
}
}
if (insecure_source_encryption_mode > 0) {
LogInfo(
"[{}] Updated insecure password user [{}] loginserver [{}] from mode [{}] ({}) to mode [{}] ({})",
__func__,
account_username, account_username,
source_loginserver, source_loginserver,
mode GetEncryptionByModeId(insecure_source_encryption_mode),
insecure_source_encryption_mode,
GetEncryptionByModeId(encryption_mode),
encryption_mode
); );
server.db->UpdateLoginserverAccountPasswordHash( server.db->UpdateLoginserverAccountPasswordHash(
@ -568,16 +553,14 @@ bool Client::VerifyLoginHash(
eqcrypt_hash( eqcrypt_hash(
account_username, account_username,
account_password, account_password,
mode encryption_mode
)); )
);
return true; return true;
} }
} }
} }
//argon2 is still secure
//scrypt is still secure
}
}
return false; return false;
} }

View File

@ -395,15 +395,15 @@ Database::DbWorldRegistration Database::GetWorldRegistration(
world_registration.server_list_type = std::stoi(row[3]); world_registration.server_list_type = std::stoi(row[3]);
world_registration.is_server_trusted = std::stoi(row[2]) > 0; world_registration.is_server_trusted = std::stoi(row[2]) > 0;
world_registration.server_list_description = row[4]; 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 (world_registration.server_admin_id <= 0) {
if (db_account_id <= 0) {
return world_registration; return world_registration;
} }
auto world_registration_query = fmt::format( auto world_registration_query = fmt::format(
"SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", "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); 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); 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_long_name
* @param server_short_name * @param server_short_name

View File

@ -149,6 +149,7 @@ public:
std::string server_list_description; std::string server_list_description;
std::string server_admin_account_name; std::string server_admin_account_name;
std::string server_admin_account_password; std::string server_admin_account_password;
uint32 server_admin_id;
}; };
/** /**
@ -269,6 +270,24 @@ public:
const std::string &source_loginserver = "local" 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 name
* @param password * @param password

View File

@ -23,8 +23,7 @@
#include <string> #include <string>
#include "../common/types.h" #include "../common/types.h"
enum EncryptionMode enum EncryptionMode {
{
EncryptionModeMD5 = 1, EncryptionModeMD5 = 1,
EncryptionModeMD5PassUser = 2, EncryptionModeMD5PassUser = 2,
EncryptionModeMD5UserPass = 3, EncryptionModeMD5UserPass = 3,
@ -41,11 +40,17 @@ enum EncryptionMode
EncryptionModeSCrypt = 14 EncryptionModeSCrypt = 14
}; };
namespace CryptoHash {
const int md5_hash_length = 32;
const int sha1_hash_length = 40;
const int sha512_hash_length = 128;
}
/** /**
* @param mode * @param mode
* @return * @return
*/ */
std::string GetEncryptionByModeId(uint32 mode); 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); 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); bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode);

View File

@ -57,6 +57,7 @@ namespace LoginserverCommandHandler {
function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken; function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken;
function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens;
function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount;
function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword;
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
} }
@ -269,4 +270,32 @@ namespace LoginserverCommandHandler {
LogInfo("Credentials were {0}", res ? "accepted" : "not accepted"); 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<std::string> arguments = {
"--username",
"--password"
};
std::vector<std::string> options = {};
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
AccountManagement::UpdateLoginserverWorldAdminAccountPasswordByName(
cmd("--username").str(),
cmd("--password").str()
);
}
} }

View File

@ -33,6 +33,7 @@ namespace LoginserverCommandHandler {
void CheckLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); 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 UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
void CheckExternalLoginserverUserCredentials(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);
}; };

View File

@ -97,7 +97,7 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe
{ {
if (server.options.IsWorldTraceOn()) { if (server.options.IsWorldTraceOn()) {
LogDebug( LogDebug(
"Application packet received from server: {0}, (size {1})", "Application packet received from server: [{0}], (size {1})",
opcode, opcode,
packet.Length() packet.Length()
); );
@ -164,7 +164,7 @@ void WorldServer::ProcessLSStatus(uint16_t opcode, const EQ::Net::Packet &packet
if (server.options.IsWorldTraceOn()) { if (server.options.IsWorldTraceOn()) {
LogDebug( LogDebug(
"World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]", "World Server Status Update Received | Server [{0}] Status [{1}] Players [{2}] Zones [{3}]",
this->GetServerLongName(), GetServerLongName(),
ls_status->status, ls_status->status,
ls_status->num_players, ls_status->num_players,
ls_status->num_zones ls_status->num_zones
@ -182,7 +182,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
{ {
if (server.options.IsWorldTraceOn()) { if (server.options.IsWorldTraceOn()) {
LogDebug( LogDebug(
"Application packet received from server: {0}, (size {1})", "Application packet received from server: [{0}], (size {1})",
opcode, opcode,
packet.Length() packet.Length()
); );
@ -272,7 +272,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
per->Message per->Message
); );
LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
} }
if (server.options.IsDumpOutPacketsOn()) { if (server.options.IsDumpOutPacketsOn()) {
@ -284,7 +284,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
} }
else { else {
LogError( 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 user_to_world_response->lsaccountid
); );
} }
@ -333,7 +333,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
); );
if (client) { 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, user_to_world_response->lsaccountid,
client->GetAccountName().c_str() client->GetAccountName().c_str()
); );
@ -353,7 +353,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
client->GetPlayServerID() 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) { if (user_to_world_response->response > 0) {
per->Allowed = 1; per->Allowed = 1;
@ -391,13 +391,13 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
if (server.options.IsTraceOn()) { if (server.options.IsTraceOn()) {
LogDebug( 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->Allowed,
per->Sequence, per->Sequence,
per->ServerNumber, per->ServerNumber,
per->Message per->Message
); );
LogDebug("[Size: {0}] {1}", outapp->size, DumpPacketToString(outapp)); LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
} }
if (server.options.IsDumpOutPacketsOn()) { if (server.options.IsDumpOutPacketsOn()) {
@ -409,7 +409,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
} }
else { else {
LogError( 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 user_to_world_response->lsaccountid
); );
} }
@ -423,7 +423,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
{ {
if (server.options.IsWorldTraceOn()) { if (server.options.IsWorldTraceOn()) {
LogDebug( LogDebug(
"Application packet received from server: {0}, (size {1})", "Application packet received from server: [{0}], (size {1})",
opcode, opcode,
packet.Length() packet.Length()
); );
@ -481,12 +481,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
return; return;
} }
if (!this->HandleNewLoginserverInfoValidation(new_world_server_info_packet)) { if (!HandleNewLoginserverInfoValidation(new_world_server_info_packet)) {
LogError("WorldServer::Handle_NewLSInfo failed validation rules"); LogError("WorldServer::Handle_NewLSInfo failed validation rules");
return; 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) ->SetLongName(new_world_server_info_packet->server_long_name)
->SetShortName(new_world_server_info_packet->server_short_name) ->SetShortName(new_world_server_info_packet->server_short_name)
->SetLocalIp(new_world_server_info_packet->local_ip_address) ->SetLocalIp(new_world_server_info_packet->local_ip_address)
@ -505,7 +505,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
} }
else { else {
if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { 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); server.server_manager->DestroyServerByName(long_name, short_name, this);
} }
} }
@ -528,15 +529,11 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
GetServerShortName() GetServerShortName()
); );
/** if (WorldServer::ValidateWorldServerAdminLogin(
* Validate password hash login_server_admin.id,
*/
auto mode = server.options.GetEncryptionMode();
if (eqcrypt_verify_hash(
GetAccountName(), GetAccountName(),
GetAccountPassword(), GetAccountPassword(),
login_server_admin.account_password, login_server_admin.account_password
mode
)) { )) {
LogDebug( LogDebug(
"WorldServer::Handle_NewLSInfo | Authenticating world admin... [{0}] ({1}) success! World ({2})", "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; 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 (!server.options.IsUnregisteredAllowed()) {
if (!this->HandleNewLoginserverRegisteredOnly(world_registration)) { if (!HandleNewLoginserverRegisteredOnly(world_registration)) {
LogError( LogError(
"WorldServer::HandleNewLoginserverRegisteredOnly checks failed with server [{0}]", "WorldServer::HandleNewLoginserverRegisteredOnly checks failed with server [{0}]",
this->GetServerLongName() GetServerLongName()
); );
return; return;
} }
} }
else { else {
if (!this->HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) { if (!HandleNewLoginserverInfoUnregisteredAllowed(world_registration)) {
LogError( LogError(
"WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed with server [{0}]", "WorldServer::HandleNewLoginserverInfoUnregisteredAllowed checks failed with server [{0}]",
this->GetServerLongName() GetServerLongName()
); );
return; 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) <= max_server_local_address_length) {
if (strlen(new_world_server_info_packet->local_ip_address) == 0) { if (strlen(new_world_server_info_packet->local_ip_address) == 0) {
LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost");
this->SetLocalIp("127.0.0.1"); SetLocalIp("127.0.0.1");
} }
else { else {
this->SetLocalIp(new_world_server_info_packet->local_ip_address); SetLocalIp(new_world_server_info_packet->local_ip_address);
} }
} }
else { 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) <= max_server_remote_address_length) {
if (strlen(new_world_server_info_packet->remote_ip_address) == 0) { if (strlen(new_world_server_info_packet->remote_ip_address) == 0) {
this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); SetRemoteIp(GetConnection()->Handle()->RemoteIP());
LogWarning( LogWarning(
"Remote address was null, defaulting to stream address {0}", "Remote address was null, defaulting to stream address [{0}]",
remote_ip_address remote_ip_address
); );
} }
else { else {
this->SetRemoteIp(new_world_server_info_packet->remote_ip_address); SetRemoteIp(new_world_server_info_packet->remote_ip_address);
} }
} }
else { else {
this->SetRemoteIp(GetConnection()->Handle()->RemoteIP()); SetRemoteIp(GetConnection()->Handle()->RemoteIP());
LogWarning( LogWarning(
"Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]", "Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]",
@ -754,7 +751,7 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
Database::DbWorldRegistration &world_registration Database::DbWorldRegistration &world_registration
) )
{ {
if (!this->GetAccountName().empty() && !this->GetAccountPassword().empty()) { if (!GetAccountName().empty() && !GetAccountPassword().empty()) {
if (world_registration.loaded) { if (world_registration.loaded) {
bool does_world_server_not_require_authentication = ( bool does_world_server_not_require_authentication = (
world_registration.server_admin_account_name.empty() || world_registration.server_admin_account_name.empty() ||
@ -762,39 +759,38 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
); );
bool does_world_server_pass_authentication_check = ( bool does_world_server_pass_authentication_check = (
world_registration.server_admin_account_name == this->GetAccountName() && world_registration.server_admin_account_name == GetAccountName() &&
eqcrypt_verify_hash( WorldServer::ValidateWorldServerAdminLogin(
world_registration.server_admin_id,
GetAccountName(), GetAccountName(),
GetAccountPassword(), GetAccountPassword(),
world_registration.server_admin_account_password, world_registration.server_admin_account_password
server.options.GetEncryptionMode()
) )
); );
this SetServerDescription(world_registration.server_description)
->SetServerDescription(world_registration.server_description)
->SetServerId(world_registration.server_id) ->SetServerId(world_registration.server_id)
->SetIsServerTrusted(world_registration.is_server_trusted) ->SetIsServerTrusted(world_registration.is_server_trusted)
->SetServerListTypeId(world_registration.server_list_type); ->SetServerListTypeId(world_registration.server_list_type);
if (does_world_server_not_require_authentication) { if (does_world_server_not_require_authentication) {
this->SetIsServerAuthorized(true); SetIsServerAuthorized(true);
LogInfo( LogInfo(
"Server long_name {0} short_name [{1}] successfully logged into account that had no user/password requirement", "Server long_name [{0}] short_name [{1}] successfully logged into account that had no user/password requirement",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
} }
else if (does_world_server_pass_authentication_check) { else if (does_world_server_pass_authentication_check) {
this->SetIsServerAuthorized(true); SetIsServerAuthorized(true);
LogInfo( LogInfo(
"Server long_name {0} short_name [{1}] successfully logged in", "Server long_name [{0}] short_name [{1}] successfully logged in",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
if (IsServerTrusted()) { if (IsServerTrusted()) {
@ -805,10 +801,10 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
} }
else { else {
LogInfo( 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", "match the entry in the database, and only registered servers are allowed",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
return false; return false;
@ -816,9 +812,9 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
} }
else { else {
LogInfo( 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", "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(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
return false; return false;
@ -826,9 +822,9 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
} }
else { else {
LogInfo( LogInfo(
"Server long_name {0} short_name [{1}] did not attempt to log in but only registered servers are allowed", "Server long_name [{0}] short_name [{1}] did not attempt to log in but only registered servers are allowed",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
return false; return false;
@ -846,38 +842,37 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
) )
{ {
if (world_registration.loaded) { if (world_registration.loaded) {
this SetServerDescription(world_registration.server_description)
->SetServerDescription(world_registration.server_description)
->SetServerId(world_registration.server_id) ->SetServerId(world_registration.server_id)
->SetIsServerTrusted(world_registration.is_server_trusted) ->SetIsServerTrusted(world_registration.is_server_trusted)
->SetServerListTypeId(world_registration.server_list_type); ->SetServerListTypeId(world_registration.server_list_type);
bool does_world_server_pass_authentication_check = ( bool does_world_server_pass_authentication_check = (
world_registration.server_admin_account_name == this->GetAccountName() && world_registration.server_admin_account_name == GetAccountName() &&
eqcrypt_verify_hash( WorldServer::ValidateWorldServerAdminLogin(
world_registration.server_admin_id,
GetAccountName(), GetAccountName(),
GetAccountPassword(), GetAccountPassword(),
world_registration.server_admin_account_password, world_registration.server_admin_account_password
server.options.GetEncryptionMode()
) )
); );
bool does_world_server_have_non_empty_credentials = ( bool does_world_server_have_non_empty_credentials = (
!this->GetAccountName().empty() && !GetAccountName().empty() &&
!this->GetAccountPassword().empty() !GetAccountPassword().empty()
); );
if (does_world_server_have_non_empty_credentials) { if (does_world_server_have_non_empty_credentials) {
if (does_world_server_pass_authentication_check) { if (does_world_server_pass_authentication_check) {
this->SetIsServerAuthorized(true); SetIsServerAuthorized(true);
LogInfo( LogInfo(
"Server long_name {0} short_name [{1}] successfully logged in", "Server long_name [{0}] short_name [{1}] successfully logged in",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
if (this->IsServerTrusted()) { if (IsServerTrusted()) {
LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world");
EQ::Net::DynamicPacket outapp; EQ::Net::DynamicPacket outapp;
connection->Send(ServerOP_LSAccountUpdate, 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 * this is the first of two cases where we should deny access even if unregistered is allowed
*/ */
LogInfo( LogInfo(
"Server long_name {0} short_name [{1}] attempted to log in but account and password did not match the entry in the database.", "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(), GetServerLongName(),
this->GetServerShortName() 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 * 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( LogInfo(
"Server [{0}] [{1}] did not login but this server required a password to login", "Server [{0}] [{1}] did not login but this server required a password to login",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
} }
else { else {
this->SetIsServerAuthorized(true); SetIsServerAuthorized(true);
LogInfo( LogInfo(
"Server [{0}] [{1}] did not login but unregistered servers are allowed", "Server [{0}] [{1}] did not login but unregistered servers are allowed",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
} }
} }
@ -920,11 +915,11 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
else { else {
LogInfo( LogInfo(
"Server [{0}] ({1}) is not registered but unregistered servers are allowed", "Server [{0}] ({1}) is not registered but unregistered servers are allowed",
this->GetServerLongName(), GetServerLongName(),
this->GetServerShortName() GetServerShortName()
); );
this->SetIsServerAuthorized(true); SetIsServerAuthorized(true);
if (world_registration.loaded) { if (world_registration.loaded) {
return true; return true;
@ -934,12 +929,11 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
uint32 server_admin_id = 0; uint32 server_admin_id = 0;
if (login_server_admin.loaded) { if (login_server_admin.loaded) {
auto mode = server.options.GetEncryptionMode(); if (WorldServer::ValidateWorldServerAdminLogin(
if (eqcrypt_verify_hash( login_server_admin.id,
GetAccountName(), GetAccountName(),
GetAccountPassword(), GetAccountPassword(),
login_server_admin.account_password, login_server_admin.account_password
mode
)) { )) {
server_admin_id = login_server_admin.id; server_admin_id = login_server_admin.id;
} }
@ -962,6 +956,88 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
return true; 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 * @param in_server_list_id
* @return * @return

View File

@ -33,8 +33,7 @@
/** /**
* World server class, controls the connected server processing. * World server class, controls the connected server processing.
*/ */
class WorldServer class WorldServer {
{
public: public:
WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection); WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection);
@ -58,7 +57,11 @@ public:
* @return * @return
*/ */
unsigned int GetServerId() const { return server_id; } 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 * @return
@ -80,7 +83,7 @@ public:
* @return * @return
*/ */
unsigned int GetServerListID() const { return server_list_type_id; } 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; } int GetStatus() const { return server_status; }
unsigned int GetZonesBooted() const { return zones_booted; } unsigned int GetZonesBooted() const { return zones_booted; }
@ -91,7 +94,7 @@ public:
* *
* @param new_world_server_info_packet * @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 * Takes the status struct we received from world and processes it
@ -111,24 +114,44 @@ public:
* @param account_id * @param account_id
* @param loginserver_name * @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); * @param world_admin_id
WorldServer * SetServerStatus(int in_server_status); * @param world_admin_username
WorldServer * SetServerProcessType(unsigned int in_server_process_type); * @param world_admin_password
WorldServer * SetLongName(const std::string &in_long_name); * @param world_admin_password_hash
WorldServer * SetShortName(const std::string &in_short_name); * @return
WorldServer * SetAccountName(const std::string &in_account_name); */
WorldServer * SetAccountPassword(const std::string &in_account_password); static bool ValidateWorldServerAdminLogin(
WorldServer * SetRemoteIp(const std::string &in_remote_ip); int world_admin_id,
WorldServer * SetLocalIp(const std::string &in_local_ip); const std::string &world_admin_username,
WorldServer * SetProtocol(const std::string &in_protocol); const std::string &world_admin_password,
WorldServer * SetVersion(const std::string &in_version); const std::string &world_admin_password_hash
WorldServer * SetServerDescription(const std::string &in_server_description); );
WorldServer * SetIsServerAuthorized(bool in_is_server_authorized);
WorldServer * SetIsServerLoggedIn(bool in_is_server_logged_in); WorldServer *SetZonesBooted(unsigned int in_zones_booted);
WorldServer * SetIsServerTrusted(bool in_is_server_trusted); 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 IsServerAuthorized() const;
bool IsServerLoggedIn() const; bool IsServerLoggedIn() const;
@ -162,6 +185,7 @@ private:
void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet); void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet);
std::shared_ptr<EQ::Net::ServertalkServerConnection> connection; std::shared_ptr<EQ::Net::ServertalkServerConnection> connection;
unsigned int zones_booted; unsigned int zones_booted;
unsigned int players_online; unsigned int players_online;
int server_status; int server_status;
@ -177,6 +201,7 @@ private:
std::string local_ip; std::string local_ip;
std::string protocol; std::string protocol;
std::string version; std::string version;
bool is_server_authorized; bool is_server_authorized;
bool is_server_logged_in; bool is_server_logged_in;
bool is_server_trusted; bool is_server_trusted;