diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1ac529422..1bbf83cc5 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -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; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 26b233d53..6335dbfdb 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -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; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a2ec45c42..4330bedff 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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 } diff --git a/src/i_system.h b/src/i_system.h index aaf37fb9a..48fef0f74 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -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. */ diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index eef9d3f5d..14b3148c0 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -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