Skip to content

Commit

Permalink
- Harden the client and server protocol against UDP spoofing attacks.…
Browse files Browse the repository at this point in the history
… This will defend ioquake3 against http://aluigi.altervista.org/papers/q3noclient.txt (#3041)

- Retains full compatibility to the old but unsecure protocol between clients and servers
- Harden the connection process against DoS attacks, possibly connected to UDP spoofing
  • Loading branch information
Thilo Schulz committed Apr 27, 2011
1 parent 06d12f6 commit a5580d8
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 75 deletions.
191 changes: 155 additions & 36 deletions code/client/cl_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,6 @@ void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
len = clc.serverMessageSequence;
swlen = LittleLong( len );
FS_Write (&swlen, 4, clc.demofile);

// skip the packet sequencing information
len = msg->cursize - headerBytes;
swlen = LittleLong(len);
Expand Down Expand Up @@ -636,14 +635,24 @@ void CL_Record_f( void ) {
if ( Cmd_Argc() == 2 ) {
s = Cmd_Argv(1);
Q_strncpyz( demoName, s, sizeof( demoName ) );
Com_sprintf (name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer );
#ifdef PROTOCOL_SUPPORT_OLD
if(clc.compat)
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_oldprotocol->integer);
else
#endif
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);
} else {
int number;

// scan for a free demo name
for ( number = 0 ; number <= 9999 ; number++ ) {
CL_DemoFilename( number, demoName );
Com_sprintf (name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer );
#ifdef PROTOCOL_SUPPORT_OLD
if(clc.compat)
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_oldprotocol->integer);
else
#endif
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);

if (!FS_FileExists(name))
break; // file doesn't exist
Expand All @@ -665,7 +674,6 @@ void CL_Record_f( void ) {
clc.spDemoRecording = qfalse;
}


Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );

// don't start saving messages until a non-delta compressed message is received
Expand Down Expand Up @@ -889,36 +897,62 @@ void CL_ReadDemoMessage( void ) {
CL_WalkDemoExt
====================
*/
static void CL_WalkDemoExt(char *arg, char *name, int *demofile)
static int CL_WalkDemoExt(char *arg, char *name, int *demofile)
{
int i = 0;
*demofile = 0;

Com_sprintf (name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_protocol->integer);

FS_FOpenFileRead( name, demofile, qtrue );

if (*demofile)
#ifdef PROTOCOL_SUPPORT_OLD
if(com_oldprotocol->integer > 0)
{
Com_Printf("Demo file: %s\n", name);
return;
Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_oldprotocol->integer);
FS_FOpenFileRead(name, demofile, qtrue);

if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
return com_oldprotocol->integer;
}
}

if(com_protocol->integer != com_oldprotocol->integer)
#endif
{
Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_protocol->integer);
FS_FOpenFileRead(name, demofile, qtrue);

if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
return com_protocol->integer;
}
}

Com_Printf("Not found: %s\n", name);

while(demo_protocols[i])
{
#ifdef PROTOCOL_SUPPORT_OLD
if(demo_protocols[i] == com_oldprotocol->integer)
continue;
#endif
if(demo_protocols[i] == com_protocol->integer)
continue;

Com_sprintf (name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, demo_protocols[i]);
FS_FOpenFileRead( name, demofile, qtrue );
if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
break;

return demo_protocols[i];
}
else
Com_Printf("Not found: %s\n", name);
i++;
}

return -1;
}

/*
Expand Down Expand Up @@ -978,7 +1012,11 @@ void CL_PlayDemo_f( void ) {
break;
}

if(demo_protocols[i] || protocol == com_protocol->integer)
if(demo_protocols[i] || protocol == com_protocol->integer
#ifdef PROTOCOL_SUPPORT_OLD
|| protocol == com_oldprotocol->integer
#endif
)
{
Com_sprintf(name, sizeof(name), "demos/%s", arg);
FS_FOpenFileRead(name, &clc.demofile, qtrue);
Expand All @@ -995,11 +1033,11 @@ void CL_PlayDemo_f( void ) {

Q_strncpyz(retry, arg, len + 1);
retry[len] = '\0';
CL_WalkDemoExt(retry, name, &clc.demofile);
protocol = CL_WalkDemoExt(retry, name, &clc.demofile);
}
}
else
CL_WalkDemoExt(arg, name, &clc.demofile);
protocol = CL_WalkDemoExt(arg, name, &clc.demofile);

if (!clc.demofile) {
Com_Error( ERR_DROP, "couldn't open %s", name);
Expand All @@ -1013,6 +1051,13 @@ void CL_PlayDemo_f( void ) {
clc.demoplaying = qtrue;
Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );

#ifdef PROTOCOL_SUPPORT_OLD
if(protocol <= com_oldprotocol->integer)
clc.compat = qtrue;
else
clc.compat = qfalse;
#endif

// read demo messages until connected
while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) {
CL_ReadDemoMessage();
Expand Down Expand Up @@ -2153,7 +2198,16 @@ void CL_CheckForResend( void ) {
port = Cvar_VariableValue ("net_qport");

Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
Info_SetValueForKey( info, "protocol", va("%i", com_protocol->integer ) );

#ifdef PROTOCOL_SUPPORT_OLD
if(com_oldprotocol->integer == com_protocol->integer)
clc.compat = qtrue;

if(clc.compat)
Info_SetValueForKey(info, "protocol", va("%i", com_oldprotocol->integer));
else
#endif
Info_SetValueForKey(info, "protocol", va("%i", com_protocol->integer));
Info_SetValueForKey( info, "qport", va("%i", port ) );
Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );

Expand Down Expand Up @@ -2394,6 +2448,7 @@ Responses to broadcasts, etc
void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
char *s;
char *c;
int challenge;

MSG_BeginReadingOOB( msg );
MSG_ReadLong( msg ); // skip the -1
Expand All @@ -2415,20 +2470,35 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
return;
}

if(!NET_CompareAdr(from, clc.serverAddress))
c = Cmd_Argv(2);
if(*c)
challenge = atoi(c);

#ifdef PROTOCOL_SUPPORT_OLD
if(!clc.compat)
{
// This challenge response is not coming from the expected address.
// Check whether we have a matching client challenge to prevent
// connection hi-jacking.

c = Cmd_Argv(2);

if(!*c || atoi(c) != clc.challenge)
if(!*c || challenge != clc.challenge)
{
Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
Com_Printf("Bad challenge for challengeResponse. Ignored.\n");
return;
}
}
else
#endif
{
if(!NET_CompareAdr(from, clc.serverAddress))
{
// This challenge response is not coming from the expected address.
// Check whether we have a matching client challenge to prevent
// connection hi-jacking.

if(!*c || challenge != clc.challenge)
{
Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
return;
}
}
}

// start sending challenge response instead of challenge request packets
clc.challenge = atoi(Cmd_Argv(1));
Expand Down Expand Up @@ -2457,7 +2527,34 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
Com_Printf( "connectResponse from wrong address. Ignored.\n" );
return;
}
Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );

#ifdef PROTOCOL_SUPPORT_OLD
if(!clc.compat)
{
c = Cmd_Argv(1);

if(*c)
challenge = atoi(c);
else
{
Com_Printf("Bad connectResponse received. Ignored.\n");
return;
}

if(challenge != clc.challenge)
{
Com_Printf("ConnectResponse with bad challenge received. Ignored.\n");
return;
}
}

Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
clc.challenge, clc.compat);
#else
Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
clc.challenge);
#endif

cls.state = CA_CONNECTED;
clc.lastPacketSentTime = -9999; // send first packet immediately
return;
Expand All @@ -2475,13 +2572,6 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
return;
}

// a disconnect message from the server, which will happen if the server
// dropped the connection but it is still getting packets from us
if (!Q_stricmp(c, "disconnect")) {
CL_DisconnectPacket( from );
return;
}

// echo request from server
if ( !Q_stricmp(c, "echo") ) {
NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
Expand All @@ -2501,10 +2591,33 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
}

// echo request from server
if ( !Q_stricmp(c, "print") ) {
if(!Q_stricmp(c, "print")){
s = MSG_ReadString( msg );

#ifdef PROTOCOL_SUPPORT_OLD
// Hack to detect legacy server protocol
if(cls.state == CA_CHALLENGING && com_oldprotocol->integer > 0)
{
char buf[128];
int len;

len = Com_sprintf(buf, sizeof(buf), "Server uses protocol version %d",
com_oldprotocol->integer);

if(len < sizeof(buf) && !Q_strncmp(s, buf, len) && !isdigit(s[len]))
{
// This is an old, but compatible protocol version.
// Go back to connecting state.
clc.compat = qtrue;
cls.state = CA_CONNECTING;
return;
}
}
#endif

Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
Com_Printf( "%s", s );

return;
}

Expand Down Expand Up @@ -3445,7 +3558,13 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {

// if this isn't the correct protocol version, ignore it
prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
if ( prot != com_protocol->integer ) {

if(prot != com_protocol->integer
#ifdef PROTOCOL_SUPPORT_OLD
&& prot != com_oldprotocol->integer
#endif
)
{
Com_DPrintf( "Different protocol info packet: %s\n", infoString );
return;
}
Expand Down
6 changes: 2 additions & 4 deletions code/client/cl_net_chan.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,6 @@ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
Netchan_Transmit( chan, msg->cursize, msg->data );
}

extern int oldsize;
int newsize = 0;

/*
=================
CL_Netchan_Process
Expand All @@ -161,7 +158,8 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
ret = Netchan_Process( chan, msg );
if (!ret)
return qfalse;

CL_Netchan_Decode( msg );
newsize += msg->cursize;

return qtrue;
}
4 changes: 4 additions & 0 deletions code/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ typedef struct {
float voipPower;
#endif

#ifdef PROTOCOL_SUPPORT_OLD
qboolean compat;
#endif

// big stuff at end of structure so most offsets are 15 bits or less
netchan_t netchan;
} clientConnection_t;
Expand Down
8 changes: 7 additions & 1 deletion code/qcommon/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif

int demo_protocols[] =
{ 66, 67, 68, 0 };
{ 67, 66, 0 };

#define MAX_NUM_ARGVS 50

Expand Down Expand Up @@ -86,6 +86,9 @@ cvar_t *com_maxfpsMinimized;
cvar_t *com_abnormalExit;
cvar_t *com_standalone;
cvar_t *com_protocol;
#ifdef PROTOCOL_SUPPORT_OLD
cvar_t *com_oldprotocol;
#endif
cvar_t *com_basegame;
cvar_t *com_homepath;
cvar_t *com_busyWait;
Expand Down Expand Up @@ -2710,6 +2713,9 @@ void Com_Init( char *commandLine ) {
s = va("%s %s %s", Q3_VERSION, PLATFORM_STRING, __DATE__ );
com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO );
com_protocol = Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_INIT);
#ifdef PROTOCOL_SUPPORT_OLD
com_oldprotocol = Cvar_Get ("oldprotocol", va("%i", PROTOCOL_OLD_VERSION), CVAR_INIT);
#endif

Sys_Init();

Expand Down
5 changes: 5 additions & 0 deletions code/qcommon/files.c
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,11 @@ qboolean FS_IsDemoExt(const char *filename, int namelen)
if(protocol == com_protocol->integer)
return qtrue;

#ifdef PROTOCOL_SUPPORT_OLD
if(protocol == PROTOCOL_OLD_VERSION)
return qtrue;
#endif

for(index = 0; demo_protocols[index]; index++)
{
if(demo_protocols[index] == protocol)
Expand Down
Loading

0 comments on commit a5580d8

Please sign in to comment.