TrinityCore
Loading...
Searching...
No Matches
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"
20#include "Common.h"
22#include "CryptoHash.h"
23#include "CryptoRandom.h"
24#include "DatabaseEnv.h"
25#include "IpNetwork.h"
26#include "ProtobufJSON.h"
27#include "Resolver.h"
28#include "SslContext.h"
29#include "Timer.h"
30#include "Util.h"
31
32namespace Battlenet
33{
35{
36 static LoginRESTService instance;
37 return instance;
38}
39
40bool LoginRESTService::StartNetwork(Trinity::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int32 threadCount)
41{
42 Trinity::Net::Resolver resolver(ioContext);
43
44 _externalHostname = sConfigMgr->GetStringDefault("LoginREST.ExternalAddress"sv, "127.0.0.1");
45
46 std::ranges::transform(resolver.ResolveAll(_externalHostname, ""),
47 std::back_inserter(_addresses),
48 [](boost::asio::ip::tcp::endpoint const& endpoint) { return endpoint.address(); });
49
50 if (_addresses.empty())
51 {
52 TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.ExternalAddress {}", _externalHostname);
53 return false;
54 }
55
56 _localHostname = sConfigMgr->GetStringDefault("LoginREST.LocalAddress"sv, "127.0.0.1");
58
59 std::ranges::transform(resolver.ResolveAll(_localHostname, ""),
60 std::back_inserter(_addresses),
61 [](boost::asio::ip::tcp::endpoint const& endpoint) { return endpoint.address(); });
62
64 {
65 TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.LocalAddress {}", _localHostname);
66 return false;
67 }
68
69 if (!HttpService::StartNetwork(ioContext, bindIp, port, threadCount))
70 return false;
71
73
74 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/login/"sv, [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
75 {
76 return HandleGetForm(std::move(session), context);
77 });
78
79 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/gameAccounts/"sv, [](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
80 {
81 return HandleGetGameAccounts(std::move(session), context);
82 });
83
84 RegisterHandler(boost::beast::http::verb::get, "/bnetserver/portal/"sv, [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
85 {
86 return HandleGetPortal(std::move(session), context);
87 });
88
89 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/login/"sv, [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
90 {
91 return HandlePostLogin(std::move(session), context);
92 }, RequestHandlerFlag::DoNotLogRequestContent);
93
94 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/login/srp/"sv, [](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
95 {
96 return HandlePostLoginSrpChallenge(std::move(session), context);
97 });
98
99 RegisterHandler(boost::beast::http::verb::post, "/bnetserver/refreshLoginTicket/"sv, [this](std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
100 {
101 return HandlePostRefreshLoginTicket(std::move(session), context);
102 });
103
104 _port = port;
105
106 // set up form inputs
109 input = _formInputs.add_inputs();
110 input->set_input_id("account_name");
111 input->set_type("text");
112 input->set_label("E-mail");
113 input->set_max_length(320);
114
115 input = _formInputs.add_inputs();
116 input->set_input_id("password");
117 input->set_type("password");
118 input->set_label("Password");
119 input->set_max_length(128);
120
121 input = _formInputs.add_inputs();
122 input->set_input_id("log_in_submit");
123 input->set_type("submit");
124 input->set_label("Log In");
125
126 _loginTicketDuration = sConfigMgr->GetIntDefault("LoginREST.TicketDuration"sv, 3600);
127
129
130 return true;
131}
132
133std::string const& LoginRESTService::GetHostnameForClient(boost::asio::ip::address const& address) const
134{
136 return *addressIndex >= _firstLocalAddressIndex ? _localHostname : _externalHostname;
137
138 if (address.is_loopback())
139 return _localHostname;
140
141 return _externalHostname;
142}
143
145{
146 std::string ticket;
147 auto itr = request.find(boost::beast::http::field::authorization);
148 if (itr == request.end())
149 return ticket;
150
151 std::string_view authorization = Trinity::Net::Http::ToStdStringView(itr->value());
152 constexpr std::string_view BASIC_PREFIX = "Basic "sv;
153
154 if (authorization.starts_with(BASIC_PREFIX))
155 authorization.remove_prefix(BASIC_PREFIX.length());
156
158 if (!decoded)
159 return ticket;
160
161 std::string_view decodedHeader(reinterpret_cast<char const*>(decoded->data()), decoded->size());
162
163 if (std::size_t ticketEnd = decodedHeader.find(':'); ticketEnd != std::string_view::npos)
164 decodedHeader.remove_suffix(decodedHeader.length() - ticketEnd);
165
166 ticket = decodedHeader;
167 return ticket;
168}
169
170LoginRESTService::RequestHandlerResult LoginRESTService::HandleGetForm(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
171{
173 form.set_srp_url(Trinity::StringFormat("http{}://{}:{}/bnetserver/login/srp/", !SslContext::UsesDevWildcardCertificate() ? "s" : "",
174 GetHostnameForClient(session->GetRemoteIpAddress()), _port));
175
176 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
177 context.response.body() = ::JSON::Serialize(form);
178 return RequestHandlerResult::Handled;
179}
180
182{
183 std::string ticket = ExtractAuthorization(context.request);
184 if (ticket.empty())
185 return HandleUnauthorized(std::move(session), context);
186
188 stmt->setString(0, ticket);
189 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
190 .WithPreparedCallback([session, context = std::move(context)](PreparedQueryResult result) mutable
191 {
192 JSON::Login::GameAccountList gameAccounts;
193 if (result)
194 {
195 auto formatDisplayName = [](char const* name) -> std::string
196 {
197 if (char const* hashPos = strchr(name, '#'))
198 return std::string("WoW") + ++hashPos;
199 else
200 return name;
201 };
202
203 time_t now = time(nullptr);
204 do
205 {
206 Field* fields = result->Fetch();
207 JSON::Login::GameAccountInfo* gameAccount = gameAccounts.add_game_accounts();
208 gameAccount->set_display_name(formatDisplayName(fields[0].GetCString()));
209 gameAccount->set_expansion(fields[1].GetUInt8());
210 if (!fields[2].IsNull())
211 {
212 uint32 banDate = fields[2].GetUInt32();
213 uint32 unbanDate = fields[3].GetUInt32();
214 gameAccount->set_is_suspended(unbanDate > now);
215 gameAccount->set_is_banned(banDate == unbanDate);
216 gameAccount->set_suspension_reason(fields[4].GetString());
217 gameAccount->set_suspension_expires(unbanDate);
218 }
219 } while (result->NextRow());
220 }
221
222 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
223 context.response.body() = ::JSON::Serialize(gameAccounts);
224 session->SendResponse(context);
225 }));
226
227 return RequestHandlerResult::Async;
228}
229
230LoginRESTService::RequestHandlerResult LoginRESTService::HandleGetPortal(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
231{
232 context.response.set(boost::beast::http::field::content_type, "text/plain");
233 context.response.body() = Trinity::StringFormat("{}:{}", GetHostnameForClient(session->GetRemoteIpAddress()), sConfigMgr->GetIntDefault("BattlenetPort", 1119));
234 return RequestHandlerResult::Handled;
235}
236
237LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostLogin(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
238{
239 std::shared_ptr<JSON::Login::LoginForm> loginForm = std::make_shared<JSON::Login::LoginForm>();
240 if (!::JSON::Deserialize(context.request.body(), loginForm.get()))
241 {
242 JSON::Login::LoginResult loginResult;
243 loginResult.set_authentication_state(JSON::Login::LOGIN);
244 loginResult.set_error_code("UNABLE_TO_DECODE");
245 loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
246
247 context.response.result(boost::beast::http::status::bad_request);
248 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
249 context.response.body() = ::JSON::Serialize(loginResult);
250 session->SendResponse(context);
251
252 return RequestHandlerResult::Handled;
253 }
254
255 auto getInputValue = [](JSON::Login::LoginForm const* loginForm, std::string_view inputId) -> std::string
256 {
257 for (int32 i = 0; i < loginForm->inputs_size(); ++i)
258 if (loginForm->inputs(i).input_id() == inputId)
259 return loginForm->inputs(i).value();
260 return "";
261 };
262
263 std::string login(getInputValue(loginForm.get(), "account_name"));
265
267 stmt->setString(0, login);
268
269 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
270 .WithChainingPreparedCallback([this, session, context = std::move(context), loginForm = std::move(loginForm), getInputValue](QueryCallback& callback, PreparedQueryResult result) mutable
271 {
272 if (!result)
273 {
274 JSON::Login::LoginResult loginResult;
275 loginResult.set_authentication_state(JSON::Login::DONE);
276 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
277 context.response.body() = ::JSON::Serialize(loginResult);
278 session->SendResponse(context);
279 return;
280 }
281
282 std::string login(getInputValue(loginForm.get(), "account_name"));
284 bool passwordCorrect = false;
285 Optional<std::string> serverM2;
286
287 Field* fields = result->Fetch();
288 uint32 accountId = fields[0].GetUInt32();
289 if (!session->GetSessionState()->Srp)
290 {
291 SrpVersion version = SrpVersion(fields[1].GetInt8());
292 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
295 session->GetSessionState()->Srp = CreateSrpImplementation(version, SrpHashFunction::Sha256, srpUsername, s, v);
296
297 std::string password(getInputValue(loginForm.get(), "password"));
298 if (version == SrpVersion::v1)
299 Utf8ToUpperOnlyLatin(password);
300
301 passwordCorrect = session->GetSessionState()->Srp->CheckCredentials(srpUsername, password);
302 }
303 else
304 {
305 BigNumber A(getInputValue(loginForm.get(), "public_A"));
306 BigNumber M1(getInputValue(loginForm.get(), "client_evidence_M1"));
307 if (Optional<BigNumber> sessionKey = session->GetSessionState()->Srp->VerifyClientEvidence(A, M1))
308 {
309 passwordCorrect = true;
310 serverM2 = session->GetSessionState()->Srp->CalculateServerEvidence(A, M1, *sessionKey).AsHexStr();
311 }
312 }
313
314 uint32 failedLogins = fields[4].GetUInt32();
315 std::string loginTicket = fields[5].GetString();
316 uint32 loginTicketExpiry = fields[6].GetUInt32();
317 bool isBanned = fields[7].GetUInt64() != 0;
318
319 if (!passwordCorrect)
320 {
321 if (!isBanned)
322 {
323 std::string ip_address = session->GetRemoteIpAddress().to_string();
324 uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0));
325
326 if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
327 TC_LOG_DEBUG("server.http.login", "[{}, Account {}, Id {}] Attempted to connect with wrong password!", ip_address, login, accountId);
328
329 if (maxWrongPassword)
330 {
331 LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
333 stmt->setUInt32(0, accountId);
334 trans->Append(stmt);
335
336 ++failedLogins;
337
338 TC_LOG_DEBUG("server.http.login", "MaxWrongPass : {}, failed_login : {}", maxWrongPassword, accountId);
339
340 if (failedLogins >= maxWrongPassword)
341 {
342 BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP)));
343 int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
344
345 if (banType == BanMode::BAN_ACCOUNT)
346 {
347 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED);
348 stmt->setUInt32(0, accountId);
349 }
350 else
351 {
352 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
353 stmt->setString(0, ip_address);
354 }
355
356 stmt->setUInt32(1, banTime);
357 trans->Append(stmt);
358
359 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS);
360 stmt->setUInt32(0, accountId);
361 trans->Append(stmt);
362 }
363
364 LoginDatabase.CommitTransaction(trans);
365 }
366 }
367
368 JSON::Login::LoginResult loginResult;
369 loginResult.set_authentication_state(JSON::Login::DONE);
370
371 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
372 context.response.body() = ::JSON::Serialize(loginResult);
373 session->SendResponse(context);
374 return;
375 }
376
377 if (loginTicket.empty() || loginTicketExpiry < time(nullptr))
378 loginTicket = "TC-" + ByteArrayToHexStr(Trinity::Crypto::GetRandomBytes<20>());
379
381 stmt->setString(0, loginTicket);
382 stmt->setUInt32(1, time(nullptr) + _loginTicketDuration);
383 stmt->setUInt32(2, accountId);
384 callback.WithPreparedCallback([session, context = std::move(context), loginTicket = std::move(loginTicket), serverM2 = std::move(serverM2)](PreparedQueryResult) mutable
385 {
386 JSON::Login::LoginResult loginResult;
387 loginResult.set_authentication_state(JSON::Login::DONE);
388 loginResult.set_login_ticket(loginTicket);
389 if (serverM2)
390 loginResult.set_server_evidence_m2(*serverM2);
391
392 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
393 context.response.body() = ::JSON::Serialize(loginResult);
394 session->SendResponse(context);
395 }).SetNextQuery(LoginDatabase.AsyncQuery(stmt));
396 }));
397
398 return RequestHandlerResult::Async;
399}
400
401LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostLoginSrpChallenge(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
402{
403 JSON::Login::LoginForm loginForm;
404 if (!::JSON::Deserialize(context.request.body(), &loginForm))
405 {
406 JSON::Login::LoginResult loginResult;
407 loginResult.set_authentication_state(JSON::Login::LOGIN);
408 loginResult.set_error_code("UNABLE_TO_DECODE");
409 loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
410
411 context.response.result(boost::beast::http::status::bad_request);
412 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
413 context.response.body() = ::JSON::Serialize(loginResult);
414 session->SendResponse(context);
415
416 return RequestHandlerResult::Handled;
417 }
418
419 std::string login;
420
421 for (int32 i = 0; i < loginForm.inputs_size(); ++i)
422 if (loginForm.inputs(i).input_id() == "account_name")
423 login = loginForm.inputs(i).value();
424
426
428 stmt->setString(0, login);
429
430 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
431 .WithPreparedCallback([session, context = std::move(context), login = std::move(login)](PreparedQueryResult result) mutable
432 {
433 if (!result)
434 {
435 JSON::Login::LoginResult loginResult;
436 loginResult.set_authentication_state(JSON::Login::DONE);
437 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
438 context.response.body() = ::JSON::Serialize(loginResult);
439 session->SendResponse(context);
440 return;
441 }
442
443 Field* fields = result->Fetch();
444 SrpVersion version = SrpVersion(fields[0].GetInt8());
445 SrpHashFunction hashFunction = SrpHashFunction::Sha256;
446 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
448 Trinity::Crypto::SRP::Verifier v = fields[2].GetBinary();
449
450 session->GetSessionState()->Srp = CreateSrpImplementation(version, hashFunction, srpUsername, s, v);
451 if (!session->GetSessionState()->Srp)
452 {
453 context.response.result(boost::beast::http::status::internal_server_error);
454 session->SendResponse(context);
455 return;
456 }
457
459 challenge.set_version(session->GetSessionState()->Srp->GetVersion());
460 challenge.set_iterations(session->GetSessionState()->Srp->GetXIterations());
461 challenge.set_modulus(session->GetSessionState()->Srp->GetN().AsHexStr());
462 challenge.set_generator(session->GetSessionState()->Srp->Getg().AsHexStr());
463 challenge.set_hash_function([=]
464 {
465 switch (hashFunction)
466 {
467 case SrpHashFunction::Sha256:
468 return "SHA-256";
469 case SrpHashFunction::Sha512:
470 return "SHA-512";
471 default:
472 break;
473 }
474 return "";
475 }());
476 challenge.set_username(srpUsername);
477 challenge.set_salt(ByteArrayToHexStr(session->GetSessionState()->Srp->s));
478 challenge.set_public_b(session->GetSessionState()->Srp->B.AsHexStr());
479
480 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
481 context.response.body() = ::JSON::Serialize(challenge);
482 session->SendResponse(context);
483 }));
484
485 return RequestHandlerResult::Async;
486}
487
488LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostRefreshLoginTicket(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
489{
490 std::string ticket = ExtractAuthorization(context.request);
491 if (ticket.empty())
492 return HandleUnauthorized(std::move(session), context);
493
495 stmt->setString(0, ticket);
496 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
497 .WithPreparedCallback([this, session, context = std::move(context), ticket = std::move(ticket)](PreparedQueryResult result) mutable
498 {
499 JSON::Login::LoginRefreshResult loginRefreshResult;
500 if (result)
501 {
502 uint32 loginTicketExpiry = (*result)[0].GetUInt32();
503 time_t now = time(nullptr);
504 if (loginTicketExpiry > now)
505 {
506 loginRefreshResult.set_login_ticket_expiry(now + _loginTicketDuration);
507
508 LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_EXISTING_AUTHENTICATION);
509 stmt->setUInt32(0, uint32(now + _loginTicketDuration));
510 stmt->setString(1, ticket);
511 LoginDatabase.Execute(stmt);
512 }
513 else
514 loginRefreshResult.set_is_expired(true);
515 }
516 else
517 loginRefreshResult.set_is_expired(true);
518
519 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
520 context.response.body() = ::JSON::Serialize(loginRefreshResult);
521 session->SendResponse(context);
522 }));
523
524 return RequestHandlerResult::Async;
525}
526
527std::unique_ptr<Trinity::Crypto::SRP::BnetSRP6Base> LoginRESTService::CreateSrpImplementation(SrpVersion version, SrpHashFunction hashFunction,
528 std::string const& username, Trinity::Crypto::SRP::Salt const& salt, Trinity::Crypto::SRP::Verifier const& verifier)
529{
530 if (version == SrpVersion::v2)
531 {
532 if (hashFunction == SrpHashFunction::Sha256)
533 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA256>>(username, salt, verifier);
534 if (hashFunction == SrpHashFunction::Sha512)
535 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA512>>(username, salt, verifier);
536 }
537
538 if (version == SrpVersion::v1)
539 {
540 if (hashFunction == SrpHashFunction::Sha256)
541 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA256>>(username, salt, verifier);
542 if (hashFunction == SrpHashFunction::Sha512)
543 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA512>>(username, salt, verifier);
544 }
545
546 return nullptr;
547}
548
549std::shared_ptr<Trinity::Net::Http::SessionState> LoginRESTService::CreateNewSessionState(boost::asio::ip::address const& address)
550{
551 std::shared_ptr<LoginSessionState> state = std::make_shared<LoginSessionState>();
552 InitAndStoreSessionState(state, address);
553 return state;
554}
555
556void LoginRESTService::MigrateLegacyPasswordHashes() const
557{
558 if (!LoginDatabase.Query("SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = 'battlenet_accounts' AND COLUMN_NAME = 'sha_pass_hash'"))
559 return;
560
561 TC_LOG_INFO(_logger, "Updating password hashes...");
562 uint32 const start = getMSTime();
563 // the auth update query nulls salt/verifier if they cannot be converted
564 // 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)
565 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");
566 if (!result)
567 {
568 TC_LOG_INFO(_logger, ">> No password hashes to update - this took us {} ms to realize", GetMSTimeDiffToNow(start));
569 return;
570 }
571
572 bool hadWarning = false;
573 uint32 c = 0;
574 LoginDatabaseTransaction tx = LoginDatabase.BeginTransaction();
575 do
576 {
577 uint32 const id = (*result)[0].GetUInt32();
578
579 Trinity::Crypto::SRP::Salt salt = Trinity::Crypto::GetRandomBytes<Trinity::Crypto::SRP::SALT_LENGTH>();
580 BigNumber x = Trinity::Crypto::SHA256::GetDigestOf(salt, HexStrToByteArray<Trinity::Crypto::SHA256::DIGEST_LENGTH>((*result)[1].GetString(), true));
582
583 if ((*result)[2].GetInt64())
584 {
585 if (!hadWarning)
586 {
587 hadWarning = true;
588 TC_LOG_WARN(_logger,
589 " ========\n"
590 "(!) You appear to be using an outdated external account management tool.\n"
591 "(!) Update your external tool.\n"
592 "(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
593 " ========");
594 }
595 }
596
599 stmt->setBinary(1, salt);
600 stmt->setBinary(2, std::move(verifier));
601 stmt->setUInt32(3, id);
602 tx->Append(stmt);
603
604 tx->Append(Trinity::StringFormat("UPDATE battlenet_accounts SET sha_pass_hash = DEFAULT(sha_pass_hash) WHERE id = {}", id).c_str());
605
606 if (tx->GetSize() >= 10000)
607 {
608 LoginDatabase.CommitTransaction(tx);
609 tx = LoginDatabase.BeginTransaction();
610 }
611
612 ++c;
613 } while (result->NextRow());
614 LoginDatabase.CommitTransaction(tx);
615
616 TC_LOG_INFO(_logger, ">> {} password hashes updated in {} ms", c, GetMSTimeDiffToNow(start));
617}
618}
#define sConfigMgr
Definition Config.h:64
SQLTransaction< LoginDatabaseConnection > LoginDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
int32_t int32
Definition Define.h:150
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:187
@ 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
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:752
std::string ByteArrayToHexStr(Container const &c, bool reverse=false)
Definition Util.h:431
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
const ::std::string & value() const
Definition Login.pb.h:1740
const ::std::string & input_id() const
Definition Login.pb.h:1664
void set_type(const ::std::string &value)
Definition Login.pb.h:1277
void set_max_length(::google::protobuf::uint32 value)
Definition Login.pb.h:1427
void set_label(const ::std::string &value)
Definition Login.pb.h:1353
void set_input_id(const ::std::string &value)
Definition Login.pb.h:1201
inline ::Battlenet::JSON::Login::FormInput * add_inputs()
Definition Login.pb.h:1477
void set_type(::Battlenet::JSON::Login::FormType value)
Definition Login.pb.h:1455
void set_srp_url(const ::std::string &value)
Definition Login.pb.h:1512
const ::Battlenet::JSON::Login::FormInputValue & inputs(int index) const
Definition Login.pb.h:2039
void set_authentication_state(::Battlenet::JSON::Login::AuthenticationState value)
Definition Login.pb.h:2616
void set_server_evidence_m2(const ::std::string &value)
Definition Login.pb.h:2947
void set_error_code(const ::std::string &value)
Definition Login.pb.h:2643
void set_login_ticket(const ::std::string &value)
Definition Login.pb.h:2871
void set_error_message(const ::std::string &value)
Definition Login.pb.h:2719
void set_iterations(::google::protobuf::uint32 value)
Definition Login.pb.h:2108
void set_version(::google::protobuf::uint32 value)
Definition Login.pb.h:2084
void set_salt(const ::std::string &value)
Definition Login.pb.h:2438
void set_public_b(const ::std::string &value)
Definition Login.pb.h:2514
void set_hash_function(const ::std::string &value)
Definition Login.pb.h:2286
void set_username(const ::std::string &value)
Definition Login.pb.h:2362
void set_modulus(const ::std::string &value)
Definition Login.pb.h:2134
void set_generator(const ::std::string &value)
Definition Login.pb.h:2210
RequestHandlerResult HandleGetPortal(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context) const
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()
std::vector< boost::asio::ip::address > _addresses
static RequestHandlerResult HandlePostLoginSrpChallenge(std::shared_ptr< LoginHttpSession > session, HttpRequestContext &context)
Trinity::Net::Http::Request HttpRequest
static bool UsesDevWildcardCertificate()
Definition SslContext.h:32
BigNumber ModExp(BigNumber const &bn1, BigNumber const &bn2) const
std::vector< uint8 > ToByteVector(int32 minSize=0, bool littleEndian=true) const
Class used to access individual fields of database query result.
Definition Field.h:94
std::vector< uint8 > GetBinary() const noexcept
Definition Field.cpp:125
uint64 GetUInt64() const noexcept
Definition Field.cpp:71
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
std::string GetString() const noexcept
Definition Field.cpp:113
void setInt8(uint8 index, int8 value)
void setBinary(uint8 index, std::vector< uint8 > &&value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
QueryCallback && WithPreparedCallback(std::function< void(PreparedQueryResult)> &&callback)
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)
void RegisterHandler(boost::beast::http::verb method, std::string_view path, Callable handler, RequestHandlerFlag flags=RequestHandlerFlag::None)
std::vector< boost::asio::ip::tcp::endpoint > ResolveAll(std::string_view host, std::string_view service)
Definition Resolver.cpp:34
TC_SHARED_API bool Deserialize(std::string const &json, google::protobuf::Message *message)
TC_SHARED_API std::string Serialize(google::protobuf::Message const &message)
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) noexcept
Default TC string format function.
static Optional< std::vector< uint8 > > Decode(std::string_view data)
Definition Base64.cpp:54