From 0ec53eff5243ecf85512d60155350bad01a43ba3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 24 Dec 2017 23:21:17 -0800 Subject: [PATCH] Reimplement some functions --- common/database.cpp | 2 +- common/database.h | 2 +- common/string_util.cpp | 13 ++++ common/string_util.h | 1 + loginserver/client.cpp | 84 +++++++++++++----------- loginserver/client.h | 5 ++ loginserver/encryption.cpp | 30 +++++---- loginserver/encryption.h | 18 ++++++ world/console.cpp | 18 +----- world/net.cpp | 40 ++++++------ zone/command.cpp | 128 ++++++++++++++++++++----------------- 11 files changed, 195 insertions(+), 146 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 8926ed4de..160a6a7ff 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -224,7 +224,7 @@ uint32 Database::CreateAccount(const char* name, const char* password, int16 sta return results.LastInsertedID(); } -bool Database::DeleteAccount(const char *loginserver, const char* name) { +bool Database::DeleteAccount(const char* name, const char *loginserver) { std::string query = StringFormat("DELETE FROM account WHERE name='%s' AND ls_id='%s'", name, loginserver); Log(Logs::General, Logs::World_Server, "Account Attempting to be deleted:'%s:%s'", loginserver, name); diff --git a/common/database.h b/common/database.h index 471e59a31..b32739f9d 100644 --- a/common/database.h +++ b/common/database.h @@ -174,7 +174,7 @@ public: /* Account Related */ - bool DeleteAccount(const char *loginserver, const char* name); + bool DeleteAccount(const char *name, const char* loginserver); bool GetLiveChar(uint32 account_id, char* cname); bool SetAccountStatus(const char* name, int16 status); bool SetLocalPassword(uint32 accid, const char* password); diff --git a/common/string_util.cpp b/common/string_util.cpp index 3e9986b48..74d5ea0c7 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -196,6 +196,19 @@ void find_replace(std::string& string_subject, const std::string& search_string, } } +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver) +{ + auto split = SplitString(s, '.'); + if (split.size() == 2) { + loginserver = split[0]; + account = split[1]; + } + else if(split.size() == 1) { + loginserver = "eqemu"; + account = split[0]; + } +} + //Const char based // normal strncpy doesnt put a null term on copied strings, this one does diff --git a/common/string_util.h b/common/string_util.h index 0db8ff2c9..636a7e352 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -34,6 +34,7 @@ void ToLowerString(std::string &s); void ToUpperString(std::string &s); std::string JoinString(const std::vector& ar, const std::string &delim); void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver); //const char based diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 0db9c1600..aae1e9f3e 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -180,8 +180,6 @@ void Client::Handle_SessionReady(const char* data, unsigned int size) void Client::Handle_Login(const char* data, unsigned int size) { - auto mode = server.options.GetEncryptionMode(); - if (status != cs_waiting_for_login) { Log(Logs::General, Logs::Error, "Login received after already having logged in."); return; @@ -241,6 +239,8 @@ void Client::Handle_Login(const char* data, unsigned int size) db_loginserver = components[0]; user = components[1]; } + + ParseAccountString(user, user, db_loginserver); if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id) == false) { status = cs_creating_account; @@ -248,41 +248,7 @@ void Client::Handle_Login(const char* data, unsigned int size) return; } else { - if (eqcrypt_verify_hash(user, cred, db_account_password_hash, mode)) { - result = true; - } - else - { - if (server.options.IsUpdatingInsecurePasswords()) { - auto len = db_account_password_hash.length(); - int start = 0; - int end = 0; - switch (len) { - case 32: - start = 1; - end = 4; - break; - case 40: - start = 5; - end = 8; - break; - case 128: - start = 9; - end = 12; - break; - } - - if (start != 0) { - for (int i = start; i <= end; ++i) { - if (eqcrypt_verify_hash(user, cred, db_account_password_hash, i)) { - result = true; - server.db->UpdateLoginHash(user, db_loginserver, eqcrypt_hash(user, cred, mode)); - break; - } - } - } - } - } + result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash); } } } @@ -432,6 +398,50 @@ void Client::DoFailedLogin() status = cs_failed_to_login; } +bool Client::VerifyLoginHash(const std::string &user, const std::string &loginserver, const std::string &cred, const std::string &hash) +{ + auto mode = server.options.GetEncryptionMode(); + if (eqcrypt_verify_hash(user, cred, hash, mode)) { + return true; + } + else { + if (server.options.IsUpdatingInsecurePasswords()) { + if (mode < EncryptionModeArgon2) { + mode = EncryptionModeArgon2; + } + + if (hash.length() == 32) { //md5 is insecure + for (int i = EncryptionModeMD5; i <= EncryptionModeMD5Triple; ++i) { + if (i != mode && eqcrypt_verify_hash(user, cred, hash, i)) { + server.db->UpdateLoginHash(user, loginserver, eqcrypt_hash(user, cred, mode)); + return true; + } + } + } + else if (hash.length() == 40) { //sha1 is insecure + for (int i = EncryptionModeSHA; i <= EncryptionModeSHATriple; ++i) { + if (i != mode && eqcrypt_verify_hash(user, cred, hash, i)) { + server.db->UpdateLoginHash(user, loginserver, eqcrypt_hash(user, cred, mode)); + return true; + } + } + } + else if (hash.length() == 128) { //sha2-512 is insecure + for (int i = EncryptionModeSHA512; i <= EncryptionModeSHA512Triple; ++i) { + if (i != mode && eqcrypt_verify_hash(user, cred, hash, i)) { + server.db->UpdateLoginHash(user, loginserver, eqcrypt_hash(user, cred, mode)); + return true; + } + } + } + //argon2 is still secure + //scrypt is still secure + } + } + + return false; +} + void Client::DoSuccessfulLogin(const std::string &user, int db_account_id, const std::string &db_loginserver) { stored_user.clear(); diff --git a/loginserver/client.h b/loginserver/client.h index 0271ec545..8225dc90a 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -141,6 +141,11 @@ public: */ void DoFailedLogin(); + /** + * Verifies a login hash, will also attempt to update a login hash if needed. + */ + bool VerifyLoginHash(const std::string &user, const std::string &loginserver, const std::string &cred, const std::string &hash); + /** * Does a successful login */ diff --git a/loginserver/encryption.cpp b/loginserver/encryption.cpp index bdbab2fbf..43d6ae694 100644 --- a/loginserver/encryption.cpp +++ b/loginserver/encryption.cpp @@ -7,6 +7,8 @@ #include #endif +#include "encryption.h" + const char* eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char* buffer_out, bool enc) { DES_key_schedule k; DES_cblock v; @@ -100,34 +102,34 @@ std::string eqcrypt_scrypt(const std::string &msg) std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode) { switch (mode) { - case 1: + case EncryptionModeMD5: return eqcrypt_md5(password); - case 2: + case EncryptionModeMD5PassUser: return eqcrypt_md5(password + ":" + username); - case 3: + case EncryptionModeMD5UserPass: return eqcrypt_md5(username + ":" + password); - case 4: + case EncryptionModeMD5Triple: return eqcrypt_md5(eqcrypt_md5(username) + eqcrypt_md5(password)); - case 5: + case EncryptionModeSHA: return eqcrypt_sha1(password); - case 6: + case EncryptionModeSHAPassUser: return eqcrypt_sha1(password + ":" + username); - case 7: + case EncryptionModeSHAUserPass: return eqcrypt_sha1(username + ":" + password); - case 8: + case EncryptionModeSHATriple: return eqcrypt_sha1(eqcrypt_sha1(username) + eqcrypt_sha1(password)); - case 9: + case EncryptionModeSHA512: return eqcrypt_sha512(password); - case 10: + case EncryptionModeSHA512PassUser: return eqcrypt_sha512(password + ":" + username); - case 11: + case EncryptionModeSHA512UserPass: return eqcrypt_sha512(username + ":" + password); - case 12: + case EncryptionModeSHA512Triple: return eqcrypt_sha512(eqcrypt_sha512(username) + eqcrypt_sha512(password)); #ifdef ENABLE_SECURITY - case 13: + case EncryptionModeArgon2: return eqcrypt_argon2(password); - case 14: + case EncryptionModeSCrypt: return eqcrypt_scrypt(password); #endif //todo bcrypt? pbkdf2? diff --git a/loginserver/encryption.h b/loginserver/encryption.h index fe27ce28a..3ec9c2743 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -2,6 +2,24 @@ #include +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 +}; + 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/world/console.cpp b/world/console.cpp index 8342b2e3c..9debd7e9b 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -19,14 +19,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string& username, const std::string prefix = "eqemu"; std::string raw_user = ""; - auto split = SplitString(username, '.'); - if (split.size() == 2) { - prefix = split[0]; - raw_user = split[1]; - } - else { - raw_user = split[0]; - } + ParseAccountString(username, raw_user, prefix); ret.account_id = database.CheckLogin(raw_user.c_str(), password.c_str(), prefix.c_str()); @@ -399,14 +392,7 @@ void ConsoleSetPass(EQ::Net::ConsoleServerConnection* connection, const std::str std::string prefix = "eqemu"; std::string raw_user = ""; - auto split = SplitString(args[0], '.'); - if (split.size() == 2) { - prefix = split[0]; - raw_user = split[1]; - } - else { - raw_user = split[0]; - } + ParseAccountString(args[0], raw_user, prefix); int16 tmpstatus = 0; uint32 tmpid = database.GetAccountIDByName(raw_user.c_str(), prefix.c_str(), &tmpstatus); diff --git a/world/net.cpp b/world/net.cpp index 2c04b9d10..c0ab16ecc 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -213,24 +213,28 @@ int main(int argc, char** argv) { } } else if (strcasecmp(argv[1], "adduser") == 0) { - //TODO: REIMPLEMENT - //if (argc == 5) { - // if (Seperator::IsNumber(argv[4])) { - // if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) { - // if (database.CreateAccount(argv[2], argv[3], atoi(argv[4])) == 0) { - // std::cerr << "database.CreateAccount failed." << std::endl; - // return 1; - // } - // else { - // std::cout << "Account created: Username='" << argv[2] << "', Password='" << argv[3] << "', status=" << argv[4] << std::endl; - // return 0; - // } - // } - // } - //} - //std::cout << "Usage: world adduser username password flag" << std::endl; - //std::cout << "flag = 0, 1 or 2" << std::endl; - //return 0; + if (argc == 5) { + if (Seperator::IsNumber(argv[4])) { + if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) { + std::string user; + std::string loginserver; + + ParseAccountString(argv[2], user, loginserver); + + if (database.CreateAccount(argv[2], argv[3], atoi(argv[4]), loginserver.c_str(), 0) == 0) { + std::cerr << "database.CreateAccount failed." << std::endl; + return 1; + } + else { + std::cout << "Account created: Username='" << argv[2] << "', Password='" << argv[3] << "', status=" << argv[4] << std::endl; + return 0; + } + } + } + } + std::cout << "Usage: world adduser username password flag" << std::endl; + std::cout << "flag = 0, 1 or 2" << std::endl; + return 0; } else if (strcasecmp(argv[1], "flag") == 0) { if (argc == 4) { diff --git a/zone/command.cpp b/zone/command.cpp index 9ca19a3b9..164e0f750 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1957,33 +1957,40 @@ void command_shutdown(Client *c, const Seperator *sep) void command_delacct(Client *c, const Seperator *sep) { - //TODO: REIMPLEMENT -// if(sep->arg[1][0] == 0) -// c->Message(0, "Format: #delacct accountname"); -// else -// if (database.DeleteAccount(sep->arg[1])) -// c->Message(0, "The account was deleted."); -// else -// c->Message(0, "Unable to delete account."); + if (sep->arg[1][0] == 0) + c->Message(0, "Format: #delacct accountname"); + else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + + if (database.DeleteAccount(user.c_str(), loginserver.c_str())) + c->Message(0, "The account was deleted."); + else + c->Message(0, "Unable to delete account."); + } } void command_setpass(Client *c, const Seperator *sep) { - //TODO: REIMPLEMENT - //if(sep->argnum != 2) - // c->Message(0, "Format: #setpass accountname password"); - //else { - // int16 tmpstatus = 0; - // uint32 tmpid = database.GetAccountIDByName(sep->arg[1], &tmpstatus); - // if (!tmpid) - // c->Message(0, "Error: Account not found"); - // else if (tmpstatus > c->Admin()) - // c->Message(0, "Cannot change password: Account's status is higher than yours"); - // else if (database.SetLocalPassword(tmpid, sep->arg[2])) - // c->Message(0, "Password changed."); - // else - // c->Message(0, "Error changing password."); - //} + if(sep->argnum != 2) + c->Message(0, "Format: #setpass accountname password"); + else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); + if (!tmpid) + c->Message(0, "Error: Account not found"); + else if (tmpstatus > c->Admin()) + c->Message(0, "Cannot change password: Account's status is higher than yours"); + else if (database.SetLocalPassword(tmpid, sep->arg[2])) + c->Message(0, "Password changed."); + else + c->Message(0, "Error changing password."); + } } void command_setlsinfo(Client *c, const Seperator *sep) @@ -4416,42 +4423,45 @@ void command_uptime(Client *c, const Seperator *sep) void command_flag(Client *c, const Seperator *sep) { - //TODO: REIMPLEMENT -// if(sep->arg[2][0] == 0) { -// if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { -// c->UpdateAdmin(); -// c->Message(0, "Refreshed your admin flag from DB."); -// } else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { -// c->GetTarget()->CastToClient()->UpdateAdmin(); -// c->Message(0, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); -// c->GetTarget()->Message(0, "%s refreshed your admin flag.", c->GetName()); -// } -// } -// else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) -// c->Message(0, "Usage: #flag [status] [acctname]"); -// -// else if (c->Admin() < commandChangeFlags) { -////this check makes banning players by less than this level -////impossible, but i'll leave it in anyways -// c->Message(0, "You may only refresh your own flag, doing so now."); -// c->UpdateAdmin(); -// } -// else { -// if (atoi(sep->arg[1]) > c->Admin()) -// c->Message(0, "You cannot set people's status to higher than your own"); -// else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) -// c->Message(0, "You have too low of status to suspend/ban"); -// else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) -// c->Message(0, "Unable to set GM Flag."); -// else { -// c->Message(0, "Set GM Flag on account."); -// auto pack = new ServerPacket(ServerOP_FlagUpdate, 6); -// *((uint32*) pack->pBuffer) = database.GetAccountIDByName(sep->argplus[2]); -// *((int16*) &pack->pBuffer[4]) = atoi(sep->arg[1]); -// worldserver.SendPacket(pack); -// delete pack; -// } -// } + if(sep->arg[2][0] == 0) { + if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { + c->UpdateAdmin(); + c->Message(0, "Refreshed your admin flag from DB."); + } else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->UpdateAdmin(); + c->Message(0, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); + c->GetTarget()->Message(0, "%s refreshed your admin flag.", c->GetName()); + } + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) + c->Message(0, "Usage: #flag [status] [acctname]"); + + else if (c->Admin() < commandChangeFlags) { + //this check makes banning players by less than this level + //impossible, but i'll leave it in anyways + c->Message(0, "You may only refresh your own flag, doing so now."); + c->UpdateAdmin(); + } + else { + if (atoi(sep->arg[1]) > c->Admin()) + c->Message(0, "You cannot set people's status to higher than your own"); + else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) + c->Message(0, "You have too low of status to suspend/ban"); + else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) + c->Message(0, "Unable to set GM Flag."); + else { + c->Message(0, "Set GM Flag on account."); + + std::string user; + std::string loginserver; + ParseAccountString(sep->argplus[2], user, loginserver); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32*) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); + *((int16*) &pack.pBuffer[4]) = atoi(sep->arg[1]); + worldserver.SendPacket(&pack); + } + } } void command_time(Client *c, const Seperator *sep)