You are on page 1of 7

/*

* This file is part of the AzerothCore Project. See AUTHORS file for Copyright
information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "Warden.h"
#include "AccountMgr.h"
#include "BanMgr.h"
#include "ByteBuffer.h"
#include "Common.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
#include "SharedDefines.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <openssl/sha.h>

Warden::Warden() : _session(nullptr), _checkTimer(10000/*10 sec*/),


_clientResponseTimer(0),
_dataSent(false), _module(nullptr), _initialized(false)
{
memset(_inputKey, 0, sizeof(_inputKey));
memset(_outputKey, 0, sizeof(_outputKey));
memset(_seed, 0, sizeof(_seed));
}

Warden::~Warden()
{
delete[] _module->CompressedData;
delete _module;
_module = nullptr;
_initialized = false;
}

void Warden::SendModuleToClient()
{
LOG_DEBUG("warden", "Send module to client");

// Create packet structure


WardenModuleTransfer packet;

uint32 sizeLeft = _module->CompressedSize;


uint32 pos = 0;
uint16 burstSize;
while (sizeLeft > 0)
{
burstSize = sizeLeft < 500 ? sizeLeft : 500;
packet.Command = WARDEN_SMSG_MODULE_CACHE;
packet.DataSize = burstSize;
memcpy(packet.Data, &_module->CompressedData[pos], burstSize);
sizeLeft -= burstSize;
pos += burstSize;

EncryptData((uint8*)&packet, burstSize + 3);


WorldPacket pkt1(SMSG_WARDEN_DATA, burstSize + 3);
pkt1.append((uint8*)&packet, burstSize + 3);
_session->SendPacket(&pkt1);
}
}

void Warden::RequestModule()
{
LOG_DEBUG("warden", "Request module");

// Create packet structure


WardenModuleUse request;
request.Command = WARDEN_SMSG_MODULE_USE;

memcpy(request.ModuleId, _module->Id, 16);


memcpy(request.ModuleKey, _module->Key, 16);
request.Size = _module->CompressedSize;

EndianConvert(request.Size);

// Encrypt with warden RC4 key.


EncryptData((uint8*)&request, sizeof(WardenModuleUse));

WorldPacket pkt(SMSG_WARDEN_DATA, sizeof(WardenModuleUse));


pkt.append((uint8*)&request, sizeof(WardenModuleUse));
_session->SendPacket(&pkt);
}

void Warden::Update(uint32 const diff)


{
if (!_initialized)
{
return;
}

if (_dataSent)
{
uint32 maxClientResponseDelay = sWorld-
>getIntConfig(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY);
if (maxClientResponseDelay > 0)
{
if (_clientResponseTimer > maxClientResponseDelay * IN_MILLISECONDS)
{
_session->KickPlayer("Warden: clientResponseTimer >
maxClientResponseDelay (Warden::Update)");
}
else
{
_clientResponseTimer += diff;
}
}
}
else
{
if (diff >= _checkTimer)
{
RequestChecks();
}
else
{
_checkTimer -= diff;
}
}
}

void Warden::DecryptData(uint8* buffer, uint32 length)


{
_inputCrypto.UpdateData(buffer, length);
}

void Warden::EncryptData(uint8* buffer, uint32 length)


{
_outputCrypto.UpdateData(buffer, length);
}

bool Warden::IsValidCheckSum(uint32 checksum, const uint8* data, const uint16


length)
{
uint32 newChecksum = BuildChecksum(data, length);

if (checksum != newChecksum)
{
LOG_DEBUG("warden", "CHECKSUM IS NOT VALID");
return false;
}
else
{
LOG_DEBUG("warden", "CHECKSUM IS VALID");
return true;
}
}

struct keyData
{
union
{
struct
{
uint8 bytes[20];
} bytes;

struct
{
uint32 ints[5];
} ints;
};
};
uint32 Warden::BuildChecksum(const uint8* data, uint32 length)
{
keyData hash;
SHA1(data, length, hash.bytes.bytes);
uint32 checkSum = 0;
for (uint8 i = 0; i < 5; ++i)
{
checkSum = checkSum ^ hash.ints.ints[i];
}

return checkSum;
}

static std::string GetWardenActionStr(uint32 action)


{
switch (action)
{
case WARDEN_ACTION_LOG:
return "WARDEN_ACTION_LOG";
case WARDEN_ACTION_KICK:
return "WARDEN_ACTION_KICK";
case WARDEN_ACTION_BAN:
return "WARDEN_ACTION_BAN";
}

return "UNHANDLED ACTION";


}

void Warden::ApplyPenalty(uint16 checkId, std::string const& reason)


{
WardenCheck const* checkData = sWardenCheckMgr->GetWardenDataById(checkId);

uint32 action = WardenActions(sWorld-


>getIntConfig(CONFIG_WARDEN_CLIENT_FAIL_ACTION));
std::string causeMsg;
if (checkId && checkData)
{
action = checkData->Action;

if (checkData->Comment.empty())
{
causeMsg = "Warden id " + std::to_string(checkId) + " violation";
}
else
{
causeMsg = "Warden: " + checkData->Comment;
}
}
else
{
// if its not warden check id based, reason must be always provided
ASSERT(!reason.empty());
causeMsg = reason;
}

switch (action)
{
case WARDEN_ACTION_LOG:
break;
case WARDEN_ACTION_KICK:
{
_session->KickPlayer(causeMsg.find("Warden") != std::string::npos ?
causeMsg : "Warden: " + causeMsg);
break;
}
case WARDEN_ACTION_BAN:
{
std::stringstream duration;
duration << sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_BAN_DURATION) <<
"s";
std::string accountName;
AccountMgr::GetName(_session->GetAccountId(), accountName);
sBan->BanAccount(accountName, duration.str(), causeMsg, "Server");
break;
}
}

std::string reportMsg;
if (checkId)
{
if (Player const* plr = _session->GetPlayer())
{
std::string const reportFormat = "Player %s (guid %u, account id: %u)
failed warden %u check (%s). Action: %s";
reportMsg = Acore::StringFormat(reportFormat, plr->GetName().c_str(),
plr->GetGUID().GetCounter(), _session->GetAccountId(),
checkId, ((checkData && !checkData-
>Comment.empty()) ? checkData->Comment.c_str() : "<warden comment is not set>"),
GetWardenActionStr(action).c_str());
}
else
{
std::string const reportFormat = "Account id: %u failed warden %u
check. Action: %s";
reportMsg = Acore::StringFormat(reportFormat, _session->GetAccountId(),
checkId, GetWardenActionStr(action).c_str());
}
}
else
{
if (Player const* plr = _session->GetPlayer())
{
std::string const reportFormat = "Player %s (guid %u, account id: %u)
triggered warden penalty by reason: %s. Action: %s";
reportMsg = Acore::StringFormat(reportFormat, plr->GetName().c_str(),
plr->GetGUID().GetCounter(), _session->GetAccountId(), causeMsg.c_str(),
GetWardenActionStr(action).c_str());
}
else
{
std::string const reportFormat = "Account id: %u failed warden %u
check. Action: %s";
reportMsg = Acore::StringFormat(reportFormat, _session->GetAccountId(),
causeMsg.c_str(), GetWardenActionStr(action).c_str());
}
}
reportMsg = "Warden: " + reportMsg;
LOG_INFO("warden", "> Warden: {}", reportMsg);
}

bool Warden::ProcessLuaCheckResponse(std::string const& msg)


{
static constexpr char WARDEN_TOKEN[] = "_TW\t";
// if msg starts with WARDEN_TOKEN
if (msg.rfind(WARDEN_TOKEN, 0) != 0)
{
return false;
}

uint16 id = 0;

{
std::stringstream msg2(msg);
std::string temp;
while (msg2 >> temp)
{
// Found check id - stop loop
if (std::stringstream(temp) >> id)
break;
}
}

if (id < sWardenCheckMgr->GetMaxValidCheckId())


{
WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(id);
if (check && check->Type == LUA_EVAL_CHECK)
{
ApplyPenalty(id, "");
return true;
}
}

ApplyPenalty(0, "Sent bogus Lua check response for Warden");


return true;
}

void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData)


{
if (!_warden || recvData.empty())
return;

_warden->DecryptData(recvData.contents(), recvData.size());
uint8 opcode;
recvData >> opcode;
LOG_DEBUG("warden", "Got packet, opcode {:02X}, size {}", opcode,
uint32(recvData.size()));
recvData.hexlike();

switch (opcode)
{
case WARDEN_CMSG_MODULE_MISSING:
_warden->SendModuleToClient();
break;
case WARDEN_CMSG_MODULE_OK:
_warden->RequestHash();
break;
case WARDEN_CMSG_CHEAT_CHECKS_RESULT:
_warden->HandleData(recvData);
break;
case WARDEN_CMSG_MEM_CHECKS_RESULT:
LOG_DEBUG("warden", "NYI WARDEN_CMSG_MEM_CHECKS_RESULT received!");
break;
case WARDEN_CMSG_HASH_RESULT:
_warden->HandleHashResult(recvData);
_warden->InitializeModule();
break;
case WARDEN_CMSG_MODULE_FAILED:
LOG_DEBUG("warden", "NYI WARDEN_CMSG_MODULE_FAILED received!");
break;
default:
LOG_DEBUG("warden", "Got unknown warden opcode {:02X} of size {}.",
opcode, uint32(recvData.size() - 1));
break;
}
}

You might also like