2010-10-07 15:35:36 +02:00
/*
2012-12-31 23:15:50 +01:00
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
2010-10-07 15:35:36 +02:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2010-06-06 23:08:23 +02:00
*/
2012-02-19 13:51:16 +01:00
#include <algorithm>
2010-12-27 09:02:02 -08:00
#include <openssl/md5.h>
2010-06-06 23:08:23 +02:00
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "ByteBuffer.h"
2010-07-29 01:22:45 +02:00
#include "Configuration/Config.h"
2010-06-06 23:08:23 +02:00
#include "Log.h"
#include "RealmList.h"
#include "AuthSocket.h"
#include "AuthCodes.h"
2013-08-25 14:02:40 +01:00
#include "TOTP.h"
2010-06-08 01:20:06 +02:00
#include "SHA1.h"
2011-10-18 17:55:05 +02:00
#include "openssl/crypto.h"
2010-06-06 23:08:23 +02:00
#define ChunkSize 2048
enum eAuthCmd
{
2010-12-27 09:02:02 -08:00
AUTH_LOGON_CHALLENGE = 0x00 ,
AUTH_LOGON_PROOF = 0x01 ,
AUTH_RECONNECT_CHALLENGE = 0x02 ,
AUTH_RECONNECT_PROOF = 0x03 ,
REALM_LIST = 0x10 ,
XFER_INITIATE = 0x30 ,
XFER_DATA = 0x31 ,
XFER_ACCEPT = 0x32 ,
XFER_RESUME = 0x33 ,
XFER_CANCEL = 0x34
2010-06-06 23:08:23 +02:00
};
enum eStatus
{
2010-12-27 09:02:02 -08:00
STATUS_CONNECTED = 0 ,
2010-06-06 23:08:23 +02:00
STATUS_AUTHED
};
2011-04-29 20:47:02 +02:00
// 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
2010-06-06 23:08:23 +02:00
#if defined(__GNUC__)
#pragma pack(1)
#else
2011-04-29 20:47:02 +02:00
#pragma pack(push, 1)
2010-06-06 23:08:23 +02:00
#endif
typedef struct AUTH_LOGON_CHALLENGE_C
{
uint8 cmd ;
uint8 error ;
uint16 size ;
uint8 gamename [ 4 ];
uint8 version1 ;
uint8 version2 ;
uint8 version3 ;
uint16 build ;
uint8 platform [ 4 ];
uint8 os [ 4 ];
uint8 country [ 4 ];
uint32 timezone_bias ;
uint32 ip ;
uint8 I_len ;
uint8 I [ 1 ];
} sAuthLogonChallenge_C ;
typedef struct AUTH_LOGON_PROOF_C
{
uint8 cmd ;
uint8 A [ 32 ];
uint8 M1 [ 20 ];
uint8 crc_hash [ 20 ];
uint8 number_of_keys ;
uint8 securityFlags ; // 0x00-0x04
} sAuthLogonProof_C ;
typedef struct AUTH_LOGON_PROOF_S
{
uint8 cmd ;
uint8 error ;
uint8 M2 [ 20 ];
uint32 unk1 ;
uint32 unk2 ;
uint16 unk3 ;
} sAuthLogonProof_S ;
typedef struct AUTH_LOGON_PROOF_S_OLD
{
uint8 cmd ;
uint8 error ;
uint8 M2 [ 20 ];
uint32 unk2 ;
} sAuthLogonProof_S_Old ;
typedef struct AUTH_RECONNECT_PROOF_C
{
uint8 cmd ;
uint8 R1 [ 16 ];
uint8 R2 [ 20 ];
uint8 R3 [ 20 ];
uint8 number_of_keys ;
} sAuthReconnectProof_C ;
typedef struct XFER_INIT
{
uint8 cmd ; // XFER_INITIATE
uint8 fileNameLen ; // strlen(fileName);
uint8 fileName [ 5 ]; // fileName[fileNameLen]
uint64 file_size ; // file size (bytes)
uint8 md5 [ MD5_DIGEST_LENGTH ]; // MD5
} XFER_INIT ;
typedef struct XFER_DATA
{
uint8 opcode ;
uint16 data_size ;
uint8 data [ ChunkSize ];
} XFER_DATA_STRUCT ;
typedef struct AuthHandler
{
eAuthCmd cmd ;
uint32 status ;
bool ( AuthSocket ::* handler )( void );
} AuthHandler ;
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
2010-11-16 14:29:01 +01:00
// Launch a thread to transfer a patch to the client
2010-06-06 23:08:23 +02:00
class PatcherRunnable : public ACE_Based :: Runnable
{
2010-12-27 09:02:02 -08:00
public :
2011-09-29 09:32:55 +02:00
PatcherRunnable ( class AuthSocket * );
2010-12-27 09:02:02 -08:00
void run ();
2010-06-06 23:08:23 +02:00
2010-12-27 09:02:02 -08:00
private :
2011-09-15 14:08:17 +02:00
AuthSocket * mySocket ;
2010-06-06 23:08:23 +02:00
};
typedef struct PATCH_INFO
{
uint8 md5 [ MD5_DIGEST_LENGTH ];
} PATCH_INFO ;
2010-11-16 14:29:01 +01:00
// Caches MD5 hash of client patches present on the server
2010-06-06 23:08:23 +02:00
class Patcher
{
2010-12-27 09:02:02 -08:00
public :
typedef std :: map < std :: string , PATCH_INFO *> Patches ;
~ Patcher ();
Patcher ();
Patches :: const_iterator begin () const { return _patches . begin (); }
Patches :: const_iterator end () const { return _patches . end (); }
void LoadPatchMD5 ( char * );
2011-04-29 20:47:02 +02:00
bool GetHash ( char * pat , uint8 mymd5 [ 16 ]);
2010-12-27 09:02:02 -08:00
private :
void LoadPatchesInfo ();
Patches _patches ;
2010-06-06 23:08:23 +02:00
};
const AuthHandler table [] =
{
{ AUTH_LOGON_CHALLENGE , STATUS_CONNECTED , & AuthSocket :: _HandleLogonChallenge },
{ AUTH_LOGON_PROOF , STATUS_CONNECTED , & AuthSocket :: _HandleLogonProof },
{ AUTH_RECONNECT_CHALLENGE , STATUS_CONNECTED , & AuthSocket :: _HandleReconnectChallenge },
{ AUTH_RECONNECT_PROOF , STATUS_CONNECTED , & AuthSocket :: _HandleReconnectProof },
{ REALM_LIST , STATUS_AUTHED , & AuthSocket :: _HandleRealmList },
{ XFER_ACCEPT , STATUS_CONNECTED , & AuthSocket :: _HandleXferAccept },
{ XFER_RESUME , STATUS_CONNECTED , & AuthSocket :: _HandleXferResume },
{ XFER_CANCEL , STATUS_CONNECTED , & AuthSocket :: _HandleXferCancel }
};
2010-12-27 09:02:02 -08:00
#define AUTH_TOTAL_COMMANDS 8
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Holds the MD5 hash of client patches present on the server
2010-06-06 23:08:23 +02:00
Patcher PatchesCache ;
2010-11-16 14:29:01 +01:00
// Constructor - set the N and g values for SRP6
2013-05-30 13:14:06 +02:00
AuthSocket :: AuthSocket ( RealmSocket & socket ) :
pPatch ( NULL ), socket_ ( socket ), _authed ( false ), _build ( 0 ),
_expversion ( 0 ), _accountSecurityLevel ( SEC_PLAYER )
2010-06-06 23:08:23 +02:00
{
N . SetHexStr ( "894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7" );
g . SetDword ( 7 );
}
2010-11-16 14:29:01 +01:00
// Close patch file descriptor before leaving
2013-10-27 22:27:46 +01:00
AuthSocket ::~ AuthSocket ( void ) { }
2010-06-06 23:08:23 +02:00
2013-02-08 01:03:56 +01:00
// Accept the connection
2010-06-06 23:08:23 +02:00
void AuthSocket :: OnAccept ( void )
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' Accepting connection" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort ());
2010-06-06 23:08:23 +02:00
}
void AuthSocket :: OnClose ( void )
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "AuthSocket::OnClose" );
2010-06-06 23:08:23 +02:00
}
2010-11-16 14:29:01 +01:00
// Read the packet from the client
2010-06-06 23:08:23 +02:00
void AuthSocket :: OnRead ()
{
2013-09-18 21:05:46 +02:00
#define MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW 3
uint32 challengesInARow = 0 ;
2010-06-06 23:08:23 +02:00
uint8 _cmd ;
while ( 1 )
{
if ( ! socket (). recv_soft (( char * ) & _cmd , 1 ))
return ;
2013-09-18 21:05:46 +02:00
if ( _cmd == AUTH_LOGON_CHALLENGE )
{
++ challengesInARow ;
if ( challengesInARow == MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW )
{
TC_LOG_WARN ( LOG_FILTER_AUTHSERVER , "Got %u AUTH_LOGON_CHALLENGE in a row from '%s', possible ongoing DoS" , challengesInARow , socket (). getRemoteAddress (). c_str ());
socket (). shutdown ();
return ;
}
}
2010-06-06 23:08:23 +02:00
size_t i ;
2010-11-16 14:29:01 +01:00
// Circle through known commands and call the correct command handler
2010-06-06 23:08:23 +02:00
for ( i = 0 ; i < AUTH_TOTAL_COMMANDS ; ++ i )
{
2010-12-27 09:02:02 -08:00
if (( uint8 ) table [ i ]. cmd == _cmd && ( table [ i ]. status == STATUS_CONNECTED || ( _authed && table [ i ]. status == STATUS_AUTHED )))
2010-06-06 23:08:23 +02:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Got data for cmd %u recv length %u" , ( uint32 ) _cmd , ( uint32 ) socket (). recv_len ());
2010-06-06 23:08:23 +02:00
if ( ! ( * this . * table [ i ]. handler )())
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Command handler failed for cmd %u recv length %u" , ( uint32 ) _cmd , ( uint32 ) socket (). recv_len ());
2010-06-06 23:08:23 +02:00
return ;
}
break ;
}
}
// Report unknown packets in the error log
if ( i == AUTH_TOTAL_COMMANDS )
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "Got unknown packet from '%s'" , socket (). getRemoteAddress (). c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return ;
}
}
}
2010-11-16 14:29:01 +01:00
// Make the SRP6 calculation from hash in dB
2010-06-06 23:08:23 +02:00
void AuthSocket :: _SetVSFields ( const std :: string & rI )
{
s . SetRand ( s_BYTE_SIZE * 8 );
BigNumber I ;
I . SetHexStr ( rI . c_str ());
// In case of leading zeros in the rI hash, restore them
uint8 mDigest [ SHA_DIGEST_LENGTH ];
memset ( mDigest , 0 , SHA_DIGEST_LENGTH );
if ( I . GetNumBytes () <= SHA_DIGEST_LENGTH )
2013-08-18 16:06:29 +02:00
memcpy ( mDigest , I . AsByteArray (). get (), I . GetNumBytes ());
2010-06-06 23:08:23 +02:00
std :: reverse ( mDigest , mDigest + SHA_DIGEST_LENGTH );
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2013-08-18 16:06:29 +02:00
sha . UpdateData ( s . AsByteArray (). get (), s . GetNumBytes ());
2010-06-06 23:08:23 +02:00
sha . UpdateData ( mDigest , SHA_DIGEST_LENGTH );
sha . Finalize ();
BigNumber x ;
x . SetBinary ( sha . GetDigest (), sha . GetLength ());
v = g . ModExp ( x , N );
2010-12-27 09:02:02 -08:00
2010-06-06 23:08:23 +02:00
// No SQL injection (username escaped)
2012-11-09 13:13:45 +01:00
char * v_hex , * s_hex ;
2010-06-06 23:08:23 +02:00
v_hex = v . AsHexStr ();
s_hex = s . AsHexStr ();
2010-09-03 01:00:49 +02:00
2011-12-31 20:14:38 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_VS );
2010-09-03 01:00:49 +02:00
stmt -> setString ( 0 , v_hex );
stmt -> setString ( 1 , s_hex );
2010-09-12 17:04:19 +02:00
stmt -> setString ( 2 , _login );
2010-09-03 01:00:49 +02:00
LoginDatabase . Execute ( stmt );
2012-11-09 13:13:45 +01:00
OPENSSL_free ( v_hex );
OPENSSL_free ( s_hex );
2010-06-06 23:08:23 +02:00
}
2010-11-16 14:29:01 +01:00
// Logon Challenge command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleLogonChallenge ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleLogonChallenge" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < sizeof ( sAuthLogonChallenge_C ))
return false ;
2010-11-16 14:29:01 +01:00
// Read the first 4 bytes (header) to get the length of the remaining of the packet
2010-06-06 23:08:23 +02:00
std :: vector < uint8 > buf ;
buf . resize ( 4 );
socket (). recv (( char * ) & buf [ 0 ], 4 );
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint16 * )( buf [ 0 ])));
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
uint16 remaining = (( sAuthLogonChallenge_C * ) & buf [ 0 ]) -> size ;
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] got header, body is %#04x bytes" , remaining );
2010-06-06 23:08:23 +02:00
if (( remaining < sizeof ( sAuthLogonChallenge_C ) - buf . size ()) || ( socket (). recv_len () < remaining ))
return false ;
//No big fear of memory outage (size is int16, i.e. < 65536)
buf . resize ( remaining + buf . size () + 1 );
buf [ buf . size () - 1 ] = 0 ;
sAuthLogonChallenge_C * ch = ( sAuthLogonChallenge_C * ) & buf [ 0 ];
2010-11-16 14:29:01 +01:00
// Read the remaining of the packet
2010-06-06 23:08:23 +02:00
socket (). recv (( char * ) & buf [ 4 ], remaining );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] got full packet, %#04x bytes" , ch -> size );
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] name(%d): '%s'" , ch -> I_len , ch -> I );
2010-06-06 23:08:23 +02:00
// BigEndian code, nop in little endian case
// size already converted
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint32 * )( & ch -> gamename [ 0 ])));
EndianConvert ( ch -> build );
EndianConvert ( * (( uint32 * )( & ch -> platform [ 0 ])));
EndianConvert ( * (( uint32 * )( & ch -> os [ 0 ])));
EndianConvert ( * (( uint32 * )( & ch -> country [ 0 ])));
EndianConvert ( ch -> timezone_bias );
EndianConvert ( ch -> ip );
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
_login = ( const char * ) ch -> I ;
_build = ch -> build ;
2012-08-28 01:11:03 +02:00
_expversion = uint8 ( AuthHelper :: IsPostBCAcceptedClientBuild ( _build ) ? POST_BC_EXP_FLAG : ( AuthHelper :: IsPreBCAcceptedClientBuild ( _build ) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG ));
2012-02-19 13:51:16 +01:00
_os = ( const char * ) ch -> os ;
if ( _os . size () > 4 )
return false ;
// Restore string order as its byte order is reversed
std :: reverse ( _os . begin (), _os . end ());
2010-06-06 23:08:23 +02:00
2012-08-28 01:11:03 +02:00
pkt << uint8 ( AUTH_LOGON_CHALLENGE );
pkt << uint8 ( 0x00 );
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Verify that this IP is not in the ip_banned table
2011-12-31 20:14:38 +01:00
LoginDatabase . Execute ( LoginDatabase . GetPreparedStatement ( LOGIN_DEL_EXPIRED_IP_BANS ));
2010-09-12 11:06:26 +02:00
2012-08-28 01:11:03 +02:00
std :: string const & ip_address = socket (). getRemoteAddress ();
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_IP_BANNED );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , ip_address );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( result )
{
2012-08-28 01:11:03 +02:00
pkt << uint8 ( WOW_FAIL_BANNED );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] Banned ip tries to login!" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort ());
2010-06-06 23:08:23 +02:00
}
else
{
2010-11-16 14:29:01 +01:00
// Get the account details from the account table
2010-09-12 11:06:26 +02:00
// No SQL injection (prepared statement)
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_LOGONCHALLENGE );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
2010-06-06 23:08:23 +02:00
2010-09-11 21:22:15 +02:00
PreparedQueryResult res2 = LoginDatabase . Query ( stmt );
if ( res2 )
2010-06-06 23:08:23 +02:00
{
2010-09-24 22:16:21 +02:00
Field * fields = res2 -> Fetch ();
2010-11-16 14:29:01 +01:00
// If the IP is 'locked', check that the player comes indeed from the correct IP address
2010-06-06 23:08:23 +02:00
bool locked = false ;
2010-11-16 14:29:01 +01:00
if ( fields [ 2 ]. GetUInt8 () == 1 ) // if ip is locked
2010-06-06 23:08:23 +02:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account '%s' is locked to IP - '%s'" , _login . c_str (), fields [ 3 ]. GetCString ());
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Player address is '%s'" , ip_address . c_str ());
2010-11-16 14:29:01 +01:00
2013-09-01 22:45:40 +03:00
if ( strcmp ( fields [ 4 ]. GetCString (), ip_address . c_str ()) != 0 )
2010-06-06 23:08:23 +02:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account IP differs" );
2013-04-15 14:56:00 +03:00
pkt << uint8 ( WOW_FAIL_LOCKED_ENFORCED );
2010-12-04 11:19:41 -08:00
locked = true ;
2010-06-06 23:08:23 +02:00
}
else
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account IP matches" );
2010-06-06 23:08:23 +02:00
}
else
2013-04-15 14:56:00 +03:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account '%s' is not locked to ip" , _login . c_str ());
2013-04-15 14:56:00 +03:00
std :: string accountCountry = fields [ 3 ]. GetString ();
if ( accountCountry . empty () || accountCountry == "00" )
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account '%s' is not locked to country" , _login . c_str ());
2013-04-15 14:56:00 +03:00
else if ( ! accountCountry . empty ())
{
uint32 ip = inet_addr ( ip_address . c_str ());
EndianConvertReverse ( ip );
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_LOGON_COUNTRY );
stmt -> setUInt32 ( 0 , ip );
if ( PreparedQueryResult sessionCountryQuery = LoginDatabase . Query ( stmt ))
{
std :: string loginCountry = ( * sessionCountryQuery )[ 0 ]. GetString ();
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'" , _login . c_str (), accountCountry . c_str (), loginCountry . c_str ());
2013-04-15 14:56:00 +03:00
if ( loginCountry != accountCountry )
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account country differs." );
2013-04-15 14:56:00 +03:00
pkt << uint8 ( WOW_FAIL_UNLOCKABLE_LOCK );
locked = true ;
}
else
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] Account country matches" );
2013-04-15 14:56:00 +03:00
}
else
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[AuthChallenge] IP2NATION Table empty" );
2013-04-15 14:56:00 +03:00
}
}
2010-06-06 23:08:23 +02:00
if ( ! locked )
{
//set expired bans to inactive
2011-12-31 20:14:38 +01:00
LoginDatabase . Execute ( LoginDatabase . GetPreparedStatement ( LOGIN_UPD_EXPIRED_ACCOUNT_BANS ));
2010-09-12 11:06:26 +02:00
2010-11-16 14:29:01 +01:00
// If the account is banned, reject the logon attempt
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_BANNED );
2010-09-24 22:16:21 +02:00
stmt -> setUInt32 ( 0 , fields [ 1 ]. GetUInt32 ());
2010-09-12 11:06:26 +02:00
PreparedQueryResult banresult = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( banresult )
{
2012-09-08 00:42:00 +01:00
if (( * banresult )[ 0 ]. GetUInt32 () == ( * banresult )[ 1 ]. GetUInt32 ())
2010-06-06 23:08:23 +02:00
{
2012-08-28 01:11:03 +02:00
pkt << uint8 ( WOW_FAIL_BANNED );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] Banned account %s tried to login!" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
}
else
{
2012-08-28 01:11:03 +02:00
pkt << uint8 ( WOW_FAIL_SUSPENDED );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
}
}
else
{
2010-11-16 14:29:01 +01:00
// Get the password from the account table, upper it, and make the SRP6 calculation
2010-09-24 22:16:21 +02:00
std :: string rI = fields [ 0 ]. GetString ();
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Don't calculate (v, s) if there are already some in the database
2013-04-15 14:56:00 +03:00
std :: string databaseV = fields [ 6 ]. GetString ();
std :: string databaseS = fields [ 7 ]. GetString ();
2010-06-06 23:08:23 +02:00
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_NETWORKIO , "database authentication values: v='%s' s='%s'" , databaseV . c_str (), databaseS . c_str ());
2010-06-06 23:08:23 +02:00
2010-12-04 11:19:41 -08:00
// multiply with 2 since bytes are stored as hexstring
if ( databaseV . size () != s_BYTE_SIZE * 2 || databaseS . size () != s_BYTE_SIZE * 2 )
2010-06-06 23:08:23 +02:00
_SetVSFields ( rI );
else
{
s . SetHexStr ( databaseS . c_str ());
v . SetHexStr ( databaseV . c_str ());
}
b . SetRand ( 19 * 8 );
BigNumber gmod = g . ModExp ( b , N );
B = (( v * 3 ) + gmod ) % N ;
ASSERT ( gmod . GetNumBytes () <= 32 );
BigNumber unk3 ;
unk3 . SetRand ( 16 * 8 );
2010-11-16 14:29:01 +01:00
// Fill the response packet with the result
2012-08-28 01:11:03 +02:00
if ( AuthHelper :: IsAcceptedClientBuild ( _build ))
pkt << uint8 ( WOW_SUCCESS );
else
pkt << uint8 ( WOW_FAIL_VERSION_INVALID );
2010-06-06 23:08:23 +02:00
// B may be calculated < 32B so we force minimal length to 32B
2013-08-18 16:06:29 +02:00
pkt . append ( B . AsByteArray ( 32 ). get (), 32 ); // 32 bytes
2010-06-06 23:08:23 +02:00
pkt << uint8 ( 1 );
2013-08-18 16:06:29 +02:00
pkt . append ( g . AsByteArray (). get (), 1 );
2010-06-06 23:08:23 +02:00
pkt << uint8 ( 32 );
2013-08-18 16:06:29 +02:00
pkt . append ( N . AsByteArray ( 32 ). get (), 32 );
pkt . append ( s . AsByteArray (). get (), s . GetNumBytes ()); // 32 bytes
pkt . append ( unk3 . AsByteArray ( 16 ). get (), 16 );
2010-06-06 23:08:23 +02:00
uint8 securityFlags = 0 ;
2013-08-25 14:02:40 +01:00
// Check if token is used
_tokenKey = fields [ 8 ]. GetString ();
if ( ! _tokenKey . empty ())
securityFlags = 4 ;
2010-06-06 23:08:23 +02:00
pkt << uint8 ( securityFlags ); // security flags (0x0...0x04)
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x01 ) // PIN input
2010-06-06 23:08:23 +02:00
{
pkt << uint32 ( 0 );
pkt << uint64 ( 0 ) << uint64 ( 0 ); // 16 bytes hash?
}
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x02 ) // Matrix input
2010-06-06 23:08:23 +02:00
{
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint8 ( 0 );
pkt << uint64 ( 0 );
}
2010-11-16 14:29:01 +01:00
if ( securityFlags & 0x04 ) // Security token input
2010-06-06 23:08:23 +02:00
pkt << uint8 ( 1 );
2013-04-15 14:56:00 +03:00
uint8 secLevel = fields [ 5 ]. GetUInt8 ();
2010-06-06 23:08:23 +02:00
_accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes ( secLevel ) : SEC_ADMINISTRATOR ;
_localizationName . resize ( 4 );
for ( int i = 0 ; i < 4 ; ++ i )
_localizationName [ i ] = ch -> country [ 4 - i - 1 ];
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (),
2012-01-24 20:56:16 +01:00
_login . c_str (), ch -> country [ 3 ], ch -> country [ 2 ], ch -> country [ 1 ], ch -> country [ 0 ], GetLocaleByName ( _localizationName )
);
2010-06-06 23:08:23 +02:00
}
}
}
2010-11-16 14:29:01 +01:00
else //no account
2013-04-15 14:56:00 +03:00
pkt << uint8 ( WOW_FAIL_UNKNOWN_ACCOUNT );
2010-06-06 23:08:23 +02:00
}
socket (). send (( char const * ) pkt . contents (), pkt . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Logon Proof command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleLogonProof ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleLogonProof" );
2010-11-16 14:29:01 +01:00
// Read the packet
2010-06-06 23:08:23 +02:00
sAuthLogonProof_C lp ;
if ( ! socket (). recv (( char * ) & lp , sizeof ( sAuthLogonProof_C )))
return false ;
2010-11-16 14:29:01 +01:00
// If the client has no valid version
2010-06-06 23:08:23 +02:00
if ( _expversion == NO_VALID_EXP_FLAG )
{
2010-11-16 14:29:01 +01:00
// Check if we have the appropriate patch on the disk
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_NETWORKIO , "Client with invalid version, patching is not implemented" );
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return true ;
}
2010-11-16 14:29:01 +01:00
// Continue the SRP6 calculation based on data received from the client
2010-06-06 23:08:23 +02:00
BigNumber A ;
A . SetBinary ( lp . A , 32 );
2010-12-04 11:19:41 -08:00
// SRP safeguard: abort if A == 0
2010-06-06 23:08:23 +02:00
if ( A . isZero ())
{
socket (). shutdown ();
return true ;
}
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2010-06-06 23:08:23 +02:00
sha . UpdateBigNumbers ( & A , & B , NULL );
sha . Finalize ();
BigNumber u ;
u . SetBinary ( sha . GetDigest (), 20 );
BigNumber S = ( A * ( v . ModExp ( u , N ))). ModExp ( b , N );
uint8 t [ 32 ];
uint8 t1 [ 16 ];
uint8 vK [ 40 ];
2013-08-18 16:06:29 +02:00
memcpy ( t , S . AsByteArray ( 32 ). get (), 32 );
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 16 ; ++ i )
t1 [ i ] = t [ i * 2 ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( t1 , 16 );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
vK [ i * 2 ] = sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 16 ; ++ i )
t1 [ i ] = t [ i * 2 + 1 ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( t1 , 16 );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
vK [ i * 2 + 1 ] = sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
K . SetBinary ( vK , 40 );
uint8 hash [ 20 ];
sha . Initialize ();
sha . UpdateBigNumbers ( & N , NULL );
sha . Finalize ();
memcpy ( hash , sha . GetDigest (), 20 );
sha . Initialize ();
sha . UpdateBigNumbers ( & g , NULL );
sha . Finalize ();
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
for ( int i = 0 ; i < 20 ; ++ i )
hash [ i ] ^= sha . GetDigest ()[ i ];
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
BigNumber t3 ;
t3 . SetBinary ( hash , 20 );
sha . Initialize ();
sha . UpdateData ( _login );
sha . Finalize ();
uint8 t4 [ SHA_DIGEST_LENGTH ];
memcpy ( t4 , sha . GetDigest (), SHA_DIGEST_LENGTH );
sha . Initialize ();
sha . UpdateBigNumbers ( & t3 , NULL );
sha . UpdateData ( t4 , SHA_DIGEST_LENGTH );
sha . UpdateBigNumbers ( & s , & A , & B , & K , NULL );
sha . Finalize ();
BigNumber M ;
M . SetBinary ( sha . GetDigest (), 20 );
2010-11-16 14:29:01 +01:00
// Check if SRP6 results match (password is correct), else send an error
2013-08-18 16:06:29 +02:00
if ( ! memcmp ( M . AsByteArray (). get (), lp . M1 , 20 ))
2010-06-06 23:08:23 +02:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' User '%s' successfully authenticated" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
2010-06-06 23:08:23 +02:00
// No SQL injection (escaped user name) and IP address as received by socket
2010-12-27 09:02:02 -08:00
const char * K_hex = K . AsHexStr ();
2010-09-12 01:40:27 +02:00
2011-12-31 20:14:38 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_LOGONPROOF );
2010-09-03 01:00:49 +02:00
stmt -> setString ( 0 , K_hex );
2012-01-24 20:56:16 +01:00
stmt -> setString ( 1 , socket (). getRemoteAddress (). c_str ());
2010-09-03 01:00:49 +02:00
stmt -> setUInt32 ( 2 , GetLocaleByName ( _localizationName ));
2012-02-19 13:51:16 +01:00
stmt -> setString ( 3 , _os );
stmt -> setString ( 4 , _login );
2010-09-03 01:00:49 +02:00
LoginDatabase . Execute ( stmt );
2010-06-06 23:08:23 +02:00
OPENSSL_free (( void * ) K_hex );
2010-11-16 14:29:01 +01:00
// Finish SRP6 and send the final result to the client
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateBigNumbers ( & A , & M , & K , NULL );
sha . Finalize ();
2013-08-25 14:02:40 +01:00
// Check auth token
if (( lp . securityFlags & 0x04 ) || ! _tokenKey . empty ())
{
uint8 size ;
socket (). recv (( char * ) & size , 1 );
char * token = new char [ size + 1 ];
token [ size ] = '\0' ;
socket (). recv ( token , size );
unsigned int validToken = TOTP :: GenerateToken ( _tokenKey . c_str ());
unsigned int incomingToken = atoi ( token );
delete [] token ;
if ( validToken != incomingToken )
{
char data [] = { AUTH_LOGON_PROOF , WOW_FAIL_UNKNOWN_ACCOUNT , 3 , 0 };
socket (). send ( data , sizeof ( data ));
return false ;
}
}
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2010-06-06 23:08:23 +02:00
{
sAuthLogonProof_S proof ;
memcpy ( proof . M2 , sha . GetDigest (), 20 );
proof . cmd = AUTH_LOGON_PROOF ;
proof . error = 0 ;
2011-11-26 01:53:44 +01:00
proof . unk1 = 0x00800000 ; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament)
proof . unk2 = 0x00 ; // SurveyId
2010-06-06 23:08:23 +02:00
proof . unk3 = 0x00 ;
socket (). send (( char * ) & proof , sizeof ( proof ));
}
else
{
sAuthLogonProof_S_Old proof ;
memcpy ( proof . M2 , sha . GetDigest (), 20 );
proof . cmd = AUTH_LOGON_PROOF ;
proof . error = 0 ;
proof . unk2 = 0x00 ;
socket (). send (( char * ) & proof , sizeof ( proof ));
}
_authed = true ;
}
else
{
2010-12-27 09:19:44 -08:00
char data [ 4 ] = { AUTH_LOGON_PROOF , WOW_FAIL_UNKNOWN_ACCOUNT , 3 , 0 };
2010-06-06 23:08:23 +02:00
socket (). send ( data , sizeof ( data ));
2010-09-25 22:03:57 +02:00
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!" , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
2013-07-15 17:31:44 +02:00
uint32 MaxWrongPassCount = sConfigMgr -> GetIntDefault ( "WrongPass.MaxCount" , 0 );
2010-06-06 23:08:23 +02:00
if ( MaxWrongPassCount > 0 )
{
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
2011-12-31 20:14:38 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_UPD_FAILEDLOGINS );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
LoginDatabase . Execute ( stmt );
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_FAILEDLOGINS );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
2010-06-06 23:08:23 +02:00
2010-09-12 11:06:26 +02:00
if ( PreparedQueryResult loginfail = LoginDatabase . Query ( stmt ))
2010-06-06 23:08:23 +02:00
{
2010-09-24 22:16:21 +02:00
uint32 failed_logins = ( * loginfail )[ 1 ]. GetUInt32 ();
2010-06-06 23:08:23 +02:00
if ( failed_logins >= MaxWrongPassCount )
{
2013-07-15 17:31:44 +02:00
uint32 WrongPassBanTime = sConfigMgr -> GetIntDefault ( "WrongPass.BanTime" , 600 );
bool WrongPassBanType = sConfigMgr -> GetBoolDefault ( "WrongPass.BanType" , false );
2010-06-06 23:08:23 +02:00
if ( WrongPassBanType )
{
2010-09-24 22:16:21 +02:00
uint32 acc_id = ( * loginfail )[ 0 ]. GetUInt32 ();
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_INS_ACCOUNT_AUTO_BANNED );
2010-09-12 11:06:26 +02:00
stmt -> setUInt32 ( 0 , acc_id );
stmt -> setUInt32 ( 1 , WrongPassBanTime );
LoginDatabase . Execute ( stmt );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times" ,
2012-01-24 20:56:16 +01:00
socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str (), WrongPassBanTime , failed_logins );
2010-06-06 23:08:23 +02:00
}
else
{
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_INS_IP_AUTO_BANNED );
2012-01-24 20:56:16 +01:00
stmt -> setString ( 0 , socket (). getRemoteAddress ());
2010-09-12 11:06:26 +02:00
stmt -> setUInt32 ( 1 , WrongPassBanTime );
LoginDatabase . Execute ( stmt );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "'%s:%d' [AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times" ,
2012-01-24 20:56:16 +01:00
socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), socket (). getRemoteAddress (). c_str (), WrongPassBanTime , _login . c_str (), failed_logins );
2010-06-06 23:08:23 +02:00
}
}
}
}
}
return true ;
}
2010-11-16 14:29:01 +01:00
// Reconnect Challenge command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleReconnectChallenge ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleReconnectChallenge" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < sizeof ( sAuthLogonChallenge_C ))
return false ;
2010-11-16 14:29:01 +01:00
// Read the first 4 bytes (header) to get the length of the remaining of the packet
2010-06-06 23:08:23 +02:00
std :: vector < uint8 > buf ;
buf . resize ( 4 );
socket (). recv (( char * ) & buf [ 0 ], 4 );
2010-12-04 11:19:41 -08:00
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
2010-06-06 23:08:23 +02:00
EndianConvert ( * (( uint16 * )( buf [ 0 ])));
2010-12-04 11:19:41 -08:00
#endif
2010-06-06 23:08:23 +02:00
uint16 remaining = (( sAuthLogonChallenge_C * ) & buf [ 0 ]) -> size ;
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[ReconnectChallenge] got header, body is %#04x bytes" , remaining );
2010-06-06 23:08:23 +02:00
if (( remaining < sizeof ( sAuthLogonChallenge_C ) - buf . size ()) || ( socket (). recv_len () < remaining ))
return false ;
2010-11-16 14:29:01 +01:00
// No big fear of memory outage (size is int16, i.e. < 65536)
2010-06-06 23:08:23 +02:00
buf . resize ( remaining + buf . size () + 1 );
buf [ buf . size () - 1 ] = 0 ;
sAuthLogonChallenge_C * ch = ( sAuthLogonChallenge_C * ) & buf [ 0 ];
2010-11-16 14:29:01 +01:00
// Read the remaining of the packet
2010-06-06 23:08:23 +02:00
socket (). recv (( char * ) & buf [ 4 ], remaining );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[ReconnectChallenge] got full packet, %#04x bytes" , ch -> size );
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "[ReconnectChallenge] name(%d): '%s'" , ch -> I_len , ch -> I );
2010-06-06 23:08:23 +02:00
_login = ( const char * ) ch -> I ;
2011-12-31 20:14:38 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_SESSIONKEY );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
// Stop if the account is not found
if ( ! result )
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database." , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
2010-12-21 05:50:19 +01:00
// Reinitialize build, expansion and the account securitylevel
_build = ch -> build ;
2012-08-28 01:11:03 +02:00
_expversion = uint8 ( AuthHelper :: IsPostBCAcceptedClientBuild ( _build ) ? POST_BC_EXP_FLAG : ( AuthHelper :: IsPreBCAcceptedClientBuild ( _build ) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG ));
2012-02-19 13:51:16 +01:00
_os = ( const char * ) ch -> os ;
if ( _os . size () > 4 )
return false ;
// Restore string order as its byte order is reversed
std :: reverse ( _os . begin (), _os . end ());
2010-12-21 05:50:19 +01:00
2010-12-21 05:57:53 +01:00
Field * fields = result -> Fetch ();
2010-12-21 05:50:19 +01:00
uint8 secLevel = fields [ 2 ]. GetUInt8 ();
_accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes ( secLevel ) : SEC_ADMINISTRATOR ;
2010-09-24 22:16:21 +02:00
K . SetHexStr (( * result )[ 0 ]. GetCString ());
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Sending response
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
2012-08-28 01:11:03 +02:00
pkt << uint8 ( AUTH_RECONNECT_CHALLENGE );
pkt << uint8 ( 0x00 );
2010-06-06 23:08:23 +02:00
_reconnectProof . SetRand ( 16 * 8 );
2013-08-18 16:06:29 +02:00
pkt . append ( _reconnectProof . AsByteArray ( 16 ). get (), 16 ); // 16 bytes random
2012-08-28 01:11:03 +02:00
pkt << uint64 ( 0x00 ) << uint64 ( 0x00 ); // 16 bytes zeros
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) pkt . contents (), pkt . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Reconnect Proof command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleReconnectProof ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleReconnectProof" );
2010-11-16 14:29:01 +01:00
// Read the packet
2010-06-06 23:08:23 +02:00
sAuthReconnectProof_C lp ;
if ( ! socket (). recv (( char * ) & lp , sizeof ( sAuthReconnectProof_C )))
return false ;
if ( _login . empty () || ! _reconnectProof . GetNumBytes () || ! K . GetNumBytes ())
return false ;
BigNumber t1 ;
t1 . SetBinary ( lp . R1 , 16 );
2010-08-08 04:49:04 +02:00
SHA1Hash sha ;
2010-06-06 23:08:23 +02:00
sha . Initialize ();
sha . UpdateData ( _login );
sha . UpdateBigNumbers ( & t1 , & _reconnectProof , & K , NULL );
sha . Finalize ();
if ( ! memcmp ( sha . GetDigest (), lp . R2 , SHA_DIGEST_LENGTH ))
{
2010-11-16 14:29:01 +01:00
// Sending response
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
2012-08-28 01:11:03 +02:00
pkt << uint8 ( AUTH_RECONNECT_PROOF );
pkt << uint8 ( 0x00 );
pkt << uint16 ( 0x00 ); // 2 bytes zeros
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) pkt . contents (), pkt . size ());
_authed = true ;
return true ;
}
else
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "'%s:%d' [ERROR] user %s tried to login, but session is invalid." , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
}
2013-01-27 17:33:01 +01:00
ACE_INET_Addr const & AuthSocket :: GetAddressForClient ( Realm const & realm , ACE_INET_Addr const & clientAddr )
{
// Attempt to send best address for client
if ( clientAddr . is_loopback ())
{
// Try guessing if realm is also connected locally
if ( realm . LocalAddress . is_loopback () || realm . ExternalAddress . is_loopback ())
return clientAddr ;
// Assume that user connecting from the machine that authserver is located on
// has all realms available in his local network
return realm . LocalAddress ;
}
// Check if connecting client is in the same network
if ( IsIPAddrInNetwork ( realm . LocalAddress , clientAddr , realm . LocalSubnetMask ))
return realm . LocalAddress ;
// Return external IP
return realm . ExternalAddress ;
}
2010-11-16 14:29:01 +01:00
// Realm List command handler
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleRealmList ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleRealmList" );
2010-06-06 23:08:23 +02:00
if ( socket (). recv_len () < 5 )
return false ;
socket (). recv_skip ( 5 );
2010-11-16 14:29:01 +01:00
// Get the user id (else close the connection)
2010-09-12 11:06:26 +02:00
// No SQL injection (prepared statement)
2011-12-31 20:14:38 +01:00
PreparedStatement * stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_ACCOUNT_ID_BY_NAME );
2010-09-12 11:06:26 +02:00
stmt -> setString ( 0 , _login );
PreparedQueryResult result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( ! result )
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database." , socket (). getRemoteAddress (). c_str (), socket (). getRemotePort (), _login . c_str ());
2010-06-06 23:08:23 +02:00
socket (). shutdown ();
return false ;
}
2010-09-24 22:16:21 +02:00
Field * fields = result -> Fetch ();
uint32 id = fields [ 0 ]. GetUInt32 ();
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Update realm list if need
2010-06-06 23:08:23 +02:00
sRealmList -> UpdateIfNeed ();
2013-01-27 17:33:01 +01:00
ACE_INET_Addr clientAddr ;
socket (). peer (). get_remote_addr ( clientAddr );
2010-11-16 14:29:01 +01:00
// Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
2010-06-06 23:08:23 +02:00
ByteBuffer pkt ;
size_t RealmListSize = 0 ;
for ( RealmList :: RealmMap :: const_iterator i = sRealmList -> begin (); i != sRealmList -> end (); ++ i )
{
2013-09-01 22:45:40 +03:00
const Realm & realm = i -> second ;
2010-06-06 23:08:23 +02:00
// don't work with realms which not compatible with the client
2013-09-01 22:45:40 +03:00
bool okBuild = (( _expversion & POST_BC_EXP_FLAG ) && realm . gamebuild == _build ) || (( _expversion & PRE_BC_EXP_FLAG ) && ! AuthHelper :: IsPreBCAcceptedClientBuild ( realm . gamebuild ));
2012-08-28 01:11:03 +02:00
// No SQL injection. id of realm is controlled by the database.
2013-09-01 22:45:40 +03:00
uint32 flag = realm . flag ;
RealmBuildInfo const * buildInfo = AuthHelper :: GetBuildInfo ( realm . gamebuild );
2012-08-28 01:11:03 +02:00
if ( ! okBuild )
{
if ( ! buildInfo )
2010-06-06 23:08:23 +02:00
continue ;
2012-08-28 01:11:03 +02:00
flag |= REALM_FLAG_OFFLINE | REALM_FLAG_SPECIFYBUILD ; // tell the client what build the realm is for
}
2010-06-06 23:08:23 +02:00
2012-08-28 01:11:03 +02:00
if ( ! buildInfo )
flag &= ~ REALM_FLAG_SPECIFYBUILD ;
std :: string name = i -> first ;
2012-08-30 22:44:03 +01:00
if ( _expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD )
2012-08-28 01:11:03 +02:00
{
std :: ostringstream ss ;
ss << name << " (" << buildInfo -> MajorVersion << '.' << buildInfo -> MinorVersion << '.' << buildInfo -> BugfixVersion << ')' ;
name = ss . str ();
}
2013-01-27 17:33:01 +01:00
// We don't need the port number from which client connects with but the realm's port
2013-09-01 22:45:40 +03:00
clientAddr . set_port_number ( realm . ExternalAddress . get_port_number ());
2013-01-27 17:33:01 +01:00
2013-09-01 22:45:40 +03:00
uint8 lock = ( realm . allowedSecurityLevel > _accountSecurityLevel ) ? 1 : 0 ;
2012-08-28 01:11:03 +02:00
uint8 AmountOfCharacters = 0 ;
2011-12-31 20:14:38 +01:00
stmt = LoginDatabase . GetPreparedStatement ( LOGIN_SEL_NUM_CHARS_ON_REALM );
2013-09-01 22:45:40 +03:00
stmt -> setUInt32 ( 0 , realm . m_ID );
2010-09-12 11:06:26 +02:00
stmt -> setUInt32 ( 1 , id );
result = LoginDatabase . Query ( stmt );
2010-06-06 23:08:23 +02:00
if ( result )
2010-09-24 22:16:21 +02:00
AmountOfCharacters = ( * result )[ 0 ]. GetUInt8 ();
2010-06-06 23:08:23 +02:00
2013-09-01 22:45:40 +03:00
pkt << realm . icon ; // realm type
2012-08-28 01:11:03 +02:00
if ( _expversion & POST_BC_EXP_FLAG ) // only 2.x and 3.x clients
2010-11-16 14:29:01 +01:00
pkt << lock ; // if 1, then realm locked
2012-08-28 01:11:03 +02:00
pkt << uint8 ( flag ); // RealmFlags
pkt << name ;
2013-09-01 22:45:40 +03:00
pkt << GetAddressString ( GetAddressForClient ( realm , clientAddr ));
pkt << realm . populationLevel ;
2010-06-06 23:08:23 +02:00
pkt << AmountOfCharacters ;
2013-09-01 22:45:40 +03:00
pkt << realm . timezone ; // realm category
2010-12-27 09:02:02 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2012-08-28 01:11:03 +02:00
pkt << uint8 ( 0x2C ); // unk, may be realm number/id?
2010-06-06 23:08:23 +02:00
else
2012-08-28 01:11:03 +02:00
pkt << uint8 ( 0x0 ); // 1.12.1 and 1.12.2 clients
2010-06-06 23:08:23 +02:00
2012-08-28 01:11:03 +02:00
if ( _expversion & POST_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD )
2012-03-26 05:14:10 +02:00
{
2012-08-30 22:44:03 +01:00
pkt << uint8 ( buildInfo -> MajorVersion );
pkt << uint8 ( buildInfo -> MinorVersion );
pkt << uint8 ( buildInfo -> BugfixVersion );
pkt << uint16 ( buildInfo -> Build );
2012-03-26 05:14:10 +02:00
}
2010-06-06 23:08:23 +02:00
++ RealmListSize ;
}
2012-08-28 01:11:03 +02:00
if ( _expversion & POST_BC_EXP_FLAG ) // 2.x and 3.x clients
2010-06-06 23:08:23 +02:00
{
2012-08-28 01:11:03 +02:00
pkt << uint8 ( 0x10 );
pkt << uint8 ( 0x00 );
2010-11-16 14:29:01 +01:00
}
else // 1.12.1 and 1.12.2 clients
{
2012-08-28 01:11:03 +02:00
pkt << uint8 ( 0x00 );
pkt << uint8 ( 0x02 );
2010-06-06 23:08:23 +02:00
}
// make a ByteBuffer which stores the RealmList's size
ByteBuffer RealmListSizeBuffer ;
2012-08-28 01:11:03 +02:00
RealmListSizeBuffer << uint32 ( 0 );
2010-12-04 11:19:41 -08:00
if ( _expversion & POST_BC_EXP_FLAG ) // only 2.x and 3.x clients
2012-08-28 01:11:03 +02:00
RealmListSizeBuffer << uint16 ( RealmListSize );
2010-06-06 23:08:23 +02:00
else
2012-08-28 01:11:03 +02:00
RealmListSizeBuffer << uint32 ( RealmListSize );
2010-06-06 23:08:23 +02:00
ByteBuffer hdr ;
2012-08-28 01:11:03 +02:00
hdr << uint8 ( REALM_LIST );
hdr << uint16 ( pkt . size () + RealmListSizeBuffer . size ());
2010-11-16 14:29:01 +01:00
hdr . append ( RealmListSizeBuffer ); // append RealmList's size buffer
hdr . append ( pkt ); // append realms in the realmlist
2010-06-06 23:08:23 +02:00
socket (). send (( char const * ) hdr . contents (), hdr . size ());
return true ;
}
2010-11-16 14:29:01 +01:00
// Resume patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferResume ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleXferResume" );
2010-11-16 14:29:01 +01:00
// Check packet length and patch existence
2012-08-30 19:07:59 +01:00
if ( socket (). recv_len () < 9 || ! pPatch ) // FIXME: pPatch is never used
2010-06-06 23:08:23 +02:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "Error while resuming patch transfer (wrong packet)" );
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch a PatcherRunnable thread starting at given patch file offset
2010-06-06 23:08:23 +02:00
uint64 start ;
socket (). recv_skip ( 1 );
2011-04-29 20:47:02 +02:00
socket (). recv (( char * ) & start , sizeof ( start ));
2010-08-29 20:28:14 -07:00
fseek ( pPatch , long ( start ), 0 );
2010-06-06 23:08:23 +02:00
ACE_Based :: Thread u ( new PatcherRunnable ( this ));
return true ;
}
2010-11-16 14:29:01 +01:00
// Cancel patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferCancel ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleXferCancel" );
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Close and delete the socket
2010-06-06 23:08:23 +02:00
socket (). recv_skip ( 1 ); //clear input buffer
socket (). shutdown ();
return true ;
}
2010-11-16 14:29:01 +01:00
// Accept patch transfer
2010-06-06 23:08:23 +02:00
bool AuthSocket :: _HandleXferAccept ()
{
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_AUTHSERVER , "Entering _HandleXferAccept" );
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Check packet length and patch existence
2010-06-06 23:08:23 +02:00
if ( ! pPatch )
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "Error while accepting patch transfer (wrong packet)" );
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch a PatcherRunnable thread, starting at the beginning of the patch file
2010-06-06 23:08:23 +02:00
socket (). recv_skip ( 1 ); // clear input buffer
fseek ( pPatch , 0 , 0 );
ACE_Based :: Thread u ( new PatcherRunnable ( this ));
return true ;
}
2011-09-15 14:08:17 +02:00
PatcherRunnable :: PatcherRunnable ( class AuthSocket * as )
2010-06-06 23:08:23 +02:00
{
mySocket = as ;
}
2010-11-16 14:29:01 +01:00
// Send content of patch file to the client
2013-10-28 14:36:07 -04:00
void PatcherRunnable :: run () { }
2010-06-06 23:08:23 +02:00
2010-11-16 14:29:01 +01:00
// Preload MD5 hashes of existing patch files on server
2010-06-06 23:08:23 +02:00
#ifndef _WIN32
#include <dirent.h>
#include <errno.h>
void Patcher :: LoadPatchesInfo ()
{
2010-12-27 09:02:02 -08:00
DIR * dirp ;
struct dirent * dp ;
2010-06-06 23:08:23 +02:00
dirp = opendir ( "./patches/" );
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
if ( ! dirp )
return ;
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
while ( dirp )
{
errno = 0 ;
if (( dp = readdir ( dirp )) != NULL )
{
int l = strlen ( dp -> d_name );
2010-12-27 09:02:02 -08:00
2010-06-06 23:08:23 +02:00
if ( l < 8 )
continue ;
2010-12-27 09:02:02 -08:00
2010-12-04 11:19:41 -08:00
if ( ! memcmp ( & dp -> d_name [ l - 4 ], ".mpq" , 4 ))
2010-06-06 23:08:23 +02:00
LoadPatchMD5 ( dp -> d_name );
}
else
{
if ( errno != 0 )
{
closedir ( dirp );
return ;
}
break ;
}
}
if ( dirp )
closedir ( dirp );
}
#else
void Patcher :: LoadPatchesInfo ()
{
WIN32_FIND_DATA fil ;
2010-12-27 09:02:02 -08:00
HANDLE hFil = FindFirstFile ( "./patches/*.mpq" , & fil );
2010-06-06 23:08:23 +02:00
if ( hFil == INVALID_HANDLE_VALUE )
return ; // no patches were found
do
LoadPatchMD5 ( fil . cFileName );
2010-12-04 11:19:41 -08:00
while ( FindNextFile ( hFil , & fil ));
2010-06-06 23:08:23 +02:00
}
#endif
2010-11-16 14:29:01 +01:00
// Calculate and store MD5 hash for a given patch file
2010-12-04 11:19:41 -08:00
void Patcher :: LoadPatchMD5 ( char * szFileName )
2010-06-06 23:08:23 +02:00
{
2010-11-16 14:29:01 +01:00
// Try to open the patch file
2010-06-06 23:08:23 +02:00
std :: string path = "./patches/" ;
path += szFileName ;
2011-09-15 14:08:17 +02:00
FILE * pPatch = fopen ( path . c_str (), "rb" );
2013-05-13 15:07:36 +02:00
TC_LOG_DEBUG ( LOG_FILTER_NETWORKIO , "Loading patch info from %s \n " , path . c_str ());
2010-11-16 14:29:01 +01:00
2010-06-06 23:08:23 +02:00
if ( ! pPatch )
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_AUTHSERVER , "Error loading patch %s \n " , path . c_str ());
2010-06-06 23:08:23 +02:00
return ;
}
2010-11-16 14:29:01 +01:00
// Calculate the MD5 hash
2010-06-06 23:08:23 +02:00
MD5_CTX ctx ;
MD5_Init ( & ctx );
2010-12-04 11:19:41 -08:00
uint8 * buf = new uint8 [ 512 * 1024 ];
2010-06-06 23:08:23 +02:00
while ( ! feof ( pPatch ))
{
2010-12-04 11:19:41 -08:00
size_t read = fread ( buf , 1 , 512 * 1024 , pPatch );
2010-06-06 23:08:23 +02:00
MD5_Update ( & ctx , buf , read );
}
2010-12-04 11:19:41 -08:00
2010-06-06 23:08:23 +02:00
delete [] buf ;
fclose ( pPatch );
2010-11-16 14:29:01 +01:00
// Store the result in the internal patch hash map
2010-06-06 23:08:23 +02:00
_patches [ path ] = new PATCH_INFO ;
MD5_Final (( uint8 * ) & _patches [ path ] -> md5 , & ctx );
}
2010-11-16 14:29:01 +01:00
// Get cached MD5 hash for a given patch file
2010-06-06 23:08:23 +02:00
bool Patcher :: GetHash ( char * pat , uint8 mymd5 [ 16 ])
{
for ( Patches :: iterator i = _patches . begin (); i != _patches . end (); ++ i )
if ( ! stricmp ( pat , i -> first . c_str ()))
2010-12-04 11:19:41 -08:00
{
memcpy ( mymd5 , i -> second -> md5 , 16 );
return true ;
}
2010-06-06 23:08:23 +02:00
return false ;
}
2010-11-16 14:29:01 +01:00
// Launch the patch hashing mechanism on object creation
2010-06-06 23:08:23 +02:00
Patcher :: Patcher ()
{
LoadPatchesInfo ();
}
2010-11-16 14:29:01 +01:00
// Empty and delete the patch map on termination
2010-06-06 23:08:23 +02:00
Patcher ::~ Patcher ()
{
for ( Patches :: iterator i = _patches . begin (); i != _patches . end (); ++ i )
delete i -> second ;
}