TrinityCore
Loading...
Searching...
No Matches
AuthSession.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 "AuthSession.h"
19#include "AES.h"
20#include "AuthCodes.h"
21#include "ByteBuffer.h"
22#include "ClientBuildInfo.h"
23#include "Config.h"
24#include "CryptoGenerics.h"
25#include "CryptoHash.h"
26#include "CryptoRandom.h"
27#include "DatabaseEnv.h"
28#include "IPLocation.h"
29#include "IoContext.h"
31#include "Log.h"
32#include "RealmList.h"
33#include "SecretMgr.h"
34#include "TOTP.h"
35#include "Util.h"
36#include <boost/endian/arithmetic.hpp>
37#include <boost/lexical_cast.hpp>
38
39using boost::asio::ip::tcp;
40using boost::endian::little_uint16_t;
41using boost::endian::little_uint32_t;
42
56
57#pragma pack(push, 1)
58
60{
63 little_uint16_t size;
64 little_uint32_t gamename;
68 little_uint16_t build;
69 little_uint32_t platform;
70 little_uint32_t os;
71 little_uint32_t country;
72 little_uint32_t timezone_bias;
73 little_uint32_t ip;
75 char I[1];
77static_assert(sizeof(sAuthLogonChallenge_C) == (1 + 1 + 2 + 4 + 1 + 1 + 1 + 2 + 4 + 4 + 4 + 4 + 4 + 1 + 1));
78
88static_assert(sizeof(sAuthLogonProof_C) == (1 + 32 + 20 + 20 + 1 + 1));
89
99static_assert(sizeof(sAuthLogonProof_S) == (1 + 1 + 20 + 4 + 4 + 2));
100
108static_assert(sizeof(sAuthLogonProof_S_Old) == (1 + 1 + 20 + 4));
109
117static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1));
118
119#pragma pack(pop)
120
121std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } };
122
123#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16)
124
125#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
126#define REALM_LIST_PACKET_SIZE 5
127
129{
132 size_t packetSize = 0;
133 bool (*handler)(AuthSession*) = nullptr;
134};
135
137{
138public:
150
151 constexpr AuthHandler const* operator[](eAuthCmd cmd) const
152 {
153 std::size_t index = GetOpcodeArrayIndex(cmd);
154 if (index >= _handlers.size())
155 return nullptr;
156
157 AuthHandler const& handler = _handlers[index];
158 if (handler.cmd != cmd)
159 return nullptr;
160
161 return &handler;
162 }
163
164private:
165 // perfect hash function for all valid client to server values of eAuthCmd
166 inline static constexpr std::size_t GetOpcodeArrayIndex(eAuthCmd c)
167 {
168 return (c & 0x7) + ((c & 0x10) >> 2) - ((c & 0x20) >> 5);
169 }
170
171 constexpr void InitializeHandler(eAuthCmd cmd, AuthStatus status, std::size_t packetSize, bool (*handler)(AuthSession*))
172 {
173 _handlers[GetOpcodeArrayIndex(cmd)] = { .cmd = cmd, .status = status, .packetSize = packetSize, .handler = handler, };
174 }
175
176 std::array<AuthHandler, 8> _handlers;
177} inline constexpr Handlers;
178
180{
181 // 0 1 2 3 4 5 6
182 //SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate,
183 // 7 8
184 // ab.unbandate = ab.bandate, aa.SecurityLevel (, more query-specific fields)
185 //FROM account a LEFT JOIN account_access aa ON a.id = aa.AccountID LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?
186
187 Id = fields[0].GetUInt32();
188 Login = fields[1].GetStringView();
189 IsLockedToIP = fields[2].GetBool();
190 LockCountry = fields[3].GetStringView();
191 LastIP = fields[4].GetStringView();
192 FailedLogins = fields[5].GetUInt32();
193 IsBanned = fields[6].GetUInt64() != 0;
194 IsPermanenetlyBanned = fields[7].GetUInt64() != 0;
195 SecurityLevel = AccountTypes(fields[8].GetUInt8());
196
197 // Use our own uppercasing of the account name instead of using UPPER() in mysql query
198 // This is how the account was created in the first place and changing it now would result in breaking
199 // login for all accounts having accented characters in their name
201}
202
204 _timeout(underlying_stream().get_executor()),
205 _status(STATUS_CHALLENGE), _locale(LOCALE_enUS), _os(0), _build(0), _expversion(0), _timezoneOffset(0min)
206{
207}
208
210{
211 // build initializer chain
212 std::array<std::shared_ptr<Trinity::Net::SocketConnectionInitializer>, 2> initializers =
213 { {
214 std::make_shared<Trinity::Net::IpBanCheckConnectionInitializer<AuthSession>>(this),
216 } };
217
219 SetTimeout();
220}
221
223{
224 if (!AuthSocket::Update())
225 return false;
226
228
229 return true;
230}
231
233{
234 MessageBuffer& packet = GetReadBuffer();
235 while (packet.GetActiveSize())
236 {
237 eAuthCmd cmd = eAuthCmd(packet.GetReadPointer()[0]);
238 AuthHandler const* itr = Handlers[cmd];
239 if (!itr || _status != itr->status)
240 {
241 CloseSocket();
243 }
244
245 std::size_t size = itr->packetSize;
246 if (packet.GetActiveSize() < size)
247 break;
248
250 {
251 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer());
252 size += challenge->size;
254 {
255 CloseSocket();
257 }
258 }
259
260 if (packet.GetActiveSize() < size)
261 break;
262
263 if (!itr->handler(this))
264 {
265 CloseSocket();
267 }
268
269 packet.ReadCompleted(size);
270 SetTimeout();
271 }
272
274}
275
277{
278 _queryProcessor.AddCallback(std::move(queryCallback));
279}
280
282{
283 if (!IsOpen())
284 return;
285
286 if (!packet.empty())
287 {
288 MessageBuffer buffer(packet.size());
289 buffer.Write(packet.contents(), packet.size());
290 QueuePacket(std::move(buffer));
291 }
292}
293
295{
297
298 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
299 if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
300 return false;
301
302 std::string_view login(challenge->I, challenge->I_len);
303 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] '{}'", login);
304
305 _build = challenge->build;
307 _os = challenge->os;
309
311
312 // Get the account details from the account table
314 stmt->setStringView(0, login);
315
316 QueueQuery(LoginDatabase.AsyncQuery(stmt)
317 .WithPreparedCallback([this](PreparedQueryResult result) { LogonChallengeCallback(std::move(result)); }));
318 return true;
319}
320
322{
323 ByteBuffer pkt;
325 pkt << uint8(0x00);
326
327 if (!result)
328 {
330 SendPacket(pkt);
331 return;
332 }
333
334 Field* fields = result->Fetch();
335
336 _accountInfo.LoadResult(fields);
337
338 std::string ipAddress = GetRemoteIpAddress().to_string();
339 uint16 port = GetRemotePort();
340
341 // If the IP is 'locked', check that the player comes indeed from the correct IP address
343 {
344 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to IP - '{}' is logging in from '{}'", _accountInfo.Login, _accountInfo.LastIP, ipAddress);
345 if (_accountInfo.LastIP != ipAddress)
346 {
348 SendPacket(pkt);
349 return;
350 }
351 }
352 else
353 {
354 if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress))
355 _ipCountry = location->CountryCode;
356
357 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to ip", _accountInfo.Login);
358 if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00")
359 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to country", _accountInfo.Login);
360 else if (!_ipCountry.empty())
361 {
362 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to country: '{}' Player country is '{}'", _accountInfo.Login, _accountInfo.LockCountry, _ipCountry);
364 {
366 SendPacket(pkt);
367 return;
368 }
369 }
370 }
371
372 // If the account is banned, reject the logon attempt
374 {
376 {
377 pkt << uint8(WOW_FAIL_BANNED);
378 SendPacket(pkt);
379 TC_LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
380 return;
381 }
382 else
383 {
385 SendPacket(pkt);
386 TC_LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Temporarily banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
387 return;
388 }
389 }
390
391 uint8 securityFlags = 0;
392 // Check if a TOTP token is needed
393 if (!fields[9].IsNull())
394 {
395 securityFlags = 4;
396 _totpSecret = fields[9].GetBinary();
397 if (auto const& secret = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY))
398 {
399 bool success = Trinity::Crypto::AEDecrypt<Trinity::Crypto::AES>(*_totpSecret, *secret);
400 if (!success)
401 {
402 pkt << uint8(WOW_FAIL_DB_BUSY);
403 TC_LOG_ERROR("server.authserver", "[AuthChallenge] Account '{}' has invalid ciphertext for TOTP token key stored", _accountInfo.Login);
404 SendPacket(pkt);
405 return;
406 }
407 }
408 }
409
410 _srp6.emplace(
414 );
415
416 // Fill the response packet with the result
418 {
419 pkt << uint8(WOW_SUCCESS);
420
421 pkt.append(_srp6->B);
422 pkt << uint8(1);
423 pkt.append(_srp6->g);
424 pkt << uint8(32);
425 pkt.append(_srp6->N);
426 pkt.append(_srp6->s);
427 pkt.append(VersionChallenge.data(), VersionChallenge.size());
428 pkt << uint8(securityFlags); // security flags (0x0...0x04)
429
430 if (securityFlags & 0x01) // PIN input
431 {
432 pkt << uint32(0);
433 pkt << uint64(0) << uint64(0); // 16 bytes hash?
434 }
435
436 if (securityFlags & 0x02) // Matrix input
437 {
438 pkt << uint8(0);
439 pkt << uint8(0);
440 pkt << uint8(0);
441 pkt << uint8(0);
442 pkt << uint64(0);
443 }
444
445 if (securityFlags & 0x04) // Security token input
446 pkt << uint8(1);
447
448 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} is using '{}' locale ({})",
449 ipAddress, port, _accountInfo.Login, localeNames[_locale], uint32(_locale));
450
452 }
453 else
455
456 SendPacket(pkt);
457}
458
459// Logon Proof command handler
461{
462 TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof");
464
465 // Read the packet
466 sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer());
467
468 // If the client has no valid version
470 {
471 // Check if we have the appropriate patch on the disk
472 TC_LOG_DEBUG("network", "Client with invalid version, patching is not implemented");
473 return false;
474 }
475
476 // Check if SRP6 results match (password is correct), else send an error
477 if (std::optional<SessionKey> K = _srp6->VerifyChallengeResponse(logonProof->A, logonProof->clientM))
478 {
479 _sessionKey = *K;
480 // Check auth token
481 bool tokenSuccess = false;
482 bool sentToken = (logonProof->securityFlags & 0x04);
483 if (sentToken && _totpSecret)
484 {
485 uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C));
486 std::string token(reinterpret_cast<char*>(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size);
487 GetReadBuffer().ReadCompleted(sizeof(size) + size);
488
489 uint32 incomingToken = atoi(token.c_str());
490 tokenSuccess = Trinity::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken);
491 memset(_totpSecret->data(), 0, _totpSecret->size());
492 }
493 else if (!sentToken && !_totpSecret)
494 tokenSuccess = true;
495
496 if (!tokenSuccess)
497 {
498 ByteBuffer packet;
499 packet << uint8(AUTH_LOGON_PROOF);
501 packet << uint16(0); // LoginFlags, 1 has account message
502 SendPacket(packet);
503 return true;
504 }
505
506 if (!VerifyVersion(logonProof->A, logonProof->crc_hash, false))
507 {
508 ByteBuffer packet;
509 packet << uint8(AUTH_LOGON_PROOF);
511 SendPacket(packet);
512 return true;
513 }
514
515 TC_LOG_DEBUG("server.authserver", "'{}:{}' User '{}' successfully authenticated", GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
516
517 // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
518 // No SQL injection (escaped user name) and IP address as received by socket
519
520 std::string address = sConfigMgr->GetBoolDefault("AllowLoggingIPAddressesInDatabase", true, true) ? GetRemoteIpAddress().to_string() : "127.0.0.1";
522 stmt->setBinary(0, _sessionKey);
523 stmt->setString(1, address);
524 stmt->setUInt32(2, _locale);
526 stmt->setInt16(4, _timezoneOffset.count());
527 stmt->setString(5, _accountInfo.Login);
528 QueueQuery(LoginDatabase.AsyncQuery(stmt)
529 .WithPreparedCallback([this, M2 = Trinity::Crypto::SRP6::GetSessionVerifier(logonProof->A, logonProof->clientM, _sessionKey)](PreparedQueryResult const&)
530 {
531 // Finish SRP6 and send the final result to the client
532 ByteBuffer packet;
533 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
534 {
535 sAuthLogonProof_S proof;
536 proof.M2 = M2;
537 proof.cmd = AUTH_LOGON_PROOF;
538 proof.error = 0;
539 proof.AccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK;
540 proof.SurveyId = 0;
541 proof.LoginFlags = 0; // 0x1 = has account message
542
543 packet.resize(sizeof(proof));
544 std::memcpy(packet.contents(), &proof, sizeof(proof));
545 }
546 else
547 {
548 sAuthLogonProof_S_Old proof;
549 proof.M2 = M2;
550 proof.cmd = AUTH_LOGON_PROOF;
551 proof.error = 0;
552 proof.unk2 = 0x00;
553
554 packet.resize(sizeof(proof));
555 std::memcpy(packet.contents(), &proof, sizeof(proof));
556 }
557
558 SendPacket(packet);
560 }));
561 }
562 else
563 {
564 ByteBuffer packet;
565 packet << uint8(AUTH_LOGON_PROOF);
567 packet << uint16(0); // LoginFlags, 1 has account message
568 SendPacket(packet);
569
570 TC_LOG_INFO("server.authserver.hack", "'{}:{}' [AuthChallenge] account {} tried to login with invalid password!",
571 GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
572
573 uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0);
574
575 // We can not include the failed account login hook. However, this is a workaround to still log this.
576 if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
577 {
579 logstmt->setUInt32(0, _accountInfo.Id);
580 logstmt->setString(1, GetRemoteIpAddress().to_string());
581 logstmt->setString(2, "Login to WoW Failed - Incorrect Password");
582
583 LoginDatabase.Execute(logstmt);
584 }
585
586 if (MaxWrongPassCount > 0)
587 {
588 //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
590 stmt->setString(0, _accountInfo.Login);
591 LoginDatabase.Execute(stmt);
592
593 if (++_accountInfo.FailedLogins >= MaxWrongPassCount)
594 {
595 uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
596 bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false);
597
598 if (WrongPassBanType)
599 {
600 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
601 stmt->setUInt32(0, _accountInfo.Id);
602 stmt->setUInt32(1, WrongPassBanTime);
603 LoginDatabase.Execute(stmt);
604
605 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} got banned for '{}' seconds because it failed to authenticate '{}' times",
606 GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login, WrongPassBanTime, _accountInfo.FailedLogins);
607 }
608 else
609 {
610 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
611 stmt->setString(0, GetRemoteIpAddress().to_string());
612 stmt->setUInt32(1, WrongPassBanTime);
613 LoginDatabase.Execute(stmt);
614
615 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] IP got banned for '{}' seconds because account {} failed to authenticate '{}' times",
616 GetRemoteIpAddress().to_string(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login, _accountInfo.FailedLogins);
617 }
618 }
619 }
620 }
621
622 return true;
623}
624
626{
628
629 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
630 if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len)
631 return false;
632
633 std::string_view login(challenge->I, challenge->I_len);
634 TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] '{}'", login);
635
636 _build = challenge->build;
638 _os = challenge->os;
640
642
643 // Get the account details from the account table
645 stmt->setStringView(0, login);
646
647 QueueQuery(LoginDatabase.AsyncQuery(stmt)
648 .WithPreparedCallback([this](PreparedQueryResult result) { ReconnectChallengeCallback(std::move(result)); }));
649 return true;
650}
651
653{
654 ByteBuffer pkt;
656
657 if (!result)
658 {
660 SendPacket(pkt);
661 return;
662 }
663
664 Field* fields = result->Fetch();
665
666 _accountInfo.LoadResult(fields);
670
671 pkt << uint8(WOW_SUCCESS);
673 pkt.append(VersionChallenge.data(), VersionChallenge.size());
674
675 SendPacket(pkt);
676}
677
679{
680 TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
682
683 sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer());
684
685 if (_accountInfo.Login.empty())
686 return false;
687
690 sha.UpdateData(reconnectProof->R1, 16);
693 sha.Finalize();
694
695 if (sha.GetDigest() == reconnectProof->R2)
696 {
697 if (!VerifyVersion(reconnectProof->R1, reconnectProof->R3, true))
698 {
699 ByteBuffer packet;
700 packet << uint8(AUTH_RECONNECT_PROOF);
702 SendPacket(packet);
703 return true;
704 }
705
706 // Sending response
707 ByteBuffer pkt;
709 pkt << uint8(WOW_SUCCESS);
710 pkt << uint16(0); // LoginFlags, 1 has account message
711 SendPacket(pkt);
713 return true;
714 }
715 else
716 {
717 TC_LOG_ERROR("server.authserver.hack", "'{}:{}' [ERROR] user {} tried to login, but session is invalid.", GetRemoteIpAddress().to_string(),
719 return false;
720 }
721}
722
724{
725 TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
726
728 stmt->setUInt32(0, _accountInfo.Id);
729
730 QueueQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1)));
732 return true;
733}
734
736{
737 std::map<uint32, uint8> characterCounts;
738 if (result)
739 {
740 do
741 {
742 Field* fields = result->Fetch();
743 characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8();
744 } while (result->NextRow());
745 }
746
747 // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
748 ByteBuffer pkt;
749
750 size_t RealmListSize = 0;
751 for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms())
752 {
753 Realm const& realm = i.second;
754 // don't work with realms which not compatible with the client
756
757 // No SQL injection. id of realm is controlled by the database.
758 uint32 flag = realm.Flags;
760 if (!okBuild)
761 {
762 if (!buildInfo)
763 continue;
764
765 flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
766 }
767
768 if (!buildInfo)
769 flag &= ~REALM_FLAG_SPECIFYBUILD;
770
771 std::string name = realm.Name;
773 Trinity::StringFormatTo(std::back_inserter(name), " ({}.{}.{})", buildInfo->MajorVersion, buildInfo->MinorVersion, buildInfo->BugfixVersion);
774
776
777 pkt << uint8(realm.Type); // realm type
778 if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
779 pkt << uint8(lock); // if 1, then realm locked
780 pkt << uint8(flag); // RealmFlags
781 pkt << name;
782 pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
783 pkt << float(realm.PopulationLevel);
784 pkt << uint8(characterCounts[realm.Id.Realm]);
785 pkt << uint8(realm.Timezone); // realm category
786 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
787 pkt << uint8(realm.Id.Realm);
788 else
789 pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
790
792 {
793 pkt << uint8(buildInfo->MajorVersion);
794 pkt << uint8(buildInfo->MinorVersion);
795 pkt << uint8(buildInfo->BugfixVersion);
796 pkt << uint16(buildInfo->Build);
797 }
798
799 ++RealmListSize;
800 }
801
802 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
803 {
804 pkt << uint8(0x10);
805 pkt << uint8(0x00);
806 }
807 else // 1.12.1 and 1.12.2 clients
808 {
809 pkt << uint8(0x00);
810 pkt << uint8(0x02);
811 }
812
813 // make a ByteBuffer which stores the RealmList's size
814 ByteBuffer RealmListSizeBuffer;
815 RealmListSizeBuffer << uint32(0);
816 if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
817 RealmListSizeBuffer << uint16(RealmListSize);
818 else
819 RealmListSizeBuffer << uint32(RealmListSize);
820
821 ByteBuffer hdr;
822 hdr << uint8(REALM_LIST);
823 hdr << uint16(pkt.size() + RealmListSizeBuffer.size());
824 hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
825 hdr.append(pkt); // append realms in the realmlist
826 SendPacket(hdr);
827
829}
830
832{
833 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept");
834
835 // empty handler meant to close the connection if received
836 return false;
837}
838
840{
841 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume");
842
843 // empty handler meant to close the connection if received
844 return false;
845}
846
848{
849 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel");
850
851 // empty handler meant to close the connection if received
852 return false;
853}
854
855bool AuthSession::VerifyVersion(std::span<uint8 const> a, Trinity::Crypto::SHA1::Digest const& versionProof, bool isReconnect)
856{
857 if (!sConfigMgr->GetBoolDefault("StrictVersionCheck", false))
858 return true;
859
861 Trinity::Crypto::SHA1::Digest const* versionHash = nullptr;
862 if (!isReconnect)
863 {
865 if (!buildInfo)
866 return false;
867
868 auto platformItr = std::ranges::find(buildInfo->ExecutableHashes, _os, &ClientBuild::ExecutableHash::Platform);
869 if (platformItr == buildInfo->ExecutableHashes.end())
870 return true; // not filled serverside
871
872 versionHash = &platformItr->Hash;
873 }
874 else
875 versionHash = &zeros;
876
877 Trinity::Crypto::SHA1 version;
878 version.UpdateData(a);
879 version.UpdateData(*versionHash);
880 version.Finalize();
881
882 return versionProof == version.GetDigest();
883}
884
886{
887 _timeout.cancel();
888
889 switch (_status)
890 {
891 case STATUS_AUTHED:
893 _timeout.expires_after(1min);
894 break;
895 case STATUS_XFER:
896 return;
897 default:
898 _timeout.expires_after(10s);
899 break;
900 }
901
902 _timeout.async_wait([selfRef = weak_from_this()](boost::system::error_code const& error)
903 {
904 std::shared_ptr<AuthSession> self = static_pointer_cast<AuthSession>(selfRef.lock());
905 if (!self)
906 return;
907
908 if (error == boost::asio::error::operation_aborted)
909 return;
910
911 TC_LOG_DEBUG("server.authserver", "{}:{} session timed out.", self->GetRemoteIpAddress().to_string(), self->GetRemotePort());
912 self->CloseSocket();
913 });
914}
@ WOW_SUCCESS
Definition AuthCodes.h:26
@ WOW_FAIL_LOCKED_ENFORCED
Definition AuthCodes.h:40
@ WOW_FAIL_SUSPENDED
Definition AuthCodes.h:36
@ WOW_FAIL_UNKNOWN_ACCOUNT
Definition AuthCodes.h:28
@ WOW_FAIL_BANNED
Definition AuthCodes.h:27
@ WOW_FAIL_DB_BUSY
Definition AuthCodes.h:32
@ WOW_FAIL_UNLOCKABLE_LOCK
Definition AuthCodes.h:49
@ WOW_FAIL_VERSION_INVALID
Definition AuthCodes.h:33
@ POST_BC_EXP_FLAG
Definition AuthCodes.h:111
@ NO_VALID_EXP_FLAG
Definition AuthCodes.h:113
@ PRE_BC_EXP_FLAG
Definition AuthCodes.h:112
constexpr size_t SESSION_KEY_LENGTH
Definition AuthDefines.h:24
struct AUTH_LOGON_PROOF_S sAuthLogonProof_S
std::array< uint8, 16 > VersionChallenge
#define MAX_ACCEPTED_CHALLENGE_SIZE
eAuthCmd
@ XFER_INITIATE
@ XFER_ACCEPT
@ XFER_CANCEL
@ AUTH_LOGON_CHALLENGE
@ REALM_LIST
@ AUTH_RECONNECT_PROOF
@ AUTH_RECONNECT_CHALLENGE
@ XFER_DATA
@ XFER_RESUME
@ AUTH_LOGON_PROOF
#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE
class AuthHandlerTable Handlers
struct AUTH_LOGON_PROOF_C sAuthLogonProof_C
struct AUTH_LOGON_PROOF_S_OLD sAuthLogonProof_S_Old
struct AUTH_RECONNECT_PROOF_C sAuthReconnectProof_C
struct AUTH_LOGON_CHALLENGE_C sAuthLogonChallenge_C
#define REALM_LIST_PACKET_SIZE
AuthStatus
Definition AuthSession.h:39
@ STATUS_RECONNECT_PROOF
Definition AuthSession.h:42
@ STATUS_XFER
Definition AuthSession.h:45
@ STATUS_WAITING_FOR_REALM_LIST
Definition AuthSession.h:44
@ STATUS_CLOSED
Definition AuthSession.h:46
@ STATUS_CHALLENGE
Definition AuthSession.h:40
@ STATUS_LOGON_PROOF
Definition AuthSession.h:41
@ STATUS_AUTHED
Definition AuthSession.h:43
char const * localeNames[TOTAL_LOCALES]
Definition Common.cpp:20
LocaleConstant GetLocaleByName(const std::string &name)
Definition Common.cpp:33
@ LOCALE_enUS
Definition Common.h:49
AccountTypes
Definition Common.h:39
#define sConfigMgr
Definition Config.h:60
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
uint8_t uint8
Definition Define.h:135
uint64_t uint64
Definition Define.h:132
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition Duration.h:30
#define sIPLocation
Definition IPLocation.h:48
#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_INS_ACCOUNT_AUTO_BANNED
@ LOGIN_INS_FALP_IP_LOGGING
@ LOGIN_UPD_LOGONPROOF
@ LOGIN_SEL_RECONNECTCHALLENGE
@ LOGIN_UPD_FAILEDLOGINS
@ LOGIN_INS_IP_AUTO_BANNED
@ LOGIN_SEL_LOGONCHALLENGE
@ LOGIN_SEL_REALM_CHARACTER_COUNTS
#define sRealmList
Definition RealmList.h:63
@ REALM_FLAG_OFFLINE
Definition Realm.h:30
@ REALM_FLAG_SPECIFYBUILD
Definition Realm.h:31
#define sSecretMgr
Definition SecretMgr.h:73
@ SECRET_TOTP_MASTER_KEY
Definition SecretMgr.h:31
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition Util.cpp:610
consteval AuthHandlerTable()
static constexpr std::size_t GetOpcodeArrayIndex(eAuthCmd c)
constexpr void InitializeHandler(eAuthCmd cmd, AuthStatus status, std::size_t packetSize, bool(*handler)(AuthSession *))
std::array< AuthHandler, 8 > _handlers
constexpr AuthHandler const * operator[](eAuthCmd cmd) const
SessionKey _sessionKey
Definition AuthSession.h:99
void QueueQuery(QueryCallback &&queryCallback)
Minutes _timezoneOffset
bool HandleXferResume()
Trinity::Asio::DeadlineTimer _timeout
AccountInfo _accountInfo
Optional< Trinity::Crypto::SRP6 > _srp6
Definition AuthSession.h:98
bool HandleLogonChallenge()
AuthStatus _status
bool VerifyVersion(std::span< uint8 const > a, Trinity::Crypto::SHA1::Digest const &versionProof, bool isReconnect)
void RealmListCallback(PreparedQueryResult result)
void Start() override
AuthSession(Trinity::Net::IoContextTcpSocket &&socket)
std::string_view _ipCountry
LocaleConstant _locale
void ReconnectChallengeCallback(PreparedQueryResult result)
bool HandleLogonProof()
bool HandleReconnectChallenge()
bool HandleXferAccept()
bool HandleRealmList()
Trinity::Net::SocketReadCallbackResult ReadHandler() override
QueryCallbackProcessor _queryProcessor
bool HandleXferCancel()
void SetTimeout()
void SendPacket(ByteBuffer &packet)
void LogonChallengeCallback(PreparedQueryResult result)
uint16 _build
Optional< std::vector< uint8 > > _totpSecret
bool Update() override
bool HandleReconnectProof()
std::array< uint8, 16 > _reconnectProof
uint8 _expversion
void append(T value)
Definition ByteBuffer.h:129
size_t size() const
Definition ByteBuffer.h:409
bool empty() const
Definition ByteBuffer.h:410
uint8 * contents()
Definition ByteBuffer.h:395
Class used to access individual fields of database query result.
Definition Field.h:92
uint8 GetUInt8() const
Definition Field.cpp:29
std::vector< uint8 > GetBinary() const
Definition Field.cpp:149
std::string_view GetStringView() const
Definition Field.cpp:137
uint64 GetUInt64() const
Definition Field.cpp:77
bool GetBool() const
Definition Field.h:100
uint32 GetUInt32() const
Definition Field.cpp:61
void ReadCompleted(size_type bytes)
uint8 * GetReadPointer()
size_type GetActiveSize() const
void Write(void const *data, std::size_t size)
void setInt16(uint8 index, int16 value)
void setUInt32(uint8 index, uint32 value)
void setStringView(uint8 index, std::string_view value)
void setBinary(uint8 index, std::vector< uint8 > const &value)
void setString(uint8 index, std::string const &value)
static constexpr size_t SALT_LENGTH
Definition SRP6.h:34
static constexpr size_t VERIFIER_LENGTH
Definition SRP6.h:36
std::array< uint8, EPHEMERAL_KEY_LENGTH > EphemeralKey
Definition SRP6.h:39
static SHA1::Digest GetSessionVerifier(EphemeralKey const &A, SHA1::Digest const &clientM, SessionKey const &K)
Definition SRP6.h:52
std::array< uint8, DIGEST_LENGTH > Digest
Definition CryptoHash.h:47
void UpdateData(uint8 const *data, size_t len)
Definition CryptoHash.h:111
Digest const & GetDigest() const
Definition CryptoHash.h:130
uint16 GetRemotePort() const
Definition Socket.h:158
bool IsOpen() const
Definition Socket.h:189
boost::asio::ip::address const & GetRemoteIpAddress() const
Definition Socket.h:153
void QueuePacket(MessageBuffer &&buffer)
Definition Socket.h:180
MessageBuffer & GetReadBuffer()
Definition Socket.h:215
Realm realm
Definition World.cpp:3605
bool IsAcceptedClientBuild(uint32 build)
Definition AuthCodes.cpp:35
bool IsPreBCAcceptedClientBuild(uint32 build)
Definition AuthCodes.cpp:25
bool IsPostBCAcceptedClientBuild(uint32 build)
Definition AuthCodes.cpp:30
Info const * GetBuildInfo(uint32 build)
std::array< char, 5 > ToCharArray(uint32 value)
std::array< uint8, S > GetRandomBytes()
SocketReadCallbackResult
Definition Socket.h:44
boost::asio::basic_stream_socket< boost::asio::ip::tcp, boost::asio::io_context::executor_type > IoContextTcpSocket
Definition Socket.h:41
OutputIt StringFormatTo(OutputIt out, FormatString< Args... > fmt, Args &&... args)
STL namespace.
little_uint32_t gamename
little_uint32_t country
little_uint16_t size
little_uint32_t platform
little_uint32_t timezone_bias
little_uint16_t build
Trinity::Crypto::SHA1::Digest crc_hash
Trinity::Crypto::SRP6::EphemeralKey A
Trinity::Crypto::SHA1::Digest clientM
Trinity::Crypto::SHA1::Digest M2
little_uint32_t AccountFlags
Trinity::Crypto::SHA1::Digest M2
little_uint32_t SurveyId
little_uint16_t LoginFlags
Trinity::Crypto::SHA1::Digest R3
Trinity::Crypto::SHA1::Digest R2
std::string LockCountry
Definition AuthSession.h:56
uint32 FailedLogins
Definition AuthSession.h:58
void LoadResult(Field *fields)
AccountTypes SecurityLevel
Definition AuthSession.h:61
bool IsLockedToIP
Definition AuthSession.h:55
std::string LastIP
Definition AuthSession.h:57
std::string Login
Definition AuthSession.h:54
bool IsPermanenetlyBanned
Definition AuthSession.h:60
bool(* handler)(AuthSession *)
size_t packetSize
AuthStatus status
std::vector< ExecutableHash > ExecutableHashes
uint32 Realm
Definition Realm.h:44
Definition Realm.h:66
RealmFlags Flags
Definition Realm.h:75
AccountTypes AllowedSecurityLevel
Definition Realm.h:77
boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const &clientAddr) const
Definition Realm.cpp:23
uint8 Timezone
Definition Realm.h:76
float PopulationLevel
Definition Realm.h:78
uint32 Build
Definition Realm.h:68
std::string Name
Definition Realm.h:73
RealmHandle Id
Definition Realm.h:67
uint8 Type
Definition Realm.h:74
static bool ValidateToken(Secret const &key, uint32 token)
Definition TOTP.cpp:43
static std::shared_ptr< SocketConnectionInitializer > & SetupChain(std::span< std::shared_ptr< SocketConnectionInitializer > > initializers)