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("ws2_32")
settings.link.libs:Add("ole32") settings.link.libs:Add("ole32")
settings.link.libs:Add("shell32") settings.link.libs:Add("shell32")
settings.link.libs:Add("advapi32")
end end
-- compile zlib if needed -- compile zlib if needed

View file

@ -57,6 +57,7 @@
#include <errno.h> #include <errno.h>
#include <process.h> #include <process.h>
#include <shellapi.h> #include <shellapi.h>
#include <wincrypt.h>
#else #else
#error NOT IMPLEMENTED #error NOT IMPLEMENTED
#endif #endif
@ -2355,6 +2356,70 @@ void shell_execute(const char *file, const char *argv)
#endif #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) #if defined(__cplusplus)
} }
#endif #endif

View file

@ -1328,6 +1328,27 @@ int pid();
void shell_execute(const char *file, const char *argv); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -101,7 +101,7 @@ void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *
net_udp_send(Socket, pAddr, aBuffer, 6+DataSize); 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]; unsigned char aBuffer[NET_MAX_PACKETSIZE];
int CompressedSize = -1; int CompressedSize = -1;
@ -117,6 +117,14 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
io_flush(ms_DataLogSent); 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 // compress
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); 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; CNetPacketConstruct Construct;
Construct.m_Flags = NET_PACKETFLAG_CONTROL; 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); mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);
// send the control message // 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 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_DELCLIENT)(int ClientID, const char* pReason, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser); typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
@ -139,6 +148,7 @@ private:
unsigned m_State; unsigned m_State;
int m_Token; int m_Token;
SECURITY_TOKEN m_SecurityToken;
int m_RemoteClosed; int m_RemoteClosed;
bool m_BlockCloseMsg; bool m_BlockCloseMsg;
@ -178,7 +188,7 @@ public:
int Update(); int Update();
int Flush(); 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); int QueueChunk(int Flags, int DataSize, const void *pData);
const char *ErrorString(); const char *ErrorString();
@ -265,6 +275,8 @@ class CNetServer
NETFUNC_DELCLIENT m_pfnDelClient; NETFUNC_DELCLIENT m_pfnDelClient;
void *m_UserPtr; void *m_UserPtr;
unsigned char m_SecurityTokenSeed[16];
CNetRecvUnpacker m_RecvUnpacker; CNetRecvUnpacker m_RecvUnpacker;
public: public:
@ -386,9 +398,9 @@ public:
static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize); 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 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 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); 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 // 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_LastRecvTime = 0;
//m_LastUpdateTime = 0; //m_LastUpdateTime = 0;
m_Token = -1; m_Token = -1;
m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN;
//mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); //mem_zero(&m_PeerAddr, sizeof(m_PeerAddr));
m_Buffer.Init(); m_Buffer.Init();
@ -79,7 +80,7 @@ int CNetConnection::Flush()
// send of the packets // send of the packets
m_Construct.m_Ack = m_Ack; 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 // update send times
m_LastSendTime = time_get(); m_LastSendTime = time_get();
@ -94,7 +95,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int
unsigned char *pChunkData; unsigned char *pChunkData;
// check if we have space for it, if not, flush the connection // 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(); Flush();
// pack all the data // pack all the data
@ -148,7 +149,7 @@ void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSi
{ {
// send the control message // send the control message
m_LastSendTime = time_get(); 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) void CNetConnection::ResendChunk(CNetChunkResend *pResend)
@ -173,7 +174,7 @@ int CNetConnection::Connect(NETADDR *pAddr)
m_PeerAddr = *pAddr; m_PeerAddr = *pAddr;
mem_zero(m_ErrorString, sizeof(m_ErrorString)); mem_zero(m_ErrorString, sizeof(m_ErrorString));
m_State = NET_CONNSTATE_CONNECT; m_State = NET_CONNSTATE_CONNECT;
SendControl(NET_CTRLMSG_CONNECT, 0, 0); SendControl(NET_CTRLMSG_CONNECT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC));
return 0; return 0;
} }
@ -197,8 +198,22 @@ void CNetConnection::Disconnect(const char *pReason)
Reset(); 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(); int64 Now = time_get();
// check if resend is requested // check if resend is requested
@ -262,7 +277,21 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
m_LastSendTime = Now; m_LastSendTime = Now;
m_LastRecvTime = Now; m_LastRecvTime = Now;
m_LastUpdateTime = 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) if(g_Config.m_Debug)
dbg_msg("connection", "got connection, sending connect+accept"); dbg_msg("connection", "got connection, sending connect+accept");
} }
@ -272,6 +301,20 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr)
// connection made // connection made
if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) 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; m_LastRecvTime = Now;
SendControl(NET_CTRLMSG_ACCEPT, 0, 0); SendControl(NET_CTRLMSG_ACCEPT, 0, 0);
m_State = NET_CONNSTATE_ONLINE; m_State = NET_CONNSTATE_ONLINE;
@ -364,12 +407,12 @@ int CNetConnection::Update()
else if(State() == NET_CONNSTATE_CONNECT) else if(State() == NET_CONNSTATE_CONNECT)
{ {
if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect every 500ms 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) else if(State() == NET_CONNSTATE_PENDING)
{ {
if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect/accept every 500ms 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; return 0;

View file

@ -4,9 +4,10 @@
#include <engine/console.h> #include <engine/console.h>
#include "config.h"
#include "netban.h" #include "netban.h"
#include "network.h" #include "network.h"
#include <engine/external/md5/md5.h>
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags) 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; 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++) for(int i = 0; i < NET_MAX_CLIENTS; i++)
m_aSlots[i].m_Connection.Init(m_Socket, true); 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))) if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf)))
{ {
// banned, reply with a message // 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; continue;
} }
@ -170,7 +179,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
{ {
char aBuf[128]; char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); 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; return 0;
} }
} }
@ -181,7 +190,22 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
{ {
Found = true; 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) if(m_pfnNewClient)
m_pfnNewClient(i, m_UserPtr); m_pfnNewClient(i, m_UserPtr);
break; break;
@ -191,7 +215,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(!Found) if(!Found)
{ {
const char FullMsg[] = "This server is full"; 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);
} }
} }
} }