Advertisement
Guest User

AuthSession.cpp

a guest
Jun 5th, 2017
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.15 KB | None | 0 0
  1. /*
  2. * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
  3. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. #include "AuthSession.h"
  20. #include "Log.h"
  21. #include "AuthCodes.h"
  22. #include "Database/DatabaseEnv.h"
  23. #include "SHA1.h"
  24. #include "TOTP.h"
  25. #include "openssl/crypto.h"
  26. #include "Configuration/Config.h"
  27. #include "RealmList.h"
  28. #include <boost/lexical_cast.hpp>
  29.  
  30. using boost::asio::ip::tcp;
  31.  
  32. enum eStatus
  33. {
  34. STATUS_CONNECTED = 0,
  35. STATUS_AUTHED
  36. };
  37.  
  38. #pragma pack(push, 1)
  39.  
  40. typedef struct AUTH_LOGON_CHALLENGE_C
  41. {
  42. uint8 cmd;
  43. uint8 error;
  44. uint16 size;
  45. uint8 gamename[4];
  46. uint8 version1;
  47. uint8 version2;
  48. uint8 version3;
  49. uint16 build;
  50. uint8 platform[4];
  51. uint8 os[4];
  52. uint8 country[4];
  53. uint32 timezone_bias;
  54. uint32 ip;
  55. uint8 I_len;
  56. uint8 I[1];
  57. } sAuthLogonChallenge_C;
  58.  
  59. typedef struct AUTH_LOGON_PROOF_C
  60. {
  61. uint8 cmd;
  62. uint8 A[32];
  63. uint8 M1[20];
  64. uint8 crc_hash[20];
  65. uint8 number_of_keys;
  66. uint8 securityFlags;
  67. } sAuthLogonProof_C;
  68.  
  69. typedef struct AUTH_LOGON_PROOF_S
  70. {
  71. uint8 cmd;
  72. uint8 error;
  73. uint8 M2[20];
  74. uint32 AccountFlags;
  75. uint32 SurveyId;
  76. uint16 unk3;
  77. } sAuthLogonProof_S;
  78.  
  79. typedef struct AUTH_LOGON_PROOF_S_OLD
  80. {
  81. uint8 cmd;
  82. uint8 error;
  83. uint8 M2[20];
  84. uint32 unk2;
  85. } sAuthLogonProof_S_Old;
  86.  
  87. typedef struct AUTH_RECONNECT_PROOF_C
  88. {
  89. uint8 cmd;
  90. uint8 R1[16];
  91. uint8 R2[20];
  92. uint8 R3[20];
  93. uint8 number_of_keys;
  94. } sAuthReconnectProof_C;
  95.  
  96. #pragma pack(pop)
  97.  
  98. enum class BufferSizes : uint32
  99. {
  100. SRP_6_V = 0x20,
  101. SRP_6_S = 0x20,
  102. };
  103.  
  104. #define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
  105. #define REALM_LIST_PACKET_SIZE 5
  106. #define XFER_ACCEPT_SIZE 1
  107. #define XFER_RESUME_SIZE 9
  108. #define XFER_CANCEL_SIZE 1
  109.  
  110. std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
  111. {
  112. std::unordered_map<uint8, AuthHandler> handlers;
  113.  
  114. handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge };
  115. handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof };
  116. handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
  117. handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECON_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
  118. handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList };
  119. handlers[XFER_ACCEPT] = { STATUS_PATCH, XFER_ACCEPT_SIZE, &AuthSession::HandleXferAccept };
  120. handlers[XFER_RESUME] = { STATUS_PATCH, XFER_RESUME_SIZE, &AuthSession::HandleXferResume };
  121. handlers[XFER_CANCEL] = { STATUS_PATCH, XFER_CANCEL_SIZE, &AuthSession::HandleXferCancel };
  122.  
  123.  
  124. return handlers;
  125. }
  126.  
  127. std::unordered_map<uint8, AuthHandler> const Handlers = AuthSession::InitHandlers();
  128.  
  129. void AuthSession::ReadHandler()
  130. {
  131. MessageBuffer& packet = GetReadBuffer();
  132. while (packet.GetActiveSize())
  133. {
  134. uint8 cmd = packet.GetReadPointer()[0];
  135. auto itr = Handlers.find(cmd);
  136. if (itr == Handlers.end())
  137. {
  138. // well we dont handle this, lets just ignore it
  139. packet.Reset();
  140. break;
  141. }
  142.  
  143. if (_status != itr->second.status)
  144. {
  145. CloseSocket();
  146. return;
  147. }
  148.  
  149. uint16 size = uint16(itr->second.packetSize);
  150. if (packet.GetActiveSize() < size)
  151. break;
  152.  
  153. if (cmd == AUTH_LOGON_CHALLENGE || cmd == AUTH_RECONNECT_CHALLENGE)
  154. {
  155. sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(packet.GetReadPointer());
  156. size += challenge->size;
  157. }
  158.  
  159. if (packet.GetActiveSize() < size)
  160. break;
  161.  
  162. if (!(*this.*Handlers.at(cmd).handler)())
  163. {
  164. CloseSocket();
  165. return;
  166. }
  167.  
  168. packet.ReadCompleted(size);
  169. }
  170.  
  171. AsyncRead();
  172. }
  173.  
  174. void AuthSession::SendPacket(ByteBuffer& packet)
  175. {
  176. if (!IsOpen())
  177. return;
  178.  
  179. if (!packet.empty())
  180. {
  181. MessageBuffer buffer;
  182. buffer.Write(packet.contents(), packet.size());
  183.  
  184. std::unique_lock<std::mutex> guard(_writeLock);
  185.  
  186. QueuePacket(std::move(buffer), guard);
  187. }
  188. }
  189.  
  190. bool AuthSession::HandleLogonChallenge()
  191. {
  192. ///- Session is closed unless overriden
  193. _status = STATUS_CLOSED;
  194.  
  195. sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
  196.  
  197. //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size);
  198. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I);
  199.  
  200. ByteBuffer pkt;
  201.  
  202. _login.assign((const char*)challenge->I, challenge->I_len);
  203. _build = challenge->build;
  204. _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
  205. _os = (const char*)challenge->os;
  206.  
  207. if (_os.size() > 4)
  208. return false;
  209.  
  210. // Restore string order as its byte order is reversed
  211. std::reverse(_os.begin(), _os.end());
  212.  
  213. pkt << uint8(AUTH_LOGON_CHALLENGE);
  214. pkt << uint8(0x00);
  215.  
  216. // Verify that this IP is not in the ip_banned table
  217. LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
  218.  
  219. std::string ipAddress = GetRemoteIpAddress().to_string();
  220. uint16 port = GetRemotePort();
  221.  
  222. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED);
  223. stmt->setString(0, ipAddress);
  224. PreparedQueryResult result = LoginDatabase.Query(stmt);
  225. if (result)
  226. {
  227. pkt << uint8(WOW_FAIL_BANNED);
  228. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned ip tries to login!", ipAddress.c_str(), port);
  229. }
  230. else
  231. {
  232. // Get the account details from the account table
  233. // No SQL injection (prepared statement)
  234. stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE);
  235. stmt->setString(0, _login);
  236.  
  237. PreparedQueryResult res2 = LoginDatabase.Query(stmt);
  238. if (res2)
  239. {
  240. Field* fields = res2->Fetch();
  241.  
  242. // If the IP is 'locked', check that the player comes indeed from the correct IP address
  243. bool locked = false;
  244. if (fields[2].GetUInt8() == 1) // if ip is locked
  245. {
  246. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[4].GetCString());
  247. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Player address is '%s'", ipAddress.c_str());
  248.  
  249. if (strcmp(fields[4].GetCString(), ipAddress.c_str()) != 0)
  250. {
  251. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP differs");
  252. pkt << uint8(WOW_FAIL_LOCKED_ENFORCED);
  253. locked = true;
  254. }
  255. else
  256. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP matches");
  257. }
  258. else
  259. {
  260. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
  261. std::string accountCountry = fields[3].GetString();
  262. if (accountCountry.empty() || accountCountry == "00")
  263. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _login.c_str());
  264. else if (!accountCountry.empty())
  265. {
  266. uint32 ip = inet_addr(ipAddress.c_str());
  267. EndianConvertReverse(ip);
  268.  
  269. stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY);
  270. stmt->setUInt32(0, ip);
  271. if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt))
  272. {
  273. std::string loginCountry = (*sessionCountryQuery)[0].GetString();
  274. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _login.c_str(),
  275. accountCountry.c_str(), loginCountry.c_str());
  276.  
  277. if (loginCountry != accountCountry)
  278. {
  279. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country differs.");
  280. pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK);
  281. locked = true;
  282. }
  283. else
  284. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country matches");
  285. }
  286. else
  287. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] IP2NATION Table empty");
  288. }
  289. }
  290.  
  291. if (!locked)
  292. {
  293. //set expired bans to inactive
  294. LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
  295.  
  296. // If the account is banned, reject the logon attempt
  297. stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED);
  298. stmt->setUInt32(0, fields[1].GetUInt32());
  299. PreparedQueryResult banresult = LoginDatabase.Query(stmt);
  300. if (banresult)
  301. {
  302. if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32())
  303. {
  304. pkt << uint8(WOW_FAIL_BANNED);
  305. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(),
  306. port, _login.c_str());
  307. }
  308. else
  309. {
  310. pkt << uint8(WOW_FAIL_SUSPENDED);
  311. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!",
  312. ipAddress.c_str(), port, _login.c_str());
  313. }
  314. }
  315. else
  316. {
  317. // Get the password from the account table, upper it, and make the SRP6 calculation
  318. std::string rI = fields[0].GetString();
  319.  
  320. // Don't calculate (v, s) if there are already some in the database
  321. std::string databaseV = fields[6].GetString();
  322. std::string databaseS = fields[7].GetString();
  323.  
  324. TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());
  325.  
  326. // multiply with 2 since bytes are stored as hexstring
  327. if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2)
  328. SetVSFields(rI);
  329. else
  330. {
  331. s.SetHexStr(databaseS.c_str());
  332. v.SetHexStr(databaseV.c_str());
  333. }
  334.  
  335. b.SetRand(19 * 8);
  336. BigNumber gmod = g.ModExp(b, N);
  337. B = ((v * 3) + gmod) % N;
  338.  
  339. ASSERT(gmod.GetNumBytes() <= 32);
  340.  
  341. BigNumber unk3;
  342. unk3.SetRand(16 * 8);
  343.  
  344. // Fill the response packet with the result
  345. if (AuthHelper::IsAcceptedClientBuild(_build))
  346. {
  347. pkt << uint8(WOW_SUCCESS);
  348. _status = STATUS_LOGON_PROOF;
  349. }
  350. else
  351. pkt << uint8(WOW_FAIL_VERSION_INVALID);
  352.  
  353. // B may be calculated < 32B so we force minimal length to 32B
  354. pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes
  355. pkt << uint8(1);
  356. pkt.append(g.AsByteArray(1).get(), 1);
  357. pkt << uint8(32);
  358. pkt.append(N.AsByteArray(32).get(), 32);
  359. pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes
  360. pkt.append(unk3.AsByteArray(16).get(), 16);
  361. uint8 securityFlags = 0;
  362.  
  363. // Check if token is used
  364. _tokenKey = fields[8].GetString();
  365. if (!_tokenKey.empty())
  366. securityFlags = 4;
  367.  
  368. pkt << uint8(securityFlags); // security flags (0x0...0x04)
  369.  
  370. if (securityFlags & 0x01) // PIN input
  371. {
  372. pkt << uint32(0);
  373. pkt << uint64(0) << uint64(0); // 16 bytes hash?
  374. }
  375.  
  376. if (securityFlags & 0x02) // Matrix input
  377. {
  378. pkt << uint8(0);
  379. pkt << uint8(0);
  380. pkt << uint8(0);
  381. pkt << uint8(0);
  382. pkt << uint64(0);
  383. }
  384.  
  385. if (securityFlags & 0x04) // Security token input
  386. pkt << uint8(1);
  387.  
  388. uint8 secLevel = fields[5].GetUInt8();
  389. _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
  390.  
  391. _localizationName.resize(4);
  392. for (int i = 0; i < 4; ++i)
  393. _localizationName[i] = challenge->country[4 - i - 1];
  394.  
  395. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)",
  396. ipAddress.c_str(), port, _login.c_str(),
  397. challenge->country[3], challenge->country[2], challenge->country[1], challenge->country[0],
  398. GetLocaleByName(_localizationName)
  399. );
  400.  
  401. ///- All good, await client's proof
  402. _status = STATUS_LOGON_PROOF;
  403. }
  404. }
  405. }
  406. else //no account
  407. pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
  408. }
  409.  
  410. SendPacket(pkt);
  411. return true;
  412. }
  413.  
  414. // Logon Proof command handler
  415. bool AuthSession::HandleLogonProof()
  416. {
  417.  
  418. ///- Session is closed unless overriden
  419. _status = STATUS_CLOSED;
  420.  
  421. // Read the packet
  422. sAuthLogonProof_C *logonProof = reinterpret_cast<sAuthLogonProof_C*>(GetReadBuffer().GetReadPointer());
  423.  
  424. // If the client has no valid version
  425. if (_expversion == NO_VALID_EXP_FLAG)
  426. {
  427. // Check if we have the appropriate patch on the disk
  428. TC_LOG_DEBUG("network", "Client with invalid version, patching is not implemented");
  429. return false;
  430. }
  431.  
  432. // Continue the SRP6 calculation based on data received from the client
  433. BigNumber A;
  434.  
  435. A.SetBinary(logonProof->A, 32);
  436.  
  437. // SRP safeguard: abort if A == 0
  438. if ((A % N).isZero())
  439. return false;
  440.  
  441. SHA1Hash sha;
  442. sha.UpdateBigNumbers(&A, &B, NULL);
  443. sha.Finalize();
  444. BigNumber u;
  445. u.SetBinary(sha.GetDigest(), 20);
  446. BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N);
  447.  
  448. uint8 t[32];
  449. uint8 t1[16];
  450. uint8 vK[40];
  451. memcpy(t, S.AsByteArray(32).get(), 32);
  452.  
  453. for (int i = 0; i < 16; ++i)
  454. t1[i] = t[i * 2];
  455.  
  456. sha.Initialize();
  457. sha.UpdateData(t1, 16);
  458. sha.Finalize();
  459.  
  460. for (int i = 0; i < 20; ++i)
  461. vK[i * 2] = sha.GetDigest()[i];
  462.  
  463. for (int i = 0; i < 16; ++i)
  464. t1[i] = t[i * 2 + 1];
  465.  
  466. sha.Initialize();
  467. sha.UpdateData(t1, 16);
  468. sha.Finalize();
  469.  
  470. for (int i = 0; i < 20; ++i)
  471. vK[i * 2 + 1] = sha.GetDigest()[i];
  472.  
  473. K.SetBinary(vK, 40);
  474.  
  475. uint8 hash[20];
  476.  
  477. sha.Initialize();
  478. sha.UpdateBigNumbers(&N, NULL);
  479. sha.Finalize();
  480. memcpy(hash, sha.GetDigest(), 20);
  481. sha.Initialize();
  482. sha.UpdateBigNumbers(&g, NULL);
  483. sha.Finalize();
  484.  
  485. for (int i = 0; i < 20; ++i)
  486. hash[i] ^= sha.GetDigest()[i];
  487.  
  488. BigNumber t3;
  489. t3.SetBinary(hash, 20);
  490.  
  491. sha.Initialize();
  492. sha.UpdateData(_login);
  493. sha.Finalize();
  494. uint8 t4[SHA_DIGEST_LENGTH];
  495. memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);
  496.  
  497. sha.Initialize();
  498. sha.UpdateBigNumbers(&t3, NULL);
  499. sha.UpdateData(t4, SHA_DIGEST_LENGTH);
  500. sha.UpdateBigNumbers(&s, &A, &B, &K, NULL);
  501. sha.Finalize();
  502. BigNumber M;
  503. M.SetBinary(sha.GetDigest(), sha.GetLength());
  504.  
  505. // Check if SRP6 results match (password is correct), else send an error
  506. if (!memcmp(M.AsByteArray(sha.GetLength()).get(), logonProof->M1, 20))
  507. {
  508. TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
  509.  
  510. // Check auth token
  511. if ((logonProof->securityFlags & 0x04) || !_tokenKey.empty())
  512. {
  513. uint8 size = *(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C));
  514. std::string token(reinterpret_cast<char*>(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size);
  515. GetReadBuffer().ReadCompleted(sizeof(size) + size);
  516. uint32 validToken = TOTP::GenerateToken(_tokenKey.c_str());
  517. uint32 incomingToken = atoi(token.c_str());
  518. if (validToken != incomingToken)
  519. {
  520. ByteBuffer packet;
  521. packet << uint8(AUTH_LOGON_PROOF);
  522. packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
  523. packet << uint8(3);
  524. packet << uint8(0);
  525. SendPacket(packet);
  526. return false;
  527. }
  528. }
  529.  
  530. // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
  531. // No SQL injection (escaped user name) and IP address as received by socket
  532. const char *K_hex = K.AsHexStr();
  533.  
  534. PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
  535. stmt->setString(0, K_hex);
  536. stmt->setString(1, GetRemoteIpAddress().to_string().c_str());
  537. stmt->setUInt32(2, GetLocaleByName(_localizationName));
  538. stmt->setString(3, _os);
  539. stmt->setString(4, _login);
  540. LoginDatabase.DirectExecute(stmt);
  541.  
  542. OPENSSL_free((void*)K_hex);
  543.  
  544. // Finish SRP6 and send the final result to the client
  545. sha.Initialize();
  546. sha.UpdateBigNumbers(&A, &M, &K, NULL);
  547. sha.Finalize();
  548.  
  549. ByteBuffer packet;
  550. if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
  551. {
  552. sAuthLogonProof_S proof;
  553. memcpy(proof.M2, sha.GetDigest(), 20);
  554. proof.cmd = AUTH_LOGON_PROOF;
  555. proof.error = 0;
  556. proof.AccountFlags = 0x00800000; // 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
  557. proof.SurveyId = 0;
  558. proof.unk3 = 0;
  559.  
  560. packet.resize(sizeof(proof));
  561. std::memcpy(packet.contents(), &proof, sizeof(proof));
  562. }
  563. else
  564. {
  565. sAuthLogonProof_S_Old proof;
  566. memcpy(proof.M2, sha.GetDigest(), 20);
  567. proof.cmd = AUTH_LOGON_PROOF;
  568. proof.error = 0;
  569. proof.unk2 = 0x00;
  570.  
  571. packet.resize(sizeof(proof));
  572. std::memcpy(packet.contents(), &proof, sizeof(proof));
  573. }
  574.  
  575. SendPacket(packet);
  576. ///- Set _status to authed!
  577. _status = STATUS_AUTHED;
  578. }
  579. else
  580. {
  581. ByteBuffer packet;
  582. packet << uint8(AUTH_LOGON_PROOF);
  583. packet << uint8(WOW_FAIL_UNKNOWN_ACCOUNT);
  584. packet << uint8(3);
  585. packet << uint8(0);
  586. SendPacket(packet);
  587.  
  588. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!",
  589. GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
  590.  
  591. uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0);
  592.  
  593. // We can not include the failed account login hook. However, this is a workaround to still log this.
  594. if (sConfigMgr->GetBoolDefault("Wrong.Password.Login.Logging", false))
  595. {
  596. PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
  597. logstmt->setString(0, _login);
  598. logstmt->setString(1, GetRemoteIpAddress().to_string());
  599. logstmt->setString(2, "Logged on failed AccountLogin due wrong password");
  600.  
  601. LoginDatabase.Execute(logstmt);
  602. }
  603.  
  604. if (MaxWrongPassCount > 0)
  605. {
  606. //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
  607. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
  608. stmt->setString(0, _login);
  609. LoginDatabase.Execute(stmt);
  610.  
  611. stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS);
  612. stmt->setString(0, _login);
  613.  
  614. if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt))
  615. {
  616. uint32 failed_logins = (*loginfail)[1].GetUInt32();
  617.  
  618. if (failed_logins >= MaxWrongPassCount)
  619. {
  620. uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600);
  621. bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false);
  622.  
  623. if (WrongPassBanType)
  624. {
  625. uint32 acc_id = (*loginfail)[0].GetUInt32();
  626. stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED);
  627. stmt->setUInt32(0, acc_id);
  628. stmt->setUInt32(1, WrongPassBanTime);
  629. LoginDatabase.Execute(stmt);
  630.  
  631. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
  632. GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str(), WrongPassBanTime, failed_logins);
  633. }
  634. else
  635. {
  636. stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED);
  637. stmt->setString(0, GetRemoteIpAddress().to_string());
  638. stmt->setUInt32(1, WrongPassBanTime);
  639. LoginDatabase.Execute(stmt);
  640.  
  641. TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times",
  642. GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _login.c_str(), failed_logins);
  643. }
  644. }
  645. }
  646. }
  647. }
  648.  
  649. return true;
  650. }
  651.  
  652. bool AuthSession::HandleReconnectChallenge()
  653. {
  654. TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge");
  655.  
  656. ///- Session is closed unless overriden
  657. _status = STATUS_CLOSED;
  658.  
  659. sAuthLogonChallenge_C* challenge = reinterpret_cast<sAuthLogonChallenge_C*>(GetReadBuffer().GetReadPointer());
  660.  
  661. //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size);
  662. TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I);
  663.  
  664. _login.assign((const char*)challenge->I, challenge->I_len);
  665.  
  666. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SESSIONKEY);
  667. stmt->setString(0, _login);
  668. PreparedQueryResult result = LoginDatabase.Query(stmt);
  669.  
  670. // Stop if the account is not found
  671. if (!result)
  672. {
  673. TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database.",
  674. GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str());
  675. return false;
  676. }
  677.  
  678. // Reinitialize build, expansion and the account securitylevel
  679. _build = challenge->build;
  680. _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG));
  681. _os = (const char*)challenge->os;
  682.  
  683. if (_os.size() > 4)
  684. return false;
  685.  
  686. // Restore string order as its byte order is reversed
  687. std::reverse(_os.begin(), _os.end());
  688.  
  689. Field* fields = result->Fetch();
  690. uint8 secLevel = fields[2].GetUInt8();
  691. _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
  692.  
  693. K.SetHexStr((*result)[0].GetCString());
  694.  
  695. // Sending response
  696. ByteBuffer pkt;
  697. pkt << uint8(AUTH_RECONNECT_CHALLENGE);
  698. pkt << uint8(0x00);
  699. _reconnectProof.SetRand(16 * 8);
  700. ///- All good, await client's proof
  701. _status = STATUS_RECON_PROOF;
  702. pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random
  703. pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros
  704.  
  705. SendPacket(pkt);
  706.  
  707. return true;
  708. }
  709. bool AuthSession::HandleReconnectProof()
  710. {
  711. TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof");
  712.  
  713. ///- Session is closed unless overriden
  714. _status = STATUS_CLOSED;
  715.  
  716. sAuthReconnectProof_C *reconnectProof = reinterpret_cast<sAuthReconnectProof_C*>(GetReadBuffer().GetReadPointer());
  717.  
  718. if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes())
  719. return false;
  720.  
  721. BigNumber t1;
  722. t1.SetBinary(reconnectProof->R1, 16);
  723.  
  724. SHA1Hash sha;
  725. sha.Initialize();
  726. sha.UpdateData(_login);
  727. sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL);
  728. sha.Finalize();
  729.  
  730. if (!memcmp(sha.GetDigest(), reconnectProof->R2, SHA_DIGEST_LENGTH))
  731. {
  732. // Sending response
  733. ByteBuffer pkt;
  734. pkt << uint8(AUTH_RECONNECT_PROOF);
  735. pkt << uint8(0x00);
  736. pkt << uint16(0x00); // 2 bytes zeros
  737. SendPacket(pkt);
  738. ///- Set _status to authed!
  739. _status = STATUS_AUTHED;
  740. return true;
  741. }
  742. else
  743. {
  744. TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().to_string().c_str(),
  745. GetRemotePort(), _login.c_str());
  746. return false;
  747. }
  748. }
  749.  
  750. tcp::endpoint const GetAddressForClient(Realm const& realm, ip::address const& clientAddr)
  751. {
  752. ip::address realmIp;
  753.  
  754. // Attempt to send best address for client
  755. if (clientAddr.is_loopback())
  756. {
  757. // Try guessing if realm is also connected locally
  758. if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback())
  759. realmIp = clientAddr;
  760. else
  761. {
  762. // Assume that user connecting from the machine that authserver is located on
  763. // has all realms available in his local network
  764. realmIp = realm.LocalAddress;
  765. }
  766. }
  767. else
  768. {
  769. if (clientAddr.is_v4() &&
  770. (clientAddr.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong()) ==
  771. (realm.LocalAddress.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong()))
  772. {
  773. realmIp = realm.LocalAddress;
  774. }
  775. else
  776. realmIp = realm.ExternalAddress;
  777. }
  778.  
  779. tcp::endpoint endpoint(realmIp, realm.port);
  780.  
  781. // Return external IP
  782. return endpoint;
  783. }
  784.  
  785. bool AuthSession::HandleRealmList()
  786. {
  787. TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
  788.  
  789. // Get the user id (else close the connection)
  790. // No SQL injection (prepared statement)
  791. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME);
  792. stmt->setString(0, _login);
  793. PreparedQueryResult result = LoginDatabase.Query(stmt);
  794. if (!result)
  795. {
  796. TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database.", GetRemoteIpAddress().to_string().c_str(),
  797. GetRemotePort(), _login.c_str());
  798. return false;
  799. }
  800.  
  801. Field* fields = result->Fetch();
  802. uint32 id = fields[0].GetUInt32();
  803.  
  804. // Update realm list if need
  805. sRealmList->UpdateIfNeed();
  806.  
  807. // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
  808. ByteBuffer pkt;
  809.  
  810. size_t RealmListSize = 0;
  811. for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i)
  812. {
  813. const Realm &realm = i->second;
  814. // don't work with realms which not compatible with the client
  815. bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.gamebuild == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.gamebuild));
  816.  
  817. // No SQL injection. id of realm is controlled by the database.
  818. uint32 flag = realm.flag;
  819. RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild);
  820. if (!okBuild)
  821. {
  822. if (!buildInfo)
  823. continue;
  824.  
  825. flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD; // tell the client what build the realm is for
  826. }
  827.  
  828. if (!buildInfo)
  829. flag &= ~REALM_FLAG_SPECIFYBUILD;
  830.  
  831. std::string name = i->first;
  832. if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
  833. {
  834. std::ostringstream ss;
  835. ss << name << " (" << buildInfo->MajorVersion << '.' << buildInfo->MinorVersion << '.' << buildInfo->BugfixVersion << ')';
  836. name = ss.str();
  837. }
  838.  
  839. uint8 lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
  840.  
  841. uint8 AmountOfCharacters = 0;
  842. stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM);
  843. stmt->setUInt32(0, realm.m_ID);
  844. stmt->setUInt32(1, id);
  845. result = LoginDatabase.Query(stmt);
  846. if (result)
  847. AmountOfCharacters = (*result)[0].GetUInt8();
  848.  
  849. pkt << realm.icon; // realm type
  850. if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
  851. pkt << lock; // if 1, then realm locked
  852. pkt << uint8(flag); // RealmFlags
  853. pkt << name;
  854. pkt << boost::lexical_cast<std::string>(GetAddressForClient(realm, GetRemoteIpAddress()));
  855. pkt << realm.populationLevel;
  856. pkt << AmountOfCharacters;
  857. pkt << realm.timezone; // realm category
  858. if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
  859. pkt << uint8(realm.m_ID);
  860. else
  861. pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients
  862.  
  863. if (_expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD)
  864. {
  865. pkt << uint8(buildInfo->MajorVersion);
  866. pkt << uint8(buildInfo->MinorVersion);
  867. pkt << uint8(buildInfo->BugfixVersion);
  868. pkt << uint16(buildInfo->Build);
  869. }
  870.  
  871. ++RealmListSize;
  872. }
  873.  
  874. if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients
  875. {
  876. pkt << uint8(0x10);
  877. pkt << uint8(0x00);
  878. }
  879. else // 1.12.1 and 1.12.2 clients
  880. {
  881. pkt << uint8(0x00);
  882. pkt << uint8(0x02);
  883. }
  884.  
  885. // make a ByteBuffer which stores the RealmList's size
  886. ByteBuffer RealmListSizeBuffer;
  887. RealmListSizeBuffer << uint32(0);
  888. if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients
  889. RealmListSizeBuffer << uint16(RealmListSize);
  890. else
  891. RealmListSizeBuffer << uint32(RealmListSize);
  892.  
  893. ByteBuffer hdr;
  894. hdr << uint8(REALM_LIST);
  895. hdr << uint16(pkt.size() + RealmListSizeBuffer.size());
  896. hdr.append(RealmListSizeBuffer); // append RealmList's size buffer
  897. hdr.append(pkt); // append realms in the realmlist
  898. SendPacket(hdr);
  899. return true;
  900. }
  901.  
  902. // Resume patch transfer
  903. bool AuthSession::HandleXferResume()
  904. {
  905. TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume");
  906. //uint8
  907. //uint64
  908. return true;
  909. }
  910.  
  911. // Cancel patch transfer
  912. bool AuthSession::HandleXferCancel()
  913. {
  914. TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel");
  915. //uint8
  916. return false;
  917. }
  918.  
  919. // Accept patch transfer
  920. bool AuthSession::HandleXferAccept()
  921. {
  922. TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept");
  923. //uint8
  924. return true;
  925. }
  926.  
  927. // Make the SRP6 calculation from hash in dB
  928. void AuthSession::SetVSFields(const std::string& rI)
  929. {
  930. s.SetRand(int32(BufferSizes::SRP_6_S) * 8);
  931.  
  932. BigNumber I;
  933. I.SetHexStr(rI.c_str());
  934.  
  935. // In case of leading zeros in the rI hash, restore them
  936. uint8 mDigest[SHA_DIGEST_LENGTH];
  937. memcpy(mDigest, I.AsByteArray(SHA_DIGEST_LENGTH).get(), SHA_DIGEST_LENGTH);
  938.  
  939. std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH);
  940.  
  941. SHA1Hash sha;
  942. sha.UpdateData(s.AsByteArray(uint32(BufferSizes::SRP_6_S)).get(), (uint32(BufferSizes::SRP_6_S)));
  943. sha.UpdateData(mDigest, SHA_DIGEST_LENGTH);
  944. sha.Finalize();
  945. BigNumber x;
  946. x.SetBinary(sha.GetDigest(), sha.GetLength());
  947. v = g.ModExp(x, N);
  948.  
  949. // No SQL injection (username escaped)
  950. char *v_hex, *s_hex;
  951. v_hex = v.AsHexStr();
  952. s_hex = s.AsHexStr();
  953.  
  954. PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS);
  955. stmt->setString(0, v_hex);
  956. stmt->setString(1, s_hex);
  957. stmt->setString(2, _login);
  958. LoginDatabase.Execute(stmt);
  959.  
  960. OPENSSL_free(v_hex);
  961. OPENSSL_free(s_hex);
  962. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement