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"
30#include "Log.h"
31#include "RealmList.h"
32#include "SecretMgr.h"
33#include "TOTP.h"
34#include "Util.h"
35#include <boost/endian/arithmetic.hpp>
36#include <boost/lexical_cast.hpp>
37
38using boost::endian::little_uint16_t;
39using boost::endian::little_uint32_t;
40
54
55#pragma pack(push, 1)
56
58{
61 little_uint16_t size;
62 little_uint32_t gamename;
66 little_uint16_t build;
67 little_uint32_t platform;
68 little_uint32_t os;
69 little_uint32_t country;
70 little_uint32_t timezone_bias;
71 little_uint32_t ip;
73 std::string_view GetLogin() const { return { reinterpret_cast<char const*>(this + 1), I_len }; }
75static_assert(sizeof(sAuthLogonChallenge_C) == (1 + 1 + 2 + 4 + 1 + 1 + 1 + 2 + 4 + 4 + 4 + 4 + 4 + 1));
76
86static_assert(sizeof(sAuthLogonProof_C) == (1 + 32 + 20 + 20 + 1 + 1));
87
97static_assert(sizeof(sAuthLogonProof_S) == (1 + 1 + 20 + 4 + 4 + 2));
98
106static_assert(sizeof(sAuthLogonProof_S_Old) == (1 + 1 + 20 + 4));
107
115static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1));
116
117#pragma pack(pop)
118
119static constexpr std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } };
120
121#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 255)
122
123#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
124#define REALM_LIST_PACKET_SIZE 5
125
127{
130 size_t packetSize = 0;
131 bool (*handler)(AuthSession*) = nullptr;
132};
133
135{
136public:
148
149 constexpr AuthHandler const* operator[](eAuthCmd cmd) const
150 {
151 std::size_t index = GetOpcodeArrayIndex(cmd);
152 if (index >= _handlers.size())
153 return nullptr;
154
155 AuthHandler const& handler = _handlers[index];
156 if (handler.cmd != cmd)
157 return nullptr;
158
159 return &handler;
160 }
161
162private:
163 // perfect hash function for all valid client to server values of eAuthCmd
164 inline static constexpr std::size_t GetOpcodeArrayIndex(eAuthCmd c)
165 {
166 return (c & 0x7) + ((c & 0x10) >> 2) - ((c & 0x20) >> 5);
167 }
168
169 constexpr void InitializeHandler(eAuthCmd cmd, AuthStatus status, std::size_t packetSize, bool (*handler)(AuthSession*))
170 {
171 _handlers[GetOpcodeArrayIndex(cmd)] = { .cmd = cmd, .status = status, .packetSize = packetSize, .handler = handler, };
172 }
173
174 std::array<AuthHandler, 8> _handlers;
175} inline constexpr Handlers;
176
178{
179 // 0 1 2 3 4 5 6
180 //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,
181 // 7 8
182 // ab.unbandate = ab.bandate, aa.SecurityLevel (, more query-specific fields)
183 //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 = ?
184
185 Id = fields[0].GetUInt32();
186 Login = fields[1].GetStringView();
187 IsLockedToIP = fields[2].GetBool();
188 LockCountry = fields[3].GetStringView();
189 LastIP = fields[4].GetStringView();
190 FailedLogins = fields[5].GetUInt32();
191 IsBanned = fields[6].GetUInt64() != 0;
192 IsPermanenetlyBanned = fields[7].GetUInt64() != 0;
193 SecurityLevel = AccountTypes(fields[8].GetUInt8());
194
195 // Use our own uppercasing of the account name instead of using UPPER() in mysql query
196 // This is how the account was created in the first place and changing it now would result in breaking
197 // login for all accounts having accented characters in their name
199}
200
202 _timeout(underlying_stream().get_executor()),
203 _status(STATUS_CHALLENGE), _locale(LOCALE_enUS), _os(0), _build(0), _expversion(0), _timezoneOffset(0min)
204{
205}
206
208{
209 // build initializer chain
210 std::array<std::shared_ptr<Trinity::Net::SocketConnectionInitializer>, 2> initializers =
211 { {
212 std::make_shared<Trinity::Net::IpBanCheckConnectionInitializer<AuthSession>>(this),
214 } };
215
217 SetTimeout();
218}
219
221{
222 if (!AuthSocket::Update())
223 return false;
224
226
227 return true;
228}
229
231{
232 MessageBuffer& packet = GetReadBuffer();
233 while (packet.GetActiveSize())
234 {
235 eAuthCmd cmd = eAuthCmd(packet.GetReadPointer()[0]);
236 AuthHandler const* itr = Handlers[cmd];
237 if (!itr || _status != itr->status)
238 {
239 CloseSocket();
241 }
242
243 std::size_t size = itr->packetSize;
244 if (packet.GetActiveSize() < size)
245 break;
246
248 {
249 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer());
250 size += challenge->size;
252 {
253 CloseSocket();
255 }
256 }
257
258 if (packet.GetActiveSize() < size)
259 break;
260
261 if (!itr->handler(this))
262 {
263 CloseSocket();
265 }
266
267 packet.ReadCompleted(size);
268 SetTimeout();
269 }
270
272}
273
275{
276 _queryProcessor.AddCallback(std::move(queryCallback));
277}
278
280{
281 if (!IsOpen())
282 return;
283
284 if (!packet.empty())
285 {
286 MessageBuffer buffer(packet.size());
287 buffer.Write(packet.contents(), packet.size());
288 QueuePacket(std::move(buffer));
289 }
290}
291
293{
295
296 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
297 if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE) != challenge->I_len)
298 return false;
299
300 std::string_view login = challenge->GetLogin();
301 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] '{}'", login);
302
303 _build = challenge->build;
305 _os = challenge->os;
307
309
310 // Get the account details from the account table
312 stmt->setStringView(0, login);
313
314 QueueQuery(LoginDatabase.AsyncQuery(stmt)
315 .WithPreparedCallback([this](PreparedQueryResult result) { LogonChallengeCallback(std::move(result)); }));
316 return true;
317}
318
320{
321 ByteBuffer pkt;
323 pkt << uint8(0x00);
324
325 if (!result)
326 {
328 SendPacket(pkt);
329 return;
330 }
331
332 Field* fields = result->Fetch();
333
334 _accountInfo.LoadResult(fields);
335
336 std::string ipAddress = GetRemoteIpAddress().to_string();
337 uint16 port = GetRemotePort();
338
339 // If the IP is 'locked', check that the player comes indeed from the correct IP address
341 {
342 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to IP - '{}' is logging in from '{}'", _accountInfo.Login, _accountInfo.LastIP, ipAddress);
343 if (_accountInfo.LastIP != ipAddress)
344 {
346 SendPacket(pkt);
347 return;
348 }
349 }
350 else
351 {
352 if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress))
353 _ipCountry = location->CountryCode;
354
355 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to ip", _accountInfo.Login);
356 if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00")
357 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is not locked to country", _accountInfo.Login);
358 else if (!_ipCountry.empty())
359 {
360 TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '{}' is locked to country: '{}' Player country is '{}'", _accountInfo.Login, _accountInfo.LockCountry, _ipCountry);
362 {
364 SendPacket(pkt);
365 return;
366 }
367 }
368 }
369
370 // If the account is banned, reject the logon attempt
372 {
374 {
375 pkt << uint8(WOW_FAIL_BANNED);
376 SendPacket(pkt);
377 TC_LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
378 return;
379 }
380 else
381 {
383 SendPacket(pkt);
384 TC_LOG_INFO("server.authserver.banned", "'{}:{}' [AuthChallenge] Temporarily banned account {} tried to login!", ipAddress, port, _accountInfo.Login);
385 return;
386 }
387 }
388
389 uint8 securityFlags = 0;
390 // Check if a TOTP token is needed
391 if (!fields[9].IsNull())
392 {
393 securityFlags = 4;
394 _totpSecret = fields[9].GetBinary();
395 if (auto const& secret = sSecretMgr->GetSecret(SECRET_TOTP_MASTER_KEY))
396 {
397 bool success = Trinity::Crypto::AEDecrypt<Trinity::Crypto::AES>(*_totpSecret, *secret);
398 if (!success)
399 {
400 pkt << uint8(WOW_FAIL_DB_BUSY);
401 TC_LOG_ERROR("server.authserver", "[AuthChallenge] Account '{}' has invalid ciphertext for TOTP token key stored", _accountInfo.Login);
402 SendPacket(pkt);
403 return;
404 }
405 }
406 }
407
408 _srp6.emplace(
412 );
413
414 // Fill the response packet with the result
416 {
417 pkt << uint8(WOW_SUCCESS);
418
419 pkt.append(_srp6->B);
420 pkt << uint8(1);
421 pkt.append(_srp6->g);
422 pkt << uint8(32);
423 pkt.append(_srp6->N);
424 pkt.append(_srp6->s);
425 pkt.append(VersionChallenge.data(), VersionChallenge.size());
426 pkt << uint8(securityFlags); // security flags (0x0...0x04)
427
428 if (securityFlags & 0x01) // PIN input
429 {
430 pkt << uint32(0);
431 pkt << uint64(0) << uint64(0); // 16 bytes hash?
432 }
433
434 if (securityFlags & 0x02) // Matrix input
435 {
436 pkt << uint8(0);
437 pkt << uint8(0);
438 pkt << uint8(0);
439 pkt << uint8(0);
440 pkt << uint64(0);
441 }
442
443 if (securityFlags & 0x04) // Security token input
444 pkt << uint8(1);
445
446 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} is using '{}' locale ({})",
447 ipAddress, port, _accountInfo.Login, localeNames[_locale], uint32(_locale));
448
450 }
451 else
453
454 SendPacket(pkt);
455}
456
457// Logon Proof command handler
459{
460 TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof");
462
463 // Read the packet
464 sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer());
465
466 // If the client has no valid version
468 {
469 // Check if we have the appropriate patch on the disk
470 TC_LOG_DEBUG("network", "Client with invalid version, patching is not implemented");
471 return false;
472 }
473
474 // Check if SRP6 results match (password is correct), else send an error
475 if (std::optional<SessionKey> K = _srp6->VerifyChallengeResponse(logonProof->A, logonProof->clientM))
476 {
477 _sessionKey = *K;
478 // Check auth token
479 bool tokenSuccess = false;
480 bool sentToken = (logonProof->securityFlags & 0x04);
481 if (sentToken && _totpSecret)
482 {
483 uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C));
484 std::string token(reinterpret_cast<char*>(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size);
485 GetReadBuffer().ReadCompleted(sizeof(size) + size);
486
487 uint32 incomingToken = atoi(token.c_str());
488 tokenSuccess = Trinity::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken);
489 memset(_totpSecret->data(), 0, _totpSecret->size());
490 }
491 else if (!sentToken && !_totpSecret)
492 tokenSuccess = true;
493
494 if (!tokenSuccess)
495 {
496 ByteBuffer packet;
497 packet << uint8(AUTH_LOGON_PROOF);
499 packet << uint16(0); // LoginFlags, 1 has account message
500 SendPacket(packet);
501 return true;
502 }
503
504 if (!VerifyVersion(logonProof->A, logonProof->crc_hash, false))
505 {
506 ByteBuffer packet;
507 packet << uint8(AUTH_LOGON_PROOF);
509 SendPacket(packet);
510 return true;
511 }
512
513 TC_LOG_DEBUG("server.authserver", "'{}:{}' User '{}' successfully authenticated", GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
514
515 // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
516 // No SQL injection (escaped user name) and IP address as received by socket
517
518 std::string address = sConfigMgr->GetBoolDefault("AllowLoggingIPAddressesInDatabase", true, true) ? GetRemoteIpAddress().to_string() : "127.0.0.1";
520 stmt->setBinary(0, _sessionKey);
521 stmt->setString(1, address);
522 stmt->setUInt32(2, _locale);
524 stmt->setInt16(4, _timezoneOffset.count());
525 stmt->setString(5, _accountInfo.Login);
526 QueueQuery(LoginDatabase.AsyncQuery(stmt)
527 .WithPreparedCallback([this, M2 = Trinity::Crypto::SRP6::GetSessionVerifier(logonProof->A, logonProof->clientM, _sessionKey)](PreparedQueryResult const&)
528 {
529 // Finish SRP6 and send the final result to the client
530 ByteBuffer packet;
531 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
532 {
533 sAuthLogonProof_S proof;
534 proof.M2 = M2;
535 proof.cmd = AUTH_LOGON_PROOF;
536 proof.error = 0;
537 proof.AccountFlags = GAMEACCOUNT_FLAG_PROPASS_LOCK;
538 proof.SurveyId = 0;
539 proof.LoginFlags = 0; // 0x1 = has account message
540
541 packet.resize(sizeof(proof));
542 std::memcpy(packet.contents(), &proof, sizeof(proof));
543 }
544 else
545 {
546 sAuthLogonProof_S_Old proof;
547 proof.M2 = M2;
548 proof.cmd = AUTH_LOGON_PROOF;
549 proof.error = 0;
550 proof.unk2 = 0x00;
551
552 packet.resize(sizeof(proof));
553 std::memcpy(packet.contents(), &proof, sizeof(proof));
554 }
555
556 SendPacket(packet);
558 }));
559 }
560 else
561 {
562 ByteBuffer packet;
563 packet << uint8(AUTH_LOGON_PROOF);
565 packet << uint16(0); // LoginFlags, 1 has account message
566 SendPacket(packet);
567
568 TC_LOG_INFO("server.authserver.hack", "'{}:{}' [AuthChallenge] account {} tried to login with invalid password!",
569 GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login);
570
571 uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0);
572
573 // We can not include the failed account login hook. However, this is a workaround to still log this.
574 if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false))
575 {
577 logstmt->setUInt32(0, _accountInfo.Id);
578 logstmt->setString(1, GetRemoteIpAddress().to_string());
579 logstmt->setString(2, "Login to WoW Failed - Incorrect Password");
580
581 LoginDatabase.Execute(logstmt);
582 }
583
584 if (MaxWrongPassCount > 0)
585 {
586 //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
588 stmt->setString(0, _accountInfo.Login);
589 LoginDatabase.Execute(stmt);
590
591 if (++_accountInfo.FailedLogins >= MaxWrongPassCount)
592 {
593 uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
594 bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false);
595
596 if (WrongPassBanType)
597 {
598 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
599 stmt->setUInt32(0, _accountInfo.Id);
600 stmt->setUInt32(1, WrongPassBanTime);
601 LoginDatabase.Execute(stmt);
602
603 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] account {} got banned for '{}' seconds because it failed to authenticate '{}' times",
604 GetRemoteIpAddress().to_string(), GetRemotePort(), _accountInfo.Login, WrongPassBanTime, _accountInfo.FailedLogins);
605 }
606 else
607 {
608 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
609 stmt->setString(0, GetRemoteIpAddress().to_string());
610 stmt->setUInt32(1, WrongPassBanTime);
611 LoginDatabase.Execute(stmt);
612
613 TC_LOG_DEBUG("server.authserver", "'{}:{}' [AuthChallenge] IP got banned for '{}' seconds because account {} failed to authenticate '{}' times",
614 GetRemoteIpAddress().to_string(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login, _accountInfo.FailedLogins);
615 }
616 }
617 }
618 }
619
620 return true;
621}
622
624{
626
627 sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
628 if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE) != challenge->I_len)
629 return false;
630
631 std::string_view login = challenge->GetLogin();
632 TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] '{}'", login);
633
634 _build = challenge->build;
636 _os = challenge->os;
638
640
641 // Get the account details from the account table
643 stmt->setStringView(0, login);
644
645 QueueQuery(LoginDatabase.AsyncQuery(stmt)
646 .WithPreparedCallback([this](PreparedQueryResult result) { ReconnectChallengeCallback(std::move(result)); }));
647 return true;
648}
649
651{
652 ByteBuffer pkt;
654
655 if (!result)
656 {
658 SendPacket(pkt);
659 return;
660 }
661
662 Field* fields = result->Fetch();
663
664 _accountInfo.LoadResult(fields);
668
669 pkt << uint8(WOW_SUCCESS);
671 pkt.append(VersionChallenge.data(), VersionChallenge.size());
672
673 SendPacket(pkt);
674}
675
677{
678 TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
680
681 sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer());
682
683 if (_accountInfo.Login.empty())
684 return false;
685
688 sha.UpdateData(reconnectProof->R1, 16);
691 sha.Finalize();
692
693 if (sha.GetDigest() == reconnectProof->R2)
694 {
695 if (!VerifyVersion(reconnectProof->R1, reconnectProof->R3, true))
696 {
697 ByteBuffer packet;
698 packet << uint8(AUTH_RECONNECT_PROOF);
700 SendPacket(packet);
701 return true;
702 }
703
704 // Sending response
705 ByteBuffer pkt;
707 pkt << uint8(WOW_SUCCESS);
708 pkt << uint16(0); // LoginFlags, 1 has account message
709 SendPacket(pkt);
711 return true;
712 }
713 else
714 {
715 TC_LOG_ERROR("server.authserver.hack", "'{}:{}' [ERROR] user {} tried to login, but session is invalid.", GetRemoteIpAddress().to_string(),
717 return false;
718 }
719}
720
722{
723 TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
724
726 stmt->setUInt32(0, _accountInfo.Id);
727
728 QueueQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1)));
730 return true;
731}
732
734{
735 std::map<uint32, uint8> characterCounts;
736 if (result)
737 {
738 do
739 {
740 Field* fields = result->Fetch();
741 characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8();
742 } while (result->NextRow());
743 }
744
745 // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
746 ByteBuffer pkt;
747
748 size_t RealmListSize = 0;
749 for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms())
750 {
751 Realm const& realm = i.second;
752 // don't work with realms which not compatible with the client
754
755 // No SQL injection. id of realm is controlled by the database.
756 uint32 flag = realm.Flags;
758 if (!okBuild)
759 {
760 if (!buildInfo)
761 continue;
762
763 flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
764 }
765
766 if (!buildInfo)
767 flag &= ~REALM_FLAG_SPECIFYBUILD;
768
769 std::string name = realm.Name;
771 Trinity::StringFormatTo(std::back_inserter(name), " ({}.{}.{})", buildInfo->MajorVersion, buildInfo->MinorVersion, buildInfo->BugfixVersion);
772
774
775 pkt << uint8(realm.Type); // realm type
776 if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
777 pkt << uint8(lock); // if 1, then realm locked
778 pkt << uint8(flag); // RealmFlags
779 pkt << name;
780 pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
781 pkt << float(realm.PopulationLevel);
782 pkt << uint8(characterCounts[realm.Id.Realm]);
783 pkt << uint8(realm.Timezone); // realm category
784 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
785 pkt << uint8(realm.Id.Realm);
786 else
787 pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
788
790 {
791 pkt << uint8(buildInfo->MajorVersion);
792 pkt << uint8(buildInfo->MinorVersion);
793 pkt << uint8(buildInfo->BugfixVersion);
794 pkt << uint16(buildInfo->Build);
795 }
796
797 ++RealmListSize;
798 }
799
800 if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
801 {
802 pkt << uint8(0x10);
803 pkt << uint8(0x00);
804 }
805 else // 1.12.1 and 1.12.2 clients
806 {
807 pkt << uint8(0x00);
808 pkt << uint8(0x02);
809 }
810
811 // make a ByteBuffer which stores the RealmList's size
812 ByteBuffer RealmListSizeBuffer;
813 RealmListSizeBuffer << uint32(0);
814 if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
815 RealmListSizeBuffer << uint16(RealmListSize);
816 else
817 RealmListSizeBuffer << uint32(RealmListSize);
818
819 ByteBuffer hdr;
820 hdr << uint8(REALM_LIST);
821 hdr << uint16(pkt.size() + RealmListSizeBuffer.size());
822 hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
823 hdr.append(pkt); // append realms in the realmlist
824 SendPacket(hdr);
825
827}
828
830{
831 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept");
832
833 // empty handler meant to close the connection if received
834 return false;
835}
836
838{
839 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume");
840
841 // empty handler meant to close the connection if received
842 return false;
843}
844
846{
847 TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel");
848
849 // empty handler meant to close the connection if received
850 return false;
851}
852
853bool AuthSession::VerifyVersion(std::span<uint8 const> a, Trinity::Crypto::SHA1::Digest const& versionProof, bool isReconnect)
854{
855 if (!sConfigMgr->GetBoolDefault("StrictVersionCheck", false))
856 return true;
857
859 Trinity::Crypto::SHA1::Digest const* versionHash = nullptr;
860 if (!isReconnect)
861 {
863 if (!buildInfo)
864 return false;
865
866 auto platformItr = std::ranges::find(buildInfo->ExecutableHashes, _os, &ClientBuild::ExecutableHash::Platform);
867 if (platformItr == buildInfo->ExecutableHashes.end())
868 return true; // not filled serverside
869
870 versionHash = &platformItr->Hash;
871 }
872 else
873 versionHash = &zeros;
874
875 Trinity::Crypto::SHA1 version;
876 version.UpdateData(a);
877 version.UpdateData(*versionHash);
878 version.Finalize();
879
880 return versionProof == version.GetDigest();
881}
882
884{
885 _timeout.cancel();
886
887 switch (_status)
888 {
889 case STATUS_AUTHED:
891 _timeout.expires_after(1min);
892 break;
893 case STATUS_XFER:
894 return;
895 default:
896 _timeout.expires_after(10s);
897 break;
898 }
899
900 _timeout.async_wait([selfRef = weak_from_this()](boost::system::error_code const& error)
901 {
902 std::shared_ptr<AuthSession> self = static_pointer_cast<AuthSession>(selfRef.lock());
903 if (!self)
904 return;
905
906 if (error == boost::asio::error::operation_aborted)
907 return;
908
909 TC_LOG_DEBUG("server.authserver", "{}:{} session timed out.", self->GetRemoteIpAddress().to_string(), self->GetRemotePort());
910 self->CloseSocket();
911 });
912}
@ 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
#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
static constexpr std::array< uint8, 16 > VersionChallenge
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:133
size_t size() const
Definition ByteBuffer.h:413
bool empty() const
Definition ByteBuffer.h:414
uint8 * contents()
Definition ByteBuffer.h:399
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:3610
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
std::string_view GetLogin() const
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)