Make servers echo challenge codes from client getchallenge commands

This commit is contained in:
Thilo Schulz 2009-06-01 05:28:47 +00:00
parent eaaaa6f5fc
commit 18cc7c1ed3
4 changed files with 68 additions and 60 deletions

View File

@ -132,8 +132,8 @@ NET
#define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit #define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit
typedef enum { typedef enum {
NA_BAD = 0, // an address lookup failed
NA_BOT, NA_BOT,
NA_BAD, // an address lookup failed
NA_LOOPBACK, NA_LOOPBACK,
NA_BROADCAST, NA_BROADCAST,
NA_IP, NA_IP,

View File

@ -204,6 +204,7 @@ typedef struct client_s {
typedef struct { typedef struct {
netadr_t adr; netadr_t adr;
int challenge; int challenge;
int clientChallenge; // challenge number coming from the client
int time; // time the last packet was sent to the autherize server int time; // time the last packet was sent to the autherize server
int pingTime; // time the challenge response was sent to client int pingTime; // time the challenge response was sent to client
int firstTime; // time the adr was first used, for authorize timeout checks int firstTime; // time the adr was first used, for authorize timeout checks
@ -326,7 +327,7 @@ void SV_SpawnServer( char *server, qboolean killBots );
// //
// sv_client.c // sv_client.c
// //
void SV_GetChallenge( netadr_t from ); void SV_GetChallenge(netadr_t from);
void SV_DirectConnect( netadr_t from ); void SV_DirectConnect( netadr_t from );

View File

@ -41,12 +41,21 @@ to be sent to the authorize server.
When an authorizeip is returned, a challenge response will be When an authorizeip is returned, a challenge response will be
sent to that ip. sent to that ip.
ioquake3: we added a possibility for clients to add a challenge
to their packets, to make it more difficult for malicious servers
to hi-jack client connections.
Also, the auth stuff is completely disabled for com_standalone games
as well as IPv6 connections, since there is no way to use the
v4-only auth server for these new types of connections.
================= =================
*/ */
void SV_GetChallenge( netadr_t from ) { void SV_GetChallenge(netadr_t from)
{
int i; int i;
int oldest; int oldest;
int oldestTime; int oldestTime;
const char *clientChallenge = Cmd_Argv(1);
challenge_t *challenge; challenge_t *challenge;
// ignore if we are in single player // ignore if we are in single player
@ -69,62 +78,57 @@ void SV_GetChallenge( netadr_t from ) {
} }
} }
if (i == MAX_CHALLENGES) { if (i == MAX_CHALLENGES)
{
// this is the first time this client has asked for a challenge // this is the first time this client has asked for a challenge
challenge = &svs.challenges[oldest]; challenge = &svs.challenges[oldest];
challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time;
challenge->clientChallenge = 0;
challenge->adr = from; challenge->adr = from;
challenge->firstTime = svs.time; challenge->firstTime = svs.time;
challenge->time = svs.time; challenge->time = svs.time;
challenge->connected = qfalse; challenge->connected = qfalse;
i = oldest;
}
#ifdef STANDALONE
challenge->pingTime = svs.time;
NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge );
#else
// if they are on a lan address, send the challengeResponse immediately
if ( Sys_IsLANAddress( from ) ) {
challenge->pingTime = svs.time;
NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge );
return;
} }
#ifndef STANDALONE
// Drop the authorize stuff if this client is coming in via v6 as the auth server does not support ipv6. // Drop the authorize stuff if this client is coming in via v6 as the auth server does not support ipv6.
if(challenge->adr.type == NA_IP) // Drop also for addresses coming in on local LAN and for stand-alone games independent from id's assets.
if(challenge->adr.type == NA_IP && !Cvar_VariableIntegerValue("com_standalone") && !Sys_IsLANAddress(from))
{ {
// look up the authorize server's IP // look up the authorize server's IP
if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { if (svs.authorizeAddress.type == NA_BAD)
{
Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP ) ) {
Com_Printf( "Couldn't resolve address\n" ); if (NET_StringToAdr(AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP))
return; {
svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE );
Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1],
svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3],
BigShort( svs.authorizeAddress.port ) );
} }
svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE );
Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1],
svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3],
BigShort( svs.authorizeAddress.port ) );
} }
// we couldn't contact the auth server, let them in.
if(svs.authorizeAddress.type == NA_BAD)
Com_Printf("Couldn't resolve auth server address\n");
// if they have been challenging for a long time and we // if they have been challenging for a long time and we
// haven't heard anything from the authorize server, go ahead and // haven't heard anything from the authorize server, go ahead and
// let them in, assuming the id server is down // let them in, assuming the id server is down
if ( svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT ) { else if(svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT)
Com_DPrintf( "authorize server timed out\n" ); Com_DPrintf( "authorize server timed out\n" );
else
challenge->pingTime = svs.time; {
NET_OutOfBandPrint( NS_SERVER, challenge->adr, // otherwise send their ip to the authorize server
"challengeResponse %i", challenge->challenge );
return;
}
// otherwise send their ip to the authorize server
if ( svs.authorizeAddress.type != NA_BAD ) {
cvar_t *fs; cvar_t *fs;
char game[1024]; char game[1024];
// If the client provided us with a client challenge, store it...
if(*clientChallenge)
challenge->clientChallenge = atoi(clientChallenge);
Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from )); Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from ));
strcpy(game, BASEGAME); strcpy(game, BASEGAME);
@ -132,22 +136,20 @@ void SV_GetChallenge( netadr_t from ) {
if (fs && fs->string[0] != 0) { if (fs && fs->string[0] != 0) {
strcpy(game, fs->string); strcpy(game, fs->string);
} }
// the 0 is for backwards compatibility with obsolete sv_allowanonymous flags // the 0 is for backwards compatibility with obsolete sv_allowanonymous flags
// getIpAuthorize <challenge> <IP> <game> 0 <auth-flag> // getIpAuthorize <challenge> <IP> <game> 0 <auth-flag>
NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress,
"getIpAuthorize %i %i.%i.%i.%i %s 0 %s", svs.challenges[i].challenge, "getIpAuthorize %i %i.%i.%i.%i %s 0 %s", challenge->challenge,
from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, sv_strictAuth->string ); from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, sv_strictAuth->string );
return;
} }
} }
else
{
challenge->pingTime = svs.time;
NET_OutOfBandPrint( NS_SERVER, challenge->adr,
"challengeResponse %i", challenge->challenge );
}
#endif #endif
challenge->pingTime = svs.time;
NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i %s", challenge->challenge, clientChallenge);
} }
#ifndef STANDALONE #ifndef STANDALONE
@ -165,6 +167,7 @@ void SV_AuthorizeIpPacket( netadr_t from ) {
int i; int i;
char *s; char *s;
char *r; char *r;
challenge_t *challengeptr;
if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) { if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) {
Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" ); Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" );
@ -182,44 +185,46 @@ void SV_AuthorizeIpPacket( netadr_t from ) {
Com_Printf( "SV_AuthorizeIpPacket: challenge not found\n" ); Com_Printf( "SV_AuthorizeIpPacket: challenge not found\n" );
return; return;
} }
challengeptr = &svs.challenges[i];
// send a packet back to the original client // send a packet back to the original client
svs.challenges[i].pingTime = svs.time; challengeptr->pingTime = svs.time;
s = Cmd_Argv( 2 ); s = Cmd_Argv( 2 );
r = Cmd_Argv( 3 ); // reason r = Cmd_Argv( 3 ); // reason
if ( !Q_stricmp( s, "demo" ) ) { if ( !Q_stricmp( s, "demo" ) ) {
// they are a demo client trying to connect to a real server // they are a demo client trying to connect to a real server
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nServer is not a demo server\n" ); NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nServer is not a demo server\n" );
// clear the challenge record so it won't timeout and let them through // clear the challenge record so it won't timeout and let them through
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); Com_Memset( challengeptr, 0, sizeof( *challengeptr ) );
return; return;
} }
if ( !Q_stricmp( s, "accept" ) ) { if ( !Q_stricmp( s, "accept" ) ) {
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, NET_OutOfBandPrint(NS_SERVER, challengeptr->adr,
"challengeResponse %i", svs.challenges[i].challenge ); "challengeResponse %d %d", challengeptr->challenge, challengeptr->clientChallenge);
return; return;
} }
if ( !Q_stricmp( s, "unknown" ) ) { if ( !Q_stricmp( s, "unknown" ) ) {
if (!r) { if (!r) {
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nAwaiting CD key authorization\n" ); NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nAwaiting CD key authorization\n" );
} else { } else {
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\n%s\n", r); NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r);
} }
// clear the challenge record so it won't timeout and let them through // clear the challenge record so it won't timeout and let them through
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); Com_Memset( challengeptr, 0, sizeof( *challengeptr ) );
return; return;
} }
// authorization failed // authorization failed
if (!r) { if (!r) {
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nSomeone is using this CD Key\n" ); NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nSomeone is using this CD Key\n" );
} else { } else {
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\n%s\n", r ); NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r );
} }
// clear the challenge record so it won't timeout and let them through // clear the challenge record so it won't timeout and let them through
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) ); Com_Memset( challengeptr, 0, sizeof(*challengeptr) );
} }
#endif #endif
@ -535,9 +540,11 @@ void SV_DropClient( client_t *drop, const char *reason ) {
// see if we already have a challenge for this ip // see if we already have a challenge for this ip
challenge = &svs.challenges[0]; challenge = &svs.challenges[0];
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++)
if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) { {
challenge->connected = qfalse; if(NET_CompareAdr(drop->netchan.remoteAddress, challenge->adr))
{
Com_Memset(challenge, 0, sizeof(*challenge));
break; break;
} }
} }

View File

@ -548,7 +548,7 @@ static void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
} else if (!Q_stricmp(c, "getinfo")) { } else if (!Q_stricmp(c, "getinfo")) {
SVC_Info( from ); SVC_Info( from );
} else if (!Q_stricmp(c, "getchallenge")) { } else if (!Q_stricmp(c, "getchallenge")) {
SV_GetChallenge( from ); SV_GetChallenge(from);
} else if (!Q_stricmp(c, "connect")) { } else if (!Q_stricmp(c, "connect")) {
SV_DirectConnect( from ); SV_DirectConnect( from );
#ifndef STANDALONE #ifndef STANDALONE