Merge pull request #554 from heinrich5991/pr_ddnet_random_timeouttoken

Generate server-specific timeout codes
This commit is contained in:
east 2016-10-05 12:21:51 +02:00 committed by GitHub
commit 823e330a76
8 changed files with 152 additions and 47 deletions

View file

@ -2555,7 +2555,40 @@ int secure_random_init()
#endif
}
void secure_random_fill(void *bytes, size_t length)
void generate_password(char *buffer, unsigned length, unsigned short *random, unsigned random_length)
{
static const char VALUES[] = "ABCDEFGHKLMNPRSTUVWXYZabcdefghjkmnopqt23456789";
static const size_t NUM_VALUES = sizeof(VALUES) - 1; // Disregard the '\0'.
unsigned i;
dbg_assert(length >= random_length * 2 + 1, "too small buffer");
dbg_assert(NUM_VALUES * NUM_VALUES >= 2048, "need at least 2048 possibilities for 2-character sequences");
buffer[random_length * 2] = 0;
for(i = 0; i < random_length; i++)
{
unsigned short random_number = random[i] % 2048;
buffer[2 * i + 0] = VALUES[random_number / NUM_VALUES];
buffer[2 * i + 1] = VALUES[random_number % NUM_VALUES];
}
}
void secure_random_password(char *buffer, unsigned length, unsigned pw_length)
{
static const size_t MAX_PASSWORD_LENGTH = 128;
unsigned short random[MAX_PASSWORD_LENGTH / 2];
// With 6 characters, we get a password entropy of log(2048) * 6/2 = 33bit.
dbg_assert(length >= pw_length + 1, "too small buffer");
dbg_assert(pw_length >= 6, "too small password length");
dbg_assert(pw_length % 2 == 0, "need an even password length");
dbg_assert(pw_length <= MAX_PASSWORD_LENGTH, "too large password length");
secure_random_fill(random, pw_length);
generate_password(buffer, length, random, pw_length / 2);
}
void secure_random_fill(void *bytes, unsigned length)
{
if(!secure_random_data.initialized)
{

View file

@ -1375,6 +1375,20 @@ void shell_execute(const char *file);
*/
int os_compare_version(int major, int minor);
/*
Function: generate_password
Generates a null-terminated password of length `2 *
random_length`.
Parameters:
buffer - Pointer to the start of the output buffer.
length - Length of the buffer.
random - Pointer to a randomly-initialized array of shorts.
random_length - Length of the short array.
*/
void generate_password(char *buffer, unsigned length, unsigned short *random, unsigned random_length);
/*
Function: secure_random_init
Initializes the secure random module.
@ -1386,6 +1400,21 @@ int os_compare_version(int major, int minor);
*/
int secure_random_init();
/*
Function: secure_random_password
Fills the buffer with the specified amount of random password
characters.
The desired password length must be greater or equal to 6, even
and smaller or equal to 128.
Parameters:
buffer - Pointer to the start of the buffer.
length - Length of the buffer.
pw_length - Length of the desired password.
*/
void secure_random_password(char *buffer, unsigned length, unsigned pw_length);
/*
Function: secure_random_fill
Fills the buffer with the specified amount of random bytes.
@ -1394,7 +1423,7 @@ int secure_random_init();
buffer - Pointer to the start of the buffer.
length - Length of the buffer.
*/
void secure_random_fill(void *bytes, size_t length);
void secure_random_fill(void *bytes, unsigned length);
/*
Function: secure_rand

View file

@ -195,6 +195,8 @@ public:
virtual void RequestDDNetSrvList() = 0;
virtual bool EditorHasUnsavedData() = 0;
virtual void GenerateTimeoutSeed() = 0;
virtual IFriends* Foes() = 0;
};

View file

@ -32,6 +32,8 @@
#include <engine/storage.h>
#include <engine/textrender.h>
#include <engine/external/md5/md5.h>
#include <engine/shared/config.h>
#include <engine/shared/compression.h>
#include <engine/shared/datafile.h>
@ -347,6 +349,8 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta)
m_DDNetSrvListTokenSet = false;
m_ReconnectTime = 0;
m_GenerateTimeoutSeed = true;
}
// ----- send functions -----
@ -678,8 +682,51 @@ void CClient::EnterGame()
OnEnterGame();
ServerInfoRequest(); // fresh one for timeout protection
m_TimeoutCodeSent[0] = false;
m_TimeoutCodeSent[1] = false;
m_aTimeoutCodeSent[0] = false;
m_aTimeoutCodeSent[1] = false;
}
void GenerateTimeoutCode(char *pBuffer, unsigned Size, char *pSeed, const NETADDR &Addr, bool Dummy)
{
md5_state_t Md5;
md5_byte_t aDigest[16];
md5_init(&Md5);
const char *pDummy = Dummy ? "dummy" : "normal";
md5_append(&Md5, (unsigned char *)pDummy, str_length(pDummy) + 1);
md5_append(&Md5, (unsigned char *)pSeed, str_length(pSeed) + 1);
md5_append(&Md5, (unsigned char *)&Addr, sizeof(Addr));
md5_finish(&Md5, aDigest);
unsigned short Random[8];
mem_copy(Random, aDigest, sizeof(Random));
generate_password(pBuffer, Size, Random, 8);
}
void CClient::GenerateTimeoutSeed()
{
secure_random_password(g_Config.m_ClTimeoutSeed, sizeof(g_Config.m_ClTimeoutSeed), 16);
}
void CClient::GenerateTimeoutCodes()
{
if(g_Config.m_ClTimeoutSeed[0])
{
for(int i = 0; i < 2; i++)
{
GenerateTimeoutCode(m_aTimeoutCodes[i], sizeof(m_aTimeoutCodes[i]), g_Config.m_ClTimeoutSeed, m_ServerAddress, i);
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "timeout code '%s' (%s)", m_aTimeoutCodes[i], i == 0 ? "normal" : "dummy");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aBuf);
}
}
else
{
str_copy(m_aTimeoutCodes[0], g_Config.m_ClTimeoutCode, sizeof(m_aTimeoutCodes[0]));
str_copy(m_aTimeoutCodes[1], g_Config.m_ClDummyTimeoutCode, sizeof(m_aTimeoutCodes[1]));
}
}
void CClient::Connect(const char *pAddress)
@ -715,6 +762,8 @@ void CClient::Connect(const char *pAddress)
m_InputtimeMarginGraph.Init(-150.0f, 150.0f);
m_GametimeMarginGraph.Init(-150.0f, 150.0f);
GenerateTimeoutCodes();
}
void CClient::DisconnectWithReason(const char *pReason)
@ -1872,15 +1921,15 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
m_GameTime[g_Config.m_ClDummy].Update(&m_GametimeMarginGraph, (GameTick-1)*time_freq()/50, TimeLeft, 0);
}
if(m_ReceivedSnapshots[g_Config.m_ClDummy] > 50 && !m_TimeoutCodeSent[g_Config.m_ClDummy])
if(m_ReceivedSnapshots[g_Config.m_ClDummy] > 50 && !m_aTimeoutCodeSent[g_Config.m_ClDummy])
{
if(IsDDNet(&m_CurrentServerInfo))
{
m_TimeoutCodeSent[g_Config.m_ClDummy] = true;
m_aTimeoutCodeSent[g_Config.m_ClDummy] = true;
CNetMsg_Cl_Say Msg;
Msg.m_Team = 0;
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "/timeout %s", g_Config.m_ClDummy ? g_Config.m_ClDummyTimeoutCode : g_Config.m_ClTimeoutCode);
str_format(aBuf, sizeof(aBuf), "/timeout %s", m_aTimeoutCodes[g_Config.m_ClDummy]);
Msg.m_pMessage = aBuf;
CMsgPacker Packer(Msg.MsgID());
Msg.Pack(&Packer);
@ -2577,7 +2626,14 @@ void CClient::Run()
m_LocalStartTime = time_get();
m_SnapshotParts = 0;
srand(time(NULL));
if(m_GenerateTimeoutSeed)
{
GenerateTimeoutSeed();
}
unsigned int Seed;
secure_random_fill(&Seed, sizeof(Seed));
srand(Seed);
// init SDL
{
@ -3278,6 +3334,14 @@ void CClient::ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, I
pfnCallback(pResult, pCallbackUserData);
}
void CClient::ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CClient *pSelf = (CClient *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments())
pSelf->m_GenerateTimeoutSeed = false;
}
void CClient::RegisterCommands()
{
m_pConsole = Kernel()->RequestInterface<IConsole>();
@ -3316,6 +3380,8 @@ void CClient::RegisterCommands()
m_pConsole->Register("demo_speed", "i[speed]", CFGFLAG_CLIENT, Con_DemoSpeed, this, "Set demo speed");
// used for server browser update
m_pConsole->Chain("cl_timeout_seed", ConchainTimeoutSeed, this);
m_pConsole->Chain("br_filter_string", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("br_filter_gametype", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("br_filter_serveraddress", ConchainServerBrowserUpdate, this);

View file

@ -119,7 +119,9 @@ class CClient : public IClient, public CDemoPlayer::IListener
char m_aCurrentMapPath[CEditor::MAX_PATH_LENGTH];
unsigned m_CurrentMapCrc;
bool m_TimeoutCodeSent[2];
char m_aTimeoutCodes[2][32];
bool m_aTimeoutCodeSent[2];
bool m_GenerateTimeoutSeed;
//
char m_aCmdConnect[256];
@ -342,6 +344,7 @@ public:
static void ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void Con_DemoSlice(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoSliceBegin(IConsole::IResult *pResult, void *pUserData);
@ -371,6 +374,9 @@ public:
// DDRace
void GenerateTimeoutSeed();
void GenerateTimeoutCodes();
virtual const char* GetCurrentMap();
virtual int GetCurrentMapCrc();
virtual const char* GetCurrentMapPath();

View file

@ -559,26 +559,7 @@ void CServer::InitRconPasswordIfEmpty()
return;
}
static const char VALUES[] = "ABCDEFGHKLMNPRSTUVWXYZabcdefghjkmnopqt23456789";
static const size_t NUM_VALUES = sizeof(VALUES) - 1; // Disregard the '\0'.
static const size_t PASSWORD_LENGTH = 6;
dbg_assert(NUM_VALUES * NUM_VALUES >= 2048, "need at least 2048 possibilities for 2-character sequences");
// With 6 characters, we get a password entropy of log(2048) * 6/2 = 33bit.
dbg_assert(PASSWORD_LENGTH % 2 == 0, "need an even password length");
unsigned short aRandom[PASSWORD_LENGTH / 2];
char aRandomPassword[PASSWORD_LENGTH+1];
aRandomPassword[PASSWORD_LENGTH] = 0;
secure_random_fill(aRandom, sizeof(aRandom));
for(size_t i = 0; i < PASSWORD_LENGTH / 2; i++)
{
unsigned short RandomNumber = aRandom[i] % 2048;
aRandomPassword[2 * i + 0] = VALUES[RandomNumber / NUM_VALUES];
aRandomPassword[2 * i + 1] = VALUES[RandomNumber % NUM_VALUES];
}
str_copy(g_Config.m_SvRconPassword, aRandomPassword, sizeof(g_Config.m_SvRconPassword));
secure_random_password(g_Config.m_SvRconPassword, sizeof(g_Config.m_SvRconPassword), 6);
m_GeneratedRconPassword = 1;
}

View file

@ -363,6 +363,7 @@ MACRO_CONFIG_INT(ClOldGunPosition, cl_old_gun_position, 0, 0, 1, CFGFLAG_SAVE|CF
MACRO_CONFIG_INT(ClConfirmDisconnect, cl_confirm_disconnect, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Confirmation popup before disconnecting")
MACRO_CONFIG_STR(ClTimeoutCode, cl_timeout_code, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Timeout code to use")
MACRO_CONFIG_STR(ClDummyTimeoutCode, cl_dummy_timeout_code, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Dummy Timeout code to use")
MACRO_CONFIG_STR(ClTimeoutSeed, cl_timeout_seed, 64, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Timeout seed")
MACRO_CONFIG_STR(ClInputFifo, cl_input_fifo, 128, "", CFGFLAG_SAVE|CFGFLAG_CLIENT, "Fifo file to use as input for client console")
MACRO_CONFIG_INT(ClShowConsole, cl_show_console, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show console window (Windows only)")
#if defined(__ANDROID__)

View file

@ -2020,24 +2020,11 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
#endif
{
static int s_ButtonTimeout = 0;
Right.HSplitTop(20.0f, &Button, &Right);
Button.VSplitLeft(190.0f, &Label, &Button);
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%s:", Localize("Timeout code"));
UI()->DoLabelScaled(&Label, aBuf, 14.0, -1);
static float s_OffsetCode = 0.0f;
DoEditBox(g_Config.m_ClTimeoutCode, &Button, g_Config.m_ClTimeoutCode, sizeof(g_Config.m_ClTimeoutCode), 14.0f, &s_OffsetCode);
}
Right.HSplitTop(5.0f, &Button, &Right);
{
Right.HSplitTop(20.0f, &Button, &Right);
Button.VSplitLeft(190.0f, &Label, &Button);
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%s:", Localize("Dummy Timeout code"));
UI()->DoLabelScaled(&Label, aBuf, 14.0, -1);
static float s_OffsetCode = 0.0f;
DoEditBox(g_Config.m_ClDummyTimeoutCode, &Button, g_Config.m_ClDummyTimeoutCode, sizeof(g_Config.m_ClDummyTimeoutCode), 14.0f, &s_OffsetCode);
if(DoButton_Menu(&s_ButtonTimeout, Localize("New random timeout code"), 0, &Button))
{
Client()->GenerateTimeoutSeed();
}
}
}