hblurgle spburgle this code sucks

This commit is contained in:
fickleheart 2019-03-05 22:27:25 -06:00
parent 515fb9adad
commit 264454ca70
4 changed files with 225 additions and 4 deletions

View file

@ -1126,13 +1126,55 @@ typedef enum
CL_DOWNLOADSAVEGAME,
#endif
CL_CONNECTED,
CL_ABORTED
CL_ABORTED,
CL_CHALLENGE
} cl_mode_t;
static void GetPackets(void);
static cl_mode_t cl_mode = CL_SEARCHING;
static UINT8 cl_challengenum = 0;
static UINT8 cl_challengequestion[17];
static char cl_challengepassword[65];
static UINT8 cl_challengeanswer[17];
static void D_JoinChallengeInput(INT32 ch)
{
size_t len;
while (ch)
{
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
|| ch == ' ') // Allow spaces, of course
{
len = strlen(cl_challengepassword);
if (len < 64)
{
cl_challengepassword[len+1] = 0;
cl_challengepassword[len] = ch;
}
}
else if (ch == KEY_BACKSPACE)
{
len = strlen(cl_challengepassword);
if (len > 0)
cl_challengepassword[len-1] = 0;
}
else if (ch == KEY_ENTER)
{
// Done?
D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
cl_mode = CL_ASKJOIN;
return;
}
ch = I_GetKey();
}
}
// Player name send/load
static void CV_SavePlayerNames(UINT8 **p)
@ -1191,11 +1233,25 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total.
const char *cltext;
for (i = 0; i < 16; ++i)
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
if (cl_mode != CL_CHALLENGE)
for (i = 0; i < 16; ++i)
V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
switch (cl_mode)
{
case CL_CHALLENGE:
{
char asterisks[65];
size_t sl = strlen(cl_challengepassword);
memset(asterisks, '*', sl);
memset(asterisks+sl, 0, 65-sl);
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_MONOSPACE|V_ALLOWLOWERCASE, asterisks);
cltext = M_GetText("Please enter the server password.");
}
break;
#ifdef JOININGAME
case CL_DOWNLOADSAVEGAME:
if (lastfilenum != -1)
@ -1292,6 +1348,8 @@ static boolean CL_SendJoin(void)
netbuffer->u.clientcfg.localplayers = localplayers;
netbuffer->u.clientcfg.version = VERSION;
netbuffer->u.clientcfg.subversion = SUBVERSION;
netbuffer->u.clientcfg.challengenum = cl_challengenum;
memcpy(netbuffer->u.clientcfg.challengeanswer, cl_challengeanswer, 16);
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
}
@ -2059,6 +2117,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
break;
#endif
case CL_CHALLENGE:
case CL_WAITJOINRESPONSE:
case CL_CONNECTED:
default:
@ -2091,6 +2150,8 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
D_StartTitle();
return false;
}
else if (cl_mode == CL_CHALLENGE)
D_JoinChallengeInput(key);
// why are these here? this is for servers, we're a client
//if (key == 's' && server)
@ -3142,6 +3203,9 @@ void D_ClientServerInit(void)
gametic = 0;
localgametic = 0;
memset(cl_challengequestion, 0x00, 17);
memset(cl_challengeanswer, 0x00, 17);
// do not send anything before the real begin
SV_StopServer();
SV_ResetServer();
@ -3631,6 +3695,26 @@ static void HandleConnect(SINT8 node)
boolean newnode = false;
#endif
if (D_IsJoinPasswordOn())
{
// Ensure node sent the correct password challenge
boolean passed = false;
if (netbuffer->u.clientcfg.challengenum && D_VerifyJoinPasswordChallenge(netbuffer->u.clientcfg.challengenum, netbuffer->u.clientcfg.challengeanswer))
passed = true;
if (!passed)
{
D_MakeJoinPasswordChallenge(&netbuffer->u.joinchallenge.challengenum, netbuffer->u.joinchallenge.question);
netbuffer->packettype = PT_JOINCHALLENGE;
HSendPacket(node, true, 0, sizeof(joinchallenge_pak));
//Net_CloseConnection(node);
return;
}
}
// client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node])
@ -3794,6 +3878,22 @@ static void HandlePacketFromAwayNode(SINT8 node)
Net_CloseConnection(node);
break;
case PT_JOINCHALLENGE:
if (server && serverrunning)
{ // But wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
SERVERONLY
if (cl_mode == CL_WAITJOINRESPONSE)
{
cl_challengenum = netbuffer->u.joinchallenge.challengenum;
memcpy(cl_challengequestion, netbuffer->u.joinchallenge.question, 16);
cl_mode = CL_CHALLENGE;
}
break;
case PT_SERVERREFUSE: // Negative response of client join request
if (server && serverrunning)
{ // But wait I thought I'm the server?

View file

@ -73,6 +73,8 @@ typedef enum
PT_CLIENT4MIS,
PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
PT_JOINCHALLENGE, // You must give a password to joinnnnn
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots.
@ -354,8 +356,16 @@ typedef struct
UINT8 subversion; // Contains build version
UINT8 localplayers;
UINT8 mode;
UINT8 challengenum; // Non-zero if trying to join with a password attempt
UINT8 challengeanswer[16]; // Join challenge
} ATTRPACK clientconfig_pak;
typedef struct
{
UINT8 challengenum; // Number to send back in join attempt
UINT8 question[16]; // Challenge data to be manipulated and answered with
} ATTRPACK joinchallenge_pak;
#define MAXSERVERNAME 32
#define MAXFILENEEDED 915
// This packet is too large
@ -447,7 +457,8 @@ typedef struct
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
clientconfig_pak clientcfg; // 153 bytes
joinchallenge_pak joinchallenge; // 17 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes

View file

@ -169,6 +169,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum);
static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void);
static void Command_RemoveAdmin_f(void);
static void Command_ChangeJoinPassword_f(void);
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
@ -534,6 +535,7 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd);
// Remote Administration
COM_AddCommand("joinpassword", Command_ChangeJoinPassword_f);
COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
@ -3427,6 +3429,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
if (len > 256-sl)
len = 256-sl;
memcpy(tmpbuf, buffer, len);
memmove(&tmpbuf[len], salt, sl);
//strcpy(&tmpbuf[len], salt);
@ -3702,6 +3705,107 @@ static void Got_Removal(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
}
// Join password stuff
#define NUMJOINCHALLENGES 32
static UINT8 joinpassmd5[17];
static boolean joinpasswordset = false;
static UINT8 joinpasschallenges[NUMJOINCHALLENGES][17];
static boolean joinpasschallengeson[NUMJOINCHALLENGES];
boolean D_IsJoinPasswordOn(void)
{
return joinpasswordset;
}
static inline void GetChallengeAnswer(UINT8 *question, UINT8 *passwordmd5, UINT8 *answer)
{
D_MD5PasswordPass(question, 16, (char *) passwordmd5, answer);
}
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer)
{
static UINT8 passwordmd5[17];
memset(passwordmd5, 0x00, 17);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &passwordmd5);
GetChallengeAnswer(question, passwordmd5, answer);
}
void D_SetJoinPassword(const char *pw)
{
memset(joinpassmd5, 0x00, 17);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &joinpassmd5);
joinpasswordset = true;
}
boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer)
{
boolean passed = false;
num %= NUMJOINCHALLENGES;
//@TODO use a constant-time memcmp....
if (joinpasschallengeson[num] && memcmp(answer, joinpasschallenges[num], 16) == 0)
passed = true;
// Wipe and reset the challenge so that it can't be tried against again, as a small measure against brute-force attacks.
memset(joinpasschallenges[num], 0x00, 17);
joinpasschallengeson[num] = false;
return passed;
}
void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question)
{
size_t i;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
(*num) = M_RandomKey(NUMJOINCHALLENGES);
if (!joinpasschallengeson[(*num)])
break;
}
// If the above loop never breaks, then uh.... we're obliterating one random stored challenge. Sorry (:
joinpasschallengeson[(*num)] = true;
memset(question, 0x00, 17);
for (i = 0; i < 16; i++)
question[i] = M_RandomByte();
// Store the answer in memory. What was the question again?
GetChallengeAnswer(question, joinpassmd5, joinpasschallenges[(*num)]);
// This ensures that num is always non-zero and will be valid when used for the answer
if ((*num) == 0)
(*num) = NUMJOINCHALLENGES;
}
// Remote Administration
static void Command_ChangeJoinPassword_f(void)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
if (client) // cannot change remotely
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("joinpassword <password>: set a password to join the server\n"));
return;
}
D_SetJoinPassword(COM_Argv(1));
CONS_Printf(M_GetText("Join password set.\n"));
#endif
}
static void Command_MotD_f(void)
{
size_t i, j;

View file

@ -248,6 +248,12 @@ void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
boolean D_IsJoinPasswordOn(void);
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer);
void D_SetJoinPassword(const char *pw);
boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer);
void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question);
// used for the player setup menu
UINT8 CanChangeSkin(INT32 playernum);