Merge branch 'join-passwords' into 'next'

join passwords

See merge request KartKrew/Kart-Public!104
This commit is contained in:
colette 2019-03-10 19:12:59 -04:00
commit f4318aed58
15 changed files with 537 additions and 85 deletions

View file

@ -1254,7 +1254,8 @@ found:
var->string = var->zstring = Z_StrDup(valstr);
if (override)
if (var->flags & CV_PASSWORD); // Don't change value for password field
else if (override)
var->value = overrideval;
else if (var->flags & CV_FLOAT)
{

View file

@ -95,7 +95,8 @@ typedef enum
CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console
// can only be set when we have the pointer to it
// used on menus
CV_CHEAT = 2048 // Don't let this be used in multiplayer unless cheats are on.
CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on.
CV_PASSWORD = 4096 // Password field
} cvflags_t;
typedef struct CV_PossibleValue_s

View file

@ -544,6 +544,22 @@ static void CON_MoveConsole(void)
}
}
INT32 CON_ShiftChar(INT32 ch)
{
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
{
if (shiftdown ^ capslock)
ch = shiftxform[ch];
}
else // if we're holding shift we should still shift non letter symbols
{
if (shiftdown)
ch = shiftxform[ch];
}
return ch;
}
// Clear time of console heads up messages
//
void CON_ClearHUD(void)
@ -1084,16 +1100,6 @@ boolean CON_Responder(event_t *ev)
else if (key == KEY_KPADSLASH)
key = '/';
// capslock
if (key == KEY_CAPSLOCK) // it's a toggle.
{
if (capslock)
capslock = false;
else
capslock = true;
return true;
}
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z'))
{

View file

@ -44,6 +44,8 @@ extern UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *or
// Console bg color (auto updated to match)
extern UINT8 *consolebgmap;
INT32 CON_ShiftChar(INT32 ch);
void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages

View file

@ -22,6 +22,7 @@
#include "i_video.h"
#include "d_net.h"
#include "d_main.h"
#include "d_event.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
@ -1126,12 +1127,22 @@ typedef enum
CL_DOWNLOADSAVEGAME,
#endif
CL_CONNECTED,
CL_ABORTED
CL_ABORTED,
CL_ASKDOWNLOADFILES,
CL_WAITDOWNLOADFILESRESPONSE,
CL_CHALLENGE
} cl_mode_t;
static void GetPackets(void);
static cl_mode_t cl_mode = CL_SEARCHING;
static boolean cl_needsdownload = false;
static UINT8 cl_challengenum = 0;
static UINT8 cl_challengequestion[MD5_LEN+1];
static char cl_challengepassword[65];
static UINT8 cl_challengeanswer[MD5_LEN+1];
static UINT8 cl_challengeattempted = 0;
// Player name send/load
@ -1168,6 +1179,8 @@ static void CV_LoadPlayerNames(UINT8 **p)
}
#ifdef CLIENT_LOADINGSCREEN
static UINT32 SL_SearchServer(INT32 node);
//
// CL_DrawConnectionStatus
//
@ -1191,11 +1204,42 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total.
const char *cltext;
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[33];
size_t sl = min(32, strlen(cl_challengepassword));
UINT32 i;
memset(asterisks, '*', sl);
memset(asterisks+sl, 0, 33-sl);
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_MONOSPACE|V_ALLOWLOWERCASE, asterisks);
V_DrawFixedPatch((BASEVIDWIDTH/2) << FRACBITS, (BASEVIDHEIGHT/2) << FRACBITS, FRACUNIT, 0, W_CachePatchName("BSRVLOCK", PU_CACHE), NULL);
i = SL_SearchServer(servernode);
if (i == -1)
{
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 1);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server is password protected."));
}
else
{
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 3);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server,"));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+8, V_ALLOWLOWERCASE, serverlist[i].info.servername);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+16, V_REDMAP, M_GetText("is password protected."));
}
cltext = M_GetText(cl_challengeattempted ? "Incorrect password. Please try again." : "Please enter the server password.");
}
break;
#ifdef JOININGAME
case CL_DOWNLOADSAVEGAME:
if (lastfilenum != -1)
@ -1215,6 +1259,9 @@ static inline void CL_DrawConnectionStatus(void)
case CL_WAITJOINRESPONSE:
cltext = M_GetText("Requesting to join...");
break;
case CL_ASKDOWNLOADFILES:
case CL_WAITDOWNLOADFILESRESPONSE:
cltext = M_GetText("Waiting to download files...");
default:
cltext = M_GetText("Connecting to server...");
break;
@ -1292,6 +1339,9 @@ static boolean CL_SendJoin(void)
netbuffer->u.clientcfg.localplayers = localplayers;
netbuffer->u.clientcfg.version = VERSION;
netbuffer->u.clientcfg.subversion = SUBVERSION;
netbuffer->u.clientcfg.needsdownload = cl_needsdownload;
netbuffer->u.clientcfg.challengenum = cl_challengenum;
memcpy(netbuffer->u.clientcfg.challengeanswer, cl_challengeanswer, MD5_LEN);
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
}
@ -1312,7 +1362,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.gametype = (UINT8)(G_BattleGametype() ? VANILLA_GT_MATCH : VANILLA_GT_RACE); // SRB2Kart: Vanilla's gametype constants for MS support
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
netbuffer->u.serverinfo.kartvars = (UINT8) (
(cv_kartspeed.value & SV_SPEEDMASK) |
(dedicated ? SV_DEDICATED : 0) |
(D_IsJoinPasswordOn() ? SV_PASSWORD : 0)
);
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
MAXSERVERNAME);
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
@ -1872,7 +1928,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
/** Called by CL_ServerConnectionTicker
*
* \param viams ???
* \param asksent ???
* \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted
* \sa CL_ServerConnectionTicker
* \sa CL_ConnectToServer
@ -1965,9 +2021,12 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
), NULL, MM_NOTHING);
return false;
}
cl_mode = CL_ASKDOWNLOADFILES;
// no problem if can't send packet, we will retry later
if (CL_SendRequestFile())
cl_mode = CL_DOWNLOADFILES;
//if (CL_SendRequestFile())
// cl_mode = CL_DOWNLOADFILES;
}
}
else
@ -1997,7 +2056,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
* \param viams ???
* \param tmpsave The name of the gamestate file???
* \param oldtic Used for knowing when to poll events and redraw
* \param asksent ???
* \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted
* \sa CL_ServerConnectionSearchTicker
* \sa CL_ConnectToServer
@ -2035,6 +2094,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
/* FALLTHRU */
case CL_ASKJOIN:
cl_needsdownload = false;
CL_LoadServerFiles();
#ifdef JOININGAME
// prepare structures to save the file
@ -2043,9 +2103,23 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
CL_PrepareDownloadSaveGame(tmpsave);
#endif
if (CL_SendJoin())
{
*asksent = I_GetTime();
cl_mode = CL_WAITJOINRESPONSE;
}
break;
case CL_ASKDOWNLOADFILES:
cl_needsdownload = true;
if (CL_SendJoin())
{
*asksent = I_GetTime();
cl_mode = CL_WAITDOWNLOADFILESRESPONSE;
}
break;
#ifdef JOININGAME
case CL_DOWNLOADSAVEGAME:
// At this state, the first (and only) needed file is the gamestate
@ -2059,7 +2133,19 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
break;
#endif
case CL_CHALLENGE:
(*asksent) = I_GetTime() - NEWTICRATE; // Send password immediately upon entering
break;
case CL_WAITJOINRESPONSE:
case CL_WAITDOWNLOADFILESRESPONSE:
if (*asksent + NEWTICRATE < I_GetTime() && CL_SendJoin())
{
*asksent = I_GetTime();
}
break;
case CL_CONNECTED:
default:
break;
@ -2077,20 +2163,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
// Call it only once by tic
if (*oldtic != I_GetTime())
{
INT32 key;
I_OsPolling();
key = I_GetKey();
// Only ESC and non-keyboard keys abort connection
if (key == KEY_ESCAPE || key >= KEY_MOUSE1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
D_ProcessEvents();
if (gamestate != GS_WAITINGPLAYERS)
return false;
}
// why are these here? this is for servers, we're a client
//if (key == 's' && server)
@ -2119,6 +2195,71 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
return true;
}
boolean CL_Responder(event_t *ev)
{
size_t len;
INT32 ch;
if (!(client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED))
return false; // Don't do anything outside of the connection screen
if (ev->type != ev_keydown)
return false;
ch = (INT32)ev->data1;
// Only ESC and non-keyboard keys abort connection
if (ch == KEY_ESCAPE || ch >= KEY_MOUSE1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
//M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
return true;
}
if (cl_mode != CL_CHALLENGE)
return false;
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] = CON_ShiftChar(ch);
}
cl_challengeattempted = 0;
}
else if (ch == KEY_BACKSPACE)
{
len = strlen(cl_challengepassword);
if (len > 0)
cl_challengepassword[len-1] = 0;
cl_challengeattempted = 0;
}
else if (ch == KEY_ENTER)
{
netgame = true;
multiplayer = true;
#ifndef NONET
SL_ClearServerList(servernode);
#endif
cl_mode = CL_SEARCHING;
D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
cl_challengeattempted = 1;
}
return true;
}
/** Use adaptive send using net_bandwidth and stat.sendbytes
*
* \param viams ???
@ -2139,6 +2280,7 @@ static void CL_ConnectToServer(boolean viams)
#endif
cl_mode = CL_SEARCHING;
cl_challengenum = 0;
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = -1;
@ -2196,6 +2338,8 @@ static void CL_ConnectToServer(boolean viams)
SL_ClearServerList(servernode);
#endif
cl_challengeattempted = 0;
do
{
// If the connection was aborted for some reason, leave
@ -3142,6 +3286,9 @@ void D_ClientServerInit(void)
gametic = 0;
localgametic = 0;
memset(cl_challengequestion, 0x00, MD5_LEN+1);
memset(cl_challengeanswer, 0x00, MD5_LEN+1);
// do not send anything before the real begin
SV_StopServer();
SV_ResetServer();
@ -3631,6 +3778,33 @@ static void HandleConnect(SINT8 node)
boolean newnode = false;
#endif
if (node != servernode && !nodeingame[node] && 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;
}
}
if (netbuffer->u.clientcfg.needsdownload)
{
netbuffer->packettype = PT_DOWNLOADFILESOKAY;
HSendPacket(node, true, 0, 0);
return;
}
// client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node])
@ -3639,6 +3813,7 @@ static void HandleConnect(SINT8 node)
#ifndef NONET
newnode = true;
#endif
SV_AddNode(node);
/// \note Wait what???
@ -3794,6 +3969,43 @@ 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_mode == CL_WAITDOWNLOADFILESRESPONSE)
{
cl_challengenum = netbuffer->u.joinchallenge.challengenum;
memcpy(cl_challengequestion, netbuffer->u.joinchallenge.question, 16);
Net_CloseConnection(node|FORCECLOSE); // Don't need to stay connected while challenging
cl_mode = CL_CHALLENGE;
switch (cl_challengeattempted)
{
case 2:
// We already sent a correct password, so throw it back up again.
D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
cl_mode = CL_ASKJOIN;
break;
case 1:
// We entered the wrong password!
S_StartSound(NULL, sfx_s26d);
break;
default:
// First entry to the password screen.
S_StartSound(NULL, sfx_s224);
break;
}
}
break;
case PT_SERVERREFUSE: // Negative response of client join request
if (server && serverrunning)
{ // But wait I thought I'm the server?
@ -3822,6 +4034,41 @@ static void HandlePacketFromAwayNode(SINT8 node)
}
break;
case PT_DOWNLOADFILESOKAY:
if (server && serverrunning)
{ // But wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
SERVERONLY
// This should've already been checked, but just to be safe...
if (!CL_CheckDownloadable())
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"You cannot connect to this server\n"
"because you cannot download the files\n"
"that you are missing from the server.\n\n"
"See the console or log file for\n"
"more details.\n\n"
"Press ESC\n"
), NULL, MM_NOTHING);
break;
}
if (cl_challengeattempted == 1) // Successful password noise.
S_StartSound(NULL, sfx_s221);
cl_challengeattempted = 2;
CONS_Printf("trying to download\n");
if (CL_SendRequestFile())
cl_mode = CL_DOWNLOADFILES;
break;
case PT_SERVERCFG: // Positive response of client join request
{
INT32 j;
@ -3837,6 +4084,9 @@ static void HandlePacketFromAwayNode(SINT8 node)
if (cl_mode != CL_WAITJOINRESPONSE)
break;
if (cl_challengeattempted == 1) // Successful password noise.
S_StartSound(NULL, sfx_s221);
if (client)
{
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);

View file

@ -13,11 +13,14 @@
#ifndef __D_CLISRV__
#define __D_CLISRV__
#include "d_event.h"
#include "d_ticcmd.h"
#include "d_netcmd.h"
#include "tables.h"
#include "d_player.h"
#include "md5.h"
// Network play related stuff.
// There is a data struct that stores network
// communication related stuff, and another
@ -73,6 +76,9 @@ 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_DOWNLOADFILESOKAY, // You can download files from the server....
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.
@ -353,9 +359,21 @@ typedef struct
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
UINT8 localplayers;
UINT8 mode;
UINT8 needsdownload;
UINT8 challengenum; // Non-zero if trying to join with a password attempt
UINT8 challengeanswer[MD5_LEN]; // Join challenge
} ATTRPACK clientconfig_pak;
typedef struct
{
UINT8 challengenum; // Number to send back in join attempt
UINT8 question[MD5_LEN]; // Challenge data to be manipulated and answered with
} ATTRPACK joinchallenge_pak;
#define SV_SPEEDMASK 0x03
#define SV_DEDICATED 0x40
#define SV_PASSWORD 0x80
#define MAXSERVERNAME 32
#define MAXFILENEEDED 915
// This packet is too large
@ -368,7 +386,7 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
UINT8 cheatsenabled;
UINT8 isdedicated;
UINT8 kartvars; // Previously isdedicated, now appropriated for our own nefarious purposes
UINT8 fileneedednum;
SINT8 adminplayer;
tic_t time;
@ -447,7 +465,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
@ -555,6 +574,7 @@ void CL_RemoveSplitscreenPlayer(UINT8 p);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
boolean CL_Responder(event_t *ev);
// Is there a game running
boolean Playing(void);

View file

@ -203,6 +203,8 @@ static inline void D_ModifierKeyResponder(event_t *ev)
case KEY_RCTRL: ctrldown |= 0x2; return;
case KEY_LALT: altdown |= 0x1; return;
case KEY_RALT: altdown |= 0x2; return;
case KEY_CAPSLOCK: capslock = !capslock; return;
default: return;
}
else if (ev->type == ev_keyup) switch (ev->data1)
@ -236,6 +238,9 @@ void D_ProcessEvents(void)
if (M_ScreenshotResponder(ev))
continue; // ate the event
if (CL_Responder(ev))
continue;
if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
{
if (cht_Responder(ev))

View file

@ -821,10 +821,6 @@ static const char *packettypename[NUMPACKETTYPE] =
"CLIENTMIS",
"CLIENT2CMD",
"CLIENT2MIS",
"CLIENT3CMD",
"CLIENT3MIS",
"CLIENT4CMD",
"CLIENT4MIS",
"NODEKEEPALIVE",
"NODEKEEPALIVEMIS",
"SERVERTICS",
@ -841,6 +837,15 @@ static const char *packettypename[NUMPACKETTYPE] =
"RESYNCHEND",
"RESYNCHGET",
"CLIENT3CMD",
"CLIENT3MIS",
"CLIENT4CMD",
"CLIENT4MIS",
"BASICKEEPALIVE",
"JOINCHALLENGE",
"DOWNLOADFILESOKAY",
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
@ -868,7 +873,7 @@ static void DebugPrintpacket(const char *header)
break;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode);
netbuffer->u.clientcfg.needsdownload);
break;
case PT_SERVERTICS:
{

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,8 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd);
// Remote Administration
CV_RegisterVar(&cv_dummyjoinpassword);
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 +3430,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);
@ -3440,7 +3444,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
}
#define BASESALT "basepasswordstorage"
static UINT8 adminpassmd5[16];
static UINT8 adminpassmd5[MD5_LEN];
static boolean adminpasswordset = false;
void D_SetPassword(const char *pw)
@ -3479,7 +3483,7 @@ static void Command_Login_f(void)
// 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
XBOXSTATIC UINT8 finalmd5[16];
XBOXSTATIC UINT8 finalmd5[MD5_LEN];
const char *pw;
if (!netgame)
@ -3502,11 +3506,11 @@ static void Command_Login_f(void)
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &finalmd5);
// Do the final pass to get the comparison the server will come up with
D_MD5PasswordPass(finalmd5, 16, va("PNUM%02d", consoleplayer), &finalmd5);
D_MD5PasswordPass(finalmd5, MD5_LEN, va("PNUM%02d", consoleplayer), &finalmd5);
CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n"));
SendNetXCmd(XD_LOGIN, finalmd5, 16);
SendNetXCmd(XD_LOGIN, finalmd5, MD5_LEN);
#endif
}
@ -3517,9 +3521,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
(void)cp;
(void)playernum;
#else
UINT8 sentmd5[16], finalmd5[16];
UINT8 sentmd5[MD5_LEN], finalmd5[MD5_LEN];
READMEM(*cp, sentmd5, 16);
READMEM(*cp, sentmd5, MD5_LEN);
if (client)
return;
@ -3531,9 +3535,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
}
// Do the final pass to compare with the sent md5
D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", playernum), &finalmd5);
D_MD5PasswordPass(adminpassmd5, MD5_LEN, va("PNUM%02d", playernum), &finalmd5);
if (!memcmp(sentmd5, finalmd5, 16))
if (!memcmp(sentmd5, finalmd5, MD5_LEN))
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
@ -3702,6 +3706,131 @@ static void Got_Removal(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
}
// Join password stuff
consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
#define NUMJOINCHALLENGES 32
static UINT8 joinpassmd5[MD5_LEN+1];
boolean joinpasswordset = false;
static UINT8 joinpasschallenges[NUMJOINCHALLENGES][MD5_LEN];
static tic_t joinpasschallengeson[NUMJOINCHALLENGES];
boolean D_IsJoinPasswordOn(void)
{
return joinpasswordset;
}
static inline void GetChallengeAnswer(UINT8 *question, UINT8 *passwordmd5, UINT8 *answer)
{
D_MD5PasswordPass(question, MD5_LEN, (char *) passwordmd5, answer);
}
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer)
{
static UINT8 passwordmd5[MD5_LEN+1];
memset(passwordmd5, 0x00, MD5_LEN+1);
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &passwordmd5);
GetChallengeAnswer(question, passwordmd5, answer);
}
void D_SetJoinPassword(const char *pw)
{
memset(joinpassmd5, 0x00, MD5_LEN+1);
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] > 0 && memcmp(answer, joinpasschallenges[num], MD5_LEN) == 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, MD5_LEN);
joinpasschallengeson[num] = 0;
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)] == 0)
break;
}
if (joinpasschallengeson[(*num)] > 0)
{
// Ugh, all challenges are (probably) taken. Let's find the oldest one and overwrite it.
tic_t oldesttic = INT32_MAX;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
if (joinpasschallengeson[i] < oldesttic)
{
(*num) = i;
oldesttic = joinpasschallengeson[i];
}
}
}
joinpasschallengeson[(*num)] = I_GetTime();
memset(question, 0x00, MD5_LEN);
for (i = 0; i < MD5_LEN; 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\nUse -remove to disable the password.\n"));
return;
}
if (strcmp(COM_Argv(1), "-remove") == 0)
{
joinpasswordset = false;
CONS_Printf(M_GetText("Join password removed.\n"));
}
else
{
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,14 @@ void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
extern consvar_t cv_dummyjoinpassword;
extern boolean joinpasswordset;
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);

View file

@ -1225,8 +1225,6 @@ static INT16 typelines = 1; // number of drawfill lines we need when drawing the
//
boolean HU_Responder(event_t *ev)
{
INT32 c=0;
if (ev->type != ev_keydown)
return false;
@ -1252,18 +1250,6 @@ boolean HU_Responder(event_t *ev)
return false;
}
c = (INT32)ev->data1;
// capslock (now handled outside of chat on so that it works everytime......)
if (c && c == KEY_CAPSLOCK) // it's a toggle.
{
if (capslock)
capslock = false;
else
capslock = true;
return true;
}
#ifndef NONET
if (!chat_on)
{
@ -1291,6 +1277,7 @@ boolean HU_Responder(event_t *ev)
}
else // if chat_on
{
INT32 c = (INT32)ev->data1;
// Ignore modifier keys
// Note that we do this here so users can still set
@ -1306,20 +1293,7 @@ boolean HU_Responder(event_t *ev)
&& ev->data1 != gamecontrol[gc_talkkey][1]))
return false;
c = (INT32)ev->data1;
// I know this looks very messy but this works. If it ain't broke, don't fix it!
// shift LETTERS to uppercase if we have capslock or are holding shift
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
if (shiftdown ^ capslock)
c = shiftxform[c];
}
else // if we're holding shift we should still shift non letter symbols
{
if (shiftdown)
c = shiftxform[c];
}
c = CON_ShiftChar(c);
// pasting. pasting is cool. chat is a bit limited, though :(
if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)

View file

@ -948,9 +948,10 @@ static menuitem_t MP_MainMenu[] =
static menuitem_t MP_ServerMenu[] =
{
{IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 10},
{IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30},
{IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 0},
{IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 10},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20},
{IT_STRING|IT_CVAR|IT_CV_PASSWORD, NULL, "Password", &cv_dummyjoinpassword, 44},
{IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68},
{IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
@ -2383,6 +2384,9 @@ static void M_NextOpt(void)
{
INT16 oldItemOn = itemOn; // prevent infinite loop
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
do
{
if (itemOn + 1 > currentMenu->numitems - 1)
@ -2396,6 +2400,9 @@ static void M_PrevOpt(void)
{
INT16 oldItemOn = itemOn; // prevent infinite loop
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
do
{
if (!itemOn)
@ -2682,8 +2689,11 @@ boolean M_Responder(event_t *ev)
// BP: one of the more big hack i have never made
if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)
{
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING)
if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING || (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
{
if (ch == KEY_TAB && (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value ^= 1;
if (shiftdown && ch >= 32 && ch <= 127)
ch = shiftxform[ch];
if (M_ChangeStringCvar(ch))
@ -3557,6 +3567,8 @@ static void M_DrawGenericMenu(void)
case IT_CVAR:
{
consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
char asterisks[MAXSTRINGLENGTH+1];
size_t sl;
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
{
case IT_CV_SLIDER:
@ -3564,6 +3576,27 @@ static void M_DrawGenericMenu(void)
case IT_CV_NOPRINT: // color use this
case IT_CV_INVISSLIDER: // monitor toggles use this
break;
case IT_CV_PASSWORD:
if (i == itemOn)
{
V_DrawRightAlignedThinString(x + MAXSTRINGLENGTH*8 + 10, y, V_ALLOWLOWERCASE, va(M_GetText("Tab: %s password"), cv->value ? "hide" : "show"));
}
if (!cv->value || i != itemOn)
{
sl = strlen(cv->string);
memset(asterisks, '*', sl);
memset(asterisks + sl, 0, MAXSTRINGLENGTH+1-sl);
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, asterisks);
if (skullAnimCounter < 4 && i == itemOn)
V_DrawCharacter(x + 8 + V_StringWidth(asterisks, 0), y + 12,
'_' | 0x80, false);
y += 16;
break;
}
/* fallthru */
case IT_CV_STRING:
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
@ -7241,6 +7274,7 @@ static void M_DrawConnectMenu(void)
{
UINT16 i, j;
const char *gt = "Unknown";
const char *spd = "";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@ -7294,7 +7328,17 @@ static void M_DrawConnectMenu(void)
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
if (serverlist[slindex].info.gametype == GT_RACE)
{
spd = kartspeed_cons_t[serverlist[slindex].info.kartvars & SV_SPEEDMASK].strvalue;
V_DrawSmallString(currentMenu->x+132, S_LINEY(i)+8, globalflags, va("(%s Speed)", spd));
}
if (serverlist[slindex].info.kartvars & SV_PASSWORD)
V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL);
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
}
@ -7529,6 +7573,11 @@ static void M_StartServer(INT32 choice)
// Still need to reset devmode
cv_debug = 0;
if (strlen(cv_dummyjoinpassword.string) > 0)
D_SetJoinPassword(cv_dummyjoinpassword.string);
else
joinpasswordset = false;
if (demoplayback)
G_StopDemo();
if (metalrecording)

View file

@ -104,6 +104,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
#define IT_CV_NOPRINT 1536
#define IT_CV_NOMOD 2048
#define IT_CV_INVISSLIDER 2560
#define IT_CV_PASSWORD 3072
//call/submenu specific
// There used to be a lot more here but ...

View file

@ -22,6 +22,8 @@
# include <limits.h>
#endif
#define MD5_LEN 16
/* The following contortions are an attempt to use the C preprocessor
to determine an unsigned integral type that is 32 bits wide. An
alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but

View file

@ -1556,7 +1556,6 @@ void *W_CachePatchName(const char *name, INT32 tag)
return W_CachePatchNum(num, tag);
}
#ifndef NOMD5
#define MD5_LEN 16
/**
* Prints an MD5 string into a human-readable textual format.