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
107 JSON::Login::FormInput* input;
108 _formInputs.set_type(JSON::Login::LOGIN_FORM);
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{
172 JSON::Login::FormInputs form = _formInputs;
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
251 return RequestHandlerResult::Handled;
252 }
253
254 auto getInputValue = [](JSON::Login::LoginForm const* loginForm, std::string_view inputId) -> std::string
255 {
256 for (int32 i = 0; i < loginForm->inputs_size(); ++i)
257 if (loginForm->inputs(i).input_id() == inputId)
258 return loginForm->inputs(i).value();
259 return "";
260 };
261
262 std::string login(getInputValue(loginForm.get(), "account_name"));
264
266 stmt->setString(0, login);
267
268 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
269 .WithChainingPreparedCallback([this, session, context = std::move(context), loginForm = std::move(loginForm), getInputValue](QueryCallback& callback, PreparedQueryResult result) mutable
270 {
271 if (!result)
272 {
273 JSON::Login::LoginResult loginResult;
274 loginResult.set_authentication_state(JSON::Login::DONE);
275 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
276 context.response.body() = ::JSON::Serialize(loginResult);
277 session->SendResponse(context);
278 return;
279 }
280
281 std::string login(getInputValue(loginForm.get(), "account_name"));
283 bool passwordCorrect = false;
284 Optional<std::string> serverM2;
285
286 Field* fields = result->Fetch();
287 uint32 accountId = fields[0].GetUInt32();
288 if (!session->GetSessionState()->Srp)
289 {
290 SrpVersion version = SrpVersion(fields[1].GetInt8());
291 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
294 session->GetSessionState()->Srp = CreateSrpImplementation(version, SrpHashFunction::Sha256, srpUsername, s, v);
295
296 std::string password(getInputValue(loginForm.get(), "password"));
297 if (version == SrpVersion::v1)
298 Utf8ToUpperOnlyLatin(password);
299
300 passwordCorrect = session->GetSessionState()->Srp->CheckCredentials(srpUsername, password);
301 }
302 else
303 {
304 BigNumber A(getInputValue(loginForm.get(), "public_A"));
305 BigNumber M1(getInputValue(loginForm.get(), "client_evidence_M1"));
306 if (Optional<BigNumber> sessionKey = session->GetSessionState()->Srp->VerifyClientEvidence(A, M1))
307 {
308 passwordCorrect = true;
309 serverM2 = session->GetSessionState()->Srp->CalculateServerEvidence(A, M1, *sessionKey).AsHexStr();
310 }
311 }
312
313 uint32 failedLogins = fields[4].GetUInt32();
314 std::string loginTicket = fields[5].GetString();
315 uint32 loginTicketExpiry = fields[6].GetUInt32();
316 bool isBanned = fields[7].GetUInt64() != 0;
317
318 if (!passwordCorrect)
319 {
320 if (!isBanned)
321 {
322 std::string ip_address = session->GetRemoteIpAddress().to_string();
323 uint32 maxWrongPassword = uint32(sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0));
324
325 if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
326 TC_LOG_DEBUG("server.http.login", "[{}, Account {}, Id {}] Attempted to connect with wrong password!", ip_address, login, accountId);
327
328 if (maxWrongPassword)
329 {
330 LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
332 stmt->setUInt32(0, accountId);
333 trans->Append(stmt);
334
335 ++failedLogins;
336
337 TC_LOG_DEBUG("server.http.login", "MaxWrongPass : {}, failed_login : {}", maxWrongPassword, accountId);
338
339 if (failedLogins >= maxWrongPassword)
340 {
341 BanMode banType = BanMode(sConfigMgr->GetIntDefault("WrongPass.BanType", uint16(BanMode::BAN_IP)));
342 int32 banTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
343
344 if (banType == BanMode::BAN_ACCOUNT)
345 {
346 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ACCOUNT_AUTO_BANNED);
347 stmt->setUInt32(0, accountId);
348 }
349 else
350 {
351 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
352 stmt->setString(0, ip_address);
353 }
354
355 stmt->setUInt32(1, banTime);
356 trans->Append(stmt);
357
358 stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_RESET_FAILED_LOGINS);
359 stmt->setUInt32(0, accountId);
360 trans->Append(stmt);
361 }
362
363 LoginDatabase.CommitTransaction(trans);
364 }
365 }
366
367 JSON::Login::LoginResult loginResult;
368 loginResult.set_authentication_state(JSON::Login::DONE);
369
370 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
371 context.response.body() = ::JSON::Serialize(loginResult);
372 session->SendResponse(context);
373 return;
374 }
375
376 if (loginTicket.empty() || loginTicketExpiry < time(nullptr))
377 loginTicket = "TC-" + ByteArrayToHexStr(Trinity::Crypto::GetRandomBytes<20>());
378
380 stmt->setString(0, loginTicket);
381 stmt->setUInt32(1, time(nullptr) + _loginTicketDuration);
382 stmt->setUInt32(2, accountId);
383 callback.WithPreparedCallback([session, context = std::move(context), loginTicket = std::move(loginTicket), serverM2 = std::move(serverM2)](PreparedQueryResult) mutable
384 {
385 JSON::Login::LoginResult loginResult;
386 loginResult.set_authentication_state(JSON::Login::DONE);
387 loginResult.set_login_ticket(loginTicket);
388 if (serverM2)
389 loginResult.set_server_evidence_m2(*serverM2);
390
391 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
392 context.response.body() = ::JSON::Serialize(loginResult);
393 session->SendResponse(context);
394 }).SetNextQuery(LoginDatabase.AsyncQuery(stmt));
395 }));
396
397 return RequestHandlerResult::Async;
398}
399
400LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostLoginSrpChallenge(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context)
401{
402 JSON::Login::LoginForm loginForm;
403 if (!::JSON::Deserialize(context.request.body(), &loginForm))
404 {
405 JSON::Login::LoginResult loginResult;
406 loginResult.set_authentication_state(JSON::Login::LOGIN);
407 loginResult.set_error_code("UNABLE_TO_DECODE");
408 loginResult.set_error_message("There was an internal error while connecting to Battle.net. Please try again later.");
409
410 context.response.result(boost::beast::http::status::bad_request);
411 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
412 context.response.body() = ::JSON::Serialize(loginResult);
413
414 return RequestHandlerResult::Handled;
415 }
416
417 std::string login;
418
419 for (int32 i = 0; i < loginForm.inputs_size(); ++i)
420 if (loginForm.inputs(i).input_id() == "account_name")
421 login = loginForm.inputs(i).value();
422
424
426 stmt->setString(0, login);
427
428 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
429 .WithPreparedCallback([session, context = std::move(context), login = std::move(login)](PreparedQueryResult result) mutable
430 {
431 if (!result)
432 {
433 JSON::Login::LoginResult loginResult;
434 loginResult.set_authentication_state(JSON::Login::DONE);
435 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
436 context.response.body() = ::JSON::Serialize(loginResult);
437 session->SendResponse(context);
438 return;
439 }
440
441 Field* fields = result->Fetch();
442 SrpVersion version = SrpVersion(fields[0].GetInt8());
443 SrpHashFunction hashFunction = SrpHashFunction::Sha256;
444 std::string srpUsername = ByteArrayToHexStr(Trinity::Crypto::SHA256::GetDigestOf(login));
446 Trinity::Crypto::SRP::Verifier v = fields[2].GetBinary();
447
448 session->GetSessionState()->Srp = CreateSrpImplementation(version, hashFunction, srpUsername, s, v);
449 if (!session->GetSessionState()->Srp)
450 {
451 context.response.result(boost::beast::http::status::internal_server_error);
452 session->SendResponse(context);
453 return;
454 }
455
456 JSON::Login::SrpLoginChallenge challenge;
457 challenge.set_version(session->GetSessionState()->Srp->GetVersion());
458 challenge.set_iterations(session->GetSessionState()->Srp->GetXIterations());
459 challenge.set_modulus(session->GetSessionState()->Srp->GetN().AsHexStr());
460 challenge.set_generator(session->GetSessionState()->Srp->Getg().AsHexStr());
461 challenge.set_hash_function([=]
462 {
463 switch (hashFunction)
464 {
465 case SrpHashFunction::Sha256:
466 return "SHA-256";
467 case SrpHashFunction::Sha512:
468 return "SHA-512";
469 default:
470 break;
471 }
472 return "";
473 }());
474 challenge.set_username(srpUsername);
475 challenge.set_salt(ByteArrayToHexStr(session->GetSessionState()->Srp->s));
476 challenge.set_public_b(session->GetSessionState()->Srp->B.AsHexStr());
477
478 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
479 context.response.body() = ::JSON::Serialize(challenge);
480 session->SendResponse(context);
481 }));
482
483 return RequestHandlerResult::Async;
484}
485
486LoginRESTService::RequestHandlerResult LoginRESTService::HandlePostRefreshLoginTicket(std::shared_ptr<LoginHttpSession> session, HttpRequestContext& context) const
487{
488 std::string ticket = ExtractAuthorization(context.request);
489 if (ticket.empty())
490 return HandleUnauthorized(std::move(session), context);
491
493 stmt->setString(0, ticket);
494 session->QueueQuery(LoginDatabase.AsyncQuery(stmt)
495 .WithPreparedCallback([this, session, context = std::move(context), ticket = std::move(ticket)](PreparedQueryResult result) mutable
496 {
497 JSON::Login::LoginRefreshResult loginRefreshResult;
498 if (result)
499 {
500 uint32 loginTicketExpiry = (*result)[0].GetUInt32();
501 time_t now = time(nullptr);
502 if (loginTicketExpiry > now)
503 {
504 loginRefreshResult.set_login_ticket_expiry(now + _loginTicketDuration);
505
506 LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_EXISTING_AUTHENTICATION);
507 stmt->setUInt32(0, uint32(now + _loginTicketDuration));
508 stmt->setString(1, ticket);
509 LoginDatabase.Execute(stmt);
510 }
511 else
512 loginRefreshResult.set_is_expired(true);
513 }
514 else
515 loginRefreshResult.set_is_expired(true);
516
517 context.response.set(boost::beast::http::field::content_type, "application/json;charset=utf-8");
518 context.response.body() = ::JSON::Serialize(loginRefreshResult);
519 session->SendResponse(context);
520 }));
521
522 return RequestHandlerResult::Async;
523}
524
525std::unique_ptr<Trinity::Crypto::SRP::BnetSRP6Base> LoginRESTService::CreateSrpImplementation(SrpVersion version, SrpHashFunction hashFunction,
526 std::string const& username, Trinity::Crypto::SRP::Salt const& salt, Trinity::Crypto::SRP::Verifier const& verifier)
527{
528 if (version == SrpVersion::v2)
529 {
530 if (hashFunction == SrpHashFunction::Sha256)
531 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA256>>(username, salt, verifier);
532 if (hashFunction == SrpHashFunction::Sha512)
533 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v2<Trinity::Crypto::SHA512>>(username, salt, verifier);
534 }
535
536 if (version == SrpVersion::v1)
537 {
538 if (hashFunction == SrpHashFunction::Sha256)
539 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA256>>(username, salt, verifier);
540 if (hashFunction == SrpHashFunction::Sha512)
541 return std::make_unique<Trinity::Crypto::SRP::BnetSRP6v1<Trinity::Crypto::SHA512>>(username, salt, verifier);
542 }
543
544 return nullptr;
545}
546
547std::shared_ptr<Trinity::Net::Http::SessionState> LoginRESTService::CreateNewSessionState(boost::asio::ip::address const& address)
548{
549 std::shared_ptr<LoginSessionState> state = std::make_shared<LoginSessionState>();
550 InitAndStoreSessionState(state, address);
551 return state;
552}
553
554void LoginRESTService::MigrateLegacyPasswordHashes() const
555{
556 if (!LoginDatabase.Query("SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = SCHEMA() AND TABLE_NAME = 'battlenet_accounts' AND COLUMN_NAME = 'sha_pass_hash'"))
557 return;
558
559 TC_LOG_INFO(_logger, "Updating password hashes...");
560 uint32 const start = getMSTime();
561 // the auth update query nulls salt/verifier if they cannot be converted
562 // 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)
563 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");
564 if (!result)
565 {
566 TC_LOG_INFO(_logger, ">> No password hashes to update - this took us {} ms to realize", GetMSTimeDiffToNow(start));
567 return;
568 }
569
570 bool hadWarning = false;
571 uint32 c = 0;
572 LoginDatabaseTransaction tx = LoginDatabase.BeginTransaction();
573 do
574 {
575 uint32 const id = (*result)[0].GetUInt32();
576
577 Trinity::Crypto::SRP::Salt salt = Trinity::Crypto::GetRandomBytes<Trinity::Crypto::SRP::SALT_LENGTH>();
578 BigNumber x = Trinity::Crypto::SHA256::GetDigestOf(salt, HexStrToByteArray<Trinity::Crypto::SHA256::DIGEST_LENGTH>((*result)[1].GetString(), true));
580
581 if ((*result)[2].GetInt64())
582 {
583 if (!hadWarning)
584 {
585 hadWarning = true;
586 TC_LOG_WARN(_logger,
587 " ========\n"
588 "(!) You appear to be using an outdated external account management tool.\n"
589 "(!) Update your external tool.\n"
590 "(!!) If no update is available, refer your tool's developer to https://github.com/TrinityCore/TrinityCore/issues/25157.\n"
591 " ========");
592 }
593 }
594
597 stmt->setBinary(1, salt);
598 stmt->setBinary(2, std::move(verifier));
599 stmt->setUInt32(3, id);
600 tx->Append(stmt);
601
602 tx->Append(Trinity::StringFormat("UPDATE battlenet_accounts SET sha_pass_hash = DEFAULT(sha_pass_hash) WHERE id = {}", id).c_str());
603
604 if (tx->GetSize() >= 10000)
605 {
606 LoginDatabase.CommitTransaction(tx);
607 tx = LoginDatabase.BeginTransaction();
608 }
609
610 ++c;
611 } while (result->NextRow());
612 LoginDatabase.CommitTransaction(tx);
613
614 TC_LOG_INFO(_logger, ">> {} password hashes updated in {} ms", c, GetMSTimeDiffToNow(start));
615}
616}
#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:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:176
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:179
@ 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
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 std::string Serialize(google::protobuf::Message const &message)
TC_SHARED_API bool Deserialize(std::string_view json, google::protobuf::Message *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