Permalink
Browse files

added support for a random entry key when joining over LAN to prevent…

… namespoofers from gaining access to your bot in some cases

fixed a very minor bug with the !sendlan command

git-svn-id: http://ghostplusplus.googlecode.com/svn/trunk@537 a7494f72-a4b0-11dd-a887-7ffe1a420f8d
  • Loading branch information...
1 parent f41f746 commit 266ce34ec7b8ceb4f40f7a00a0d0e12405ace41e hogantp committed Jul 16, 2010
Showing with 54 additions and 12 deletions.
  1. +11 −2 ghost/game.cpp
  2. +30 −3 ghost/game_base.cpp
  3. +1 −0 ghost/game_base.h
  4. +6 −5 ghost/gameprotocol.cpp
  5. +4 −2 ghost/gameprotocol.h
  6. +2 −0 ghost/ghost.cpp
View
@@ -1434,6 +1434,15 @@ bool CGame :: EventPlayerBotCommand( CGamePlayer *player, string command, string
CONSOLE_Print( "[GAME: " + m_GameName + "] bad inputs to sendlan command" );
else
{
+ // construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN)
+ // the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
+ // the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
+ // since we're destroying 4 bits of information here the actual host counter should not be greater than 2^28 which is a reasonable assumption
+ // when a player joins a game we can obtain the ID from the received host counter
+ // note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused
+
+ uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;
+
// we send 12 for SlotsTotal because this determines how many PID's Warcraft 3 allocates
// we need to make sure Warcraft 3 allocates at least SlotsTotal + 1 but at most 12 PID's
// this is because we need an extra PID for the virtual host player (but we always delete the virtual host player when the 12th person joins)
@@ -1454,15 +1463,15 @@ bool CGame :: EventPlayerBotCommand( CGamePlayer *player, string command, string
BYTEARRAY MapHeight;
MapHeight.push_back( 0 );
MapHeight.push_back( 0 );
- m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, m_HostCounter ) );
+ m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
}
else
{
// note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
// note: we do not use m_Map->GetMapGameType because none of the filters are set when broadcasting to LAN (also as you might expect)
uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0;
- m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, m_HostCounter ) );
+ m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
}
}
}
View
@@ -88,6 +88,7 @@ CBaseGame :: CBaseGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16
m_HCLCommandString = m_Map->GetMapDefaultHCL( );
m_RandomSeed = GetTicks( );
m_HostCounter = m_GHost->m_HostCounter++;
+ m_EntryKey = rand( );
m_Latency = m_GHost->m_Latency;
m_SyncLimit = m_GHost->m_SyncLimit;
m_SyncCounter = 0;
@@ -469,6 +470,15 @@ bool CBaseGame :: Update( void *fd, void *send_fd )
uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;
+ // we send 12 for SlotsTotal because this determines how many PID's Warcraft 3 allocates
+ // we need to make sure Warcraft 3 allocates at least SlotsTotal + 1 but at most 12 PID's
+ // this is because we need an extra PID for the virtual host player (but we always delete the virtual host player when the 12th person joins)
+ // however, we can't send 13 for SlotsTotal because this causes Warcraft 3 to crash when sharing control of units
+ // nor can we send SlotsTotal because then Warcraft 3 crashes when playing maps with less than 12 PID's (because of the virtual host player taking an extra PID)
+ // we also send 12 for SlotsOpen because Warcraft 3 assumes there's always at least one player in the game (the host)
+ // so if we try to send accurate numbers it'll always be off by one and results in Warcraft 3 assuming the game is full when it still needs one more player
+ // the easiest solution is to simply send 12 for both so the game will always show up as (1/12) players
+
if( m_SaveGame )
{
// note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
@@ -480,15 +490,15 @@ bool CBaseGame :: Update( void *fd, void *send_fd )
BYTEARRAY MapHeight;
MapHeight.push_back( 0 );
MapHeight.push_back( 0 );
- m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter ) );
+ m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
}
else
{
// note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
// note: we do not use m_Map->GetMapGameType because none of the filters are set when broadcasting to LAN (also as you might expect)
uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0;
- m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, FixedHostCounter ) );
+ m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
}
}
@@ -1710,7 +1720,24 @@ void CBaseGame :: EventPlayerJoined( CPotentialPlayer *potential, CIncomingJoinP
// we use an ID value of 0 to denote joining via LAN
- if( HostCounterID != 0 )
+ if( HostCounterID == 0 )
+ {
+ // the player is pretending to join via LAN, which they might or might not be (i.e. it could be spoofed)
+ // however, we've been broadcasting a random entry key to the LAN
+ // if the player is really on the LAN they'll know the entry key, otherwise they won't
+ // or they're very lucky since it's a 32 bit number
+
+ if( joinPlayer->GetEntryKey( ) != m_EntryKey )
+ {
+ // oops!
+
+ CONSOLE_Print( "[GAME: " + m_GameName + "] player [" + joinPlayer->GetName( ) + "|" + potential->GetExternalIPString( ) + "] is trying to join the game over LAN but used an incorrect entry key" );
+ potential->Send( m_Protocol->SEND_W3GS_REJECTJOIN( REJECTJOIN_WRONGPASSWORD ) );
+ potential->SetDeleteMe( true );
+ return;
+ }
+ }
+ else
{
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
View
@@ -80,6 +80,7 @@ class CBaseGame
string m_HCLCommandString; // the "HostBot Command Library" command string, used to pass a limited amount of data to specially designed maps
uint32_t m_RandomSeed; // the random seed sent to the Warcraft III clients
uint32_t m_HostCounter; // a unique game number
+ uint32_t m_EntryKey; // random entry key for LAN, used to prove that a player is actually joining from LAN
uint32_t m_Latency; // the number of ms to wait between sending action packets (we queue any received during this time)
uint32_t m_SyncLimit; // the maximum number of packets a player can fall out of sync before starting the lag screen
uint32_t m_SyncCounter; // the number of actions sent so far (for determining if anyone is lagging)
View
@@ -63,12 +63,13 @@ CIncomingJoinPlayer *CGameProtocol :: RECEIVE_W3GS_REQJOIN( BYTEARRAY data )
if( ValidateLength( data ) && data.size( ) >= 20 )
{
uint32_t HostCounter = UTIL_ByteArrayToUInt32( data, false, 4 );
+ uint32_t EntryKey = UTIL_ByteArrayToUInt32( data, false, 8 );
BYTEARRAY Name = UTIL_ExtractCString( data, 19 );
if( !Name.empty( ) && data.size( ) >= Name.size( ) + 30 )
{
BYTEARRAY InternalIP = BYTEARRAY( data.begin( ) + Name.size( ) + 26, data.begin( ) + Name.size( ) + 30 );
- return new CIncomingJoinPlayer( HostCounter, string( Name.begin( ), Name.end( ) ), InternalIP );
+ return new CIncomingJoinPlayer( HostCounter, EntryKey, string( Name.begin( ), Name.end( ) ), InternalIP );
}
}
@@ -652,12 +653,11 @@ BYTEARRAY CGameProtocol :: SEND_W3GS_SEARCHGAME( bool TFT, unsigned char war3Ver
return packet;
}
-BYTEARRAY CGameProtocol :: SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCounter )
+BYTEARRAY CGameProtocol :: SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCounter, uint32_t entryKey )
{
unsigned char ProductID_ROC[] = { 51, 82, 65, 87 }; // "WAR3"
unsigned char ProductID_TFT[] = { 80, 88, 51, 87 }; // "W3XP"
unsigned char Version[] = { war3Version, 0, 0, 0 };
- unsigned char Unknown1[] = { 1, 2, 3, 4 };
unsigned char Unknown2[] = { 1, 0, 0, 0 };
BYTEARRAY packet;
@@ -691,7 +691,7 @@ BYTEARRAY CGameProtocol :: SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Versi
UTIL_AppendByteArray( packet, Version, 4 ); // Version
UTIL_AppendByteArray( packet, hostCounter, false ); // Host Counter
- UTIL_AppendByteArray( packet, Unknown1, 4 ); // ??? (this varies wildly even between two identical games created one after another)
+ UTIL_AppendByteArray( packet, entryKey, false ); // Entry Key
UTIL_AppendByteArrayFast( packet, gameName ); // Game Name
packet.push_back( 0 ); // ??? (maybe game password)
UTIL_AppendByteArrayFast( packet, StatString ); // Stat String
@@ -961,9 +961,10 @@ BYTEARRAY CGameProtocol :: EncodeSlotInfo( vector<CGameSlot> &slots, uint32_t ra
// CIncomingJoinPlayer
//
-CIncomingJoinPlayer :: CIncomingJoinPlayer( uint32_t nHostCounter, string nName, BYTEARRAY &nInternalIP )
+CIncomingJoinPlayer :: CIncomingJoinPlayer( uint32_t nHostCounter, uint32_t nEntryKey, string nName, BYTEARRAY &nInternalIP )
{
m_HostCounter = nHostCounter;
+ m_EntryKey = nEntryKey;
m_Name = nName;
m_InternalIP = nInternalIP;
}
View
@@ -133,7 +133,7 @@ class CGameProtocol
BYTEARRAY SEND_W3GS_START_LAG( vector<CGamePlayer *> players, bool loadInGame = false );
BYTEARRAY SEND_W3GS_STOP_LAG( CGamePlayer *player, bool loadInGame = false );
BYTEARRAY SEND_W3GS_SEARCHGAME( bool TFT, unsigned char war3Version );
- BYTEARRAY SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCounter );
+ BYTEARRAY SEND_W3GS_GAMEINFO( bool TFT, unsigned char war3Version, BYTEARRAY mapGameType, BYTEARRAY mapFlags, BYTEARRAY mapWidth, BYTEARRAY mapHeight, string gameName, string hostName, uint32_t upTime, string mapPath, BYTEARRAY mapCRC, uint32_t slotsTotal, uint32_t slotsOpen, uint16_t port, uint32_t hostCounter, uint32_t entryKey );
BYTEARRAY SEND_W3GS_CREATEGAME( bool TFT, unsigned char war3Version );
BYTEARRAY SEND_W3GS_REFRESHGAME( uint32_t players, uint32_t playerSlots );
BYTEARRAY SEND_W3GS_DECREATEGAME( );
@@ -158,14 +158,16 @@ class CIncomingJoinPlayer
{
private:
uint32_t m_HostCounter;
+ uint32_t m_EntryKey;
string m_Name;
BYTEARRAY m_InternalIP;
public:
- CIncomingJoinPlayer( uint32_t nHostCounter, string nName, BYTEARRAY &nInternalIP );
+ CIncomingJoinPlayer( uint32_t nHostCounter, uint32_t nEntryKey, string nName, BYTEARRAY &nInternalIP );
~CIncomingJoinPlayer( );
uint32_t GetHostCounter( ) { return m_HostCounter; }
+ uint32_t GetEntryKey( ) { return m_EntryKey; }
string GetName( ) { return m_Name; }
BYTEARRAY GetInternalIP( ) { return m_InternalIP; }
};
View
@@ -231,6 +231,8 @@ void DEBUG_Print( BYTEARRAY b )
int main( int argc, char **argv )
{
+ srand( time( NULL ) );
+
gCFGFile = "ghost.cfg";
if( argc > 1 && argv[1] )

0 comments on commit 266ce34

Please sign in to comment.