diff options
9 files changed, 133 insertions, 355 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index babf39ec3e5..8b7baf385ce 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -45,21 +45,21 @@ void CharacterDatabaseConnection::DoPrepareStatements() "subject, deliver_time, expire_time, money, has_items FROM mail WHERE receiver = ? ", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MAIL_LIST_ITEMS, "SELECT itemEntry,count FROM item_instance WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, " - "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot " + "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time " "FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, " "c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, " - "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " + "cb.guid, c.slot, c.logout_time, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_UNDELETE_ENUM, "SELECT c.guid, c.deleteInfos_Name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, " - "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot " + "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot, c.logout_time " "FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ? AND c.deleteInfos_Name IS NOT NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_UNDELETE_ENUM_DECLINED_NAME, "SELECT c.guid, c.deleteInfos_Name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, " "c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, " - "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " + "cb.guid, c.slot, c.logout_time, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.deleteInfos_Account = ? AND c.deleteInfos_Name IS NOT NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT name, at_login FROM characters WHERE guid = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 964748a2682..2ca59c05ecc 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -39,7 +39,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, " + PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, ba.id, aa.gmLevel, " "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id " "FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id " "LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 " diff --git a/src/server/game/Server/Packets/AuthenticationPackets.cpp b/src/server/game/Server/Packets/AuthenticationPackets.cpp index 6a76f8ee3b8..648d03ec4ef 100644 --- a/src/server/game/Server/Packets/AuthenticationPackets.cpp +++ b/src/server/game/Server/Packets/AuthenticationPackets.cpp @@ -20,34 +20,39 @@ WorldPacket const* WorldPackets::Auth::AuthChallenge::Write() { - _worldPacket << uint32(Challenge); _worldPacket.append(DosChallenge, 8); + _worldPacket.append(Challenge.data(), Challenge.size()); _worldPacket << uint8(DosZeroBits); return &_worldPacket; } void WorldPackets::Auth::AuthSession::Read() { - uint32 addonDataSize; + uint32 addonDataSize, realmJoinTicketSize; - _worldPacket >> LoginServerID; _worldPacket >> Build; + _worldPacket >> BuildType; _worldPacket >> RegionID; _worldPacket >> BattlegroupID; _worldPacket >> RealmID; - _worldPacket >> LoginServerType; - _worldPacket >> BuildType; - _worldPacket >> LocalChallenge; + _worldPacket.read(LocalChallenge.data(), LocalChallenge.size()); + _worldPacket.read(Digest.data(), Digest.size()); _worldPacket >> DosResponse; - _worldPacket.read(Digest, SHA_DIGEST_LENGTH); - Account = _worldPacket.ReadString(_worldPacket.ReadBits(11)); - UseIPv6 = _worldPacket.ReadBit(); // UseIPv6 _worldPacket >> addonDataSize; if (addonDataSize) { - AddonInfo.resize(addonDataSize); - _worldPacket.read(AddonInfo.contents(), addonDataSize); + AddonInfo.resize(std::min(addonDataSize, uint32(_worldPacket.size() - _worldPacket.rpos()))); + _worldPacket.read(AddonInfo.contents(), AddonInfo.size()); + } + + _worldPacket >> realmJoinTicketSize; + if (realmJoinTicketSize) + { + RealmJoinTicket.resize(std::min(realmJoinTicketSize, uint32(_worldPacket.size() - _worldPacket.rpos()))); + _worldPacket.read(reinterpret_cast<uint8*>(&RealmJoinTicket[0]), RealmJoinTicket.size()); } + + UseIPv6 = _worldPacket.ReadBit(); // UseIPv6 } WorldPackets::Auth::AuthResponse::AuthResponse() @@ -57,7 +62,7 @@ WorldPackets::Auth::AuthResponse::AuthResponse() WorldPacket const* WorldPackets::Auth::AuthResponse::Write() { - _worldPacket << uint8(Result); + _worldPacket << uint32(Result); _worldPacket.WriteBit(SuccessInfo.is_initialized()); _worldPacket.WriteBit(WaitInfo.is_initialized()); _worldPacket.FlushBits(); @@ -294,7 +299,7 @@ WorldPacket const* WorldPackets::Auth::ConnectTo::Write() ByteBuffer payload; uint16 port = Payload.Where.port(); uint8 address[16] = { 0 }; - uint32 addressType = 3; + uint8 addressType = 3; if (Payload.Where.address().is_v4()) { memcpy(address, Payload.Where.address().to_v4().to_bytes().data(), 4); @@ -308,7 +313,7 @@ WorldPacket const* WorldPackets::Auth::ConnectTo::Write() HmacSha1 hmacHash(64, WherePacketHmac); hmacHash.UpdateData(address, 16); - hmacHash.UpdateData((uint8* const)&addressType, 4); + hmacHash.UpdateData(&addressType, 1); hmacHash.UpdateData((uint8* const)&port, 2); hmacHash.UpdateData((uint8* const)Haiku.c_str(), 71); hmacHash.UpdateData(Payload.PanamaKey, 32); @@ -316,260 +321,15 @@ WorldPacket const* WorldPackets::Auth::ConnectTo::Write() hmacHash.UpdateData(&Payload.XorMagic, 1); hmacHash.Finalize(); - uint8* hmac = hmacHash.GetDigest(); - - payload << uint8(Haiku[53]); - payload << uint8(PiDigits[12]); - payload << uint8(Haiku[27]); - payload << uint8(PiDigits[65]); - payload << uint8(PiDigits[96]); - payload << uint8(Haiku[29]); - payload << uint8(Payload.PanamaKey[12]); - payload << uint8(hmac[3]); - payload << uint8(PiDigits[10]); - payload << uint8(Payload.PanamaKey[20]); - payload << uint8(PiDigits[14]); - payload << uint8(hmac[17]); - payload << uint8(Haiku[3]); - payload << uint8(PiDigits[26]); - payload << uint8(PiDigits[94]); - payload << uint8(PiDigits[7]); - payload << uint8(PiDigits[93]); - payload << uint8(Payload.PanamaKey[8]); - payload << uint8(PiDigits[40]); - payload << uint8(PiDigits[52]); - payload << uint8(Haiku[44]); - payload << uint8(address[13]); - payload << uint8(Haiku[57]); - payload << uint8(PiDigits[59]); - payload << uint8(PiDigits[97]); - payload << uint8(PiDigits[42]); - payload << uint8(Haiku[70]); - payload << uint8(Haiku[42]); - payload << uint8(PiDigits[50]); - payload << uint8(address[3]); - payload << uint8(PiDigits[60]); - payload << uint8(address[12]); - payload << uint8(PiDigits[86]); - payload << uint8(Haiku[56]); - payload << uint8(PiDigits[70]); - payload << uint8(Haiku[26]); - payload << uint8(PiDigits[74]); - payload << uint8(PiDigits[49]); - payload << uint8(Payload.PanamaKey[28]); - payload << uint8(Haiku[69]); - payload << uint8(hmac[9]); - payload << uint8(Haiku[15]); - payload << uint8(Payload.PanamaKey[0]); - payload << uint8(Payload.PanamaKey[6]); - payload << uint8(Haiku[17]); - payload << uint8((port >> 8) & 0xFF); - payload << uint8(PiDigits[20]); - payload << uint8(PiDigits[99]); - payload << uint8(PiDigits[95]); - payload << uint8(PiDigits[100]); - payload << uint8(PiDigits[105]); - payload << uint8(hmac[13]); - payload << uint8(Payload.PanamaKey[9]); - payload << uint8(PiDigits[41]); - payload << uint8(PiDigits[35]); - payload << uint8(Haiku[10]); - payload << uint8(Haiku[20]); - payload << uint8(Haiku[14]); - payload << uint8(Haiku[47]); - payload << uint8(Payload.PanamaKey[10]); - payload << uint8(PiDigits[54]); - payload << uint8(PiDigits[67]); - payload << uint8(PiDigits[79]); - payload << uint8(Payload.PanamaKey[3]); - payload << uint8(PiDigits[84]); - payload << uint8(Haiku[33]); - payload << uint8(Haiku[23]); - payload << uint8(Haiku[48]); - payload << uint8(PiDigits[64]); - payload << uint8(PiDigits[85]); - payload << uint8(Payload.PanamaKey[24]); - payload << uint8(hmac[8]); - payload << uint8(PiDigits[101]); - payload << uint8(addressType); - payload << uint8(PiDigits[53]); - payload << uint8(Haiku[39]); - payload << uint8(hmac[19]); - payload << uint8(Payload.PanamaKey[31]); - payload << uint8(Payload.PanamaKey[18]); - payload << uint8(hmac[4]); - payload << uint8(Haiku[54]); - payload << uint8(PiDigits[76]); - payload << uint8(address[9]); - payload << uint8(hmac[7]); - payload << uint8(PiDigits[80]); - payload << uint8(hmac[2]); - payload << uint8(Haiku[13]); - payload << uint8(Haiku[11]); - payload << uint8(Payload.PanamaKey[7]); - payload << uint8(Haiku[64]); - payload << uint8(Haiku[62]); - payload << uint8(Haiku[9]); - payload << uint8(Payload.PanamaKey[26]); - payload << uint8(hmac[1]); - payload << uint8(hmac[15]); - payload << uint8(PiDigits[25]); - payload << uint8(hmac[14]); - payload << uint8(Haiku[35]); - payload << uint8(Haiku[43]); - payload << uint8(PiDigits[58]); - payload << uint8(Payload.PanamaKey[1]); - payload << uint8(PiDigits[45]); - payload << uint8(Payload.PanamaKey[25]); - payload << uint8(PiDigits[69]); - payload << uint8(PiDigits[27]); - payload << uint8(address[4]); - payload << uint8(PiDigits[11]); - payload << uint8(Payload.PanamaKey[16]); - payload << uint8(PiDigits[38]); - payload << uint8(PiDigits[82]); - payload << uint8(PiDigits[1]); - payload << uint8(PiDigits[103]); - payload << uint8(address[10]); - payload << uint8(Haiku[18]); - payload << uint8(PiDigits[91]); - payload << uint8(PiDigits[75]); - payload << uint8(PiDigits[34]); - payload << uint8(Payload.PanamaKey[27]); - payload << uint8(PiDigits[106]); - payload << uint8(PiDigits[28]); - payload << uint8(Haiku[8]); - payload << uint8(PiDigits[5]); - payload << uint8(Haiku[68]); - payload << uint8(Payload.PanamaKey[15]); - payload << uint8(address[2]); - payload << uint8(PiDigits[48]); - payload << uint8(PiDigits[107]); - payload << uint8(Payload.PanamaKey[17]); - payload << uint8(PiDigits[15]); payload << uint32(Payload.Adler32); - payload << uint8(PiDigits[36]); - payload << uint8(address[5]); - payload << uint8(PiDigits[71]); - payload << uint8(address[15]); - payload << uint8(address[0]); - payload << uint8(hmac[5]); - payload << uint8(PiDigits[83]); - payload << uint8(Payload.PanamaKey[14]); - payload << uint8(PiDigits[55]); - payload << uint8(PiDigits[31]); - payload << uint8(Haiku[52]); - payload << uint8(PiDigits[22]); - payload << uint8(PiDigits[8]); - payload << uint8(PiDigits[6]); - payload << uint8(Payload.PanamaKey[21]); - payload << uint8(port & 0xFF); - payload << uint8(PiDigits[51]); - payload << uint8(Haiku[31]); - payload << uint8(Haiku[59]); - payload << uint8(PiDigits[17]); - payload << uint8(PiDigits[88]); - payload << uint8(PiDigits[32]); - payload << uint8(address[6]); - payload << uint8(Haiku[49]); - payload << uint8(PiDigits[21]); - payload << uint8(Payload.PanamaKey[13]); - payload << uint8(address[1]); - payload << uint8(Haiku[19]); - payload << uint8(Haiku[63]); - payload << uint8(PiDigits[4]); - payload << uint8(Haiku[65]); - payload << uint8(PiDigits[39]); - payload << uint8(Haiku[21]); - payload << uint8(Haiku[32]); - payload << uint8(Haiku[25]); - payload << uint8(Haiku[1]); - payload << uint8(PiDigits[9]); - payload << uint8(Haiku[2]); - payload << uint8(Payload.PanamaKey[22]); - payload << uint8(PiDigits[78]); - payload << uint8(Haiku[4]); - payload << uint8(Haiku[50]); - payload << uint8(Payload.PanamaKey[29]); - payload << uint8(hmac[12]); - payload << uint8(PiDigits[56]); - payload << uint8(address[11]); - payload << uint8(PiDigits[102]); - payload << uint8(Payload.PanamaKey[19]); - payload << uint8(PiDigits[33]); - payload << uint8(Haiku[30]); - payload << uint8(Payload.PanamaKey[5]); - payload << uint8(PiDigits[46]); - payload << uint8(PiDigits[30]); - payload << uint8(PiDigits[98]); - payload << uint8(Haiku[66]); - payload << uint8(PiDigits[77]); - payload << uint8(PiDigits[63]); - payload << uint8(Payload.PanamaKey[11]); - payload << uint8(PiDigits[2]); - payload << uint8(Haiku[61]); - payload << uint8(address[7]); - payload << uint8(PiDigits[3]); - payload << uint8(Haiku[12]); - payload << uint8(Haiku[40]); - payload << uint8(PiDigits[57]); - payload << uint8(Haiku[28]); - payload << uint8(PiDigits[29]); - payload << uint8(Haiku[46]); - payload << uint8(PiDigits[44]); - payload << uint8(address[14]); - payload << uint8(PiDigits[16]); - payload << uint8(PiDigits[87]); - payload << uint8(PiDigits[81]); - payload << uint8(hmac[18]); - payload << uint8(address[8]); - payload << uint8(Haiku[6]); - payload << uint8(hmac[16]); - payload << uint8(PiDigits[62]); - payload << uint8(PiDigits[72]); - payload << uint8(PiDigits[18]); - payload << uint8(PiDigits[43]); - payload << uint8(Haiku[7]); - payload << uint8(Haiku[37]); + payload << uint8(addressType); + payload.append(address, 16); + payload << uint16(port); + payload.append(Haiku.data(), 71); + payload.append(Payload.PanamaKey, 32); + payload.append(PiDigits, 108); payload << uint8(Payload.XorMagic); - payload << uint8(Payload.PanamaKey[2]); - payload << uint8(PiDigits[92]); - payload << uint8(PiDigits[47]); - payload << uint8(PiDigits[13]); - payload << uint8(PiDigits[66]); - payload << uint8(Haiku[55]); - payload << uint8(PiDigits[73]); - payload << uint8(Haiku[5]); - payload << uint8(PiDigits[0]); - payload << uint8(Haiku[16]); - payload << uint8(Haiku[34]); - payload << uint8(hmac[10]); - payload << uint8(PiDigits[89]); - payload << uint8(Haiku[38]); - payload << uint8(Haiku[22]); - payload << uint8(hmac[11]); - payload << uint8(PiDigits[61]); - payload << uint8(Haiku[0]); - payload << uint8(hmac[0]); - payload << uint8(Haiku[24]); - payload << uint8(PiDigits[37]); - payload << uint8(hmac[6]); - payload << uint8(Haiku[41]); - payload << uint8(Haiku[60]); - payload << uint8(PiDigits[90]); - payload << uint8(Haiku[67]); - payload << uint8(Haiku[51]); - payload << uint8(PiDigits[24]); - payload << uint8(PiDigits[23]); - payload << uint8(Haiku[58]); - payload << uint8(Haiku[45]); - payload << uint8(PiDigits[104]); - payload << uint8(PiDigits[68]); - payload << uint8(Payload.PanamaKey[30]); - payload << uint8(Payload.PanamaKey[23]); - payload << uint8(Payload.PanamaKey[4]); - payload << uint8(Haiku[36]); - payload << uint8(PiDigits[19]); + payload.append(hmacHash.GetDigest(), hmacHash.GetLength()); BigNumber bnData; bnData.SetBinary(payload.contents(), payload.size()); @@ -594,7 +354,8 @@ void WorldPackets::Auth::AuthContinuedSession::Read() { _worldPacket >> DosResponse; _worldPacket >> Key; - _worldPacket.read(Digest, SHA_DIGEST_LENGTH); + _worldPacket.read(LocalChallenge.data(), LocalChallenge.size()); + _worldPacket.read(Digest.data(), Digest.size()); } void WorldPackets::Auth::ConnectToFailed::Read() diff --git a/src/server/game/Server/Packets/AuthenticationPackets.h b/src/server/game/Server/Packets/AuthenticationPackets.h index 123d4a24f10..5bd433ebb57 100644 --- a/src/server/game/Server/Packets/AuthenticationPackets.h +++ b/src/server/game/Server/Packets/AuthenticationPackets.h @@ -38,7 +38,7 @@ namespace WorldPackets WorldPacket const* Write() override; - uint32 Challenge = 0; + std::array<uint8, 16> Challenge; uint32 DosChallenge[8]; ///< Encryption seeds uint8 DosZeroBits = 0; }; @@ -46,26 +46,27 @@ namespace WorldPackets class AuthSession final : public ClientPacket { public: + static uint32 const DigestLength = 24; + AuthSession(WorldPacket&& packet) : ClientPacket(CMSG_AUTH_SESSION, std::move(packet)) { - memset(Digest, 0, SHA_DIGEST_LENGTH); + LocalChallenge.fill(0); + Digest.fill(0); } void Read() override; - uint32 BattlegroupID = 0; - int8 LoginServerType = 0; ///< Auth type used - 0 GRUNT, 1 battle.net - int8 BuildType = 0; - uint32 RealmID = 0; uint16 Build = 0; - uint32 LocalChallenge = 0; - int32 LoginServerID = 0; + int8 BuildType = 0; uint32 RegionID = 0; + uint32 BattlegroupID = 0; + uint32 RealmID = 0; + std::array<uint8, 16> LocalChallenge; + std::array<uint8, DigestLength> Digest; uint64 DosResponse = 0; - uint8 Digest[SHA_DIGEST_LENGTH]; - std::string Account; - bool UseIPv6 = false; ByteBuffer AddonInfo; + std::string RealmJoinTicket; + bool UseIPv6 = false; }; class AuthResponse final : public ServerPacket @@ -121,7 +122,7 @@ namespace WorldPackets Optional<AuthSuccessInfo> SuccessInfo; ///< contains the packet data in case that it has account information (It is never set when WaitInfo is set), otherwise its contents are undefined. Optional<AuthWaitInfo> WaitInfo; ///< contains the queue wait information in case the account is in the login queue. - uint8 Result = 0; ///< the result of the authentication process, it is AUTH_OK if it succeeded and the account is ready to log in. It can also be AUTH_WAIT_QUEUE if the account entered the login queue (Queued, QueuePos), possible values are @ref ResponseCodes + uint32 Result = 0; ///< the result of the authentication process, it is AUTH_OK if it succeeded and the account is ready to log in. It can also be AUTH_WAIT_QUEUE if the account entered the login queue (Queued, QueuePos), possible values are @ref ResponseCodes }; enum class ConnectToSerial : uint32 @@ -169,16 +170,20 @@ namespace WorldPackets class AuthContinuedSession final : public ClientPacket { public: + static uint32 const DigestLength = 24; + AuthContinuedSession(WorldPacket&& packet) : ClientPacket(CMSG_AUTH_CONTINUED_SESSION, std::move(packet)) { - memset(Digest, 0, SHA_DIGEST_LENGTH); + LocalChallenge.fill(0); + Digest.fill(0); } void Read() override; uint64 DosResponse = 0; uint64 Key = 0; - uint8 Digest[SHA_DIGEST_LENGTH]; + std::array<uint8, 16> LocalChallenge; + std::array<uint8, DigestLength> Digest; }; class ResumeComms final : public ServerPacket diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index d54402d40c7..d30779d49c2 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -28,8 +28,8 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel // "characters.hairColor, characters.facialStyle, characters.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, " // 16 17 18 19 20 21 22 // "guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, " - // 23 24 25 - // "character_banned.guid, characters.slot, character_declinedname.genitive" + // 23 24 25 26 + // "character_banned.guid, characters.slot, characters.logout_time, character_declinedname.genitive" Guid = ObjectGuid::Create<HighGuid::Player>(fields[0].GetUInt64()); Name = fields[1].GetString(); @@ -72,7 +72,7 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel if (fields[23].GetUInt64()) Flags |= CHARACTER_FLAG_LOCKED_BY_BILLING; - if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) && !fields[25].GetString().empty()) + if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED) && !fields[26].GetString().empty()) Flags |= CHARACTER_FLAG_DECLINED; if (atLoginFlags & AT_LOGIN_CUSTOMIZE) @@ -102,6 +102,7 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel Tokenizer equipment(fields[22].GetString(), ' '); ListPosition = fields[24].GetUInt8(); + LastPlayedTime = fields[25].GetUInt32(); for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot) { @@ -155,6 +156,7 @@ WorldPacket const* WorldPackets::Character::EnumCharactersResult::Write() _worldPacket << uint8(charInfo.VisualItems[slot].InventoryType); } + _worldPacket << uint32(charInfo.LastPlayedTime); _worldPacket.WriteBits(charInfo.Name.length(), 6); _worldPacket.WriteBit(charInfo.FirstLogin); _worldPacket.WriteBit(charInfo.BoostInProgress); diff --git a/src/server/game/Server/Packets/CharacterPackets.h b/src/server/game/Server/Packets/CharacterPackets.h index bb163b075cc..017dddd1d06 100644 --- a/src/server/game/Server/Packets/CharacterPackets.h +++ b/src/server/game/Server/Packets/CharacterPackets.h @@ -133,6 +133,7 @@ namespace WorldPackets uint32 Flags3 = 0; ///< Character flags 3 @todo research bool FirstLogin = false; uint8 unkWod61x = 0; + uint32 LastPlayedTime = 0; struct PetInfo { @@ -721,7 +722,7 @@ namespace WorldPackets { public: SetPlayerDeclinedNames(WorldPacket&& packet) : ClientPacket(CMSG_SET_PLAYER_DECLINED_NAMES, std::move(packet)) { } - + void Read() override; ObjectGuid Player; diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index fe60f15a3f7..19aff2468a9 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -846,8 +846,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_WON_NOTIFICATION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURA_POINTS_DEPLETED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURA_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_CHALLENGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_CHALLENGE, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AVAILABLE_VOICE_CHANNEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BAN_REASON, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BARBER_SHOP_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1048,7 +1048,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENCHANTMENT_LOG, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENCOUNTER_END, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENCOUNTER_START, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENUM_CHARACTERS_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENUM_CHARACTERS_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ENVIRONMENTAL_DAMAGE_LOG, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_EQUIPMENT_SET_ID, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_EXPECTED_SPAM_RECORDS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 8cf122f77d8..84561dbd37e 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -20,10 +20,12 @@ #include "AuthenticationPackets.h" #include "BigNumber.h" #include "CharacterPackets.h" +#include "HmacHash.h" #include "Opcodes.h" -#include "ScriptMgr.h" -#include "SHA1.h" #include "PacketLog.h" +#include "ScriptMgr.h" +#include "SessionKeyGeneration.h" +#include "SHA256.h" #include "World.h" #include <zlib.h> @@ -61,10 +63,15 @@ uint32 const WorldSocket::MinSizeForCompression = 0x400; uint32 const SizeOfClientHeader[2] = { sizeof(uint16) + sizeof(uint16), sizeof(uint32) + sizeof(uint16) }; uint32 const SizeOfServerHeader[2] = { sizeof(uint16) + sizeof(uint16), sizeof(uint32) + sizeof(uint16) }; +uint8 const WorldSocket::AuthCheckSeed[16] = { 0xC5, 0xC6, 0x98, 0x95, 0x76, 0x3F, 0x1D, 0xCD, 0xB6, 0xA1, 0x37, 0x28, 0xB3, 0x12, 0xFF, 0x8A }; +uint8 const WorldSocket::SessionKeySeed[16] = { 0x58, 0xCB, 0xCF, 0x40, 0xFE, 0x2E, 0xCE, 0xA6, 0x5A, 0x90, 0xB8, 0x01, 0x68, 0x6C, 0x28, 0x0B }; +uint8 const WorldSocket::ContinuedSessionSeed[16] = { 0x16, 0xAD, 0x0C, 0xD4, 0x46, 0xF9, 0x4F, 0xB2, 0xEF, 0x7D, 0xEA, 0x2A, 0x17, 0x66, 0x4D, 0x2F }; + WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket)), - _type(CONNECTION_TYPE_REALM), _authSeed(rand32()), _OverSpeedPings(0), + _type(CONNECTION_TYPE_REALM), _OverSpeedPings(0), _worldSession(nullptr), _authed(false), _compressionStream(nullptr) { + _serverChallenge.SetRand(8 * 16); _headerBuffer.Resize(SizeOfClientHeader[0]); } @@ -244,7 +251,7 @@ void WorldSocket::HandleSendAuthSession() _decryptSeed.SetRand(16 * 8); WorldPackets::Auth::AuthChallenge challenge; - challenge.Challenge = _authSeed; + memcpy(challenge.Challenge.data(), _serverChallenge.AsByteArray(16).get(), 16); memcpy(&challenge.DosChallenge[0], _encryptSeed.AsByteArray(16).get(), 16); memcpy(&challenge.DosChallenge[4], _decryptSeed.AsByteArray(16).get(), 16); challenge.DosZeroBits = 1; @@ -399,10 +406,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() } std::shared_ptr<WorldPackets::Auth::AuthSession> authSession = std::make_shared<WorldPackets::Auth::AuthSession>(std::move(packet)); - //authSession->Read(); - //HandleAuthSession(authSession); - //return ReadDataHandlerResult::WaitingForQuery; - return ReadDataHandlerResult::Error; + authSession->Read(); + HandleAuthSession(authSession); + return ReadDataHandlerResult::WaitingForQuery; } case CMSG_AUTH_CONTINUED_SESSION: { @@ -416,10 +422,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() } std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession = std::make_shared<WorldPackets::Auth::AuthContinuedSession>(std::move(packet)); - //authSession->Read(); - //HandleAuthContinuedSession(authSession); - //return ReadDataHandlerResult::WaitingForQuery; - return ReadDataHandlerResult::Error; + authSession->Read(); + HandleAuthContinuedSession(authSession); + return ReadDataHandlerResult::WaitingForQuery; } case CMSG_KEEP_ALIVE: LogOpcodeText(opcode, sessionGuard); @@ -594,7 +599,6 @@ struct AccountInfo std::string LastIP; std::string LockCountry; LocaleConstant Locale; - std::string OS; bool IsBanned; } BattleNet; @@ -606,6 +610,7 @@ struct AccountInfo uint8 Expansion; int64 MuteTime; uint32 Recruiter; + std::string OS; bool IsRectuiter; AccountTypes Security; bool IsBanned; @@ -615,8 +620,8 @@ struct AccountInfo explicit AccountInfo(Field* fields) { - // 0 1 2 3 4 5 6 7 8 9 10 11 - // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, + // 0 1 2 3 4 5 6 7 8 9 10 11 + // SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, ba.id, aa.gmLevel, // 12 13 14 // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) @@ -631,7 +636,7 @@ struct AccountInfo Game.MuteTime = fields[6].GetInt64(); BattleNet.Locale = LocaleConstant(fields[7].GetUInt8()); Game.Recruiter = fields[8].GetUInt32(); - BattleNet.OS = fields[9].GetString(); + Game.OS = fields[9].GetString(); BattleNet.Id = fields[10].GetUInt32(); Game.Security = AccountTypes(fields[11].GetUInt8()); BattleNet.IsBanned = fields[12].GetUInt64() != 0; @@ -655,7 +660,7 @@ void WorldSocket::HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSess // Get the account information from the auth database PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); stmt->setInt32(0, int32(realm.Id.Realm)); - stmt->setString(1, authSession->Account); + stmt->setString(1, authSession->RealmJoinTicket); _queryCallback = std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1); _queryFuture = LoginDatabase.AsyncQuery(stmt); @@ -667,7 +672,6 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: if (!result) { // We can not log here, as we do not know the account. Thus, no accountId. - SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); DelayedCloseSocket(); return; @@ -678,16 +682,43 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: // For hook purposes, we get Remoteaddress at this point. std::string address = GetRemoteIpAddress().to_string(); + HmacSha256 hmac(SHA256_DIGEST_LENGTH, account.Game.SessionKey.AsByteArray(SHA256_DIGEST_LENGTH).get()); + hmac.UpdateData(authSession->LocalChallenge.data(), authSession->LocalChallenge.size()); + hmac.UpdateData(_serverChallenge.AsByteArray(16).get(), 16); + hmac.UpdateData(AuthCheckSeed, 16); + hmac.Finalize(); + + if (memcmp(hmac.GetDigest(), authSession->Digest.data(), authSession->Digest.size()) != 0) + { + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", account.Game.Id, authSession->RealmJoinTicket.c_str(), address.c_str()); + DelayedCloseSocket(); + return; + } + + HmacSha256 sessionKeyHmac(SHA256_DIGEST_LENGTH, account.Game.SessionKey.AsByteArray(SHA256_DIGEST_LENGTH).get()); + sessionKeyHmac.UpdateData(_serverChallenge.AsByteArray(16).get(), 16); + sessionKeyHmac.UpdateData(authSession->LocalChallenge.data(), authSession->LocalChallenge.size()); + sessionKeyHmac.UpdateData(SessionKeySeed, 16); + sessionKeyHmac.Finalize(); + + uint8 sessionKey[40]; + SessionKeyGenerator<SHA256Hash> sessionKeyGenerator(sessionKeyHmac.GetDigest(), sessionKeyHmac.GetLength()); + sessionKeyGenerator.Generate(sessionKey, 40); + + BigNumber K; + K.SetBinary(sessionKey, 40); + + // Check that Key and account name are the same on client and server + // only do this after verifying credentials - no error message is better than bad encryption making the client crash + _authCrypt.Init(&K); + // As we don't know if attempted login process by ip works, we update last_attempt_ip right away PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); stmt->setString(0, address); - stmt->setString(1, authSession->Account); + stmt->setString(1, authSession->RealmJoinTicket); LoginDatabase.Execute(stmt); // This also allows to check for possible "hack" attempts on account - // even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it - _authCrypt.Init(&account.Game.SessionKey); - // First reject the connection if packet contains invalid data or realm state doesn't allow logging in if (sWorld->IsClosed()) { @@ -708,37 +739,10 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: // Must be done before WorldSession is created bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); - if (wardenActive && account.BattleNet.OS != "Win" && account.BattleNet.OS != "Wn64" && account.BattleNet.OS != "Mc64") - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), account.BattleNet.OS.c_str()); - DelayedCloseSocket(); - return; - } - - if (!account.BattleNet.Id || authSession->LoginServerType != 1) + if (wardenActive && account.Game.OS != "Win" && account.Game.OS != "Wn64" && account.Game.OS != "Mc64") { SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s (%s) attempted to log in using deprecated login method (GRUNT).", authSession->Account.c_str(), address.c_str()); - DelayedCloseSocket(); - return; - } - - // Check that Key and account name are the same on client and server - uint32 t = 0; - - SHA1Hash sha; - sha.UpdateData(authSession->Account); - sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&authSession->LocalChallenge, 4); - sha.UpdateData((uint8*)&_authSeed, 4); - sha.UpdateBigNumbers(&account.Game.SessionKey, NULL); - sha.Finalize(); - - if (memcmp(sha.GetDigest(), authSession->Digest, SHA_DIGEST_LENGTH) != 0) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", account.Game.Id, authSession->Account.c_str(), address.c_str()); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), account.Game.OS.c_str()); DelayedCloseSocket(); return; } @@ -802,13 +806,13 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: return; } - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", authSession->Account.c_str(), address.c_str()); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", authSession->RealmJoinTicket.c_str(), address.c_str()); // Update the last_ip in the database as it was successful for login stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); stmt->setString(0, address); - stmt->setString(1, authSession->Account); + stmt->setString(1, authSession->RealmJoinTicket); LoginDatabase.Execute(stmt); @@ -816,13 +820,13 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: sScriptMgr->OnAccountLogin(account.Game.Id); _authed = true; - _worldSession = new WorldSession(account.Game.Id, std::move(authSession->Account), account.BattleNet.Id, shared_from_this(), account.Game.Security, + _worldSession = new WorldSession(account.Game.Id, std::move(authSession->RealmJoinTicket), account.BattleNet.Id, shared_from_this(), account.Game.Security, account.Game.Expansion, mutetime, account.BattleNet.Locale, account.Game.Recruiter, account.Game.IsRectuiter); _worldSession->ReadAddonsInfo(authSession->AddonInfo); // Initialize Warden system only if it is enabled by config if (wardenActive) - _worldSession->InitWarden(&account.Game.SessionKey, account.BattleNet.OS); + _worldSession->InitWarden(&account.Game.SessionKey, account.Game.OS); _queryCallback = std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1); _queryFuture = _worldSession->LoadPermissionsAsync(); @@ -881,13 +885,14 @@ void WorldSocket::HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPacket _authCrypt.Init(&k, _encryptSeed.AsByteArray().get(), _decryptSeed.AsByteArray().get()); - SHA1Hash sha; - sha.UpdateData(login); - sha.UpdateBigNumbers(&k, NULL); - sha.UpdateData((uint8*)&_authSeed, 4); - sha.Finalize(); + HmacSha256 hmac(40, k.AsByteArray(40).get()); + hmac.UpdateData(reinterpret_cast<uint8 const*>(&authSession->Key), sizeof(authSession->Key)); + hmac.UpdateData(authSession->LocalChallenge.data(), authSession->LocalChallenge.size()); + hmac.UpdateData(_serverChallenge.AsByteArray(16).get(), 16); + hmac.UpdateData(ContinuedSessionSeed, 16); + hmac.Finalize(); - if (memcmp(sha.GetDigest(), authSession->Digest, sha.GetLength())) + if (memcmp(hmac.GetDigest(), authSession->Digest.data(), authSession->Digest.size())) { SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); TC_LOG_ERROR("network", "WorldSocket::HandleAuthContinuedSession: Authentication failed for account: %u ('%s') address: %s", accountId, login.c_str(), GetRemoteIpAddress().to_string().c_str()); diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index a77ce0d55a1..a5593e83521 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -74,6 +74,10 @@ class TC_GAME_API WorldSocket : public Socket<WorldSocket> static std::string const ClientConnectionInitialize; static uint32 const MinSizeForCompression; + static uint8 const AuthCheckSeed[16]; + static uint8 const SessionKeySeed[16]; + static uint8 const ContinuedSessionSeed[16]; + typedef Socket<WorldSocket> BaseSocket; public: @@ -132,7 +136,7 @@ private: ConnectionType _type; - uint32 _authSeed; + BigNumber _serverChallenge; WorldPacketCrypt _authCrypt; BigNumber _encryptSeed; BigNumber _decryptSeed; |