[7146] Trailing whitespace code cleanup
[getmangos.git] / src / realmd / AuthSocket.cpp
blob820979a0906226aae9f1486a94697ec421a7da86
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /** \file
20 \ingroup realmd
23 #include "Common.h"
24 #include "Database/DatabaseEnv.h"
25 #include "ByteBuffer.h"
26 #include "Config/ConfigEnv.h"
27 #include "Log.h"
28 #include "RealmList.h"
29 #include "AuthSocket.h"
30 #include "AuthCodes.h"
31 #include <openssl/md5.h>
32 #include "Auth/Sha1.h"
33 //#include "Util.h" -- for commented utf8ToUpperOnlyLatin
35 extern RealmList m_realmList;
37 extern DatabaseType dbRealmServer;
39 #define ChunkSize 2048
41 enum eAuthCmd
43 //AUTH_NO_CMD = 0xFF,
44 AUTH_LOGON_CHALLENGE = 0x00,
45 AUTH_LOGON_PROOF = 0x01,
46 AUTH_RECONNECT_CHALLENGE = 0x02,
47 AUTH_RECONNECT_PROOF = 0x03,
48 //update srv =4
49 REALM_LIST = 0x10,
50 XFER_INITIATE = 0x30,
51 XFER_DATA = 0x31,
52 XFER_ACCEPT = 0x32,
53 XFER_RESUME = 0x33,
54 XFER_CANCEL = 0x34
57 enum eStatus
59 STATUS_CONNECTED = 0,
60 STATUS_AUTHED
63 // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some paltform
64 #if defined( __GNUC__ )
65 #pragma pack(1)
66 #else
67 #pragma pack(push,1)
68 #endif
70 typedef struct AUTH_LOGON_CHALLENGE_C
72 uint8 cmd;
73 uint8 error;
74 uint16 size;
75 uint8 gamename[4];
76 uint8 version1;
77 uint8 version2;
78 uint8 version3;
79 uint16 build;
80 uint8 platform[4];
81 uint8 os[4];
82 uint8 country[4];
83 uint32 timezone_bias;
84 uint32 ip;
85 uint8 I_len;
86 uint8 I[1];
87 } sAuthLogonChallenge_C;
89 //typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C;
91 typedef struct
93 uint8 cmd;
94 uint8 error;
95 uint8 unk2;
96 uint8 B[32];
97 uint8 g_len;
98 uint8 g[1];
99 uint8 N_len;
100 uint8 N[32];
101 uint8 s[32];
102 uint8 unk3[16];
103 } sAuthLogonChallenge_S;
106 typedef struct AUTH_LOGON_PROOF_C
108 uint8 cmd;
109 uint8 A[32];
110 uint8 M1[20];
111 uint8 crc_hash[20];
112 uint8 number_of_keys;
113 uint8 unk; // Added in 1.12.x client branch
114 } sAuthLogonProof_C;
116 typedef struct
118 uint16 unk1;
119 uint32 unk2;
120 uint8 unk3[4];
121 uint16 unk4[20];
122 } sAuthLogonProofKey_C;
124 typedef struct AUTH_LOGON_PROOF_S
126 uint8 cmd;
127 uint8 error;
128 uint8 M2[20];
129 uint32 unk1;
130 uint32 unk2;
131 uint16 unk3;
132 } sAuthLogonProof_S;
134 typedef struct AUTH_RECONNECT_PROOF_C
136 uint8 cmd;
137 uint8 R1[16];
138 uint8 R2[20];
139 uint8 R3[20];
140 uint8 number_of_keys;
141 } sAuthReconnectProof_C;
143 typedef struct XFER_INIT
145 uint8 cmd; // XFER_INITIATE
146 uint8 fileNameLen; // strlen(fileName);
147 uint8 fileName[5]; // fileName[fileNameLen]
148 uint64 file_size; // file size (bytes)
149 uint8 md5[MD5_DIGEST_LENGTH]; // MD5
150 }XFER_INIT;
152 typedef struct XFER_DATA
154 uint8 opcode;
155 uint16 data_size;
156 uint8 data[ChunkSize];
157 }XFER_DATA_STRUCT;
159 typedef struct AuthHandler
161 eAuthCmd cmd;
162 uint32 status;
163 bool (AuthSocket::*handler)(void);
164 }AuthHandler;
166 // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
167 #if defined( __GNUC__ )
168 #pragma pack()
169 #else
170 #pragma pack(pop)
171 #endif
173 /// Launch a thread to transfer a patch to the client
174 class PatcherRunnable: public ZThread::Runnable
176 public:
177 PatcherRunnable(class AuthSocket *);
178 void run();
180 private:
181 AuthSocket * mySocket;
184 typedef struct PATCH_INFO
186 uint8 md5[MD5_DIGEST_LENGTH];
187 }PATCH_INFO;
189 /// Caches MD5 hash of client patches present on the server
190 class Patcher
192 public:
193 typedef std::map<std::string, PATCH_INFO*> Patches;
194 ~Patcher();
195 Patcher();
196 Patches::const_iterator begin() const { return _patches.begin(); }
197 Patches::const_iterator end() const { return _patches.end(); }
198 void LoadPatchMD5(char*);
199 bool GetHash(char * pat,uint8 mymd5[16]);
201 private:
202 void LoadPatchesInfo();
203 Patches _patches;
206 const AuthHandler table[] =
208 { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge },
209 { AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleLogonProof },
210 { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge},
211 { AUTH_RECONNECT_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof },
212 { REALM_LIST, STATUS_AUTHED, &AuthSocket::_HandleRealmList },
213 { XFER_ACCEPT, STATUS_CONNECTED, &AuthSocket::_HandleXferAccept },
214 { XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume },
215 { XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel }
218 #define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler)
220 ///Holds the MD5 hash of client patches present on the server
221 Patcher PatchesCache;
223 /// Constructor - set the N and g values for SRP6
224 AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h)
226 N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
227 g.SetDword(7);
228 _authed = false;
229 pPatch = NULL;
231 _accountSecurityLevel = SEC_PLAYER;
234 /// Close patch file descriptor before leaving
235 AuthSocket::~AuthSocket()
237 ZThread::Guard<ZThread::Mutex> g(patcherLock);
238 if(pPatch)
239 fclose(pPatch);
242 /// Accept the connection and set the s random value for SRP6
243 void AuthSocket::OnAccept()
245 sLog.outBasic("Accepting connection from '%s:%d'",
246 GetRemoteAddress().c_str(), GetRemotePort());
248 s.SetRand(s_BYTE_SIZE * 8);
251 /// Read the packet from the client
252 void AuthSocket::OnRead()
254 ///- Read the packet
255 TcpSocket::OnRead();
256 uint8 _cmd;
257 while (1)
259 if (!ibuf.GetLength())
260 return;
262 ///- Get the command out of it
263 ibuf.SoftRead((char *)&_cmd, 1); // UQ1: No longer exists in new net code ???
264 //ibuf.Read((char *)&_cmd, 1);
265 /*char *command = (char *)malloc(1);
267 ibuf.Read(command, 1);
269 _cmd = (uint8)command;*/
270 // assert(0);
271 size_t i;
273 ///- Circle through known commands and call the correct command handler
274 for (i=0;i<AUTH_TOTAL_COMMANDS; i++)
276 if ((uint8)table[i].cmd == _cmd &&
277 (table[i].status == STATUS_CONNECTED ||
278 (_authed && table[i].status == STATUS_AUTHED)))
280 DEBUG_LOG("[Auth] got data for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
282 if (!(*this.*table[i].handler)())
284 DEBUG_LOG("Command handler failed for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
285 return;
287 break;
291 ///- Report unknown commands in the debug log
292 if (i==AUTH_TOTAL_COMMANDS)
294 DEBUG_LOG("[Auth] got unknown packet %u", (uint32)_cmd);
295 return;
300 /// Make the SRP6 calculation from hash in dB
301 void AuthSocket::_SetVSFields(const std::string& rI)
303 BigNumber I;
304 I.SetHexStr(rI.c_str());
306 //In case of leading zeroes in the rI hash, restore them
307 uint8 mDigest[SHA_DIGEST_LENGTH];
308 memset(mDigest,0,SHA_DIGEST_LENGTH);
309 if (I.GetNumBytes() <= SHA_DIGEST_LENGTH)
310 memcpy(mDigest,I.AsByteArray(),I.GetNumBytes());
312 std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH);
314 Sha1Hash sha;
315 sha.UpdateData(s.AsByteArray(), s.GetNumBytes());
316 sha.UpdateData(mDigest, SHA_DIGEST_LENGTH);
317 sha.Finalize();
318 BigNumber x;
319 x.SetBinary(sha.GetDigest(), sha.GetLength());
320 v = g.ModExp(x, N);
321 // No SQL injection (username escaped)
322 const char *v_hex, *s_hex;
323 v_hex = v.AsHexStr();
324 s_hex = s.AsHexStr();
325 dbRealmServer.PExecute("UPDATE account SET v = '%s', s = '%s' WHERE username = '%s'",v_hex,s_hex, _safelogin.c_str() );
326 OPENSSL_free((void*)v_hex);
327 OPENSSL_free((void*)s_hex);
330 /// Logon Challenge command handler
331 bool AuthSocket::_HandleLogonChallenge()
333 DEBUG_LOG("Entering _HandleLogonChallenge");
334 if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C))
335 return false;
337 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
338 std::vector<uint8> buf;
339 buf.resize(4);
341 ibuf.Read((char *)&buf[0], 4);
343 EndianConvert(*((uint16*)(buf[0])));
344 uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
345 DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining);
347 if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining))
348 return false;
350 //No big fear of memory outage (size is int16, i.e. < 65536)
351 buf.resize(remaining + buf.size() + 1);
352 buf[buf.size() - 1] = 0;
353 sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];
355 // BigEndian code, nop in little endian case
356 // size already converted
357 EndianConvert(*((uint32*)(&ch->gamename[0])));
358 EndianConvert(ch->build);
359 EndianConvert(*((uint32*)(&ch->platform[0])));
360 EndianConvert(*((uint32*)(&ch->os[0])));
361 EndianConvert(*((uint32*)(&ch->country[0])));
362 EndianConvert(ch->timezone_bias);
363 EndianConvert(ch->ip);
365 ///- Read the remaining of the packet
366 ibuf.Read((char *)&buf[4], remaining);
367 DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size);
368 DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I);
370 ByteBuffer pkt;
372 _login = (const char*)ch->I;
373 _build = ch->build;
375 ///- Normalize account name
376 //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form
378 //Escape the user login to avoid further SQL injection
379 //Memory will be freed on AuthSocket object destruction
380 _safelogin=_login;
381 dbRealmServer.escape_string(_safelogin);
383 pkt << (uint8) AUTH_LOGON_CHALLENGE;
384 pkt << (uint8) 0x00;
386 ///- Verify that this IP is not in the ip_banned table
387 // No SQL injection possible (paste the IP address as passed by the socket)
388 dbRealmServer.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
390 std::string address = GetRemoteAddress();
391 dbRealmServer.escape_string(address);
392 QueryResult *result = dbRealmServer.PQuery( "SELECT * FROM ip_banned WHERE ip = '%s'",address.c_str());
393 if(result)
395 pkt << (uint8)REALM_AUTH_ACCOUNT_BANNED;
396 sLog.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ());
397 delete result;
399 else
401 ///- Get the account details from the account table
402 // No SQL injection (escaped user name)
404 result = dbRealmServer.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel FROM account WHERE username = '%s'",_safelogin.c_str ());
405 if( result )
407 ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
408 bool locked = false;
409 if((*result)[2].GetUInt8() == 1) // if ip is locked
411 DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString());
412 DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str());
413 if ( strcmp((*result)[3].GetString(),GetRemoteAddress().c_str()) )
415 DEBUG_LOG("[AuthChallenge] Account IP differs");
416 pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED;
417 locked=true;
419 else
421 DEBUG_LOG("[AuthChallenge] Account IP matches");
424 else
426 DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
429 if (!locked)
431 //set expired bans to inactive
432 dbRealmServer.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
433 ///- If the account is banned, reject the logon attempt
434 QueryResult *banresult = dbRealmServer.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = %u AND active = 1", (*result)[1].GetUInt32());
435 if(banresult)
437 if((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64())
439 pkt << (uint8) REALM_AUTH_ACCOUNT_BANNED;
440 sLog.outBasic("[AuthChallenge] Banned account %s tries to login!",_login.c_str ());
442 else
444 pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED;
445 sLog.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ());
448 delete banresult;
450 else
452 ///- Get the password from the account table, upper it, and make the SRP6 calculation
453 std::string rI = (*result)[0].GetCppString();
454 _SetVSFields(rI);
456 b.SetRand(19 * 8);
457 BigNumber gmod=g.ModExp(b, N);
458 B = ((v * 3) + gmod) % N;
460 ASSERT(gmod.GetNumBytes() <= 32);
462 BigNumber unk3;
463 unk3.SetRand(16*8);
465 ///- Fill the response packet with the result
466 pkt << (uint8)REALM_AUTH_SUCCESS;
468 // B may be calculated < 32B so we force minnimal length to 32B
469 pkt.append(B.AsByteArray(32), 32); // 32 bytes
470 pkt << (uint8)1;
471 pkt.append(g.AsByteArray(), 1);
472 pkt << (uint8)32;
473 pkt.append(N.AsByteArray(), 32);
474 pkt.append(s.AsByteArray(), s.GetNumBytes()); // 32 bytes
475 pkt.append(unk3.AsByteArray(), 16);
476 pkt << (uint8)0; // Added in 1.12.x client branch
478 uint8 secLevel = (*result)[4].GetUInt8();
479 _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
481 _localizationName.resize(4);
482 for(int i = 0; i <4; ++i)
483 _localizationName[i] = ch->country[4-i-1];
485 sLog.outBasic("[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", _login.c_str (), ch->country[3],ch->country[2],ch->country[1],ch->country[0], GetLocaleByName(_localizationName));
488 delete result;
490 else //no account
492 pkt<< (uint8) REALM_AUTH_NO_MATCH;
495 SendBuf((char const*)pkt.contents(), pkt.size());
496 return true;
499 /// Logon Proof command handler
500 bool AuthSocket::_HandleLogonProof()
502 DEBUG_LOG("Entering _HandleLogonProof");
503 ///- Read the packet
504 if (ibuf.GetLength() < sizeof(sAuthLogonProof_C))
505 return false;
506 sAuthLogonProof_C lp;
507 ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C));
509 ///- Check if the client has one of the expected version numbers
510 bool valid_version=false;
511 int accepted_versions[]=EXPECTED_MANGOS_CLIENT_BUILD;
512 for(int i=0;accepted_versions[i];i++)
514 if(_build==accepted_versions[i])
516 valid_version=true;
517 break;
521 /// <ul><li> If the client has no valid version
522 if(!valid_version)
524 ///- Check if we have the apropriate patch on the disk
526 // 24 = len("./patches/65535enGB.mpq")+1
527 char tmp[24];
528 // No buffer overflow (fixed length of arguments)
529 sprintf(tmp,"./patches/%d%s.mpq",_build, _localizationName.c_str());
530 // This will be closed at the destruction of the AuthSocket (client deconnection)
531 FILE *pFile=fopen(tmp,"rb");
533 if(!pFile)
535 ByteBuffer pkt;
536 pkt << (uint8) AUTH_LOGON_CHALLENGE;
537 pkt << (uint8) 0x00;
538 pkt << (uint8) REALM_AUTH_WRONG_BUILD_NUMBER;
539 DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build);
540 DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp);
541 SendBuf((char const*)pkt.contents(), pkt.size());
542 return true;
544 else // have patch
546 pPatch=pFile;
547 XFER_INIT xferh;
549 ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it)
550 if(PatchesCache.GetHash(tmp,(uint8*)&xferh.md5))
552 DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp);
554 else
555 { //calculate patch md5
556 printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp);
557 PatchesCache.LoadPatchMD5(tmp);
558 PatchesCache.GetHash(tmp,(uint8*)&xferh.md5);
561 ///- Send a packet to the client with the file length and MD5 hash
562 uint8 data[2]={AUTH_LOGON_PROOF,REALM_AUTH_UPDATE_CLIENT};
563 SendBuf((const char*)data,sizeof(data));
565 memcpy(&xferh,"0\x05Patch",7);
566 xferh.cmd=XFER_INITIATE;
567 fseek(pPatch,0,SEEK_END);
568 xferh.file_size=ftell(pPatch);
570 SendBuf((const char*)&xferh,sizeof(xferh));
571 return true;
574 /// </ul>
576 ///- Continue the SRP6 calculation based on data received from the client
577 BigNumber A;
578 A.SetBinary(lp.A, 32);
580 Sha1Hash sha;
581 sha.UpdateBigNumbers(&A, &B, NULL);
582 sha.Finalize();
583 BigNumber u;
584 u.SetBinary(sha.GetDigest(), 20);
585 BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N);
587 uint8 t[32];
588 uint8 t1[16];
589 uint8 vK[40];
590 memcpy(t, S.AsByteArray(), 32);
591 for (int i = 0; i < 16; i++)
593 t1[i] = t[i*2];
595 sha.Initialize();
596 sha.UpdateData(t1, 16);
597 sha.Finalize();
598 for (int i = 0; i < 20; i++)
600 vK[i*2] = sha.GetDigest()[i];
602 for (int i = 0; i < 16; i++)
604 t1[i] = t[i*2+1];
606 sha.Initialize();
607 sha.UpdateData(t1, 16);
608 sha.Finalize();
609 for (int i = 0; i < 20; i++)
611 vK[i*2+1] = sha.GetDigest()[i];
613 K.SetBinary(vK, 40);
615 uint8 hash[20];
617 sha.Initialize();
618 sha.UpdateBigNumbers(&N, NULL);
619 sha.Finalize();
620 memcpy(hash, sha.GetDigest(), 20);
621 sha.Initialize();
622 sha.UpdateBigNumbers(&g, NULL);
623 sha.Finalize();
624 for (int i = 0; i < 20; i++)
626 hash[i] ^= sha.GetDigest()[i];
628 BigNumber t3;
629 t3.SetBinary(hash, 20);
631 sha.Initialize();
632 sha.UpdateData(_login);
633 sha.Finalize();
634 uint8 t4[SHA_DIGEST_LENGTH];
635 memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);
637 sha.Initialize();
638 sha.UpdateBigNumbers(&t3, NULL);
639 sha.UpdateData(t4, SHA_DIGEST_LENGTH);
640 sha.UpdateBigNumbers(&s, &A, &B, &K, NULL);
641 sha.Finalize();
642 BigNumber M;
643 M.SetBinary(sha.GetDigest(), 20);
645 ///- Check if SRP6 results match (password is correct), else send an error
646 if (!memcmp(M.AsByteArray(), lp.M1, 20))
648 sLog.outBasic("User '%s' successfully authenticated", _login.c_str());
650 ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
651 // No SQL injection (escaped user name) and IP address as received by socket
652 const char* K_hex = K.AsHexStr();
653 dbRealmServer.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '%s'", K_hex, GetRemoteAddress().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str() );
654 OPENSSL_free((void*)K_hex);
656 ///- Finish SRP6 and send the final result to the client
657 sha.Initialize();
658 sha.UpdateBigNumbers(&A, &M, &K, NULL);
659 sha.Finalize();
661 sAuthLogonProof_S proof;
662 memcpy(proof.M2, sha.GetDigest(), 20);
663 proof.cmd = AUTH_LOGON_PROOF;
664 proof.error = 0;
665 proof.unk1 = 0x00800000;
666 proof.unk2 = 0x00;
667 proof.unk3 = 0x00;
669 SendBuf((char *)&proof, sizeof(proof));
671 ///- Set _authed to true!
672 _authed = true;
674 else
676 char data[4]={AUTH_LOGON_PROOF,REALM_AUTH_NO_MATCH,3,0};
677 SendBuf(data,sizeof(data));
678 sLog.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ());
680 uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0);
681 if(MaxWrongPassCount > 0)
683 //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
684 dbRealmServer.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin.c_str());
686 if(QueryResult *loginfail = dbRealmServer.PQuery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safelogin.c_str()))
688 Field* fields = loginfail->Fetch();
689 uint32 failed_logins = fields[1].GetUInt32();
691 if( failed_logins >= MaxWrongPassCount )
693 uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600);
694 bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false);
696 if(WrongPassBanType)
698 uint32 acc_id = fields[0].GetUInt32();
699 dbRealmServer.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)",
700 acc_id, WrongPassBanTime);
701 sLog.outBasic("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
702 _login.c_str(), WrongPassBanTime, failed_logins);
704 else
706 std::string current_ip = GetRemoteAddress();
707 dbRealmServer.escape_string(current_ip);
708 dbRealmServer.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')",
709 current_ip.c_str(), WrongPassBanTime);
710 sLog.outBasic("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times",
711 current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins);
714 delete loginfail;
718 return true;
721 /// Reconnect Challenge command handler
722 bool AuthSocket::_HandleReconnectChallenge()
724 DEBUG_LOG("Entering _HandleReconnectChallenge");
725 if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C))
726 return false;
728 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
729 std::vector<uint8> buf;
730 buf.resize(4);
732 ibuf.Read((char *)&buf[0], 4);
734 EndianConvert(*((uint16*)(buf[0])));
735 uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
736 DEBUG_LOG("[ReconnectChallenge] got header, body is %#04x bytes", remaining);
738 if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining))
739 return false;
741 //No big fear of memory outage (size is int16, i.e. < 65536)
742 buf.resize(remaining + buf.size() + 1);
743 buf[buf.size() - 1] = 0;
744 sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];
746 ///- Read the remaining of the packet
747 ibuf.Read((char *)&buf[4], remaining);
748 DEBUG_LOG("[ReconnectChallenge] got full packet, %#04x bytes", ch->size);
749 DEBUG_LOG("[ReconnectChallenge] name(%d): '%s'", ch->I_len, ch->I);
751 _login = (const char*)ch->I;
752 _safelogin = _login;
754 QueryResult *result = dbRealmServer.PQuery ("SELECT sessionkey FROM account WHERE username = '%s'", _safelogin.c_str ());
756 // Stop if the account is not found
757 if (!result)
759 sLog.outError("[ERROR] user %s tried to login and we cannot find his session key in the database.", _login.c_str());
760 SetCloseAndDelete();
761 return false;
764 Field* fields = result->Fetch ();
765 K.SetHexStr (fields[0].GetString ());
766 delete result;
768 ///- Sending response
769 ByteBuffer pkt;
770 pkt << (uint8) AUTH_RECONNECT_CHALLENGE;
771 pkt << (uint8) 0x00;
772 _reconnectProof.SetRand(16*8);
773 pkt.append(_reconnectProof.AsByteBuffer()); // 16 bytes random
774 pkt << (uint64) 0x00 << (uint64) 0x00; // 16 bytes zeros
775 SendBuf((char const*)pkt.contents(), pkt.size());
776 return true;
779 /// Reconnect Proof command handler
780 bool AuthSocket::_HandleReconnectProof()
782 DEBUG_LOG("Entering _HandleReconnectProof");
783 ///- Read the packet
784 if (ibuf.GetLength() < sizeof(sAuthReconnectProof_C))
785 return false;
786 if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes())
787 return false;
788 sAuthReconnectProof_C lp;
789 ibuf.Read((char *)&lp, sizeof(sAuthReconnectProof_C));
791 BigNumber t1;
792 t1.SetBinary(lp.R1, 16);
794 Sha1Hash sha;
795 sha.Initialize();
796 sha.UpdateData(_login);
797 sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL);
798 sha.Finalize();
800 if (!memcmp(sha.GetDigest(), lp.R2, SHA_DIGEST_LENGTH))
802 ///- Sending response
803 ByteBuffer pkt;
804 pkt << (uint8) AUTH_RECONNECT_PROOF;
805 pkt << (uint8) 0x00;
806 pkt << (uint16) 0x00; // 2 bytes zeros
807 SendBuf((char const*)pkt.contents(), pkt.size());
809 ///- Set _authed to true!
810 _authed = true;
812 return true;
814 else
816 sLog.outError("[ERROR] user %s tried to login, but session invalid.", _login.c_str());
817 SetCloseAndDelete();
818 return false;
822 /// %Realm List command handler
823 bool AuthSocket::_HandleRealmList()
825 DEBUG_LOG("Entering _HandleRealmList");
826 if (ibuf.GetLength() < 5)
827 return false;
829 ibuf.Remove(5);
831 ///- Get the user id (else close the connection)
832 // No SQL injection (escaped user name)
834 QueryResult *result = dbRealmServer.PQuery("SELECT id,sha_pass_hash FROM account WHERE username = '%s'",_safelogin.c_str());
835 if(!result)
837 sLog.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login.c_str());
838 SetCloseAndDelete();
839 return false;
842 uint32 id = (*result)[0].GetUInt32();
843 std::string rI = (*result)[1].GetCppString();
844 delete result;
846 ///- Update realm list if need
847 m_realmList.UpdateIfNeed();
849 ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
850 ByteBuffer pkt;
851 pkt << (uint32) 0;
852 pkt << (uint16) m_realmList.size();
853 RealmList::RealmMap::const_iterator i;
854 for( i = m_realmList.begin(); i != m_realmList.end(); i++ )
856 uint8 AmountOfCharacters;
858 // No SQL injection. id of realm is controlled by the database.
859 result = dbRealmServer.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i->second.m_ID,id);
860 if( result )
862 Field *fields = result->Fetch();
863 AmountOfCharacters = fields[0].GetUInt8();
864 delete result;
866 else
867 AmountOfCharacters = 0;
869 uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
871 pkt << i->second.icon; // realm type
872 pkt << lock; // if 1, then realm locked
873 pkt << i->second.color; // if 2, then realm is offline
874 pkt << i->first;
875 pkt << i->second.address;
876 pkt << i->second.populationLevel;
877 pkt << AmountOfCharacters;
878 pkt << i->second.timezone; // realm category
879 pkt << (uint8) 0x2C; // unk, may be realm number/id?
881 pkt << (uint8) 0x10;
882 pkt << (uint8) 0x00;
884 ByteBuffer hdr;
885 hdr << (uint8) REALM_LIST;
886 hdr << (uint16)pkt.size();
887 hdr.append(pkt);
889 SendBuf((char const*)hdr.contents(), hdr.size());
891 // Set check field before possible relogin to realm
892 _SetVSFields(rI);
893 return true;
896 /// Resume patch transfer
897 bool AuthSocket::_HandleXferResume()
899 DEBUG_LOG("Entering _HandleXferResume");
900 ///- Check packet length and patch existence
901 if (ibuf.GetLength()<9 || !pPatch)
903 sLog.outError("Error while resuming patch transfer (wrong packet)");
904 return false;
907 ///- Launch a PatcherRunnable thread starting at given patch file offset
908 uint64 start;
909 ibuf.Remove(1);
910 ibuf.Read((char*)&start,sizeof(start));
911 fseek(pPatch,start,0);
913 ZThread::Thread u(new PatcherRunnable(this));
914 return true;
917 /// Cancel patch transfer
918 bool AuthSocket::_HandleXferCancel()
920 DEBUG_LOG("Entering _HandleXferCancel");
922 ///- Close and delete the socket
923 ibuf.Remove(1); //clear input buffer
925 //ZThread::Thread::sleep(15);
926 SetCloseAndDelete();
928 return true;
931 /// Accept patch transfer
932 bool AuthSocket::_HandleXferAccept()
934 DEBUG_LOG("Entering _HandleXferAccept");
936 ///- Check packet length and patch existence
937 if (!pPatch)
939 sLog.outError("Error while accepting patch transfer (wrong packet)");
940 return false;
943 ///- Launch a PatcherRunnable thread, starting at the begining of the patch file
944 ibuf.Remove(1); //clear input buffer
945 fseek(pPatch,0,0);
947 ZThread::Thread u(new PatcherRunnable(this));
949 return true;
952 /// Check if there is lag on the connection to the client
953 bool AuthSocket::IsLag()
955 return (TCP_BUFSIZE_READ-GetOutputLength()< 2*ChunkSize);
958 PatcherRunnable::PatcherRunnable(class AuthSocket * as)
960 mySocket=as;
963 /// Send content of patch file to the client
964 void PatcherRunnable::run()
966 ZThread::Guard<ZThread::Mutex> g(mySocket->patcherLock);
967 XFER_DATA_STRUCT xfdata;
968 xfdata.opcode = XFER_DATA;
970 while(!feof(mySocket->pPatch) && mySocket->Ready())
972 ///- Wait until output buffer is reasonably empty
973 while(mySocket->Ready() && mySocket->IsLag())
975 ZThread::Thread::sleep(1);
977 ///- And send content of the patch file to the client
978 xfdata.data_size=fread(&xfdata.data,1,ChunkSize,mySocket->pPatch);
979 mySocket->SendBuf((const char*)&xfdata,xfdata.data_size +(sizeof(XFER_DATA_STRUCT)-ChunkSize));
983 /// Preload MD5 hashes of existing patch files on server
984 #ifndef _WIN32
985 #include <dirent.h>
986 #include <errno.h>
987 void Patcher::LoadPatchesInfo()
989 DIR * dirp;
990 //int errno;
991 struct dirent * dp;
992 dirp = opendir("./patches/");
993 if(!dirp)
994 return;
995 while (dirp)
997 errno = 0;
998 if ((dp = readdir(dirp)) != NULL)
1000 int l=strlen(dp->d_name);
1001 if(l<8)continue;
1002 if(!memcmp(&dp->d_name[l-4],".mpq",4))
1003 LoadPatchMD5(dp->d_name);
1005 else
1007 if(errno != 0)
1009 closedir(dirp);
1010 return;
1012 break;
1016 if(dirp)
1017 closedir(dirp);
1020 #else
1021 void Patcher::LoadPatchesInfo()
1023 WIN32_FIND_DATA fil;
1024 HANDLE hFil=FindFirstFile("./patches/*.mpq",&fil);
1025 if(hFil==INVALID_HANDLE_VALUE)
1026 return; //no patches were found
1030 LoadPatchMD5(fil.cFileName);
1032 while(FindNextFile(hFil,&fil));
1034 #endif
1036 /// Calculate and store MD5 hash for a given patch file
1037 void Patcher::LoadPatchMD5(char * szFileName)
1039 ///- Try to open the patch file
1040 std::string path = "./patches/";
1041 path += szFileName;
1042 FILE * pPatch=fopen(path.c_str(),"rb");
1043 sLog.outDebug("Loading patch info from %s\n",path.c_str());
1044 if(!pPatch)
1046 sLog.outError("Error loading patch %s\n",path.c_str());
1047 return;
1050 ///- Calculate the MD5 hash
1051 MD5_CTX ctx;
1052 MD5_Init(&ctx);
1053 uint8* buf = new uint8[512*1024];
1055 while (!feof(pPatch))
1057 size_t read = fread(buf, 1, 512*1024, pPatch);
1058 MD5_Update(&ctx, buf, read);
1060 delete [] buf;
1061 fclose(pPatch);
1063 ///- Store the result in the internal patch hash map
1064 _patches[path] = new PATCH_INFO;
1065 MD5_Final((uint8 *)&_patches[path]->md5 , &ctx);
1068 /// Get cached MD5 hash for a given patch file
1069 bool Patcher::GetHash(char * pat,uint8 mymd5[16])
1071 for( Patches::iterator i = _patches.begin(); i != _patches.end(); i++ )
1072 if(!stricmp(pat,i->first.c_str () ))
1074 memcpy(mymd5,i->second->md5,16);
1075 return true;
1078 return false;
1081 /// Launch the patch hashing mechanism on object creation
1082 Patcher::Patcher()
1084 LoadPatchesInfo();
1087 /// Empty and delete the patch map on termination
1088 Patcher::~Patcher()
1090 for(Patches::iterator i = _patches.begin(); i != _patches.end(); i++ )
1091 delete i->second;