Merge pull request #160 from heinrich5991/pr_ddnet_random_windows

Make the secure random stuff platform-independent
This commit is contained in:
Dennis Felsing 2015-03-06 01:47:02 +01:00
commit 1d061986d3
7 changed files with 193 additions and 19 deletions

View file

@ -240,6 +240,7 @@ function build(settings)
settings.link.libs:Add("ws2_32")
settings.link.libs:Add("ole32")
settings.link.libs:Add("shell32")
settings.link.libs:Add("advapi32")
end
-- compile zlib if needed

View file

@ -57,6 +57,7 @@
#include <errno.h>
#include <process.h>
#include <shellapi.h>
#include <wincrypt.h>
#else
#error NOT IMPLEMENTED
#endif
@ -2355,6 +2356,70 @@ void shell_execute(const char *file, const char *argv)
#endif
}
struct SECURE_RANDOM_DATA
{
int initialized;
#if defined(CONF_FAMILY_WINDOWS)
HCRYPTPROV provider;
#else
IOHANDLE urandom;
#endif
};
static struct SECURE_RANDOM_DATA secure_random_data = { 0 };
int secure_random_init()
{
if(secure_random_data.initialized)
{
return 0;
}
#if defined(CONF_FAMILY_WINDOWS)
if(CryptAcquireContext(&secure_random_data.provider, NULL, NULL, PROV_RSA_FULL, 0))
{
secure_random_data.initialized = 1;
return 0;
}
else
{
return 1;
}
#else
secure_random_data.urandom = io_open("/dev/urandom", IOFLAG_READ);
if(secure_random_data.urandom)
{
secure_random_data.initialized = 1;
return 0;
}
else
{
return 1;
}
#endif
}
void secure_random_fill(unsigned char *bytes, size_t length)
{
if(!secure_random_data.initialized)
{
dbg_msg("secure", "called secure_random_fill before secure_random_init");
dbg_break();
}
#if defined(CONF_FAMILY_WINDOWS)
if(!CryptGenRandom(secure_random_data.provider, length, bytes))
{
dbg_msg("secure", "CryptGenRandom failed, last_error=%d", GetLastError());
dbg_break();
}
#else
if(length != io_read(secure_random_data.urandom, bytes, length))
{
dbg_msg("secure", "io_read returned with a short read");
dbg_break();
}
#endif
}
#if defined(__cplusplus)
}
#endif

View file

@ -1328,6 +1328,27 @@ int pid();
void shell_execute(const char *file, const char *argv);
/*
Function: secure_random_init
Initializes the secure random module.
You *MUST* check the return value of this function.
Returns:
0 - Initialization succeeded.
1 - Initialization failed.
*/
int secure_random_init();
/*
Function: secure_random_fill
Fills the buffer with the specified amount of random bytes.
Parameters:
buffer - Pointer to the start of the buffer.
length - Length of the buffer.
*/
void secure_random_fill(unsigned char *bytes, size_t length);
#ifdef __cplusplus
}
#endif

View file

@ -101,7 +101,7 @@ void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *
net_udp_send(Socket, pAddr, aBuffer, 6+DataSize);
}
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket)
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken)
{
unsigned char aBuffer[NET_MAX_PACKETSIZE];
int CompressedSize = -1;
@ -117,6 +117,14 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
io_flush(ms_DataLogSent);
}
if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// append security token
// if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it
mem_copy(&pPacket->m_aChunkData[pPacket->m_DataSize], &SecurityToken, sizeof(SecurityToken));
pPacket->m_DataSize += sizeof(SecurityToken);
}
// compress
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
@ -228,7 +236,7 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
}
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize)
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
{
CNetPacketConstruct Construct;
Construct.m_Flags = NET_PACKETFLAG_CONTROL;
@ -239,7 +247,7 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con
mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
// send the control message
CNetBase::SendPacket(Socket, pAddr, &Construct);
CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken);
}

View file

@ -78,6 +78,15 @@ enum
NET_ENUM_TERMINATOR
};
typedef int SECURITY_TOKEN;
static const unsigned char SECURITY_TOKEN_MAGIC[] = {'T', 'K', 'E', 'N'};
enum
{
NET_SECURITY_TOKEN_UNKNOWN = -1,
NET_SECURITY_TOKEN_UNSUPPORTED = 0,
};
typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
@ -139,6 +148,7 @@ private:
unsigned m_State;
int m_Token;
SECURITY_TOKEN m_SecurityToken;
int m_RemoteClosed;
bool m_BlockCloseMsg;
@ -178,7 +188,7 @@ public:
int Update();
int Flush();
int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr);
int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED);
int QueueChunk(int Flags, int DataSize, const void *pData);
const char *ErrorString();
@ -265,6 +275,8 @@ class CNetServer
NETFUNC_DELCLIENT m_pfnDelClient;
void *m_UserPtr;
unsigned char m_SecurityTokenSeed[16];
CNetRecvUnpacker m_RecvUnpacker;
public:
@ -386,9 +398,9 @@ public:
static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize);
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not

View file

@ -24,6 +24,7 @@ void CNetConnection::Reset()
m_LastRecvTime = 0;
//m_LastUpdateTime = 0;
m_Token = -1;
m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN;
//mem_zero(&m_PeerAddr, sizeof(m_PeerAddr));
m_Buffer.Init();
@ -79,7 +80,7 @@ int CNetConnection::Flush()
// send of the packets
m_Construct.m_Ack = m_Ack;
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct);
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken);
// update send times
m_LastSendTime = time_get();
@ -94,7 +95,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int
unsigned char *pChunkData;
// check if we have space for it, if not, flush the connection
if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData))
if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData) - (int)sizeof(SECURITY_TOKEN))
Flush();
// pack all the data
@ -148,7 +149,7 @@ void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSi
{
// send the control message
m_LastSendTime = time_get();
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize);
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken);
}
void CNetConnection::ResendChunk(CNetChunkResend *pResend)
@ -173,7 +174,7 @@ int CNetConnection::Connect(NETADDR *pAddr)
m_PeerAddr = *pAddr;
mem_zero(m_ErrorString, sizeof(m_ErrorString));
m_State = NET_CONNSTATE_CONNECT;
SendControl(NET_CTRLMSG_CONNECT, 0, 0);
SendControl(NET_CTRLMSG_CONNECT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
return 0;
}
@ -197,8 +198,22 @@ void CNetConnection::Disconnect(const char *pReason)
Reset();
}
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken)
{
if (m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// supposed to have a valid token in this packet, check it
if (pPacket->m_DataSize < sizeof(m_SecurityToken))
return -1;
pPacket->m_DataSize -= sizeof(m_SecurityToken);
if (m_SecurityToken != *(SECURITY_TOKEN*)&pPacket->m_aChunkData[pPacket->m_DataSize])
{
if(g_Config.m_Debug)
dbg_msg("security", "token mismatch, expected %d got %d", m_SecurityToken, *(SECURITY_TOKEN*)&pPacket->m_aChunkData[pPacket->m_DataSize]);
return -1;
}
}
int64 Now = time_get();
// check if resend is requested
@ -262,7 +277,21 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
m_LastSendTime = Now;
m_LastRecvTime = Now;
m_LastUpdateTime = Now;
SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
if (m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN
&& pPacket->m_DataSize >= 1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)
&& !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)))
{
m_SecurityToken = SecurityToken;
if(g_Config.m_Debug)
dbg_msg("security", "generated token %d", m_SecurityToken);
}
else
{
if(g_Config.m_Debug)
dbg_msg("security", "token not supported by client (packet size %d)", pPacket->m_DataSize);
m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
}
SendControl(NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
if(g_Config.m_Debug)
dbg_msg("connection", "got connection, sending connect+accept");
}
@ -272,6 +301,20 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
// connection made
if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT)
{
if (m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN
&& pPacket->m_DataSize >= 1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)
&& !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)))
{
m_SecurityToken = *(SECURITY_TOKEN*)(&pPacket->m_aChunkData[1 + sizeof(SECURITY_TOKEN_MAGIC)]);
if(g_Config.m_Debug)
dbg_msg("security", "got token %d", m_SecurityToken);
}
else
{
m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
if(g_Config.m_Debug)
dbg_msg("security", "token not supported by server");
}
m_LastRecvTime = Now;
SendControl(NET_CTRLMSG_ACCEPT, 0, 0);
m_State = NET_CONNSTATE_ONLINE;
@ -364,12 +407,12 @@ int CNetConnection::Update()
else if(State() == NET_CONNSTATE_CONNECT)
{
if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect every 500ms
SendControl(NET_CTRLMSG_CONNECT, 0, 0);
SendControl(NET_CTRLMSG_CONNECT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
}
else if(State() == NET_CONNSTATE_PENDING)
{
if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect/accept every 500ms
SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0);
SendControl(NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
}
return 0;

View file

@ -4,9 +4,10 @@
#include <engine/console.h>
#include "config.h"
#include "netban.h"
#include "network.h"
#include <engine/external/md5/md5.h>
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags)
{
@ -29,6 +30,14 @@ bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int Ma
m_MaxClientsPerIP = MaxClientsPerIP;
if(secure_random_init() != 0)
{
dbg_msg("secure", "could not initialize secure RNG");
return false;
}
secure_random_fill(m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed));
for(int i = 0; i < NET_MAX_CLIENTS; i++)
m_aSlots[i].m_Connection.Init(m_Socket, true);
@ -108,7 +117,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf)))
{
// banned, reply with a message
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1, NET_SECURITY_TOKEN_UNSUPPORTED);
continue;
}
@ -170,7 +179,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf));
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf), NET_SECURITY_TOKEN_UNSUPPORTED);
return 0;
}
}
@ -181,7 +190,22 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
{
Found = true;
m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr);
long timestamp = time_get();
md5_state_t md5;
md5_byte_t digest[16];
SECURITY_TOKEN securityToken;
do
{
md5_init(&md5);
md5_append(&md5, (unsigned char*)m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed));
md5_append(&md5, (unsigned char*)&Addr, sizeof(Addr));
md5_append(&md5, (unsigned char*)&timestamp, sizeof(timestamp));
md5_finish(&md5, digest);
securityToken = *(SECURITY_TOKEN*)digest;
timestamp++;
}
while (securityToken == NET_SECURITY_TOKEN_UNKNOWN || securityToken == NET_SECURITY_TOKEN_UNSUPPORTED);
m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr, securityToken);
if(m_pfnNewClient)
m_pfnNewClient(i, m_UserPtr);
break;
@ -191,7 +215,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(!Found)
{
const char FullMsg[] = "This server is full";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg));
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), NET_SECURITY_TOKEN_UNSUPPORTED);
}
}
}