std::string CalculateShaPassHash(std::string& name, std::string& password) { SHA1Hash sha; sha.Initialize(); sha.UpdateData(name); sha.UpdateData(":"); sha.UpdateData(password); sha.Finalize(); std::string encoded; hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded); return encoded; }
std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& password) const { Sha1Hash sha; sha.Initialize(); sha.UpdateData(name); sha.UpdateData(":"); sha.UpdateData(password); sha.Finalize(); std::string encoded; hexEncodeByteArray(sha.GetDigest(), Sha1Hash::GetLength(), encoded); return encoded; }
/// Logon Challenge command handler bool AuthSocket::_HandleLogonChallenge() { DEBUG_LOG("Entering _HandleLogonChallenge"); if (recv_len() < sizeof(sAuthLogonChallenge_C)) return false; ///- Read the first 4 bytes (header) to get the length of the remaining of the packet std::vector<uint8> buf; buf.resize(4); recv((char *)&buf[0], 4); EndianConvert(*((uint16*)(buf[0]))); uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining); if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (recv_len() < remaining)) return false; //No big fear of memory outage (size is int16, i.e. < 65536) buf.resize(remaining + buf.size() + 1); buf[buf.size() - 1] = 0; sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; ///- Read the remaining of the packet recv((char *)&buf[4], remaining); DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); // BigEndian code, nop in little endian case // size already converted EndianConvert(*((uint32*)(&ch->gamename[0]))); EndianConvert(ch->build); EndianConvert(*((uint32*)(&ch->platform[0]))); EndianConvert(*((uint32*)(&ch->os[0]))); EndianConvert(*((uint32*)(&ch->country[0]))); EndianConvert(ch->timezone_bias); EndianConvert(ch->ip); ByteBuffer pkt; _login = (const char*)ch->I; _build = ch->build; _os = (const char*)ch->os; if(_os.size() > 4) return false; ///- Normalize account name //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form //Escape the user login to avoid further SQL injection //Memory will be freed on AuthSocket object destruction _safelogin = _login; LoginDatabase.escape_string(_safelogin); // Starting CMD_AUTH_LOGON_CHALLENGE AuthResult result = WOW_FAIL_UNKNOWN0; ///- Verify that this IP is not in the ip_banned table // No SQL injection possible (paste the IP address as passed by the socket) std::string address = get_remote_address(); LoginDatabase.escape_string(address); QueryResult* qresult = LoginDatabase.PQuery("SELECT unbandate FROM ip_banned WHERE " // permanent still banned "(unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", address.c_str()); if (qresult) { result = WOW_FAIL_BANNED; BASIC_LOG("[AuthChallenge] Banned ip %s tries to login!", get_remote_address().c_str()); delete qresult; } else { ///- Get the account details from the account table // No SQL injection (escaped user name) //qresult = LoginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '******'",_safelogin.c_str()); qresult = LoginDatabase.PQuery("SELECT a.sha_pass_hash,a.id,a.locked,a.last_ip,aa.gmlevel,a.v,a.s FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = '******'", _safelogin.c_str()); if (qresult) { std::string rI = (*qresult)[0].GetCppString(); uint32 accountId = (*qresult)[1].GetUInt32(); uint8 locked = (*qresult)[2].GetUInt8(); std::string lastIP = (*qresult)[3].GetString(); uint8 secLevel = (*qresult)[4].GetUInt8(); std::string databaseV = (*qresult)[5].GetCppString(); std::string databaseS = (*qresult)[6].GetCppString(); bool blockLogin = false; if (sConfig.GetBoolDefault("MultiIPCheck", false)) { int32 iplimit = sConfig.GetIntDefault("MultiIPLimit", 10); int32 multiIPdelay = sConfig.GetIntDefault("MultiIPPeriodInHours", 48); // If a GM account login ignore MultiIP QueryResult* ipcheck = LoginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s' AND id != %u AND last_login > NOW() - INTERVAL %u HOUR ORDER BY last_login DESC;", get_remote_address().c_str(), accountId, multiIPdelay); if (ipcheck) { // build whitelist std::list<uint32> accountsInWhitelist; accountsInWhitelist.clear(); QueryResult* IDsinwhite = LoginDatabase.PQuery("SELECT whitelist FROM multi_IP_whitelist WHERE whitelist LIKE '%|%u|%'", accountId); if (IDsinwhite) { Tokens whitelistaccounts((*IDsinwhite)[0].GetCppString(),'|'); bool isInWhite = false; for (Tokens::const_iterator itr = whitelistaccounts.begin(); itr != whitelistaccounts.end(); ++itr) accountsInWhitelist.push_back(atoi(*itr)); delete IDsinwhite; } do { Field* pFields =ipcheck->Fetch(); uint32 MultiAccountID = pFields[0].GetUInt32(); bool isInWhite = false; for (std::list<uint32>::const_iterator itr = accountsInWhitelist.begin(); itr != accountsInWhitelist.end(); ++itr) { if (*itr == MultiAccountID) isInWhite = true; } if (!isInWhite) { --iplimit; } } while (ipcheck->NextRow()); delete ipcheck; } /* * default case 10 allowed account with same last_ip * we found 9 account with current ip. NOTE: actual account is not in list * 10 - 9 - 1 * ^ current account * ^ account in list * ^ allowed */ if (iplimit < 1) { DEBUG_LOG("[AuthChallenge] Account '%s' is multi IP - '%s'", _login.c_str(), lastIP.c_str()); result = WOW_FAIL_PARENTCONTROL; blockLogin = true; } } ///- If the IP is 'locked', check that the player comes indeed from the correct IP address if (locked == 1) // if ip is locked { DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), lastIP.c_str()); DEBUG_LOG("[AuthChallenge] Player address is '%s'", get_remote_address().c_str()); if (strcmp(lastIP.c_str(),get_remote_address().c_str()) ) { DEBUG_LOG("[AuthChallenge] Account IP differs"); result = WOW_FAIL_SUSPENDED; blockLogin = true; } else { DEBUG_LOG("[AuthChallenge] Account IP matches"); } } else { DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); } if (!blockLogin) { ///- If the account is banned, reject the logon attempt QueryResult* banresult = LoginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE " "id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)", accountId); if (banresult) { if ((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64()) { result = WOW_FAIL_BANNED; BASIC_LOG("[AuthChallenge] Banned account %s (Id: %u) tries to login!", _login.c_str(), accountId); } else { result = WOW_FAIL_SUSPENDED; BASIC_LOG("[AuthChallenge] Temporarily banned account %s (Id: %u) tries to login!",_login.c_str(), accountId); } delete banresult; } else { DEBUG_LOG("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()); } result = WOW_SUCCESS; _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; _localizationName.resize(4); for (int i = 0; i < 4; ++i) _localizationName[i] = ch->country[4-i-1]; BASIC_LOG("[AuthChallenge] account %s (Id: %u) is using '%c%c%c%c' locale (%u)", _login.c_str (), accountId, ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName)); } } delete qresult; } else if (sConfig.GetBoolDefault("AutoRegistration", false)) { if (_safelogin.find_first_of("\t\v\b\f\a\n\r\\\"\'\? <>[](){}_=+-|/!@#$%^&*~`.,\0") == _safelogin.npos && _safelogin.length() > 3) { QueryResult* checkIPresult = LoginDatabase.PQuery("SELECT COUNT(last_ip) FROM account WHERE last_ip = '%s'",get_remote_address().c_str()); int32 regCount = checkIPresult ? (*checkIPresult)[0].GetUInt32() : 0; if (regCount >= sConfig.GetIntDefault("AutoRegistration.Amount", 1)) { BASIC_LOG("[AuthChallenge] Impossible auto-register account %s, number of auto-registered accouts is %u, but allowed only %u", _safelogin.c_str(),regCount, sConfig.GetIntDefault("AutoRegistration.Amount", 1)); // result = WOW_FAIL_DB_BUSY; result = WOW_FAIL_DISCONNECTED; } else { std::transform(_safelogin.begin(), _safelogin.end(), _safelogin.begin(), std::towupper); Sha1Hash sha; sha.Initialize(); sha.UpdateData(_safelogin); sha.UpdateData(":"); sha.UpdateData(_safelogin); sha.Finalize(); std::string encoded; hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded); LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", _safelogin.c_str(), encoded.c_str()); _SetVSFields(encoded); BASIC_LOG("[AuthChallenge] account %s auto-registered (count %u)!",_safelogin.c_str(), ++regCount); result = WOW_SUCCESS; _accountSecurityLevel = SEC_PLAYER; _localizationName.resize(4); for (int i = 0; i < 4; ++i) _localizationName[i] = ch->country[4-i-1]; } if (checkIPresult) delete checkIPresult; } } else result = WOW_FAIL_UNKNOWN_ACCOUNT; } pkt << uint8(CMD_AUTH_LOGON_CHALLENGE); pkt << uint8(0x00); pkt << uint8(result); switch (result) { case WOW_SUCCESS: { b.SetRand(19 * 8); BigNumber gmod = g.ModExp(b, N); B = ((v * 3) + gmod) % N; MANGOS_ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk3; unk3.SetRand(16 * 8); // B may be calculated < 32B so we force minimal length to 32B pkt.append(B.AsByteArray(32), 32); // 32 bytes pkt << uint8(1); pkt.append(g.AsByteArray(), 1); pkt << uint8(32); pkt.append(N.AsByteArray(32), 32); pkt.append(s.AsByteArray(), s.GetNumBytes());// 32 bytes pkt.append(unk3.AsByteArray(16), 16); uint8 securityFlags = 0; pkt << uint8(securityFlags); // security flags (0x0...0x04) if (securityFlags & 0x01) // PIN input { pkt << uint32(0); pkt << uint64(0) << uint64(0); // 16 bytes hash? } if (securityFlags & 0x02) // Matrix input { pkt << uint8(0); pkt << uint8(0); pkt << uint8(0); pkt << uint8(0); pkt << uint64(0); } if (securityFlags & 0x04) // Security token input { pkt << uint8(1); } break; } case WOW_FAIL_UNKNOWN0: case WOW_FAIL_UNKNOWN1: case WOW_FAIL_SUSPENDED: case WOW_FAIL_BANNED: case WOW_FAIL_UNKNOWN_ACCOUNT: case WOW_FAIL_INCORRECT_PASSWORD: case WOW_FAIL_ALREADY_ONLINE: case WOW_FAIL_NO_TIME: case WOW_FAIL_DB_BUSY: case WOW_FAIL_VERSION_INVALID: case WOW_FAIL_VERSION_UPDATE: case WOW_FAIL_INVALID_SERVER: case WOW_FAIL_FAIL_NOACCESS: case WOW_SUCCESS_SURVEY: case WOW_FAIL_PARENTCONTROL: case WOW_FAIL_LOCKED_ENFORCED: case WOW_FAIL_TRIAL_ENDED: case WOW_FAIL_USE_BATTLENET: case WOW_FAIL_TOO_FAST: case WOW_FAIL_CHARGEBACK: case WOW_FAIL_GAME_ACCOUNT_LOCKED: case WOW_FAIL_INTERNET_GAME_ROOM_WITHOUT_BNET: case WOW_FAIL_UNLOCKABLE_LOCK: case WOW_FAIL_DISCONNECTED: break; default: BASIC_LOG("[AuthChallenge] unknown CMD_AUTH_LOGON_CHALLENGE execution result %u!", result); break; } send((char const*)pkt.contents(), pkt.size()); return true; }
bool WardenMgr::ValidateCheatCheckResult(WorldSession* const session, WorldPacket& clientPacket) { uint32 accountId = session->GetAccountId(); sLog->outStaticDebug("Wardend::ValidateCheatCheckResult(%u, *pkt)", accountId); bool valid = true; uint16 pktLen; uint32 checksum; clientPacket >> pktLen; clientPacket >> checksum; if (checksum != BuildChecksum(clientPacket.contents() + clientPacket.rpos(), clientPacket.size() - clientPacket.rpos())) { if (session->GetPlayer()) { sLog->outWarden("Warden Cheat-check: Kicking player %s for failed check, Packet Checksum 0x%08X is invalid!", session->GetPlayerName(), checksum); } else { sLog->outWarden("Warden Cheat-check: Kicking account %u for failed check, Packet Checksum 0x%08X is invalid!", session->GetAccountId(), checksum); } ReactToCheatCheckResult(session, false); return false; } if (pktLen == 0) return false; // parse the timing check always sent sLog->outStaticDebug("TimeCheck"); uint8 res; uint32 ticks; clientPacket >> res; // should be 1 clientPacket >> ticks; // Need to compare ticks based on last one using server ticks diff since sLog->outStaticDebug("Warden: Time unk 0x%08X", ticks); pktLen = pktLen - 5; WardenClientCheckList* checkList = (WardenClientCheckList*)session->m_WardenClientChecks; if (!checkList) return false; for (uint8 i=0; i<checkList->size(); ++i) { switch ((*checkList)[i].check) { case WARD_CHECK_TIMING: { sLog->outStaticDebug("Warden: TimeCheck"); uint8 res; uint32 ticks; clientPacket >> res; // should be 1 clientPacket >> ticks; // Need to compare ticks based on last one using server ticks diff since sLog->outStaticDebug("Warden: Time unk 0x%08X", ticks); pktLen = pktLen - 5; break; } case WARD_CHECK_MEMORY: { sLog->outStaticDebug("Warden: MemCheck"); uint8 res; clientPacket >> res; // should be 0 if (res) { valid = false; if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed check, MEM at Offset 0x%04X, lentgh %u could not be read by client", session->GetPlayerName(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length); } else { sLog->outWarden("Warden: account %u failed check, MEM at Offset 0x%04X, lentgh %u could not be read by client", session->GetAccountId(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length); } } else { uint8 memContent[20]; bool memcheck_failed = false; for (uint8 pos=0; pos<(*checkList)[i].mem->Length; ++pos) { clientPacket >> memContent[pos]; if (memContent[pos]!=(*checkList)[i].mem->Result[pos]) { valid = false; memcheck_failed = true; } } pktLen = pktLen - (1 + (*checkList)[i].mem->Length); if (memcheck_failed) { std::string strContent, strContent2; hexEncodeByteArray(memContent, (*checkList)[i].mem->Length, strContent); hexEncodeByteArray((*checkList)[i].mem->Result, (*checkList)[i].mem->Length, strContent2); if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed check, MEM Offset 0x%04X length %u has content '%s' instead of '%s'", session->GetPlayerName(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length, strContent.c_str(), strContent2.c_str()); } else { sLog->outWarden("Warden: account %u failed check, MEM Offset 0x%04X length %u has content '%s' instead of '%s'", session->GetAccountId(), (*checkList)[i].mem->Offset, (*checkList)[i].mem->Length, strContent.c_str(), strContent2.c_str()); } } } sLog->outStaticDebug("Warden: Mem %s", valid ? "Ok" : "Failed"); break; } case WARD_CHECK_FILE: { sLog->outStaticDebug("Warden: MPQCheck"); uint8 res; uint8 resSHA1[20]; clientPacket >> res; // should be 0 if (res) { valid = false; if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed check, MPQ '%s' not found by client", session->GetPlayerName(), (*checkList)[i].file->String.c_str()); } else { sLog->outWarden("Warden: account %u failed check, MPQ '%s' not found by client", session->GetAccountId(), (*checkList)[i].file->String.c_str()); } pktLen = pktLen - 1; } else { for (uint8 pos=0; pos<20; ++pos) clientPacket >> resSHA1[pos]; if (res || memcmp(resSHA1, (*checkList)[i].file->SHA, 20)) { valid = false; std::string strResSHA1, strReqSHA1; hexEncodeByteArray(resSHA1, 20, strResSHA1); hexEncodeByteArray((*checkList)[i].file->SHA, 20, strReqSHA1); if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed check, MPQ '%s' SHA1 is '%s' instead of '%s'", session->GetPlayerName(), (*checkList)[i].file->String.c_str(), strResSHA1.c_str(), strReqSHA1.c_str()); } else { sLog->outWarden("Warden: account %u failed check, MPQ '%s' SHA1 is '%s' instead of '%s'", session->GetAccountId(), (*checkList)[i].file->String.c_str(), strResSHA1.c_str(), strReqSHA1.c_str()); } } pktLen = pktLen - 21; } sLog->outStaticDebug("Warden: MPQ %s", valid ? "Ok" : "Failed"); break; } case WARD_CHECK_LUA: { sLog->outStaticDebug("Warden: LUACheck"); uint8 res; uint8 foundLuaLen; clientPacket >> res; // should be 0 clientPacket >> foundLuaLen; // should be 0 uint8 *luaStr; if (foundLuaLen > 0) { luaStr = (uint8*)malloc(foundLuaLen+1); for (uint8 pos=0; pos<foundLuaLen; ++pos) { clientPacket >> luaStr[pos]; } luaStr[foundLuaLen] = 0; if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed lua check, Lua '%s' found as '%s'", session->GetPlayerName(), (*checkList)[i].lua->String.c_str(), (char*)luaStr); } else { sLog->outWarden("Warden: account %u failed lua check, Lua '%s' found as '%s'", session->GetAccountId(), (*checkList)[i].lua->String.c_str(), (char*)luaStr); } valid = false; free(luaStr); } sLog->outStaticDebug("Lua %s", valid ? "Ok" : "Failed"); pktLen = pktLen - 2; break; } case WARD_CHECK_PAGE1: case WARD_CHECK_PAGE2: case WARD_CHECK_DRIVER: { sLog->outStaticDebug("PageCheck or DriverCheck"); uint8 res; clientPacket >> res; // should be 0xE9 if (res != 0xE9) { if ((*checkList)[i].check == WARD_CHECK_DRIVER) { if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed driver check '%s'", session->GetPlayerName(), (*checkList)[i].driver->String.c_str()); } else { sLog->outWarden("Warden: account %u failed driver check '%s'", session->GetAccountId(), (*checkList)[i].driver->String.c_str()); } } else { if (session->GetPlayer()) { sLog->outWarden("Warden: player %s failed page check Offset 0x%08X, length %u", session->GetPlayerName(), (*checkList)[i].page->Offset, (*checkList)[i].page->Length); } else { sLog->outWarden("Warden: account %u failed page check Offset 0x%08X, length %u", session->GetAccountId(), (*checkList)[i].page->Offset, (*checkList)[i].page->Length); } } valid = false; } sLog->outStaticDebug("Page or Driver %s",valid?"Ok":"Failed"); pktLen = pktLen - 1; break; } default: sLog->outStaticDebug("Warden: Other!!"); // Finish skiping the rest of the packet and return failed checks if (session->GetPlayer()) { sLog->outWarden("Wrong packet for player %s or problem to parse it, I had to clean %u bytes", session->GetPlayerName(), clientPacket.size() - clientPacket.rpos()); } else { sLog->outWarden("Wrong packet for account %u or problem to parse it, I had to clean %u bytes", session->GetAccountId(), clientPacket.size() - clientPacket.rpos()); } clientPacket.read_skip(clientPacket.size() - clientPacket.rpos()); return false; } }