diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/bnetserver/REST/LoginRESTService.cpp | 168 | ||||
-rw-r--r-- | src/server/bnetserver/REST/LoginRESTService.h | 21 | ||||
-rw-r--r-- | src/server/bnetserver/Server/Session.cpp | 183 | ||||
-rw-r--r-- | src/server/bnetserver/Server/Session.h | 12 | ||||
-rw-r--r-- | src/server/bnetserver/Services/AuthenticationService.cpp | 11 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/LoginDatabase.cpp | 8 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/LoginDatabase.h | 2 |
7 files changed, 186 insertions, 219 deletions
diff --git a/src/server/bnetserver/REST/LoginRESTService.cpp b/src/server/bnetserver/REST/LoginRESTService.cpp index 8b84b1a86f5..9fd4b246df7 100644 --- a/src/server/bnetserver/REST/LoginRESTService.cpp +++ b/src/server/bnetserver/REST/LoginRESTService.cpp @@ -51,13 +51,10 @@ public: soap* GetClient() const { return _client.get(); } void SetCallback(std::unique_ptr<QueryCallback> callback) { _callback = std::move(callback); } - std::unique_ptr<Battlenet::Session::AccountInfo>& GetResult() { return _result; } - void SetResult(std::unique_ptr<Battlenet::Session::AccountInfo> result) { _result = std::move(result); } private: std::shared_ptr<soap> _client; std::unique_ptr<QueryCallback> _callback; - std::unique_ptr<Battlenet::Session::AccountInfo> _result; }; int32 handle_get_plugin(soap* soapClient) @@ -127,10 +124,6 @@ bool LoginRESTService::Start(boost::asio::io_service* ioService) input->set_type("submit"); input->set_label("Log In"); - _loginTicketCleanupTimer = new boost::asio::deadline_timer(*ioService); - _loginTicketCleanupTimer->expires_from_now(boost::posix_time::seconds(10)); - _loginTicketCleanupTimer->async_wait(std::bind(&LoginRESTService::CleanupLoginTickets, this, std::placeholders::_1)); - _thread = std::thread(std::bind(&LoginRESTService::Run, this)); return true; } @@ -138,7 +131,6 @@ bool LoginRESTService::Start(boost::asio::io_service* ioService) void LoginRESTService::Stop() { _stopped = true; - _loginTicketCleanupTimer->cancel(); _thread.join(); } @@ -277,49 +269,68 @@ int32 LoginRESTService::HandlePost(soap* soapClient) Utf8ToUpperOnlyLatin(login); Utf8ToUpperOnlyLatin(password); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_AUTHENTICATION); stmt->setString(0, login); std::string sentPasswordHash = CalculateShaPassHash(login, password); std::shared_ptr<AsyncLoginRequest> request = std::make_shared<AsyncLoginRequest>(*reinterpret_cast<std::shared_ptr<soap>*>(soapClient->user)); request->SetCallback(Trinity::make_unique<QueryCallback>(LoginDatabase.AsyncQuery(stmt) - .WithChainingPreparedCallback([request, login, sentPasswordHash](QueryCallback& callback, PreparedQueryResult result) + .WithChainingPreparedCallback([request, login, sentPasswordHash, this](QueryCallback& callback, PreparedQueryResult result) { if (result) { - std::string pass_hash = result->Fetch()[13].GetString(); - - request->SetResult(Trinity::make_unique<Battlenet::Session::AccountInfo>()); - request->GetResult()->LoadResult(result); + Field* fields = result->Fetch(); + uint32 accountId = fields[0].GetUInt32(); + std::string pass_hash = fields[1].GetString(); + uint32 failedLogins = fields[2].GetUInt32(); + std::string loginTicket = fields[3].GetString(); + uint32 loginTicketExpiry = fields[4].GetUInt32(); + bool isBanned = fields[5].GetUInt64() != 0; if (sentPasswordHash == pass_hash) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID); - stmt->setUInt32(0, request->GetResult()->Id); - callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); + if (loginTicket.empty() || loginTicketExpiry < time(nullptr)) + { + BigNumber ticket; + ticket.SetRand(20 * 8); + + loginTicket = "TC-" + ByteArrayToHexStr(ticket.AsByteArray(20).get(), 20); + } + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_AUTHENTICATION); + stmt->setString(0, loginTicket); + stmt->setUInt32(1, time(nullptr) + 3600); + stmt->setUInt32(2, accountId); + callback.WithPreparedCallback([request, loginTicket](PreparedQueryResult) + { + Battlenet::JSON::Login::LoginResult loginResult; + loginResult.set_authentication_state(Battlenet::JSON::Login::DONE); + loginResult.set_login_ticket(loginTicket); + sLoginService.SendResponse(request->GetClient(), loginResult); + }).SetNextQuery(LoginDatabase.AsyncQuery(stmt)); return; } - else if (!request->GetResult()->IsBanned) + else if (!isBanned) { std::string ip_address = boost::asio::ip::address_v4(request->GetClient()->ip).to_string(); uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0)); if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false)) - TC_LOG_DEBUG("server.rest", "[%s, Account %s, Id %u] Attempted to connect with wrong password!", ip_address.c_str(), login.c_str(), request->GetResult()->Id); + TC_LOG_DEBUG("server.rest", "[%s, Account %s, Id %u] Attempted to connect with wrong password!", ip_address.c_str(), login.c_str(), accountId); if (maxWrongPassword) { SQLTransaction trans = LoginDatabase.BeginTransaction(); PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_FAILED_LOGINS); - stmt->setUInt32(0, request->GetResult()->Id); + stmt->setUInt32(0, accountId); trans->Append(stmt); - ++request->GetResult()->FailedLogins; + ++failedLogins; - TC_LOG_DEBUG("server.rest", "MaxWrongPass : %u, failed_login : %u", maxWrongPassword, request->GetResult()->Id); + TC_LOG_DEBUG("server.rest", "MaxWrongPass : %u, failed_login : %u", maxWrongPassword, accountId); - if (request->GetResult()->FailedLogins >= maxWrongPassword) + if (failedLogins >= maxWrongPassword) { BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP))); int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); @@ -327,7 +338,7 @@ int32 LoginRESTService::HandlePost(soap* soapClient) if (banType == BanMode::BAN_ACCOUNT) { stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED); - stmt->setUInt32(0, request->GetResult()->Id); + stmt->setUInt32(0, accountId); } else { @@ -339,7 +350,7 @@ int32 LoginRESTService::HandlePost(soap* soapClient) trans->Append(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS); - stmt->setUInt32(0, request->GetResult()->Id); + stmt->setUInt32(0, accountId); trans->Append(stmt); } @@ -350,52 +361,6 @@ int32 LoginRESTService::HandlePost(soap* soapClient) Battlenet::JSON::Login::LoginResult loginResult; loginResult.set_authentication_state(Battlenet::JSON::Login::DONE); sLoginService.SendResponse(request->GetClient(), loginResult); - }) - .WithChainingPreparedCallback([request](QueryCallback& callback, PreparedQueryResult characterCountsResult) - { - if (characterCountsResult) - { - do - { - Field* fields = characterCountsResult->Fetch(); - request->GetResult()->GameAccounts[fields[0].GetUInt32()] - .CharacterCounts[Battlenet::RealmHandle{ fields[3].GetUInt8(), fields[4].GetUInt8(), fields[2].GetUInt32() }.GetAddress()] = fields[1].GetUInt8(); - - } while (characterCountsResult->NextRow()); - } - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS); - stmt->setUInt32(0, request->GetResult()->Id); - callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); - }) - .WithPreparedCallback([request](PreparedQueryResult lastPlayerCharactersResult) - { - if (lastPlayerCharactersResult) - { - do - { - Field* fields = lastPlayerCharactersResult->Fetch(); - Battlenet::RealmHandle realmId{ fields[1].GetUInt8(), fields[2].GetUInt8(), fields[3].GetUInt32() }; - Battlenet::Session::LastPlayedCharacterInfo& lastPlayedCharacter = request->GetResult()->GameAccounts[fields[0].GetUInt32()] - .LastPlayedCharacters[realmId.GetSubRegionAddress()]; - - lastPlayedCharacter.RealmId = realmId; - lastPlayedCharacter.CharacterName = fields[4].GetString(); - lastPlayedCharacter.CharacterGUID = fields[5].GetUInt64(); - lastPlayedCharacter.LastPlayedTime = fields[6].GetUInt32(); - - } while (lastPlayerCharactersResult->NextRow()); - } - - BigNumber ticket; - ticket.SetRand(20 * 8); - - Battlenet::JSON::Login::LoginResult loginResult; - loginResult.set_authentication_state(Battlenet::JSON::Login::DONE); - loginResult.set_login_ticket("TC-" + ByteArrayToHexStr(ticket.AsByteArray(20).get(), 20)); - sLoginService.SendResponse(request->GetClient(), loginResult); - - sLoginService.AddLoginTicket(loginResult.login_ticket(), std::move(request->GetResult())); }))); _ioService->post(std::bind(&LoginRESTService::HandleAsyncRequest, this, std::move(request))); @@ -433,68 +398,9 @@ std::string LoginRESTService::CalculateShaPassHash(std::string const& name, std: return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength(), true); } -std::unique_ptr<Battlenet::Session::AccountInfo> LoginRESTService::VerifyLoginTicket(std::string const& id) -{ - std::unique_lock<std::mutex> lock(_loginTicketMutex); - - auto itr = _validLoginTickets.find(id); - if (itr != _validLoginTickets.end()) - { - if (itr->second.ExpiryTime > time(nullptr)) - { - std::unique_ptr<Battlenet::Session::AccountInfo> accountInfo = std::move(itr->second.Account); - _validLoginTickets.erase(itr); - return accountInfo; - } - } - - return std::unique_ptr<Battlenet::Session::AccountInfo>(); -} - -void LoginRESTService::AddLoginTicket(std::string const& id, std::unique_ptr<Battlenet::Session::AccountInfo> accountInfo) -{ - std::unique_lock<std::mutex> lock(_loginTicketMutex); - - _validLoginTickets[id] = { id, std::move(accountInfo), time(nullptr) + 10 }; -} - -void LoginRESTService::CleanupLoginTickets(boost::system::error_code const& error) -{ - if (error) - return; - - time_t now = time(nullptr); - - { - std::unique_lock<std::mutex> lock(_loginTicketMutex); - for (auto itr = _validLoginTickets.begin(); itr != _validLoginTickets.end();) - { - if (itr->second.ExpiryTime < now) - itr = _validLoginTickets.erase(itr); - else - ++itr; - } - } - - _loginTicketCleanupTimer->expires_from_now(boost::posix_time::seconds(10)); - _loginTicketCleanupTimer->async_wait(std::bind(&LoginRESTService::CleanupLoginTickets, this, std::placeholders::_1)); -} - -LoginRESTService::LoginTicket& LoginRESTService::LoginTicket::operator=(LoginTicket&& right) -{ - if (this != &right) - { - Id = std::move(right.Id); - Account = std::move(right.Account); - ExpiryTime = right.ExpiryTime; - } - - return *this; -} - Namespace namespaces[] = { - { NULL, NULL, NULL, NULL } + { nullptr, nullptr, nullptr, nullptr } }; LoginRESTService& LoginRESTService::Instance() diff --git a/src/server/bnetserver/REST/LoginRESTService.h b/src/server/bnetserver/REST/LoginRESTService.h index ff9729e3333..e3a74d92907 100644 --- a/src/server/bnetserver/REST/LoginRESTService.h +++ b/src/server/bnetserver/REST/LoginRESTService.h @@ -24,9 +24,7 @@ #include <boost/asio/io_service.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/address.hpp> -#include <boost/asio/deadline_timer.hpp> #include <atomic> -#include <mutex> #include <thread> class AsyncLoginRequest; @@ -42,7 +40,7 @@ enum class BanMode class LoginRESTService { public: - LoginRESTService() : _ioService(nullptr), _stopped(false), _port(0), _loginTicketCleanupTimer(nullptr) { } + LoginRESTService() : _ioService(nullptr), _stopped(false), _port(0) { } static LoginRESTService& Instance(); @@ -51,8 +49,6 @@ public: boost::asio::ip::tcp::endpoint const& GetAddressForClient(boost::asio::ip::address const& address) const; - std::unique_ptr<Battlenet::Session::AccountInfo> VerifyLoginTicket(std::string const& id); - private: void Run(); @@ -68,18 +64,6 @@ private: std::string CalculateShaPassHash(std::string const& name, std::string const& password); - void AddLoginTicket(std::string const& id, std::unique_ptr<Battlenet::Session::AccountInfo> accountInfo); - void CleanupLoginTickets(boost::system::error_code const& error); - - struct LoginTicket - { - LoginTicket& operator=(LoginTicket&& right); - - std::string Id; - std::unique_ptr<Battlenet::Session::AccountInfo> Account; - std::time_t ExpiryTime; - }; - struct ResponseCodePlugin { static char const* const PluginId; @@ -110,9 +94,6 @@ private: int32 _port; boost::asio::ip::tcp::endpoint _externalAddress; boost::asio::ip::tcp::endpoint _localAddress; - std::mutex _loginTicketMutex; - std::unordered_map<std::string, LoginTicket> _validLoginTickets; - boost::asio::deadline_timer* _loginTicketCleanupTimer; }; #define sLoginService LoginRESTService::Instance() diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp index 1c7d4aed521..bc6c4dfb92a 100644 --- a/src/server/bnetserver/Server/Session.cpp +++ b/src/server/bnetserver/Server/Session.cpp @@ -30,14 +30,14 @@ void Battlenet::Session::AccountInfo::LoadResult(PreparedQueryResult result) { - // ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab WHERE email = ? + // ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab WHERE email = ? Field* fields = result->Fetch(); Id = fields[0].GetUInt32(); Login = fields[1].GetString(); IsLockedToIP = fields[2].GetBool(); LockCountry = fields[3].GetString(); LastIP = fields[4].GetString(); - FailedLogins = fields[5].GetUInt32(); + LoginTicketExpiry = fields[5].GetUInt32(); IsBanned = fields[6].GetUInt64() != 0; IsPermanenetlyBanned = fields[7].GetUInt64() != 0; @@ -206,7 +206,7 @@ void Battlenet::Session::SendRequest(uint32 serviceHash, uint32 methodId, pb::Me AsyncWrite(&packet); } -uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* logonRequest) +uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* logonRequest, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& /*continuation*/) { if (logonRequest->program() != "WoW") { @@ -228,6 +228,7 @@ uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* l _locale = logonRequest->locale(); _os = logonRequest->platform(); + _build = logonRequest->application_version(); boost::asio::ip::tcp::endpoint const& endpoint = sLoginService.GetAddressForClient(GetRemoteIpAddress()); @@ -238,77 +239,152 @@ uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* l return ERROR_OK; } -uint32 Battlenet::Session::HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* verifyWebCredentialsRequest) +uint32 Battlenet::Session::HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* verifyWebCredentialsRequest, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) { - authentication::v1::LogonResult logonResult; - logonResult.set_error_code(0); - _accountInfo = sLoginService.VerifyLoginTicket(verifyWebCredentialsRequest->web_credentials()); - if (!_accountInfo) - return ERROR_DENIED; + return VerifyWebCredentials(verifyWebCredentialsRequest->web_credentials(), continuation); +} - std::string ip_address = GetRemoteIpAddress().to_string(); +uint32 Battlenet::Session::VerifyWebCredentials(std::string const& webCredentials, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) +{ + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO); + stmt->setString(0, webCredentials); - // If the IP is 'locked', check that the player comes indeed from the correct IP address - if (_accountInfo->IsLockedToIP) + std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)> asyncContinuation = std::move(continuation); + std::shared_ptr<AccountInfo> accountInfo = std::make_shared<AccountInfo>(); + _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithChainingPreparedCallback([this, accountInfo, asyncContinuation](QueryCallback& callback, PreparedQueryResult result) { - TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is locked to IP - '%s' is logging in from '%s'", - _accountInfo->Login.c_str(), _accountInfo->LastIP.c_str(), ip_address.c_str()); + if (!result) + { + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_DENIED, &NoData()); + return; + } - if (_accountInfo->LastIP != ip_address) - return ERROR_RISK_ACCOUNT_LOCKED; - } - else + accountInfo->LoadResult(result); + + if (accountInfo->LoginTicketExpiry < time(nullptr)) + { + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_TIMED_OUT, &NoData()); + return; + } + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID); + stmt->setUInt32(0, accountInfo->Id); + callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); + }) + .WithChainingPreparedCallback([accountInfo](QueryCallback& callback, PreparedQueryResult characterCountsResult) { - TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is not locked to ip", _accountInfo->Login.c_str()); - if (_accountInfo->LockCountry.empty() || _accountInfo->LockCountry == "00") - TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is not locked to country", _accountInfo->Login.c_str()); - else if (!_accountInfo->LockCountry.empty() && !_ipCountry.empty()) + if (characterCountsResult) { - TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is locked to country: '%s' Player country is '%s'", - _accountInfo->Login.c_str(), _accountInfo->LockCountry.c_str(), _ipCountry.c_str()); + do + { + Field* fields = characterCountsResult->Fetch(); + accountInfo->GameAccounts[fields[0].GetUInt32()] + .CharacterCounts[Battlenet::RealmHandle{ fields[3].GetUInt8(), fields[4].GetUInt8(), fields[2].GetUInt32() }.GetAddress()] = fields[1].GetUInt8(); - if (_ipCountry != _accountInfo->LockCountry) - return ERROR_RISK_ACCOUNT_LOCKED; + } while (characterCountsResult->NextRow()); } - } - // If the account is banned, reject the logon attempt - if (_accountInfo->IsBanned) + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_LAST_PLAYER_CHARACTERS); + stmt->setUInt32(0, accountInfo->Id); + callback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); + }) + .WithPreparedCallback([this, accountInfo, asyncContinuation](PreparedQueryResult lastPlayerCharactersResult) { - if (_accountInfo->IsPermanenetlyBanned) + if (lastPlayerCharactersResult) + { + do + { + Field* fields = lastPlayerCharactersResult->Fetch(); + Battlenet::RealmHandle realmId{ fields[1].GetUInt8(), fields[2].GetUInt8(), fields[3].GetUInt32() }; + Battlenet::Session::LastPlayedCharacterInfo& lastPlayedCharacter = accountInfo->GameAccounts[fields[0].GetUInt32()] + .LastPlayedCharacters[realmId.GetSubRegionAddress()]; + + lastPlayedCharacter.RealmId = realmId; + lastPlayedCharacter.CharacterName = fields[4].GetString(); + lastPlayedCharacter.CharacterGUID = fields[5].GetUInt64(); + lastPlayedCharacter.LastPlayedTime = fields[6].GetUInt32(); + + } while (lastPlayerCharactersResult->NextRow()); + } + + _accountInfo = accountInfo; + + std::string ip_address = GetRemoteIpAddress().to_string(); + + // If the IP is 'locked', check that the player comes indeed from the correct IP address + if (_accountInfo->IsLockedToIP) { - TC_LOG_DEBUG("session", "%s [Session::HandleVerifyWebCredentials] Banned account %s tried to login!", GetClientInfo().c_str(), _accountInfo->Login.c_str()); - return ERROR_GAME_ACCOUNT_BANNED; + TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is locked to IP - '%s' is logging in from '%s'", + _accountInfo->Login.c_str(), _accountInfo->LastIP.c_str(), ip_address.c_str()); + + if (_accountInfo->LastIP != ip_address) + { + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_RISK_ACCOUNT_LOCKED, &NoData()); + return; + } } else { - TC_LOG_DEBUG("session", "%s [Session::HandleVerifyWebCredentials] Temporarily banned account %s tried to login!", GetClientInfo().c_str(), _accountInfo->Login.c_str()); - return ERROR_GAME_ACCOUNT_SUSPENDED; + TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is not locked to ip", _accountInfo->Login.c_str()); + if (_accountInfo->LockCountry.empty() || _accountInfo->LockCountry == "00") + TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is not locked to country", _accountInfo->Login.c_str()); + else if (!_accountInfo->LockCountry.empty() && !_ipCountry.empty()) + { + TC_LOG_DEBUG("session", "[Session::HandleVerifyWebCredentials] Account '%s' is locked to country: '%s' Player country is '%s'", + _accountInfo->Login.c_str(), _accountInfo->LockCountry.c_str(), _ipCountry.c_str()); + + if (_ipCountry != _accountInfo->LockCountry) + { + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_RISK_ACCOUNT_LOCKED, &NoData()); + return; + } + } } - } - logonResult.mutable_account_id()->set_low(_accountInfo->Id); - logonResult.mutable_account_id()->set_high(UI64LIT(0x100000000000000)); - for (auto itr = _accountInfo->GameAccounts.begin(); itr != _accountInfo->GameAccounts.end(); ++itr) - { - if (!itr->second.IsBanned) + // If the account is banned, reject the logon attempt + if (_accountInfo->IsBanned) { - EntityId* gameAccountId = logonResult.add_game_account_id(); - gameAccountId->set_low(itr->second.Id); - gameAccountId->set_high(UI64LIT(0x200000200576F57)); + if (_accountInfo->IsPermanenetlyBanned) + { + TC_LOG_DEBUG("session", "%s [Session::HandleVerifyWebCredentials] Banned account %s tried to login!", GetClientInfo().c_str(), _accountInfo->Login.c_str()); + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_GAME_ACCOUNT_BANNED, &NoData()); + return; + } + else + { + TC_LOG_DEBUG("session", "%s [Session::HandleVerifyWebCredentials] Temporarily banned account %s tried to login!", GetClientInfo().c_str(), _accountInfo->Login.c_str()); + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_GAME_ACCOUNT_SUSPENDED, &NoData()); + return; + } } - } - if (!_ipCountry.empty()) - logonResult.set_geoip_country(_ipCountry); + authentication::v1::LogonResult logonResult; + logonResult.set_error_code(0); + logonResult.mutable_account_id()->set_low(_accountInfo->Id); + logonResult.mutable_account_id()->set_high(UI64LIT(0x100000000000000)); + for (auto itr = _accountInfo->GameAccounts.begin(); itr != _accountInfo->GameAccounts.end(); ++itr) + { + if (!itr->second.IsBanned) + { + EntityId* gameAccountId = logonResult.add_game_account_id(); + gameAccountId->set_low(itr->second.Id); + gameAccountId->set_high(UI64LIT(0x200000200576F57)); + } + } + + if (!_ipCountry.empty()) + logonResult.set_geoip_country(_ipCountry); + + BigNumber k; + k.SetRand(8 * 64); + logonResult.set_session_key(k.AsByteArray(64).get(), 64); - BigNumber k; - k.SetRand(8 * 64); - logonResult.set_session_key(k.AsByteArray(64).get(), 64); + _authed = true; - _authed = true; + asyncContinuation(&Battlenet::Services::Authentication(this), ERROR_OK, &NoData()); + Service<authentication::v1::AuthenticationListener>(this).OnLogonComplete(&logonResult); + })); - Service<authentication::v1::AuthenticationListener>(this).OnLogonComplete(&logonResult); return ERROR_OK; } @@ -425,6 +501,7 @@ uint32 Battlenet::Session::GetRealmListTicket(std::unordered_map<std::string, Va if (!_gameAccountInfo) return ERROR_UTIL_SERVER_INVALID_IDENTITY_ARGS; + bool clientInfoOk = false; if (Variant const* clientInfo = GetParam(params, "Param_ClientInfo")) { ::JSON::RealmList::RealmListTicketClientInformation data; @@ -433,13 +510,13 @@ uint32 Battlenet::Session::GetRealmListTicket(std::unordered_map<std::string, Va { if (_clientSecret.size() == data.info().secret().size()) { - _build = data.info().version().versionbuild(); + clientInfoOk = true; memcpy(_clientSecret.data(), data.info().secret().data(), _clientSecret.size()); } } } - if (!_build) + if (!clientInfoOk) return ERROR_WOW_SERVICES_DENIED_REALM_LIST_TICKET; PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO); diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h index 99a0ec088a1..398ca176840 100644 --- a/src/server/bnetserver/Server/Session.h +++ b/src/server/bnetserver/Server/Session.h @@ -34,6 +34,8 @@ using boost::asio::ip::tcp; namespace pb = google::protobuf; +class ServiceBase; + namespace bgs { namespace protocol @@ -114,7 +116,7 @@ namespace Battlenet bool IsLockedToIP; std::string LockCountry; std::string LastIP; - uint32 FailedLogins; + uint32 LoginTicketExpiry; bool IsBanned; bool IsPermanenetlyBanned; @@ -141,8 +143,8 @@ namespace Battlenet void SendRequest(uint32 serviceHash, uint32 methodId, pb::Message const* request); - uint32 HandleLogon(authentication::v1::LogonRequest const* logonRequest); - uint32 HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* verifyWebCredentialsRequest); + uint32 HandleLogon(authentication::v1::LogonRequest const* logonRequest, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation); + uint32 HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* verifyWebCredentialsRequest, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation); uint32 HandleGetAccountState(account::v1::GetAccountStateRequest const* request, account::v1::GetAccountStateResponse* response); uint32 HandleGetGameAccountState(account::v1::GetGameAccountStateRequest const* request, account::v1::GetGameAccountStateResponse* response); uint32 HandleProcessClientRequest(game_utilities::v1::ClientRequest const* request, game_utilities::v1::ClientResponse* response); @@ -164,6 +166,8 @@ namespace Battlenet void CheckIpCallback(PreparedQueryResult result); + uint32 VerifyWebCredentials(std::string const& webCredentials, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation); + typedef uint32(Session::*ClientRequestHandler)(std::unordered_map<std::string, Variant const*> const&, game_utilities::v1::ClientResponse*); static std::unordered_map<std::string, ClientRequestHandler> const ClientRequestHandlers; @@ -176,7 +180,7 @@ namespace Battlenet MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; - std::unique_ptr<AccountInfo> _accountInfo; + std::shared_ptr<AccountInfo> _accountInfo; GameAccountInfo* _gameAccountInfo; // Points at selected game account (inside _gameAccounts) std::string _locale; diff --git a/src/server/bnetserver/Services/AuthenticationService.cpp b/src/server/bnetserver/Services/AuthenticationService.cpp index 45e9a0920f2..6fe9b481907 100644 --- a/src/server/bnetserver/Services/AuthenticationService.cpp +++ b/src/server/bnetserver/Services/AuthenticationService.cpp @@ -22,17 +22,12 @@ Battlenet::Services::Authentication::Authentication(Session* session) : Authenti { } -uint32 Battlenet::Services::Authentication::HandleLogon(authentication::v1::LogonRequest const* request, NoData* response, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) +uint32 Battlenet::Services::Authentication::HandleLogon(authentication::v1::LogonRequest const* request, NoData* /*response*/, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) { - uint32 status = _session->HandleLogon(request); - // turning this into async call will be done by stealing the continuation and calling it when done - // just a test here - continuation(this, status, response); - continuation = nullptr; - return status; + return _session->HandleLogon(request, continuation); } uint32 Battlenet::Services::Authentication::HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* request, NoData* /*response*/, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) { - return _session->HandleVerifyWebCredentials(request); + return _session->HandleVerifyWebCredentials(request, continuation); } diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 7c49b1846fd..dba35a7c906 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -116,12 +116,14 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC); -#define BnetAccountInfo "ba.id, UPPER(ba.email), ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate" +#define BnetAccountInfo "ba.id, UPPER(ba.email), ba.locked, ba.lock_country, ba.last_ip, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate" #define BnetGameAccountInfo "a.id, a.username, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, aa.gmlevel" - PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", " BnetGameAccountInfo ", ba.sha_pass_hash" + PrepareStatement(LOGIN_SEL_BNET_AUTHENTICATION, "SELECT ba.id, ba.sha_pass_hash, ba.failed_logins, ba.LoginTicket, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id WHERE email = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_BNET_AUTHENTICATION, "UPDATE battlenet_accounts SET LoginTicket = ?, LoginTicketExpiry = ? WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", " BnetGameAccountInfo "" " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" - " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? ORDER BY a.id", CONNECTION_ASYNC); + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.LoginTicket = ? ORDER BY a.id", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index b58630b4ae5..dae89a9fb3a 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -110,6 +110,8 @@ enum LoginDatabaseStatements : uint32 LOGIN_SEL_ACCOUNT_MUTE_INFO, LOGIN_DEL_ACCOUNT_MUTED, + LOGIN_SEL_BNET_AUTHENTICATION, + LOGIN_UPD_BNET_AUTHENTICATION, LOGIN_SEL_BNET_ACCOUNT_INFO, LOGIN_UPD_BNET_LAST_LOGIN_INFO, LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, |