diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index d34dbc4e0..3bb168de2 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -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 @@ -859,22 +862,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; } @@ -882,18 +928,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 } @@ -1201,7 +1250,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; @@ -1215,6 +1265,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; @@ -1893,6 +1944,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; diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 342173dff..260a594cb 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -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; diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index 630999cd0..fe6b6c55e 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -2954,14 +2954,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; } @@ -3029,18 +3032,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/netcode/protocol.h b/src/netcode/protocol.h index 4b39fab66..23e024b47 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -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;