[8445] Fixed redundant calculation of v and s on every login

The SRP-6 specifications clearly say, that v and s are only to be
calculated on registering a user and changing his password; calculating
them on every login is plain waste.
This commit is contained in:
arrai 2009-09-01 02:31:36 +02:00
parent 4d0088e7a9
commit 3b1b68595b
8 changed files with 47 additions and 60 deletions

View file

@ -142,7 +142,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd)
normalizeString(new_passwd); normalizeString(new_passwd);
loginDatabase.escape_string(new_passwd); loginDatabase.escape_string(new_passwd);
if(!loginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1("_CONCAT3_("username","':'","'%s'")") WHERE id='%d'", new_passwd.c_str(), accid)) // also reset s and v to force update at next realmd login
if(!loginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash=SHA1("_CONCAT3_("username","':'","'%s'")") WHERE id='%d'", new_passwd.c_str(), accid))
return AOR_DB_INTERNAL_ERROR; // unexpected error return AOR_DB_INTERNAL_ERROR; // unexpected error
return AOR_OK; return AOR_OK;

View file

@ -119,9 +119,6 @@ class CharacterHandler
void WorldSession::HandleCharEnum(QueryResult * result) void WorldSession::HandleCharEnum(QueryResult * result)
{ {
// keys can be non cleared if player open realm list and close it by 'cancel'
loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId());
WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
uint8 num = 0; uint8 num = 0;

View file

@ -760,7 +760,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
LocaleConstant locale; LocaleConstant locale;
std::string account; std::string account;
Sha1Hash sha1; Sha1Hash sha1;
BigNumber v, s, g, N, x, I; BigNumber v, s, g, N;
WorldPacket packet, SendAddonPacked; WorldPacket packet, SendAddonPacked;
BigNumber K; BigNumber K;
@ -792,12 +792,11 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
"sessionkey, " //2 "sessionkey, " //2
"last_ip, " //3 "last_ip, " //3
"locked, " //4 "locked, " //4
"sha_pass_hash, " //5 "v, " //5
"v, " //6 "s, " //6
"s, " //7 "expansion, " //7
"expansion, " //8 "mutetime, " //8
"mutetime, " //9 "locale " //9
"locale " //10
"FROM account " "FROM account "
"WHERE username = '%s'", "WHERE username = '%s'",
safe_account.c_str ()); safe_account.c_str ());
@ -816,57 +815,21 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
Field* fields = result->Fetch (); Field* fields = result->Fetch ();
expansion = ((sWorld.getConfig(CONFIG_EXPANSION) > fields[8].GetUInt8()) ? fields[8].GetUInt8() : sWorld.getConfig(CONFIG_EXPANSION)); expansion = ((sWorld.getConfig(CONFIG_EXPANSION) > fields[7].GetUInt8()) ? fields[7].GetUInt8() : sWorld.getConfig(CONFIG_EXPANSION));
N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
g.SetDword (7); g.SetDword (7);
I.SetHexStr (fields[5].GetString ());
//In case of leading zeros in the I hash, restore them v.SetHexStr(fields[5].GetString());
uint8 mDigest[SHA_DIGEST_LENGTH]; s.SetHexStr (fields[6].GetString ());
memset (mDigest, 0, SHA_DIGEST_LENGTH);
if (I.GetNumBytes () <= SHA_DIGEST_LENGTH)
memcpy (mDigest, I.AsByteArray (), I.GetNumBytes ());
std::reverse (mDigest, mDigest + SHA_DIGEST_LENGTH);
s.SetHexStr (fields[7].GetString ());
sha1.UpdateData (s.AsByteArray (), s.GetNumBytes ());
sha1.UpdateData (mDigest, SHA_DIGEST_LENGTH);
sha1.Finalize ();
x.SetBinary (sha1.GetDigest (), sha1.GetLength ());
v = g.ModExp (x, N);
const char* sStr = s.AsHexStr (); //Must be freed by OPENSSL_free() const char* sStr = s.AsHexStr (); //Must be freed by OPENSSL_free()
const char* vStr = v.AsHexStr (); //Must be freed by OPENSSL_free() const char* vStr = v.AsHexStr (); //Must be freed by OPENSSL_free()
const char* vold = fields[6].GetString ();
DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v_old: %s v_new: %s", DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v: %s",
sStr, sStr,
vold,
vStr); vStr);
loginDatabase.PExecute ("UPDATE account "
"SET "
"v = '0', "
"s = '0' "
"WHERE username = '%s'",
safe_account.c_str ());
if (!vold || strcmp (vStr, vold))
{
packet.Initialize (SMSG_AUTH_RESPONSE, 1);
packet << uint8 (AUTH_UNKNOWN_ACCOUNT);
SendPacket (packet);
delete result;
OPENSSL_free ((void*) sStr);
OPENSSL_free ((void*) vStr);
sLog.outBasic ("WorldSocket::HandleAuthSession: User not logged.");
return -1;
}
OPENSSL_free ((void*) sStr); OPENSSL_free ((void*) sStr);
OPENSSL_free ((void*) vStr); OPENSSL_free ((void*) vStr);
@ -892,9 +855,9 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
K.SetHexStr (fields[2].GetString ()); K.SetHexStr (fields[2].GetString ());
time_t mutetime = time_t (fields[9].GetUInt64 ()); time_t mutetime = time_t (fields[8].GetUInt64 ());
locale = LocaleConstant (fields[10].GetUInt8 ()); locale = LocaleConstant (fields[9].GetUInt8 ());
if (locale >= MAX_LOCALE) if (locale >= MAX_LOCALE)
locale = LOCALE_enUS; locale = LOCALE_enUS;

View file

@ -246,7 +246,6 @@ void AuthSocket::OnAccept()
sLog.outBasic("Accepting connection from '%s:%d'", sLog.outBasic("Accepting connection from '%s:%d'",
GetRemoteAddress().c_str(), GetRemotePort()); GetRemoteAddress().c_str(), GetRemotePort());
s.SetRand(s_BYTE_SIZE * 8);
} }
/// Read the packet from the client /// Read the packet from the client
@ -295,6 +294,8 @@ void AuthSocket::OnRead()
/// Make the SRP6 calculation from hash in dB /// Make the SRP6 calculation from hash in dB
void AuthSocket::_SetVSFields(const std::string& rI) void AuthSocket::_SetVSFields(const std::string& rI)
{ {
s.SetRand(s_BYTE_SIZE * 8);
BigNumber I; BigNumber I;
I.SetHexStr(rI.c_str()); I.SetHexStr(rI.c_str());
@ -396,7 +397,7 @@ bool AuthSocket::_HandleLogonChallenge()
///- Get the account details from the account table ///- Get the account details from the account table
// No SQL injection (escaped user name) // No SQL injection (escaped user name)
result = loginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel FROM account WHERE username = '%s'",_safelogin.c_str ()); result = loginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '%s'",_safelogin.c_str ());
if( result ) if( result )
{ {
///- If the IP is 'locked', check that the player comes indeed from the correct IP address ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
@ -446,7 +447,21 @@ bool AuthSocket::_HandleLogonChallenge()
{ {
///- Get the password from the account table, upper it, and make the SRP6 calculation ///- Get the password from the account table, upper it, and make the SRP6 calculation
std::string rI = (*result)[0].GetCppString(); std::string rI = (*result)[0].GetCppString();
_SetVSFields(rI);
///- Don't calculate (v, s) if there are already some in the database
std::string databaseV = (*result)[5].GetCppString();
std::string databaseS = (*result)[6].GetCppString();
sLog.outDebug("database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());
// multiply with 2, bytes are stored as hexstring
if(databaseV.size() != s_BYTE_SIZE*2 || databaseS.size() != s_BYTE_SIZE*2)
_SetVSFields(rI);
else
{
s.SetHexStr(databaseS.c_str());
v.SetHexStr(databaseV.c_str());
}
b.SetRand(19 * 8); b.SetRand(19 * 8);
BigNumber gmod = g.ModExp(b, N); BigNumber gmod = g.ModExp(b, N);
@ -591,8 +606,13 @@ bool AuthSocket::_HandleLogonProof()
///- Continue the SRP6 calculation based on data received from the client ///- Continue the SRP6 calculation based on data received from the client
BigNumber A; BigNumber A;
A.SetBinary(lp.A, 32); A.SetBinary(lp.A, 32);
// SRP safeguard: abort if A==0
if (A.isZero())
return false;
Sha1Hash sha; Sha1Hash sha;
sha.UpdateBigNumbers(&A, &B, NULL); sha.UpdateBigNumbers(&A, &B, NULL);
sha.Finalize(); sha.Finalize();
@ -603,7 +623,7 @@ bool AuthSocket::_HandleLogonProof()
uint8 t[32]; uint8 t[32];
uint8 t1[16]; uint8 t1[16];
uint8 vK[40]; uint8 vK[40];
memcpy(t, S.AsByteArray(), 32); memcpy(t, S.AsByteArray(32), 32);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
t1[i] = t[i * 2]; t1[i] = t[i * 2];
@ -905,8 +925,6 @@ bool AuthSocket::_HandleRealmList()
SendBuf((char const*)hdr.contents(), hdr.size()); SendBuf((char const*)hdr.contents(), hdr.size());
// Set check field before possible relogin to realm
_SetVSFields(rI);
return true; return true;
} }

View file

@ -145,6 +145,7 @@ extern int main(int argc, char **argv)
sLog.outError("Could not find configuration file %s.", cfg_file); sLog.outError("Could not find configuration file %s.", cfg_file);
return 1; return 1;
} }
sLog.Initialize();
sLog.outString( "%s [realm-daemon]", _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_NR,REVISION_ID) ); sLog.outString( "%s [realm-daemon]", _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_NR,REVISION_ID) );
sLog.outString( "<Ctrl-C> to stop.\n" ); sLog.outString( "<Ctrl-C> to stop.\n" );

View file

@ -159,6 +159,11 @@ uint32 BigNumber::AsDword()
return (uint32)BN_get_word(_bn); return (uint32)BN_get_word(_bn);
} }
bool BigNumber::isZero() const
{
return BN_is_zero(_bn)!=0;
}
uint8 *BigNumber::AsByteArray(int minSize) uint8 *BigNumber::AsByteArray(int minSize)
{ {
int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes(); int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes();

View file

@ -72,6 +72,8 @@ class BigNumber
return t %= bn; return t %= bn;
} }
bool isZero() const;
BigNumber ModExp(const BigNumber &bn1, const BigNumber &bn2); BigNumber ModExp(const BigNumber &bn1, const BigNumber &bn2);
BigNumber Exp(const BigNumber &); BigNumber Exp(const BigNumber &);

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "8444" #define REVISION_NR "8445"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__