diff --git a/src/base/system.c b/src/base/system.c index d665e5dfe..e4ed05a72 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -2146,6 +2146,49 @@ void str_hex(char *dst, int dst_size, const void *data, int data_size) } } +static int hexval(char x) +{ + switch (x) + { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': + case 'A': return 10; + case 'b': + case 'B': return 11; + case 'c': + case 'C': return 12; + case 'd': + case 'D': return 13; + case 'e': + case 'E': return 14; + case 'f': + case 'F': return 15; + default: return -1; + } +} + +static unsigned char byteval(const char *byte) +{ + return hexval(byte[0]) * 16 + hexval(byte[1]); +} + +void str_hex_decode(unsigned char *dst, int dst_size, const char *src) +{ + int len = str_length(src)/2; + int i = 0; + for(; i < len && dst_size; i++, dst_size--) + *dst++ = byteval(src + i * 2); +} + void str_timestamp_ex(time_t time_data, char *buffer, int buffer_size, const char *format) { struct tm *time_info; diff --git a/src/base/system.h b/src/base/system.h index 7fd32c5c3..b00da4079 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -1016,6 +1016,17 @@ const char *str_find(const char *haystack, const char *needle); */ void str_hex(char *dst, int dst_size, const void *data, int data_size); +/* + Function: str_hex_decode + Takes a hex string and returns a byte array. + + Parameters: + dst - Buffer for the byte array + dst_size - size of the buffer + data - String to decode + data_size - Size of the data +*/ +void str_hex_decode(unsigned char *dst, int dst_size, const char *src); /* Function: str_timestamp Copies a time stamp in the format year-month-day_hour-minute-second to the string. diff --git a/src/engine/server/authmanager.cpp b/src/engine/server/authmanager.cpp new file mode 100644 index 000000000..fbabc0cae --- /dev/null +++ b/src/engine/server/authmanager.cpp @@ -0,0 +1,162 @@ +#include + +#include +#include "authmanager.h" + +#define ADMIN_IDENT "default_admin" +#define MOD_IDENT "default_mod" +#define HELPER_IDENT "default_helper" + +CAuthManager::CAuthManager() +{ + m_aDefault[0] = -1; + m_aDefault[1] = -1; + m_aDefault[2] = -1; +} + +void CAuthManager::Init() +{ + if(g_Config.m_SvRconPassword[0]) + AddAdminKey(g_Config.m_SvRconPassword); + if(g_Config.m_SvRconModPassword[0]) + AddModKey(g_Config.m_SvRconModPassword); + if (g_Config.m_SvRconHelperPassword[0]) + AddHelperKey(g_Config.m_SvRconHelperPassword); +} + +int CAuthManager::AddKeyHash(const char *pIdent, const unsigned char *pHash, const unsigned char *pSalt, int AuthLevel) +{ + CKey Key; + str_copy(Key.m_aIdent, pIdent, sizeof Key.m_aIdent); + mem_copy(Key.m_aPw, pHash, MD5_BYTES); + mem_copy(Key.m_aSalt, pSalt, SALT_BYTES); + Key.m_Level = AuthLevel; + + return m_aKeys.add(Key); +} + +int CAuthManager::AddKey(const char *pIdent, const char *pPw, int AuthLevel) +{ + md5_state_t ctx; + unsigned char aHash[MD5_BYTES]; + unsigned char aSalt[SALT_BYTES]; + + //Generate random salt + secure_random_fill(aSalt, SALT_BYTES); + + //Hash the password and the salt + md5_init(&ctx); + md5_append(&ctx, (unsigned char *)pPw, str_length(pPw)); + md5_append(&ctx, aSalt, SALT_BYTES); + md5_finish(&ctx, aHash); + + return AddKeyHash(pIdent, aHash, aSalt, AuthLevel); +} + +void CAuthManager::RemoveKey(int Slot) +{ + m_aKeys.remove_index_fast(Slot); +} + +int CAuthManager::FindKey(const char *pIdent) +{ + for(int i = 0; i < m_aKeys.size(); i++) + if(!str_comp(m_aKeys[i].m_aIdent, pIdent)) + return i; + + return -1; +} + +bool CAuthManager::CheckKey(int Slot, const char *pPw) +{ + if(Slot < 0 || Slot > m_aKeys.size()) + return false; + + md5_state_t ctx; + unsigned char aHash[MD5_BYTES]; + + //Hash the password and the salt + md5_init(&ctx); + md5_append(&ctx, (unsigned char*)pPw, str_length(pPw)); + md5_append(&ctx, m_aKeys[Slot].m_aSalt, SALT_BYTES); + md5_finish(&ctx, aHash); + + return !mem_comp(m_aKeys[Slot].m_aPw, aHash, MD5_BYTES); +} + +int CAuthManager::DefaultKey(int AuthLevel) +{ + if(AuthLevel < 0 || AuthLevel > AUTHED_ADMIN) + return -1; + + return m_aDefault[AUTHED_ADMIN - AuthLevel]; +} + +int CAuthManager::KeyLevel(int Slot) +{ + if(Slot < 0 || Slot > m_aKeys.size()) + return AUTHED_NO; + + return m_aKeys[Slot].m_Level; +} + +const char *CAuthManager::KeyIdent(int Slot) +{ + if(Slot < 0 || Slot > m_aKeys.size()) + return 0; + + return m_aKeys[Slot].m_aIdent; +} + +void CAuthManager::UpdateKeyHash(int Slot, const unsigned char *pHash, const unsigned char *pSalt, int AuthLevel) +{ + if(Slot < 0 || Slot > m_aKeys.size()) + return; + + CKey *pKey = &m_aKeys[Slot]; + mem_copy(pKey->m_aPw, pHash, MD5_BYTES); + mem_copy(pKey->m_aSalt, pSalt, SALT_BYTES); + pKey->m_Level = AuthLevel; +} + +void CAuthManager::UpdateKey(int Slot, const char *pPw, int AuthLevel) +{ + if(Slot < 0 || Slot > m_aKeys.size()) + return; + + md5_state_t ctx; + unsigned char aHash[MD5_BYTES]; + unsigned char aSalt[SALT_BYTES]; + + //Generate random salt + secure_random_fill(aSalt, SALT_BYTES); + + //Hash the password and the salt + md5_init(&ctx); + md5_append(&ctx, (unsigned char *)pPw, str_length(pPw)); + md5_append(&ctx, aSalt, SALT_BYTES); + md5_finish(&ctx, aHash); + + UpdateKeyHash(Slot, aHash, aSalt, AuthLevel); +} + +void CAuthManager::ListKeys(FListCallback pfnListCallback, void *pUser) +{ + for(int i = 0; i < m_aKeys.size(); i++) + pfnListCallback(m_aKeys[i].m_aIdent, m_aKeys[i].m_Level, pUser); +} + +void CAuthManager::AddAdminKey(const char *pPw) +{ + m_aDefault[0] = AddKey(ADMIN_IDENT, pPw, AUTHED_ADMIN); +} + +void CAuthManager::AddModKey(const char *pPw) +{ + m_aDefault[1] = AddKey(MOD_IDENT, pPw, AUTHED_MOD); +} + +void CAuthManager::AddHelperKey(const char *pPw) +{ + m_aDefault[2] = AddKey(HELPER_IDENT, pPw, AUTHED_HELPER); +} \ No newline at end of file diff --git a/src/engine/server/authmanager.h b/src/engine/server/authmanager.h new file mode 100644 index 000000000..5460956fb --- /dev/null +++ b/src/engine/server/authmanager.h @@ -0,0 +1,50 @@ +#ifndef ENGINE_SERVER_AUTH_MANAGER_H +#define ENGINE_SERVER_AUTH_MANAGER_H + +#include + +#define MD5_BYTES 16 +#define SALT_BYTES 8 + +class CAuthManager +{ +private: + enum { //:( + AUTHED_NO = 0, + AUTHED_HELPER, + AUTHED_MOD, + AUTHED_ADMIN + }; + struct CKey + { + char m_aIdent[64]; + unsigned char m_aPw[MD5_BYTES]; + unsigned char m_aSalt[SALT_BYTES]; + int m_Level; + }; + array m_aKeys; + + int m_aDefault[3]; +public: + typedef void (*FListCallback)(const char *pIdent, int Level, void *pUser); + + CAuthManager(); + + void Init(); + int AddKeyHash(const char *pIdent, const unsigned char *pHash, const unsigned char *pSalt, int AuthLevel); + int AddKey(const char *pIdent, const char *pPw, int AuthLevel); + void RemoveKey(int Slot); + int FindKey(const char *pIdent); + bool CheckKey(int Slot, const char *pPw); + int DefaultKey(int AuthLevel); + int KeyLevel(int Slot); + const char *KeyIdent(int Slot); + void UpdateKeyHash(int Slot, const unsigned char *pHash, const unsigned char *pSalt, int AuthLevel); + void UpdateKey(int Slot, const char *pPw, int AuthLevel); + void ListKeys(FListCallback pfnListCallbac, void *pUser); + void AddAdminKey(const char *pPw); + void AddModKey(const char *pPw); + void AddHelperKey(const char *pPw); +}; + +#endif //ENGINE_SERVER_AUTH_MANAGER_H diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 810b5e1ea..754ec957c 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -468,6 +468,7 @@ int CServer::Init() m_aClients[i].m_Snapshots.Init(); m_aClients[i].m_Traffic = 0; m_aClients[i].m_TrafficSince = 0; + m_aClients[i].m_AuthKey = -1; } m_CurrentGameTick = 0; @@ -770,6 +771,7 @@ int CServer::ClientRejoinCallback(int ClientID, void *pUser) CServer *pThis = (CServer *)pUser; pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; + pThis->m_aClients[ClientID].m_AuthKey = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].Reset(); @@ -792,6 +794,7 @@ int CServer::NewClientNoAuthCallback(int ClientID, bool Reset, void *pUser) pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; + pThis->m_aClients[ClientID].m_AuthKey = 0; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].Reset(); @@ -811,6 +814,7 @@ int CServer::NewClientCallback(int ClientID, void *pUser) pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; + pThis->m_aClients[ClientID].m_AuthKey = 0; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].m_Traffic = 0; @@ -860,6 +864,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) pThis->m_aClients[ClientID].m_aClan[0] = 0; pThis->m_aClients[ClientID].m_Country = -1; pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; + pThis->m_aClients[ClientID].m_AuthKey = 0; pThis->m_aClients[ClientID].m_AuthTries = 0; pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; pThis->m_aClients[ClientID].m_Traffic = 0; @@ -1188,9 +1193,8 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } else if(Msg == NETMSG_RCON_AUTH) { - const char *pPw; - Unpacker.GetString(); // login name, not used - pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC); + const char *pName = Unpacker.GetString(CUnpacker::SANITIZE_CC); // login name, now used + const char *pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(!str_utf8_check(pPw)) { return; @@ -1199,17 +1203,23 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) if((pPacket->m_Flags&NET_CHUNKFLAG_VITAL) != 0 && Unpacker.Error() == 0) { int AuthLevel = -1; + int KeySlot = -1; - if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0 && g_Config.m_SvRconHelperPassword[0] == 0) + if(!pName[0]) { - SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console."); + if(m_AuthManager.CheckKey((KeySlot = m_AuthManager.DefaultKey(AUTHED_ADMIN)), pPw)) + AuthLevel = AUTHED_ADMIN; + else if(m_AuthManager.CheckKey((KeySlot = m_AuthManager.DefaultKey(AUTHED_MOD)), pPw)) + AuthLevel = AUTHED_MOD; + else if(m_AuthManager.CheckKey((KeySlot = m_AuthManager.DefaultKey(AUTHED_HELPER)), pPw)) + AuthLevel = AUTHED_HELPER; + } + else + { + KeySlot = m_AuthManager.FindKey(pName); + if(m_AuthManager.CheckKey(KeySlot, pPw)) + AuthLevel = m_AuthManager.KeyLevel(KeySlot); } - else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0) - AuthLevel = AUTHED_ADMIN; - else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0) - AuthLevel = AUTHED_MOD; - else if(g_Config.m_SvRconHelperPassword[0] && str_comp(pPw, g_Config.m_SvRconHelperPassword) == 0) - AuthLevel = AUTHED_HELPER; if(AuthLevel != -1) { @@ -1220,31 +1230,33 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); - m_aClients[ClientID].m_Authed = AuthLevel; + m_aClients[ClientID].m_Authed = AuthLevel; //Keeping m_Authed around is unwise... + m_aClients[ClientID].m_AuthKey = KeySlot; int SendRconCmds = Unpacker.GetInt(); if(Unpacker.Error() == 0 && SendRconCmds) // AUTHED_ADMIN - AuthLevel gets the proper IConsole::ACCESS_LEVEL_ m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(AUTHED_ADMIN - AuthLevel, CFGFLAG_SERVER); char aBuf[256]; + const char *pIdent = m_AuthManager.KeyIdent(KeySlot); switch (AuthLevel) { case AUTHED_ADMIN: { SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted."); - str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID); + str_format(aBuf, sizeof(aBuf), "ClientID=%d authed with key=%s (admin)", ClientID, pIdent); break; } case AUTHED_MOD: { SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted."); - str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID); + str_format(aBuf, sizeof(aBuf), "ClientID=%d authed with key=%s (moderator)", ClientID, pIdent); break; } case AUTHED_HELPER: { SendRconLine(ClientID, "Helper authentication successful. Limited remote console access granted."); - str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (helper)", ClientID); + str_format(aBuf, sizeof(aBuf), "ClientID=%d authed with key=%s (helper)", ClientID, pIdent); break; } } @@ -1593,6 +1605,8 @@ void CServer::InitRegister(CNetServer *pNetServer, IEngineMasterServer *pMasterS int CServer::Run() { + m_AuthManager.Init(); + // m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, SendRconLineAuthed, this); @@ -1944,6 +1958,178 @@ void CServer::ConDnsblStatus(IConsole::IResult *pResult, void *pUser) } } +static int GetAuthLevel(const char *pLevel) +{ + int Level = -1; + if(!str_comp_nocase(pLevel, "admin")) + Level = CServer::AUTHED_ADMIN; + else if(!str_comp_nocase_num(pLevel, "mod", 3)) + Level = CServer::AUTHED_MOD; + else if(!str_comp_nocase(pLevel, "helper")) + Level = CServer::AUTHED_HELPER; + + return Level; +} + +void CServer::ConAuthAdd(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + const char *pIdent = pResult->GetString(0); + const char *pLevel = pResult->GetString(1); + const char *pPw = pResult->GetString(2); + + int KeySlot = pManager->FindKey(pIdent); + if(KeySlot != -1) + { + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "ident already exists"); + return; + } + + int Level = GetAuthLevel(pLevel); + if(Level == -1) + { + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "level can be one of {\"admin\", \"mod(erator)\", \"helper\"}"); + return; + } + + pManager->AddKey(pIdent, pPw, Level); + + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "key added"); +} + +void CServer::ConAuthAddHashed(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + const char *pIdent = pResult->GetString(0); + const char *pLevel = pResult->GetString(1); + const char *pPw = pResult->GetString(2); + const char *pSalt = pResult->GetString(3); + + int KeySlot = pManager->FindKey(pIdent); + if(KeySlot != -1){ + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "ident already exists"); + return; + } + + int Level = GetAuthLevel(pLevel); + if(Level == -1) + { + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "level can be one of {\"admin\", \"mod(erator)\", \"helper\"}"); + return; + } + + unsigned char aHash[MD5_BYTES]; + unsigned char aSalt[SALT_BYTES]; + + str_hex_decode(aHash, sizeof aHash, pPw); + str_hex_decode(aSalt, sizeof aSalt, pSalt); + + pManager->AddKeyHash(pIdent, aHash, aSalt, Level); + + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "key added"); +} + +void CServer::ConAuthUpdate(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + const char *pIdent = pResult->GetString(0); + const char *pLevel = pResult->GetString(1); + const char *pPw = pResult->GetString(2); + + int KeySlot = pManager->FindKey(pIdent); + if(KeySlot == -1){ + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "ident couldn't be found"); + return; + } + + int Level = GetAuthLevel(pLevel); + if(Level == -1) + { + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "level can be one of {\"admin\", \"mod(erator)\", \"helper\"}"); + return; + } + + pManager->UpdateKey(KeySlot, pPw, Level); + + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "key updated"); +} + +void CServer::ConAuthUpdateHashed(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + const char *pIdent = pResult->GetString(0); + const char *pLevel = pResult->GetString(1); + const char *pPw = pResult->GetString(2); + const char *pSalt = pResult->GetString(3); + + int KeySlot = pManager->FindKey(pIdent); + if(KeySlot == -1){ + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "ident couldn't be found"); + return; + } + + int Level = GetAuthLevel(pLevel); + if(Level == -1) + { + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "level can be one of {\"admin\", \"mod(erator)\", \"helper\"}"); + return; + } + + unsigned char aHash[MD5_BYTES]; + unsigned char aSalt[SALT_BYTES]; + + str_hex_decode(aHash, sizeof aHash, pPw); + str_hex_decode(aSalt, sizeof aSalt, pSalt); + + pManager->UpdateKeyHash(KeySlot, aHash, aSalt, Level); + pThis->LogoutKey(KeySlot, "key update"); + + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "key updated"); +} + +void CServer::ConAuthRemove(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + const char *pIdent = pResult->GetString(0); + + int KeySlot = pManager->FindKey(pIdent); + if(KeySlot == -1){ + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "ident couldn't be found"); + return; + } + + pManager->RemoveKey(KeySlot); + pThis->LogoutKey(KeySlot, "key removal"); + pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", "key removed, all users logged out"); +} + +static void ListKeysCallback(const char *pIdent, int Level, void *pUser) +{ + static const char lstring[][10] = {"helper", "moderator", "admin"}; + + char aBuf[256]; + str_format(aBuf, sizeof aBuf, "%s %s", pIdent, lstring[Level - 1]); + ((CServer *)pUser)->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "auth", aBuf); +} + +void CServer::ConAuthList(IConsole::IResult *pResult, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + CAuthManager *pManager = &pThis->m_AuthManager; + + pManager->ListKeys(ListKeysCallback, pThis); +} + void CServer::ConShutdown(IConsole::IResult *pResult, void *pUser) { ((CServer *)pUser)->m_RunServer = 0; @@ -2048,18 +2234,7 @@ void CServer::ConLogout(IConsole::IResult *pResult, void *pUser) if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS && pServer->m_aClients[pServer->m_RconClientID].m_State != CServer::CClient::STATE_EMPTY) { - CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); - Msg.AddInt(0); //authed - Msg.AddInt(0); //cmdlist - pServer->SendMsgEx(&Msg, MSGFLAG_VITAL, pServer->m_RconClientID, true); - - pServer->m_aClients[pServer->m_RconClientID].m_Authed = AUTHED_NO; - pServer->m_aClients[pServer->m_RconClientID].m_AuthTries = 0; - pServer->m_aClients[pServer->m_RconClientID].m_pRconCmdToSend = 0; - pServer->SendRconLine(pServer->m_RconClientID, "Logout successful."); - char aBuf[32]; - str_format(aBuf, sizeof(aBuf), "ClientID=%d logged out", pServer->m_RconClientID); - pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + pServer->LogoutClient(pServer->m_RconClientID, ""); } } @@ -2199,29 +2374,38 @@ void CServer::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void } } -void CServer::LogoutByAuthLevel(int AuthLevel) // AUTHED_ +void CServer::LogoutClient(int ClientID, const char *pReason) +{ + CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); + Msg.AddInt(0); //authed + Msg.AddInt(0); //cmdlist + SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); + + m_aClients[ClientID].m_AuthTries = 0; + m_aClients[ClientID].m_pRconCmdToSend = 0; + + char aBuf[64]; + if(*pReason){ + str_format(aBuf, sizeof aBuf, "Logged out by %s.", pReason); + SendRconLine(ClientID, aBuf); + str_format(aBuf, sizeof aBuf, "ClientID=%d with key=%s logged out by %s", ClientID, m_AuthManager.KeyIdent(m_aClients[ClientID].m_AuthKey), pReason); + } + else{ + SendRconLine(ClientID, "Logout successful."); + str_format(aBuf, sizeof aBuf, "ClientID=%d with key=%s logged out", ClientID, m_AuthManager.KeyIdent(m_aClients[ClientID].m_AuthKey)); + } + + m_aClients[ClientID].m_Authed = AUTHED_NO; + m_aClients[ClientID].m_AuthKey = -1; + + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); +} + +void CServer::LogoutKey(int Key, const char *pReason) { for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_aClients[i].m_State == CServer::CClient::STATE_EMPTY) - continue; - if(m_aClients[i].m_Authed == AuthLevel) - { - CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); - Msg.AddInt(0); //authed - Msg.AddInt(0); //cmdlist - SendMsgEx(&Msg, MSGFLAG_VITAL, i, true); - - m_aClients[i].m_Authed = AUTHED_NO; - m_aClients[i].m_AuthTries = 0; - m_aClients[i].m_pRconCmdToSend = 0; - - SendRconLine(i, "Logged out by password change."); - char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "ClientID=%d logged out by password change", i); - Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); - } - } + if(m_aClients[i].m_AuthKey == Key) + LogoutClient(i, pReason); } void CServer::ConchainRconPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) @@ -2230,7 +2414,14 @@ void CServer::ConchainRconPasswordChange(IConsole::IResult *pResult, void *pUser if(pResult->NumArguments() == 1) { CServer *pServer = (CServer *)pUserData; - pServer->LogoutByAuthLevel(AUTHED_ADMIN); + CAuthManager *pManager = &pServer->m_AuthManager; + + int KeySlot = pManager->DefaultKey(AUTHED_ADMIN); + if(KeySlot == -1) + pManager->AddAdminKey(pResult->GetString(0));//Shouldn't happen + + pManager->UpdateKey(KeySlot, pResult->GetString(0), AUTHED_ADMIN); + pServer->LogoutKey(KeySlot, "key update"); } } @@ -2240,7 +2431,16 @@ void CServer::ConchainRconModPasswordChange(IConsole::IResult *pResult, void *pU if(pResult->NumArguments() == 1) { CServer *pServer = (CServer *)pUserData; - pServer->LogoutByAuthLevel(AUTHED_MOD); + CAuthManager *pManager = &pServer->m_AuthManager; + + int KeySlot = pManager->DefaultKey(AUTHED_MOD); + if(KeySlot == -1) + pManager->AddModKey(pResult->GetString(0)); + else + { + pManager->UpdateKey(KeySlot, pResult->GetString(0), AUTHED_MOD); + pServer->LogoutKey(KeySlot, "key update"); + } } } @@ -2250,7 +2450,16 @@ void CServer::ConchainRconHelperPasswordChange(IConsole::IResult *pResult, void if(pResult->NumArguments() == 1) { CServer *pServer = (CServer *)pUserData; - pServer->LogoutByAuthLevel(AUTHED_HELPER); + CAuthManager *pManager = &pServer->m_AuthManager; + + int KeySlot = pManager->DefaultKey(AUTHED_HELPER); + if(KeySlot == -1) + pManager->AddHelperKey(pResult->GetString(0)); + else + { + pManager->UpdateKey(KeySlot, pResult->GetString(0), AUTHED_HELPER); + pServer->LogoutKey(KeySlot, "key update"); + } } } @@ -2281,6 +2490,13 @@ void CServer::RegisterCommands() Console()->Register("dnsbl_status", "", CFGFLAG_SERVER, ConDnsblStatus, this, "List blacklisted players"); + Console()->Register("auth_add", "s[ident] s[level] s[pw]", CFGFLAG_SERVER, ConAuthAdd, this, "add a rcon key."); + Console()->Register("auth_add_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER, ConAuthAddHashed, this, "add a prehashed rcon key."); + Console()->Register("auth_change", "s[ident] s[level] s[pw]", CFGFLAG_SERVER, ConAuthUpdate, this, "update a rcon key."); + Console()->Register("auth_change_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER, ConAuthUpdateHashed, this, "update a rcon key with prehashed data."); + Console()->Register("auth_remove", "s[ident]", CFGFLAG_SERVER, ConAuthRemove, this, "remove a rcon key."); + Console()->Register("auth_list", "", CFGFLAG_SERVER, ConAuthList, this, "list all rcon keys."); + Console()->Chain("sv_name", ConchainSpecialInfoupdate, this); Console()->Chain("password", ConchainSpecialInfoupdate, this); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index fe971f108..7c491429b 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -19,6 +19,8 @@ #include #include +#include "authmanager.h" + #if defined (CONF_SQL) #include "sql_connector.h" #include "sql_server.h" @@ -153,6 +155,7 @@ public: int m_Country; int m_Score; int m_Authed; + int m_AuthKey; int m_AuthTries; int m_NextMapChunk; @@ -206,6 +209,7 @@ public: CDemoRecorder m_aDemoRecorder[MAX_CLIENTS+1]; CRegister m_Register; CMapChecker m_MapChecker; + CAuthManager m_AuthManager; int m_RconRestrict; @@ -296,6 +300,13 @@ public: static void ConLogout(IConsole::IResult *pResult, void *pUser); static void ConDnsblStatus(IConsole::IResult *pResult, void *pUser); + static void ConAuthAdd(IConsole::IResult *pResult, void *pUser); + static void ConAuthAddHashed(IConsole::IResult *pResult, void *pUser); + static void ConAuthUpdate(IConsole::IResult *pResult, void *pUser); + static void ConAuthUpdateHashed(IConsole::IResult *pResult, void *pUser); + static void ConAuthRemove(IConsole::IResult *pResult, void *pUser); + static void ConAuthList(IConsole::IResult *pResult, void *pUser); + #if defined (CONF_SQL) // console commands for sqlmasters static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData); @@ -309,7 +320,8 @@ public: static void ConchainCommandAccessUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); - void LogoutByAuthLevel(int AuthLevel); + void LogoutClient(int ClientID, const char *pReason); + void LogoutKey(int Key, const char *pReason); static void ConchainRconPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); static void ConchainRconModPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);