TrinityCore
LoginRESTService.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "LoginRESTService.h"
19#include "Base64.h"
21#include "CryptoHash.h"
22#include "CryptoRandom.h"
23#include "DatabaseEnv.h"
24#include "IpNetwork.h"
25#include "IteratorPair.h"
26#include "ProtobufJSON.h"
27#include "Resolver.h"
28#include "Timer.h"
29#include "Util.h"
30
31namespace Battlenet
32{
34{
35 static LoginRESTService instance;
36 return instance;
37}
38
39bool LoginRESTService::StartNetwork(Trinity::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int32 threadCount)
40{
41 if (!HttpService::StartNetwork(ioContext, bindIp, port, threadCount))
42 return false;
43
45
46 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/login/", [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
47 {
48 return HandleGetForm(std::move(session), context);
49 });
50
51 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/gameAccounts/", [](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
52 {
53 return HandleGetGameAccounts(std::move(session), context);
54 });
55
56 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/portal/", [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
57 {
58 return HandleGetPortal(std::move(session), context);
59 });
60
61 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/login/", [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
62 {
63 return HandlePostLogin(std::move(session), context);
64 }, RequestHandlerFlag::DoNotLogRequestContent);
65
66 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/login/srp/", [](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
67 {
68 return HandlePostLoginSrpChallenge(std::move(session), context);
69 });
70
71 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/refreshLoginTicket/", [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
72 {
73 return HandlePostRefreshLoginTicket(std::move(session), context);
74 });
75
76 _bindIP = bindIp;
77 _port = port;
78
79 Trinity::Asio::Resolver resolver(ioContext);
80
81 _hostnames[0] = sConfigMgr->GetStringDefault("LoginREST.ExternalAddress", "127.0.0.1");
82 Optional<boost::asio::ip::tcp::endpoint> externalAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), _hostnames[0], std::to_string(_port));
83 if (!externalAddress)
84 {
85 TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.ExternalAddress {}", _hostnames[0]);
86 return false;
87 }
88
89 _addresses[0] = externalAddress->address();
90
91 _hostnames[1] = sConfigMgr->GetStringDefault("LoginREST.LocalAddress", "127.0.0.1");
92 Optional<boost::asio::ip::tcp::endpoint> localAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), _hostnames[1], std::to_string(_port));
93 if (!localAddress)
94 {
95 TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.LocalAddress {}", _hostnames[1]);
96 return false;
97 }
98
99 _addresses[1] = localAddress->address();
100
101 // set up form inputs
104 input = _formInputs.add_inputs();
105 input->set_input_id("account_name");
106 input->set_type("text");
107 input->set_label("E-mail");
108 input->set_max_length(320);
109
110 input = _formInputs.add_inputs();
111 input->set_input_id("password");
112 input->set_type("password");
113 input->set_label("Password");
114 input->set_max_length(128);
115
116 input = _formInputs.add_inputs();
117 input->set_input_id("log_in_submit");
118 input->set_type("submit");
119 input->set_label("Log In");
120
121 _loginTicketDuration = sConfigMgr->GetIntDefault("LoginREST.TicketDuration", 3600);
122
124
126 return true;
127}
128
129std::string const& LoginRESTService::GetHostnameForClient(boost::asio::ip::address const& address) const
130{
131 if (auto addressIndex = Trinity::Net::SelectAddressForClient(address, _addresses))
132 return _hostnames[*addressIndex];
133
134 if (address.is_loopback())
135 return _hostnames[1];
136
137 return _hostnames[0];
138}
139
141{
142 using namespace std::string_view_literals;
143
144 std::string ticket;
145 auto itr = request.find(boost::beast::http::field::authorization);
146 if (itr == request.end())
147 return ticket;
148
149 std::string_view authorization = Trinity::Net::Http::ToStdStringView(itr->value());
150 constexpr std::string_view BASIC_PREFIX = "Basic "sv;
151
152 if (authorization.starts_with(BASIC_PREFIX))
153 authorization.remove_prefix(BASIC_PREFIX.length());
154
156 if (!decoded)
157 return ticket;
158
159 std::string_view decodedHeader(reinterpret_cast<char const*>(decoded->data()), decoded->size());
160
161 if (std::size_t ticketEnd = decodedHeader.find(':'); ticketEnd != std::string_view::npos)
162 decodedHeader.remove_suffix(decodedHeader.length() - ticketEnd);
163
164 ticket = decodedHeader;
165 return ticket;
166}
167
168LoginRESTService::RequestHandlerResult LoginRESTService::HandleGetForm(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
169{
171 form.set_srp_url(Trinity::StringFormat("https://{}:{}/bnetserver/login/srp/", GetHostnameForClient(session->GetRemoteIpAddress()), _port));
172
173 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
174 context.response.body() = ::JSON::Serialize(form);
175 return RequestHandlerResult::Handled;
176}
177
179{
180 std::string ticket = ExtractAuthorization(context.request);
181 if (ticket.empty())
182 return HandleUnauthorized(std::move(session), context);
183
185 stmt->setString(0, ticket);
186 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
187 .WithPreparedCallback([session, context = std::move(context)](PreparedQueryResult result) mutable
188 {
189 JSON::Login::GameAccountList gameAccounts;
190 if (result)
191 {
192 auto formatDisplayName = [](char const* name) -> std::string
193 {
194 if (char const* hashPos = strchr(name, '#'))
195 return std::string("WoW") + ++hashPos;
196 else
197 return name;
198 };
199
200 time_t now = time(nullptr);
201 do
202 {
203 Field* fields = result->Fetch();
204 JSON::Login::GameAccountInfo* gameAccount = gameAccounts.add_game_accounts();
205 gameAccount->set_display_name(formatDisplayName(fields[0].GetCString()));
206 gameAccount->set_expansion(fields[1].GetUInt8());
207 if (!fields[2].IsNull())
208 {
209 uint32 banDate = fields[2].GetUInt32();
210 uint32 unbanDate = fields[3].GetUInt32();
211 gameAccount->set_is_suspended(unbanDate > now);
212 gameAccount->set_is_banned(banDate == unbanDate);
213 gameAccount->set_suspension_reason(fields[4].GetString());
214 gameAccount->set_suspension_expires(unbanDate);
215 }
216 } while (result->NextRow());
217 }
218
219 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
220 context.response.body() = ::JSON::Serialize(gameAccounts);
221 session->SendResponse(context);
222 }));
223
224 return RequestHandlerResult::Async;
225}
226
227LoginRESTService::RequestHandlerResult LoginRESTService::HandleGetPortal(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
228{
229 context.response.set(boost::beast::http::field::content_type, "text/plain");
230 context.response.body() = Trinity::StringFormat("{}:{}", GetHostnameForClient(session->GetRemoteIpAddress()), sConfigMgr->GetIntDefault("BattlenetPort", 1119));
231 return RequestHandlerResult::Handled;
232}
233
234LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostLogin(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
235{
236 std::shared_ptr<JSON::Login::LoginForm> loginForm = std::make_shared<JSON::Login::LoginForm>();
237 if (!::JSON::Deserialize(context.request.body(), loginForm.get()))
238 {
239 JSON::Login::LoginResult loginResult;
241 loginResult.set_error_code("UNABLE_TO_DECODE");
242 loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
243
244 context.response.result(boost::beast::http::status::bad_request);
245 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
246 context.response.body() = ::JSON::Serialize(loginResult);
247 session->SendResponse(context);
248
249 return RequestHandlerResult::Handled;
250 }
251
252 auto getInputValue = [](JSON::Login::LoginForm const* loginForm, std::string_view inputId) -> std::string
253 {
254 for (int32 i = 0; i < loginForm->inputs_size(); ++i)
255 if (loginForm->inputs(i).input_id() == inputId)
256 return loginForm->inputs(i).value();
257 return "";
258 };
259
260 std::string login(getInputValue(loginForm.get(), "account_name"));
262
264 stmt->setString(0, login);
265
266 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
267 .WithChainingPreparedCallback([this, session, context = std::move(context), loginForm = std::move(loginForm), getInputValue](QueryCallback& callback, PreparedQueryResult result) mutable
268 {
269 if (!result)
270 {
271 JSON::Login::LoginResult loginResult;
272 loginResult.set_authentication_state(JSON::Login::DONE);
273 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
274 context.response.body() = ::JSON::Serialize(loginResult);
275 session->SendResponse(context);
276 return;
277 }
278
279 std::string login(getInputValue(loginForm.get(), "account_name"));
281 bool passwordCorrect = false;
282 Optional<std::string> serverM2;
283
284 Field* fields = result->Fetch();
285 uint32 accountId = fields[0].GetUInt32();
286 if (!session->GetSessionState()->Srp)
287 {
288 SrpVersion version = SrpVersion(fields[1].GetInt8());
289 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
290 Trinity::Crypto::SRP::Salt s = fields[2].GetBinary<Trinity::Crypto::SRP::SALT_LENGTH>();
291 Trinity::Crypto::SRP::Verifier v = fields[3].GetBinary();
292 session->GetSessionState()->Srp = CreateSrpImplementation(version, SrpHashFunction::Sha256, srpUsername, s, v);
293
294 std::string password(getInputValue(loginForm.get(), "password"));
295 if (version == SrpVersion::v1)
296 Utf8ToUpperOnlyLatin(password);
297
298 passwordCorrect = session->GetSessionState()->Srp->CheckCredentials(srpUsername, password);
299 }
300 else
301 {
302 BigNumber A(getInputValue(loginForm.get(), "public_A"));
303 BigNumber M1(getInputValue(loginForm.get(), "client_evidence_M1"));
304 if (Optional<BigNumber> sessionKey = session->GetSessionState()->Srp->VerifyClientEvidence(A, M1))
305 {
306 passwordCorrect = true;
307 serverM2 = session->GetSessionState()->Srp->CalculateServerEvidence(A, M1, *sessionKey).AsHexStr();
308 }
309 }
310
311 uint32 failedLogins = fields[4].GetUInt32();
312 std::string loginTicket = fields[5].GetString();
313 uint32 loginTicketExpiry = fields[6].GetUInt32();
314 bool isBanned = fields[7].GetUInt64() != 0;
315
316 if (!passwordCorrect)
317 {
318 if (!isBanned)
319 {
320 std::string ip_address = session->GetRemoteIpAddress().to_string();
321 uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0));
322
323 if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
324 TC_LOG_DEBUG("server.http.login", "[{}, Account {}, Id {}] Attempted to connect with wrong password!", ip_address, login, accountId);
325
326 if (maxWrongPassword)
327 {
328 LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
330 stmt->setUInt32(0, accountId);
331 trans->Append(stmt);
332
333 ++failedLogins;
334
335 TC_LOG_DEBUG("server.http.login", "MaxWrongPass : {}, failed_login : {}", maxWrongPassword, accountId);
336
337 if (failedLogins >= maxWrongPassword)
338 {
339 BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP)));
340 int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
341
342 if (banType == BanMode::BAN_ACCOUNT)
343 {
344 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED);
345 stmt->setUInt32(0, accountId);
346 }
347 else
348 {
349 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
350 stmt->setString(0, ip_address);
351 }
352
353 stmt->setUInt32(1, banTime);
354 trans->Append(stmt);
355
356 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS);
357 stmt->setUInt32(0, accountId);
358 trans->Append(stmt);
359 }
360
361 LoginDatabase.CommitTransaction(trans);
362 }
363 }
364
365 JSON::Login::LoginResult loginResult;
366 loginResult.set_authentication_state(JSON::Login::DONE);
367
368 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
369 context.response.body() = ::JSON::Serialize(loginResult);
370 session->SendResponse(context);
371 return;
372 }
373
374 if (loginTicket.empty() || loginTicketExpiry < time(nullptr))
375 loginTicket = "TC-" + ByteArrayToHexStr(Trinity::Crypto::GetRandomBytes<20>());
376
378 stmt->setString(0, loginTicket);
379 stmt->setUInt32(1, time(nullptr) + _loginTicketDuration);
380 stmt->setUInt32(2, accountId);
381 callback.WithPreparedCallback([session, context = std::move(context), loginTicket = std::move(loginTicket), serverM2 = std::move(serverM2)](PreparedQueryResult) mutable
382 {
383 JSON::Login::LoginResult loginResult;
384 loginResult.set_authentication_state(JSON::Login::DONE);
385 loginResult.set_login_ticket(loginTicket);
386 if (serverM2)
387 loginResult.set_server_evidence_m2(*serverM2);
388
389 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
390 context.response.body() = ::JSON::Serialize(loginResult);
391 session->SendResponse(context);
392 }).SetNextQuery(LoginDatabase.AsyncQuery(stmt));
393 }));
394
395 return RequestHandlerResult::Async;
396}
397
398LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostLoginSrpChallenge(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
399{
400 JSON::Login::LoginForm loginForm;
401 if (!::JSON::Deserialize(context.request.body(), &loginForm))
402 {
403 JSON::Login::LoginResult loginResult;
405 loginResult.set_error_code("UNABLE_TO_DECODE");
406 loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
407
408 context.response.result(boost::beast::http::status::bad_request);
409 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
410 context.response.body() = ::JSON::Serialize(loginResult);
411 session->SendResponse(context);
412
413 return RequestHandlerResult::Handled;
414 }
415
416 std::string login;
417
418 for (int32 i = 0; i < loginForm.inputs_size(); ++i)
419 if (loginForm.inputs(i).input_id() == "account_name")
420 login = loginForm.inputs(i).value();
421
423
425 stmt->setString(0, login);
426
427 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
428 .WithPreparedCallback([session, context = std::move(context), login = std::move(login)](PreparedQueryResult result) mutable
429 {
430 if (!result)
431 {
432 JSON::Login::LoginResult loginResult;
433 loginResult.set_authentication_state(JSON::Login::DONE);
434 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
435 context.response.body() = ::JSON::Serialize(loginResult);
436 session->SendResponse(context);
437 return;
438 }
439
440 Field* fields = result->Fetch();
441 SrpVersion version = SrpVersion(fields[0].GetInt8());
442 SrpHashFunction hashFunction = SrpHashFunction::Sha256;
443 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
445 Trinity::Crypto::SRP::Verifier v = fields[2].GetBinary();
446
447 session->GetSessionState()->Srp = CreateSrpImplementation(version, hashFunction, srpUsername, s, v);
448 if (!session->GetSessionState()->Srp)
449 {
450 context.response.result(boost::beast::http::status::internal_server_error);
451 session->SendResponse(context);
452 return;
453 }
454
456 challenge.set_version(session->GetSessionState()->Srp->GetVersion());
457 challenge.set_iterations(session->GetSessionState()->Srp->GetXIterations());
458 challenge.set_modulus(session->GetSessionState()->Srp->GetN().AsHexStr());
459 challenge.set_generator(session->GetSessionState()->Srp->Getg().AsHexStr());
460 challenge.set_hash_function([=]
461 {
462 switch (hashFunction)
463 {
464 case SrpHashFunction::Sha256:
465 return "SHA-256";
466 case SrpHashFunction::Sha512:
467 return "SHA-512";
468 default:
469 break;
470 }
471 return "";
472 }());
473 challenge.set_username(srpUsername);
474 challenge.set_salt(ByteArrayToHexStr(session->GetSessionState()->Srp->s));
475 challenge.set_public_b(session->GetSessionState()->Srp->B.AsHexStr());
476
477 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
478 context.response.body() = ::JSON::Serialize(challenge);
479 session->SendResponse(context);
480 }));
481
482 return RequestHandlerResult::Async;
483}
484
485LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostRefreshLoginTicket(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
486{
487 std::string ticket = ExtractAuthorization(context.request);
488 if (ticket.empty())
489 return HandleUnauthorized(std::move(session), context);
490
492 stmt->setString(0, ticket);
493 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
494 .WithPreparedCallback([this, session, context = std::move(context), ticket = std::move(ticket)](PreparedQueryResult result) mutable
495 {
496 JSON::Login::LoginRefreshResult loginRefreshResult;
497 if (result)
498 {
499 uint32 loginTicketExpiry = (*result)[0].GetUInt32();
500 time_t now = time(nullptr);
501 if (loginTicketExpiry > now)
502 {
503 loginRefreshResult.set_login_ticket_expiry(now + _loginTicketDuration);
504
505 LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_EXISTING_AUTHENTICATION);
506 stmt->setUInt32(0, uint32(now + _loginTicketDuration));
507 stmt->setString(1, ticket);
508 LoginDatabase.Execute(stmt);
509 }
510 else
511 loginRefreshResult.set_is_expired(true);
512 }
513 else
514 loginRefreshResult.set_is_expired(true);
515
516 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
517 context.response.body() = ::JSON::Serialize(loginRefreshResult);
518 session->SendResponse(context);
519 }));
520
521 return RequestHandlerResult::Async;
522}
523
524std::unique_ptr<Trinity::Crypto::SRP::BnetSRP6Base> LoginRESTService::CreateSrpImplementation(SrpVersion version, SrpHashFunction hashFunction,
525 std::string const& username, Trinity::Crypto::SRP::Salt const& salt, Trinity::Crypto::SRP::Verifier const& verifier)
526{
527 if (version == SrpVersion::v2)
528 {
529 if (hashFunction == SrpHashFunction::Sha256)
530 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA256>>(username, salt, verifier);
531 if (hashFunction == SrpHashFunction::Sha512)
532 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA512>>(username, salt, verifier);
533 }
534
535 if (version == SrpVersion::v1)
536 {
537 if (hashFunction == SrpHashFunction::Sha256)
538 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA256>>(username, salt, verifier);
539 if (hashFunction == SrpHashFunction::Sha512)
540 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA512>>(username, salt, verifier);
541 }
542
543 return nullptr;
544}
545
546std::shared_ptr<Trinity::Net::Http::SessionState> LoginRESTService::CreateNewSessionState(boost::asio::ip::address const& address)
547{
548 std::shared_ptr<LoginSessionState> state = std::make_shared<LoginSessionState>();
549 InitAndStoreSessionState(state, address);
550 return state;
551}
552
553void LoginRESTService::OnSocketAccept(boost::asio::ip::tcp::socket&& sock, uint32 threadIndex)
554{
555 sLoginService.OnSocketOpen(std::move(sock), threadIndex);
556}
557
558void LoginRESTService::MigrateLegacyPasswordHashes() const
559{
560 if (!LoginDatabase.Query("SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = 'battlenet_accounts' AND COLUMN_NAME = 'sha_pass_hash'"))
561 return;
562
563 TC_LOG_INFO(_logger, "Updating password hashes...");
564 uint32 const start = getMSTime();
565 // the auth update query nulls salt/verifier if they cannot be converted
566 // if they are non-null but s/v have been cleared, that means a legacy tool touched our auth DB (otherwise, the core might've done it itself, it used to use those hacks too)
567 QueryResult result = LoginDatabase.Query("SELECT id, sha_pass_hash, IF((salt IS null) OR (verifier IS null), 0, 1) AS shouldWarn FROM battlenet_accounts WHERE sha_pass_hash != DEFAULT(sha_pass_hash) OR salt IS NULL OR verifier IS NULL");
568 if (!result)
569 {
570 TC_LOG_INFO(_logger, ">> No password hashes to update - this took us {} ms to realize", GetMSTimeDiffToNow(start));
571 return;
572 }
573
574 bool hadWarning = false;
575 uint32 c = 0;
576 LoginDatabaseTransaction tx = LoginDatabase.BeginTransaction();
577 do
578 {
579 uint32 const id = (*result)[0].GetUInt32();
580
581 Trinity::Crypto::SRP::Salt salt = Trinity::Crypto::GetRandomBytes<Trinity::Crypto::SRP::SALT_LENGTH>();
582 BigNumber x = Trinity::Crypto::SHA256::GetDigestOf(salt, HexStrToByteArray<Trinity::Crypto::SHA256::DIGEST_LENGTH>((*result)[1].GetString(), true));
584
585 if ((*result)[2].GetInt64())
586 {
587 if (!hadWarning)
588 {
589 hadWarning = true;
590 TC_LOG_WARN(_logger,
591 " ========\n"
592 "(!) You appear to be using an outdated external account management tool.\n"
593 "(!) Update your external tool.\n"
594 "(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
595 " ========");
596 }
597 }
598
601 stmt->setBinary(1, salt);
602 stmt->setBinary(2, verifier);
603 stmt->setUInt32(3, id);
604 tx->Append(stmt);
605
606 tx->Append(Trinity::StringFormat("UPDATE battlenet_accounts SET sha_pass_hash = DEFAULT(sha_pass_hash) WHERE id = {}", id).c_str());
607
608 if (tx->GetSize() >= 10000)
609 {
610 LoginDatabase.CommitTransaction(tx);
611 tx = LoginDatabase.BeginTransaction();
612 }
613
614 ++c;
615 } while (result->NextRow());
616 LoginDatabase.CommitTransaction(tx);
617
618 TC_LOG_INFO(_logger, ">> {} password hashes updated in {} ms", c, GetMSTimeDiffToNow(start));
619}
620}
#define sConfigMgr
Definition: Config.h:61
SQLTransaction< LoginDatabaseConnection > LoginDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
Definition: DatabaseEnv.cpp:22
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
@ DONE
#define TC_LOG_WARN(filterType__,...)
Definition: Log.h:162
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
@ LOGIN_UPD_BNET_RESET_FAILED_LOGINS
@ LOGIN_SEL_BNET_AUTHENTICATION
@ LOGIN_UPD_BNET_FAILED_LOGINS
@ LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED
@ LOGIN_SEL_BNET_CHECK_PASSWORD_BY_EMAIL
@ LOGIN_UPD_BNET_LOGON
@ LOGIN_UPD_BNET_AUTHENTICATION
@ LOGIN_SEL_BNET_EXISTING_AUTHENTICATION
@ LOGIN_SEL_BNET_GAME_ACCOUNT_LIST
@ LOGIN_INS_IP_AUTO_BANNED
Definition: LoginDatabase.h:35
#define sLoginService
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
BanMode
Ban function modes.
@ BAN_ACCOUNT
@ BAN_IP
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition: Util.cpp:795
std::string ByteArrayToHexStr(Container const &c, bool reverse=false)
Definition: Util.h:368
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
static void OnSocketAccept(boost::asio::ip::tcp::socket &&sock, uint32 threadIndex)
void AsyncAcceptWithCallback()
Definition: AsyncAcceptor.h:45
const ::std::string & value() const
Definition: Login.pb.h:1725
const ::std::string & input_id() const
Definition: Login.pb.h:1649
void set_type(const ::std::string &value)
Definition: Login.pb.h:1262
void set_max_length(::google::protobuf::uint32 value)
Definition: Login.pb.h:1412
void set_label(const ::std::string &value)
Definition: Login.pb.h:1338
void set_input_id(const ::std::string &value)
Definition: Login.pb.h:1186
inline ::Battlenet::JSON::Login::FormInput * add_inputs()
Definition: Login.pb.h:1462
void set_type(::Battlenet::JSON::Login::FormType value)
Definition: Login.pb.h:1440
void set_srp_url(const ::std::string &value)
Definition: Login.pb.h:1497
const ::Battlenet::JSON::Login::FormInputValue & inputs(int index) const
Definition: Login.pb.h:2024
void set_authentication_state(::Battlenet::JSON::Login::AuthenticationState value)
Definition: Login.pb.h:2601
void set_error_code(const ::std::string &value)
Definition: Login.pb.h:2628
void set_error_message(const ::std::string &value)
Definition: Login.pb.h:2704
void set_iterations(::google::protobuf::uint32 value)
Definition: Login.pb.h:2093
void set_version(::google::protobuf::uint32 value)
Definition: Login.pb.h:2069
void set_salt(const ::std::string &value)
Definition: Login.pb.h:2423
void set_public_b(const ::std::string &value)
Definition: Login.pb.h:2499
void set_hash_function(const ::std::string &value)
Definition: Login.pb.h:2271
void set_username(const ::std::string &value)
Definition: Login.pb.h:2347
void set_modulus(const ::std::string &value)
Definition: Login.pb.h:2119
void set_generator(const ::std::string &value)
Definition: Login.pb.h:2195
std::array< boost::asio::ip::address, 2 > _addresses
RequestHandlerResult HandleGetPortal(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context) const
std::array< std::string, 2 > _hostnames
RequestHandlerResult HandlePostLogin(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context) const
RequestHandlerResult HandleGetForm(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context) const
std::string const & GetHostnameForClient(boost::asio::ip::address const &address) const
static std::string ExtractAuthorization(HttpRequest const &request)
static RequestHandlerResult HandleGetGameAccounts(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context)
RequestHandlerResult HandlePostRefreshLoginTicket(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context) const
JSON::Login::FormInputs _formInputs
bool StartNetwork(Trinity::Asio::IoContext &ioContext, std::string const &bindIp, uint16 port, int32 threadCount=1) override
static LoginRESTService & Instance()
static void OnSocketAccept(boost::asio::ip::tcp::socket &&sock, uint32 threadIndex)
static RequestHandlerResult HandlePostLoginSrpChallenge(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context)
Trinity::Net::Http::Request HttpRequest
BigNumber ModExp(BigNumber const &bn1, BigNumber const &bn2) const
Definition: BigNumber.cpp:152
std::vector< uint8 > ToByteVector(int32 minSize=0, bool littleEndian=true) const
Definition: BigNumber.cpp:195
Class used to access individual fields of database query result.
Definition: Field.h:90
uint32 GetUInt32() const
Definition: Field.cpp:62
void setBinary(const uint8 index, const std::vector< uint8 > &value)
void setUInt32(const uint8 index, const uint32 value)
void setString(const uint8 index, const std::string &value)
void setInt8(const uint8 index, const int8 value)
AsyncAcceptor * _acceptor
Definition: SocketMgr.h:137
Optional< boost::asio::ip::tcp::endpoint > Resolve(boost::asio::ip::tcp const &protocol, std::string const &host, std::string const &service)
Definition: Resolver.h:38
static BigNumber const g
Definition: SRP6.h:157
static BigNumber const N
Definition: SRP6.h:156
static Digest GetDigestOf(uint8 const *data, size_t len)
Definition: CryptoHash.h:49
static RequestHandlerResult HandleUnauthorized(std::shared_ptr< AbstractSocket > session, RequestContext &context)
Definition: HttpService.cpp:90
void RegisterHandler(boost::beast::http::verb method, std::string_view path, Callable handler, RequestHandlerFlag flags=RequestHandlerFlag::None)
Definition: HttpService.h:143
TC_SHARED_API bool Deserialize(std::string const &json, google::protobuf::Message *message)
TC_SHARED_API std::string Serialize(google::protobuf::Message const &message)
decltype(auto) post(boost::asio::io_context &ioContext, T &&t)
Definition: IoContext.h:54
std::array< uint8, SALT_LENGTH > Salt
Definition: SRP6.h:33
std::vector< uint8 > Verifier
Definition: SRP6.h:35
static constexpr size_t SALT_LENGTH
Definition: SRP6.h:32
std::string_view ToStdStringView(boost::beast::string_view bsw)
Definition: HttpCommon.h:43
Optional< std::size_t > SelectAddressForClient(boost::asio::ip::address const &clientAddress, std::span< boost::asio::ip::address const > const &addresses)
Definition: IpNetwork.cpp:71
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
static Optional< std::vector< uint8 > > Decode(std::string_view data)
Definition: Base64.cpp:54