Multipassword and salts

SRB2Classic ports by Hanicef
This commit is contained in:
NepDisk 2025-08-23 20:42:10 -04:00
parent 6131cfd99d
commit bc82e2564c
5 changed files with 156 additions and 45 deletions

View file

@ -143,8 +143,11 @@ UINT8 hu_redownloadinggamestate = 0;
// kart, 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]));
// Client specific
static ticcmd_t localcmds[MAXSPLITSCREENPLAYERS][MAXGENTLEMENDELAY];
@ -4276,6 +4279,97 @@ static void HandleShutdown(SINT8 node)
M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
}
static void PT_Login(SINT8 node)
{
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
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)
{
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
#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)
{
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
#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;
}
for (i = 0; i < adminpasscount; i++)
{
// Do the final pass to compare with the sent md5
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
}
/** Called when a PT_NODETIMEOUT packet is received
*
* \param node The packet sender (should be the server)
@ -4629,10 +4723,6 @@ static void HandlePacketFromPlayer(SINT8 node)
INT32 netconsole;
tic_t realend, realstart;
UINT8 *pak, *txtpak, numtxtpak;
UINT32 i;
#ifndef NOMD5
UINT8 finalmd5[16];/* Well, it's the cool thing to do? */
#endif
txtpak = NULL;
@ -4868,34 +4958,10 @@ static void HandlePacketFromPlayer(SINT8 node)
}
break;
case PT_LOGIN:
if (client)
break;
#ifndef NOMD5
if (doomcom->datalength < 16)/* ignore partial sends */
break;
if (adminpasscount == 0)
{
CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]);
break;
}
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);
if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
continue;
}
}
CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
#endif
PT_Login(node);
break;
case PT_LOGINAUTH:
PT_LoginAuth(node, netconsole);
break;
case PT_NODETIMEOUT:
case PT_CLIENTQUIT:
@ -5071,6 +5137,9 @@ static void HandlePacketFromPlayer(SINT8 node)
if (server)
PT_FileReceived();
break;
case PT_LOGINCHALLENGE:
PT_LoginChallenge(node);
break;
case PT_WILLRESENDGAMESTATE:
PT_WillResendGamestate();
break;
@ -6236,6 +6305,7 @@ rewind_t *CL_RewindToTime(tic_t time)
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

@ -117,6 +117,8 @@ typedef enum
PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
PT_LOGIN, // Login attempt from the client.
PT_LOGINCHALLENGE,// Challenge request sent to the client.
PT_LOGINAUTH, // Challenge response from the client.
PT_PING, // Packet sent to tell clients the other client's latency to server.
NUMPACKETTYPE
@ -374,6 +376,7 @@ struct doomdata_t
char fileack[sizeof (fileack_pak)];
UINT8 filereceived;
clientconfig_pak clientcfg; // 136 bytes
char salt[9];
UINT8 md5sum[16];
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
@ -588,7 +591,8 @@ void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *
extern UINT8 hu_redownloadinggamestate;
extern UINT8 (*adminpassmd5)[16];
extern char *reqpass;
extern char **adminpass;
extern UINT32 adminpasscount;
extern boolean hu_stopped;

View file

@ -4067,14 +4067,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;
}
@ -4144,18 +4147,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

@ -52,6 +52,10 @@ UINT32 I_GetFreeMem(UINT32 *total);
*/
precise_t I_GetPreciseTime(void);
/** \brief Fills a buffer with random data, returns amount of data obtained.
*/
size_t I_GetRandomBytes(char *destination, size_t count);
/** \brief Get the precision of precise_t in units per second. Invocations of
this function for the program's duration MUST return the same value.
*/

View file

@ -2317,6 +2317,38 @@ char *I_GetEnv(const char *name)
#endif
}
size_t I_GetRandomBytes(char *destination, size_t count)
{
#if defined (__unix__) || defined (UNIXCOMMON) || defined(__APPLE__)
FILE *rndsource;
size_t actual_bytes = 0;
if (!(rndsource = fopen("/dev/urandom", "r")))
if (!(rndsource = fopen("/dev/random", "r")))
actual_bytes = 0;
if (rndsource)
{
actual_bytes = fread(destination, 1, count, rndsource);
fclose(rndsource);
}
if (actual_bytes == 0)
I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
return actual_bytes;
#elif defined (_WIN32)
if (RtlGenRandom(destination, count))
return count;
I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
return 0;
#else
#warning SDL I_GetRandomBytes is not implemented on this platform.
return 0;
#endif
}
INT32 I_PutEnv(char *variable)
{
#ifdef NEED_SDL_GETENV