removal of sha_pass_hash, s and v, and everything that uses them

This commit is contained in:
Treeston
2020-08-06 18:30:30 +02:00
parent f45aa5cac1
commit 70d6aebafc
10 changed files with 30 additions and 177 deletions

View File

@@ -25,13 +25,10 @@ DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifier',
`username` varchar(32) NOT NULL DEFAULT '',
`salt` binary(32) DEFAULT NULL,
`verifier` binary(32) DEFAULT NULL,
`salt` binary(32) NOT NULL,
`verifier` binary(32) NOT NULL,
`session_key_auth` binary(40) DEFAULT NULL,
`session_key_bnet` varbinary(64) DEFAULT NULL,
`sha_pass_hash` varchar(40) NOT NULL DEFAULT '',
`v` varchar(64) NOT NULL DEFAULT 'dummy value, use `verifier` instead',
`s` varchar(64) NOT NULL DEFAULT 'dummy value, use `salt` instead',
`totp_secret` varbinary(128) DEFAULT NULL,
`email` varchar(255) NOT NULL DEFAULT '',
`reg_mail` varchar(255) NOT NULL DEFAULT '',
@@ -1972,8 +1969,9 @@ INSERT INTO `updates` VALUES
('2020_08_03_00_auth.sql','492CA77C0FAEEEF3E0492121B3A92689373ECFA3','ARCHIVED','2020-08-03 00:00:00',0),
('2020_08_03_01_auth.sql','EC1063396CA20A2303D83238470D41EF4439EC72','ARCHIVED','2020-08-03 00:00:01',0),
('2020_08_11_00_auth.sql','14C99177E43003D83A4D6F2227722F15FC15A1D0','ARCHIVED','2020-08-11 00:00:00',0),
('2020_08_15_00_auth.sql','A49F4A776E1583B1FF63DFE99BC0E0DD97A74674','ARCHIVED','2020-08-15 09:34:44',0),
('2020_08_22_00_auth.sql','060A87FCC8F836A96D9D55BEDC32CBAD05008B4C','ARCHIVED','2020-08-22 16:27:26',0);
('2020_08_15_00_auth.sql','A49F4A776E1583B1FF63DFE99BC0E0DD97A74674','ARCHIVED','2020-08-15 00:00:00',0),
('2020_08_22_00_auth.sql','060A87FCC8F836A96D9D55BEDC32CBAD05008B4C','RELEASED','2020-08-22 00:00:00',0),
('2020_09_06_00_auth.sql','DC4B5D4C65EB138D5609F137799C3289B9CC2493','RELEASED','2020-09-06 00:00:00',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;

View File

@@ -0,0 +1,8 @@
--
UPDATE `account` SET `salt`=UNHEX(CONCAT(MD5(RAND()),MD5(RAND()))), `verifier`=UNHEX(CONCAT(MD5(RAND()),MD5(RAND()))) WHERE `salt` IS NULL OR `verifier` IS NULL;
ALTER TABLE `account`
DROP COLUMN `s`,
DROP COLUMN `v`,
DROP COLUMN `sha_pass_hash`,
MODIFY COLUMN `salt` BINARY(32) NOT NULL,
MODIFY COLUMN `verifier` BINARY(32) NOT NULL;

View File

@@ -37,24 +37,15 @@ using SRP6 = Trinity::Crypto::SRP6;
return res;
}
/*static*/ std::pair<SRP6::Salt, SRP6::Verifier> SRP6::MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(SHA1::Digest const& hash)
{
std::pair<SRP6::Salt, SRP6::Verifier> res;
Crypto::GetRandomBytes(res.first);
res.second = CalculateVerifierFromHash(hash, res.first);
return res;
}
/*static*/ SRP6::Verifier SRP6::CalculateVerifier(std::string const& username, std::string const& password, SRP6::Salt const& salt)
{
// v = g ^ H(s || H(u || ':' || p)) mod N
return CalculateVerifierFromHash(SHA1::GetDigestOf(username, ":", password), salt);
}
// merge this into CalculateVerifier once the sha_pass hack finally gets nuked from orbit
/*static*/ SRP6::Verifier SRP6::CalculateVerifierFromHash(SHA1::Digest const& hash, SRP6::Salt const& salt)
{
return _g.ModExp(SHA1::GetDigestOf(salt, hash), _N).ToByteArray<32>();
return _g.ModExp(
SHA1::GetDigestOf(
salt,
SHA1::GetDigestOf(username, ":", password)
)
,_N).ToByteArray<32>();
}
/*static*/ SessionKey SRP6::SHA1Interleave(SRP6::EphemeralKey const& S)

View File

@@ -41,11 +41,6 @@ namespace Trinity::Crypto
static std::array<uint8, 1> const g;
static std::array<uint8, 32> const N;
// this is the old sha_pass_hash hack
// YOU SHOULD NEVER STORE THIS HASH, if you do you are breaking SRP6 guarantees
// use MakeRegistrationData instead
static std::pair<Salt, Verifier> MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(SHA1::Digest const& hash);
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
static std::pair<Salt, Verifier> MakeRegistrationData(std::string const& username, std::string const& password);
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
@@ -66,7 +61,6 @@ namespace Trinity::Crypto
bool _used = false; // a single instance can only be used to verify once
static Verifier CalculateVerifier(std::string const& username, std::string const& password, Salt const& salt);
static Verifier CalculateVerifierFromHash(SHA1::Digest const& hash, Salt const& salt);
static SessionKey SHA1Interleave(EphemeralKey const& S);
/* global algorithm parameters */

View File

@@ -143,8 +143,6 @@ int main(int argc, char** argv)
sSecretMgr->Initialize();
AuthSession::ServerStartup();
// Load IP Location Database
sIPLocation->Load();

View File

@@ -121,76 +121,6 @@ std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B,
#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
#define REALM_LIST_PACKET_SIZE 5
/*static*/ void AuthSession::ServerStartup()
{
TC_LOG_INFO("server.authserver", "Updating password hashes...");
uint32 const start = getMSTime();
// the auth update query nulls salt/verifier if they cannot be converted
// if they are non-null but s/v have been cleared, that means a legacy tool touched our auth DB (otherwise, the core might've done it itself, it used to use those hacks too)
QueryResult result = LoginDatabase.Query("SELECT id, sha_pass_hash, IF((salt IS null) AND (verifier IS null), 0, 1) AS shouldWarn FROM account WHERE s != DEFAULT(s) OR v != DEFAULT(v) OR salt IS NULL OR verifier IS NULL");
if (!result)
{
TC_LOG_INFO("server.authserver", ">> No password hashes to update - this took us %u ms to realize", GetMSTimeDiffToNow(start));
return;
}
bool const shouldUpdate = sConfigMgr->GetBoolDefault("AllowDeprecatedExternalPasswords", false, true);
bool hadWarning = false;
uint32 c = 0;
LoginDatabaseTransaction tx = LoginDatabase.BeginTransaction();
do
{
uint32 const id = (*result)[0].GetUInt32();
auto [salt, verifier] = Trinity::Crypto::SRP6::MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(
HexStrToByteArray<Trinity::Crypto::SHA1::DIGEST_LENGTH>((*result)[1].GetString())
);
if ((*result)[2].GetInt64())
{
if (!hadWarning)
{
hadWarning = true;
if (shouldUpdate)
{
TC_LOG_WARN("server.authserver",
" ========\n"
"(!) You appear to be using an outdated external account management tool.\n"
"(!!) This is INSECURE, has been deprecated, and will cease to function entirely on September 6, 2020.\n"
"(!) Update your external tool.\n"
"(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
" ========");
}
else
{
TC_LOG_ERROR("server.authserver",
" ========\n"
"(!) You appear to be using an outdated external account management tool.\n"
"(!!) This is INSECURE, and the account(s) in question will not be able to log in.\n"
"(!) Update your external tool.\n"
"(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
"(!) You can override this behavior by adding \"AllowDeprecatedExternalPasswords = 1\" to your authserver.conf file.\n"
"(!!) Note that this override will cease to function entirely on September 6, 2020.\n"
" ========");
}
}
if (!shouldUpdate)
continue;
}
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON);
stmt->setBinary(0, salt);
stmt->setBinary(1, verifier);
stmt->setUInt32(2, id);
tx->Append(stmt);
++c;
} while (result->NextRow());
LoginDatabase.CommitTransaction(tx);
TC_LOG_INFO("server.authserver", ">> %u password hashes updated in %u ms", c, GetMSTimeDiffToNow(start));
}
std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
{
std::unordered_map<uint8, AuthHandler> handlers;
@@ -210,8 +140,8 @@ void AccountInfo::LoadResult(Field* fields)
{
// 0 1 2 3 4 5 6
//SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate,
// 7 8 9 10 11 12
// ab.unbandate = ab.bandate, aa.SecurityLevel, a.totp_secret, a.sha_pass_hash, a.v, a.s
// 7 8
// ab.unbandate = ab.bandate, aa.SecurityLevel (, more query-specific fields)
//FROM account a LEFT JOIN account_access aa ON a.id = aa.AccountID LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?
Id = fields[0].GetUInt32();
@@ -467,51 +397,11 @@ void AuthSession::LogonChallengeCallback(PreparedQueryResult result)
}
}
if (!fields[10].IsNull())
{
if (!sConfigMgr->GetBoolDefault("AllowDeprecatedExternalPasswords", false, true))
{
TC_LOG_ERROR("server.authserver",
" ========\n"
"(!) You appear to be using an outdated external account management tool.\n"
"(!!) This is INSECURE, and the login attempt from account '%s' was BLOCKED.\n"
"(!) Update your external tool.\n"
"(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
"(!) You can override this behavior by adding \"AllowDeprecatedExternalPasswords = 1\" to your authserver.conf file.\n"
"(!!) Note that this override will cease to function entirely on September 6, 2020.\n"
" ========", _accountInfo.Login.c_str());
pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
SendPacket(pkt);
return;
}
// if this is reached, s/v were reset and we need to recalculate from sha_pass_hash
Trinity::Crypto::SHA1::Digest sha_pass_hash;
HexStrToByteArray(fields[10].GetString(), sha_pass_hash);
auto [salt, verifier] = Trinity::Crypto::SRP6::MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(sha_pass_hash);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON);
stmt->setBinary(0, salt);
stmt->setBinary(1, verifier);
stmt->setUInt32(2, _accountInfo.Id);
LoginDatabase.Execute(stmt);
TC_LOG_WARN("server.authserver",
" ========\n"
"(!) You appear to be using an outdated external account management tool.\n"
"(!!) This is INSECURE, has been deprecated, and will cease to function entirely on September 6, 2020.\n"
"(!) Update your external tool.\n"
"(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
" ========");
_srp6.emplace(_accountInfo.Login, salt, verifier);
}
else
{
Trinity::Crypto::SRP6::Salt salt = fields[11].GetBinary<Trinity::Crypto::SRP6::SALT_LENGTH>();
Trinity::Crypto::SRP6::Verifier verifier = fields[12].GetBinary<Trinity::Crypto::SRP6::VERIFIER_LENGTH>();
_srp6.emplace(_accountInfo.Login, salt, verifier);
}
_srp6.emplace(
_accountInfo.Login,
fields[10].GetBinary<Trinity::Crypto::SRP6::SALT_LENGTH>(),
fields[11].GetBinary<Trinity::Crypto::SRP6::VERIFIER_LENGTH>()
);
// Fill the response packet with the result
if (AuthHelper::IsAcceptedClientBuild(_build))

View File

@@ -65,7 +65,6 @@ class AuthSession : public Socket<AuthSession>
typedef Socket<AuthSession> AuthSocket;
public:
static void ServerStartup();
static std::unordered_map<uint8, AuthHandler> InitHandlers();
AuthSession(tcp::socket&& socket);

View File

@@ -35,11 +35,10 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username = ? GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned (id, bandate, unbandate, bannedby, banreason, active) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban', 1)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ?, s = DEFAULT, v = DEFAULT WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGON_LEGACY, "UPDATE account SET sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET session_key_auth = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
"ab.unbandate = ab.bandate, aa.SecurityLevel, a.totp_secret, IF(a.s != DEFAULT(a.s) OR a.v != DEFAULT(a.v) OR a.salt IS NULL OR a.verifier IS NULL, a.sha_pass_hash, NULL), a.salt, a.verifier "
"ab.unbandate = ab.bandate, aa.SecurityLevel, a.totp_secret, a.salt, a.verifier "
"FROM account a LEFT JOIN account_access aa ON a.id = aa.AccountID LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
"ab.unbandate = ab.bandate, aa.SecurityLevel, a.session_key_auth "
@@ -84,8 +83,8 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_GET_GMLEVEL_BY_REALMID, "SELECT SecurityLevel FROM account_access WHERE AccountID = ? AND (RealmID = ? OR RealmID = -1) ORDER BY RealmID DESC", CONNECTION_BOTH);
PrepareStatement(LOGIN_GET_USERNAME_BY_ID, "SELECT username FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ? AND salt IS NOT NULL AND verifier IS NOT NULL", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ? AND salt IS NOT NULL AND verifier IS NOT NULL", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.SecurityLevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.AccountID AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.SecurityLevel FROM account a, account_access aa WHERE a.id = aa.AccountID AND aa.SecurityLevel >= ? AND (aa.RealmID = -1 OR aa.RealmID = ?)", CONNECTION_SYNCH);

View File

@@ -39,7 +39,6 @@ enum LoginDatabaseStatements : uint32
LOGIN_INS_ACCOUNT_AUTO_BANNED,
LOGIN_DEL_ACCOUNT_BANNED,
LOGIN_UPD_LOGON,
LOGIN_UPD_LOGON_LEGACY,
LOGIN_UPD_LOGONPROOF,
LOGIN_SEL_LOGONCHALLENGE,
LOGIN_SEL_RECONNECTCHALLENGE,

View File

@@ -149,13 +149,6 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accountId)
return AccountOpResult::AOR_OK;
}
// Do not use this. Use the appropriate methods on Trinity::Crypto::SRP6 to do whatever you are trying to do.
// See issue #25157.
static std::string CalculateShaPassHash_DEPRECATED_DONOTUSE(std::string const& name, std::string const& password)
{
return ByteArrayToHexStr(Trinity::Crypto::SHA1::GetDigestOf(name, ":", password));
}
AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
{
// Check if accounts exists
@@ -187,14 +180,6 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUser
stmt->setUInt32(2, accountId);
LoginDatabase.Execute(stmt);
if (sWorld->getBoolConfig(CONFIG_SET_SHAPASSHASH))
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON_LEGACY);
stmt->setString(0, CalculateShaPassHash_DEPRECATED_DONOTUSE(newUsername, newPassword));
stmt->setUInt32(1, accountId);
LoginDatabase.Execute(stmt);
}
return AccountOpResult::AOR_OK;
}
@@ -224,14 +209,6 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
stmt->setUInt32(2, accountId);;
LoginDatabase.Execute(stmt);
if (sWorld->getBoolConfig(CONFIG_SET_SHAPASSHASH))
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON_LEGACY);
stmt->setString(0, CalculateShaPassHash_DEPRECATED_DONOTUSE(username, newPassword));
stmt->setUInt32(1, accountId);
LoginDatabase.Execute(stmt);
}
sScriptMgr->OnPasswordChange(accountId);
return AccountOpResult::AOR_OK;
}