Merge branch 'login-challenge-auth' into 'next'

Use salted challenge-auth for login requests

See merge request STJr/SRB2!2428
This commit is contained in:
Hanicef 2025-03-21 05:46:00 +00:00
commit b4bfd59ae4
4 changed files with 75 additions and 18 deletions

View file

@ -90,8 +90,11 @@ INT16 consistancy[BACKUPTICS];
// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
boolean hu_stopped = false;
UINT8 (*adminpassmd5)[16];
char *reqpass;
char **adminpass;
UINT32 adminpasscount = 0;
char adminsalt[MAXPLAYERS][9];
I_StaticAssert(sizeof(netbuffer->u.salt) == sizeof(adminsalt[0]));
tic_t neededtic;
SINT8 servernode = 0; // the number of the server node
@ -862,22 +865,65 @@ static void PT_ServerShutdown(SINT8 node)
M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
}
static void PT_Login(SINT8 node, INT32 netconsole)
static void PT_Login(SINT8 node)
{
(void)node;
if (client)
return;
#ifndef NOMD5
// I_GetRandomBytes should get it's data from a CSPRNG, so it's safe to use it here.
I_GetRandomBytes(adminsalt[node], sizeof(adminsalt[node]));
adminsalt[node][8] = '\0'; // convenience
netbuffer->packettype = PT_LOGINCHALLENGE;
memcpy(netbuffer->u.salt, adminsalt[node], sizeof(adminsalt[node]));
HSendPacket(node, true, 0, sizeof(adminsalt[node]));
#else
(void)node;
#endif
}
static void PT_LoginChallenge(SINT8 node)
{
#ifndef NOMD5
if (node != servernode)
return;
if ((size_t)doomcom->datalength < sizeof(netbuffer->u.salt))/* ignore partial sends */
return;
if (reqpass == NULL)
return; // got PT_LOGINCHALLENGE but we didn't request a login
D_MD5PasswordPass((const UINT8 *)reqpass, strlen(reqpass), netbuffer->u.salt, &netbuffer->u.md5sum);
Z_Free(reqpass);
reqpass = NULL;
netbuffer->packettype = PT_LOGINAUTH;
HSendPacket(servernode, true, 0, 16);
#else
(void)node;
#endif
}
static void PT_LoginAuth(SINT8 node, INT32 netconsole)
{
#ifndef NOMD5
UINT8 finalmd5[16];/* Well, it's the cool thing to do? */
UINT32 i;
if (client)
return;
if (doomcom->datalength < 16)/* ignore partial sends */
return;
if (adminsalt[node][0] == 0)
{
CONS_Printf(M_GetText("Password from %s failed (no login request).\n"), player_names[netconsole]);
return;
}
if (adminpasscount == 0)
{
adminsalt[node][0] = 0;
CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]);
return;
}
@ -885,18 +931,21 @@ static void PT_Login(SINT8 node, INT32 netconsole)
for (i = 0; i < adminpasscount; i++)
{
// Do the final pass to compare with the sent md5
D_MD5PasswordPass(adminpassmd5[i], 16, va("PNUM%02d", netconsole), &finalmd5);
D_MD5PasswordPass((const UINT8 *)adminpass[i], strlen(adminpass[i]), adminsalt[node], finalmd5);
if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
{
adminsalt[node][0] = 0;
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
return;
}
}
adminsalt[node][0] = 0;
CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
#else
(void)node;
(void)netconsole;
#endif
}
@ -1204,7 +1253,8 @@ static void HandlePacketFromPlayer(SINT8 node)
case PT_BASICKEEPALIVE : PT_BasicKeepAlive (node, netconsole); break;
case PT_TEXTCMD : PT_TextCmd (node, netconsole); break;
case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break;
case PT_LOGIN : PT_Login (node, netconsole); break;
case PT_LOGIN : PT_Login (node ); break;
case PT_LOGINAUTH : PT_LoginAuth (node, netconsole); break;
case PT_CLIENTQUIT : PT_ClientQuit (node, netconsole); break;
case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node ); break;
case PT_ASKLUAFILE : PT_AskLuaFile (node ); break;
@ -1218,6 +1268,7 @@ static void HandlePacketFromPlayer(SINT8 node)
case PT_FILEFRAGMENT : PT_FileFragment (node, netconsole); break;
case PT_FILEACK : PT_FileAck (node ); break;
case PT_FILERECEIVED : PT_FileReceived (node ); break;
case PT_LOGINCHALLENGE : PT_LoginChallenge (node ); break;
case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break;
case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break;
case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break;
@ -1896,6 +1947,7 @@ tic_t GetLag(INT32 node)
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
{
// FIXME: replace with SHA-256, since MD5 is busted.
#ifdef NOMD5
(void)buffer;
(void)len;

View file

@ -129,7 +129,8 @@ tic_t GetLag(INT32 node);
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
extern UINT8 (*adminpassmd5)[16];
extern char *reqpass;
extern char **adminpass;
extern UINT32 adminpasscount;
extern boolean hu_stopped;

View file

@ -2952,14 +2952,17 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
void D_SetPassword(const char *pw)
{
adminpassmd5 = Z_Realloc(adminpassmd5, sizeof(*adminpassmd5) * ++adminpasscount, PU_STATIC, NULL);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5[adminpasscount-1]);
adminpass = Z_Realloc(adminpass, sizeof(*adminpass) * ++adminpasscount, PU_STATIC, NULL);
adminpass[adminpasscount-1] = Z_StrDup(pw);
}
void D_ClearPassword(void)
{
Z_Free(adminpassmd5);
adminpassmd5 = NULL;
UINT32 i;
for (i = 0; i < adminpasscount; i++)
Z_Free(adminpass[i]);
Z_Free(adminpass);
adminpass = NULL;
adminpasscount = 0;
}
@ -3027,18 +3030,16 @@ static void Command_Login_f(void)
return;
}
if (reqpass)
Z_Free(reqpass);
pw = COM_Argv(1);
// Do the base pass to get what the server has (or should?)
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &netbuffer->u.md5sum);
// Do the final pass to get the comparison the server will come up with
D_MD5PasswordPass(netbuffer->u.md5sum, 16, va("PNUM%02d", consoleplayer), &netbuffer->u.md5sum);
reqpass = Z_StrDup(pw);
CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n"));
netbuffer->packettype = PT_LOGIN;
HSendPacket(servernode, true, 0, 16);
HSendPacket(servernode, true, 0, 0);
#endif
}

View file

@ -89,6 +89,8 @@ typedef enum
PT_CLIENTJOIN, // Client wants to join; used in start game.
PT_LOGIN, // Login attempt from the client.
PT_LOGINCHALLENGE,// Challenge request sent to the client.
PT_LOGINAUTH, // Challenge response from the client.
PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?"
PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
@ -306,6 +308,7 @@ typedef struct
fileack_pak fileack;
UINT8 filereceived;
clientconfig_pak clientcfg;
char salt[9];
UINT8 md5sum[16];
serverinfo_pak serverinfo;
serverrefuse_pak serverrefuse;