root/trunk/src/trinityrealm/AuthSocket.cpp

Revision 171, 34.2 kB (checked in by yumileroy, 17 years ago)

[svn] * fixed compile

Original author: KingPin?
Date: 2008-11-05 16:13:04-06:00

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