diff options
-rw-r--r-- | src/common/Cryptography/AES.cpp | 22 | ||||
-rw-r--r-- | src/common/Cryptography/AES.h | 1 | ||||
-rw-r--r-- | src/common/Cryptography/Authentication/WorldPacketCrypt.cpp | 12 | ||||
-rw-r--r-- | src/common/Cryptography/Authentication/WorldPacketCrypt.h | 1 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 25 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.h | 8 |
6 files changed, 61 insertions, 8 deletions
diff --git a/src/common/Cryptography/AES.cpp b/src/common/Cryptography/AES.cpp index 7d4ccc7c46f..1b011da7c9b 100644 --- a/src/common/Cryptography/AES.cpp +++ b/src/common/Cryptography/AES.cpp @@ -63,3 +63,25 @@ bool Trinity::Crypto::AES::Process(IV const& iv, uint8* data, size_t length, Tag return true; } + +bool Trinity::Crypto::AES::ProcessNoIntegrityCheck(IV const& iv, uint8* data, size_t partialLength) +{ + ASSERT(!_encrypting, "Partial encryption is not allowed"); + ASSERT(partialLength <= std::numeric_limits<int>::max()); + int len = static_cast<int>(partialLength); + if (!EVP_CipherInit_ex(_ctx, nullptr, nullptr, nullptr, iv.data(), -1)) + return false; + + int outLen; + if (!EVP_CipherUpdate(_ctx, data, &outLen, data, len)) + return false; + + len -= outLen; + + if (!EVP_CipherFinal_ex(_ctx, data + outLen, &outLen)) + return false; + + ASSERT(len == outLen); + + return true; +} diff --git a/src/common/Cryptography/AES.h b/src/common/Cryptography/AES.h index d3b9f48c552..e559be75a16 100644 --- a/src/common/Cryptography/AES.h +++ b/src/common/Cryptography/AES.h @@ -43,6 +43,7 @@ namespace Crypto void Init(Key const& key); bool Process(IV const& iv, uint8* data, size_t length, Tag& tag); + bool ProcessNoIntegrityCheck(IV const& iv, uint8* data, size_t partialLength); private: EVP_CIPHER_CTX* _ctx; diff --git a/src/common/Cryptography/Authentication/WorldPacketCrypt.cpp b/src/common/Cryptography/Authentication/WorldPacketCrypt.cpp index 723657048d5..926f77d757d 100644 --- a/src/common/Cryptography/Authentication/WorldPacketCrypt.cpp +++ b/src/common/Cryptography/Authentication/WorldPacketCrypt.cpp @@ -41,6 +41,18 @@ struct WorldPacketCryptIV std::array<uint8, 12> Value; }; +bool WorldPacketCrypt::PeekDecryptRecv(uint8* data, size_t length) +{ + if (_initialized) + { + WorldPacketCryptIV iv{ _clientCounter, 0x544E4C43 }; + if (!_clientDecrypt.ProcessNoIntegrityCheck(iv.Value, data, length)) + return false; + } + + return true; +} + bool WorldPacketCrypt::DecryptRecv(uint8* data, size_t length, Trinity::Crypto::AES::Tag& tag) { if (_initialized) diff --git a/src/common/Cryptography/Authentication/WorldPacketCrypt.h b/src/common/Cryptography/Authentication/WorldPacketCrypt.h index 5cd6e1282b8..600d0d1715e 100644 --- a/src/common/Cryptography/Authentication/WorldPacketCrypt.h +++ b/src/common/Cryptography/Authentication/WorldPacketCrypt.h @@ -27,6 +27,7 @@ public: WorldPacketCrypt(); void Init(Trinity::Crypto::AES::Key const& key); + bool PeekDecryptRecv(uint8* data, size_t length); bool DecryptRecv(uint8* data, size_t length, Trinity::Crypto::AES::Tag& tag); bool EncryptSend(uint8* data, size_t length, Trinity::Crypto::AES::Tag& tag); diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 0b0eeda6b86..3e22c91e2a1 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -71,11 +71,11 @@ uint8 const WorldSocket::EncryptionKeySeed[16] = { 0xE9, 0x75, 0x3C, 0x50, 0x90, WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket)), _type(CONNECTION_TYPE_REALM), _key(0), _OverSpeedPings(0), - _worldSession(nullptr), _authed(false), _sendBufferSize(4096), _compressionStream(nullptr) + _worldSession(nullptr), _authed(false), _canRequestHotfixes(true), _sendBufferSize(4096), _compressionStream(nullptr) { Trinity::Crypto::GetRandomBytes(_serverChallenge); _encryptKey.fill(0); - _headerBuffer.Resize(sizeof(PacketHeader)); + _headerBuffer.Resize(sizeof(IncomingPacketHeader)); } WorldSocket::~WorldSocket() @@ -324,18 +324,26 @@ void WorldSocket::SetWorldSession(WorldSession* session) bool WorldSocket::ReadHeaderHandler() { - ASSERT(_headerBuffer.GetActiveSize() == sizeof(PacketHeader), "Header size " SZFMTD " different than expected " SZFMTD, _headerBuffer.GetActiveSize(), sizeof(PacketHeader)); + ASSERT(_headerBuffer.GetActiveSize() == sizeof(IncomingPacketHeader), "Header size " SZFMTD " different than expected " SZFMTD, _headerBuffer.GetActiveSize(), sizeof(IncomingPacketHeader)); - PacketHeader* header = reinterpret_cast<PacketHeader*>(_headerBuffer.GetReadPointer()); + IncomingPacketHeader* header = reinterpret_cast<IncomingPacketHeader*>(_headerBuffer.GetReadPointer()); + uint16 encryptedOpcode = header->EncryptedOpcode; if (!header->IsValidSize()) { - TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %u)", - GetRemoteIpAddress().to_string().c_str(), header->Size); - return false; + _authCrypt.PeekDecryptRecv(reinterpret_cast<uint8*>(&header->EncryptedOpcode), sizeof(encryptedOpcode)); + + // CMSG_HOTFIX_REQUEST can be much larger than normal packets, allow receiving it once per session + if (header->EncryptedOpcode != CMSG_HOTFIX_REQUEST || header->Size > 0x100000 || !_canRequestHotfixes) + { + TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %u, opcode %u)", + GetRemoteIpAddress().to_string().c_str(), header->Size, uint32(header->EncryptedOpcode)); + return false; + } } _packetBuffer.Resize(header->Size); + _packetBuffer.Write(&encryptedOpcode, sizeof(encryptedOpcode)); return true; } @@ -453,6 +461,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() LogOpcodeText(opcode, sessionGuard); HandleEnterEncryptedModeAck(); break; + case CMSG_HOTFIX_REQUEST: + _canRequestHotfixes = false; + /* fallthrough */ default: { sessionGuard.lock(); diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index da7890d4b07..5327db10ed6 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -57,7 +57,12 @@ struct PacketHeader uint32 Size; uint8 Tag[12]; - bool IsValidSize() { return Size < 0x40000; } + bool IsValidSize() { return Size < 0x10000; } +}; + +struct IncomingPacketHeader : PacketHeader +{ + uint16 EncryptedOpcode; }; #pragma pack(pop) @@ -142,6 +147,7 @@ private: std::mutex _worldSessionLock; WorldSession* _worldSession; bool _authed; + bool _canRequestHotfixes; MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; |