engine: server: rewrite challenge generator to something more simple: salted MD5 of an IP address
The idea was taken from ReHLDS project.
This commit is contained in:
parent
c5d4af802c
commit
9c50288bbb
3 changed files with 51 additions and 65 deletions
|
@ -279,19 +279,6 @@ typedef struct sv_client_s
|
||||||
a program error, like an overflowed reliable buffer
|
a program error, like an overflowed reliable buffer
|
||||||
=============================================================================
|
=============================================================================
|
||||||
*/
|
*/
|
||||||
// MAX_CHALLENGES is made large to prevent a denial
|
|
||||||
// of service attack that could cycle all of them
|
|
||||||
// out before legitimate users connected
|
|
||||||
#define MAX_CHALLENGES 1024
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
netadr_t adr;
|
|
||||||
double time;
|
|
||||||
int challenge;
|
|
||||||
qboolean connected;
|
|
||||||
} challenge_t;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char name[32]; // in GoldSrc max name length is 12
|
char name[32]; // in GoldSrc max name length is 12
|
||||||
|
@ -385,7 +372,7 @@ typedef struct
|
||||||
entity_state_t *baselines; // [GI->max_edicts]
|
entity_state_t *baselines; // [GI->max_edicts]
|
||||||
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
|
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
|
||||||
|
|
||||||
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
|
uint32_t challenge_salt[16]; // pregenerated random numbers for generating challenged based on IP's MD5 address
|
||||||
|
|
||||||
sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching
|
sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching
|
||||||
byte *testpacket_buf; // check for NULL if testpacket is available
|
byte *testpacket_buf; // check for NULL if testpacket is available
|
||||||
|
|
|
@ -86,38 +86,53 @@ flood the server with invalid connection IPs. With a
|
||||||
challenge, they must give a valid IP address.
|
challenge, they must give a valid IP address.
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
static void SV_GetChallenge( netadr_t from )
|
static int SV_GetChallenge( netadr_t from, qboolean *error )
|
||||||
{
|
{
|
||||||
int i, oldest = 0;
|
const netadrtype_t type = NET_NetadrType( &from );
|
||||||
double oldestTime;
|
MD5Context_t ctx;
|
||||||
|
byte digest[16];
|
||||||
|
|
||||||
oldestTime = 0x7fffffff;
|
*error = false;
|
||||||
|
|
||||||
// see if we already have a challenge for this ip
|
MD5Init( &ctx );
|
||||||
for( i = 0; i < MAX_CHALLENGES; i++ )
|
|
||||||
|
switch( type )
|
||||||
{
|
{
|
||||||
if( !svs.challenges[i].connected && NET_CompareAdr( from, svs.challenges[i].adr ))
|
case NA_IP:
|
||||||
break;
|
MD5Update( &ctx, from.ip, sizeof( from.ip ));
|
||||||
|
break;
|
||||||
if( svs.challenges[i].time < oldestTime )
|
case NA_IPX:
|
||||||
{
|
MD5Update( &ctx, from.ipx, sizeof( from.ipx ));
|
||||||
oldestTime = svs.challenges[i].time;
|
break;
|
||||||
oldest = i;
|
case NA_IP6:
|
||||||
}
|
{
|
||||||
|
byte ip6[16];
|
||||||
|
NET_NetadrToIP6Bytes( ip6, &from );
|
||||||
|
MD5Update( &ctx, ip6, sizeof( ip6 ));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NA_LOOPBACK:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
*error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( i == MAX_CHALLENGES )
|
MD5Update( &ctx, (byte *)svs.challenge_salt, sizeof( svs.challenge_salt ));
|
||||||
{
|
MD5Final( digest, &ctx );
|
||||||
// this is the first time this client has asked for a challenge
|
|
||||||
svs.challenges[oldest].challenge = (COM_RandomLong( 0, 0x7FFF ) << 16) | COM_RandomLong( 0, 0xFFFF );
|
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
|
||||||
svs.challenges[oldest].adr = from;
|
}
|
||||||
svs.challenges[oldest].time = host.realtime;
|
|
||||||
svs.challenges[oldest].connected = false;
|
static void SV_SendChallenge( netadr_t from )
|
||||||
i = oldest;
|
{
|
||||||
}
|
qboolean error = false;
|
||||||
|
int challenge = SV_GetChallenge( from, &error );
|
||||||
|
|
||||||
|
if( error )
|
||||||
|
return;
|
||||||
|
|
||||||
// send it back
|
// send it back
|
||||||
Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, S2C_CHALLENGE" %i", svs.challenges[i].challenge );
|
Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CHALLENGE" %i", challenge );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SV_GetFragmentSize( void *pcl, fragsize_t mode )
|
static int SV_GetFragmentSize( void *pcl, fragsize_t mode )
|
||||||
|
@ -211,35 +226,16 @@ Make sure connecting client is not spoofing
|
||||||
*/
|
*/
|
||||||
static int SV_CheckChallenge( netadr_t from, int challenge )
|
static int SV_CheckChallenge( netadr_t from, int challenge )
|
||||||
{
|
{
|
||||||
int i;
|
qboolean error = false;
|
||||||
|
int challenge2 = SV_GetChallenge( from, &error );
|
||||||
|
|
||||||
// see if the challenge is valid
|
if( error || challenge2 != challenge )
|
||||||
// don't care if it is a local address.
|
|
||||||
if( NET_IsLocalAddress( from ))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
for( i = 0; i < MAX_CHALLENGES; i++ )
|
|
||||||
{
|
|
||||||
if( NET_CompareAdr( from, svs.challenges[i].adr ))
|
|
||||||
{
|
|
||||||
if( challenge == svs.challenges[i].challenge )
|
|
||||||
break; // valid challenge
|
|
||||||
#if 0
|
|
||||||
// g-cont. this breaks multiple connections from single machine
|
|
||||||
SV_RejectConnection( from, "bad challenge %i\n", challenge );
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( i == MAX_CHALLENGES )
|
|
||||||
{
|
{
|
||||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
svs.challenges[i].connected = true;
|
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -844,7 +840,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||||
( packetsize > FRAGMENT_MAX_SIZE ))
|
( packetsize > FRAGMENT_MAX_SIZE ))
|
||||||
{
|
{
|
||||||
// skip the test and just get challenge
|
// skip the test and just get challenge
|
||||||
SV_GetChallenge( from );
|
SV_SendChallenge( from );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,7 +848,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||||
ofs = packetsize - svs.testpacket_filepos - 1;
|
ofs = packetsize - svs.testpacket_filepos - 1;
|
||||||
if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen ))
|
if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen ))
|
||||||
{
|
{
|
||||||
SV_GetChallenge( from );
|
SV_SendChallenge( from );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3166,7 +3162,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE ))
|
else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE ))
|
||||||
{
|
{
|
||||||
SV_GetChallenge( from );
|
SV_SendChallenge( from );
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( pcmd, C2S_CONNECT ))
|
else if( !Q_strcmp( pcmd, C2S_CONNECT ))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1027,6 +1027,9 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
|
||||||
svs.timestart = Sys_DoubleTime();
|
svs.timestart = Sys_DoubleTime();
|
||||||
svs.spawncount++; // any partially connected client will be restarted
|
svs.spawncount++; // any partially connected client will be restarted
|
||||||
|
|
||||||
|
for( i = 0; i < ARRAYSIZE( svs.challenge_salt ); i++ )
|
||||||
|
svs.challenge_salt[i] = COM_RandomLong( 0, 0x7FFFFFFE );
|
||||||
|
|
||||||
cycle = Cvar_VariableString( "mapchangecfgfile" );
|
cycle = Cvar_VariableString( "mapchangecfgfile" );
|
||||||
|
|
||||||
if( COM_CheckString( cycle ))
|
if( COM_CheckString( cycle ))
|
||||||
|
|
Loading…
Add table
Reference in a new issue