diff --git a/docs/pokerth.asn1 b/docs/pokerth.asn1 index 2c1c710d..f358f623 100644 --- a/docs/pokerth.asn1 +++ b/docs/pokerth.asn1 @@ -631,8 +631,8 @@ ErrorMessage ::= [APPLICATION 255] SEQUENCE { errorInitPlayerNameInUse (4), errorInitInvalidPlayerName (5), errorInitServerMaintenance (6), - errorAvatarTooLarge (7), - errorAvatarUploadBlocked (8), + errorInitBlocked (7), + errorAvatarTooLarge (8), errorInvalidPacket (256), errorInvalidState (257), errorKickedFromServer (258), diff --git a/src/gui/qt/startwindow/startwindowimpl.cpp b/src/gui/qt/startwindow/startwindowimpl.cpp index dcdd4b41..e5b3fb90 100644 --- a/src/gui/qt/startwindow/startwindowimpl.cpp +++ b/src/gui/qt/startwindow/startwindowimpl.cpp @@ -779,9 +779,9 @@ void startWindowImpl::networkError(int errorID, int /*osErrorID*/) { tr("The selected avatar file is too large. Please choose a different avatar."), QMessageBox::Close); } break; - case ERR_NET_AVATAR_UPLOAD_BLOCKED: + case ERR_NET_INIT_BLOCKED: { QMessageBox::warning(this, tr("Network Error"), - tr("You cannot upload a new avatar file at this time. Please try again in a few seconds."), + tr("You cannot login at this time. Please try again in a few seconds."), QMessageBox::Close); } break; case ERR_NET_INVALID_REQUEST_ID: diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index bbb8bdda..2045db3e 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -202,12 +202,12 @@ NetPacket::NetErrorToGameError(long netErrorReason) case errorReason_errorInitServerMaintenance : retVal = ERR_NET_SERVER_MAINTENANCE; break; + case errorReason_errorInitBlocked : + retVal = ERR_NET_INIT_BLOCKED; + break; case errorReason_errorAvatarTooLarge : retVal = ERR_NET_AVATAR_TOO_LARGE; break; - case errorReason_errorAvatarUploadBlocked : - retVal = ERR_NET_AVATAR_UPLOAD_BLOCKED; - break; case errorReason_errorInvalidPacket : retVal = ERR_SOCK_INVALID_PACKET; break; @@ -254,12 +254,12 @@ NetPacket::GameErrorToNetError(int gameErrorReason) case ERR_NET_SERVER_MAINTENANCE : retVal = errorReason_errorInitServerMaintenance; break; + case ERR_NET_INIT_BLOCKED : + retVal = errorReason_errorInitBlocked; + break; case ERR_NET_AVATAR_TOO_LARGE : retVal = errorReason_errorAvatarTooLarge; break; - case ERR_NET_AVATAR_UPLOAD_BLOCKED : - retVal = errorReason_errorAvatarUploadBlocked; - break; case ERR_SOCK_INVALID_PACKET : retVal = errorReason_errorInvalidPacket; break; diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index abc5d39e..568d0081 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -54,10 +54,10 @@ #define SERVER_CHECK_SESSION_TIMEOUTS_INTERVAL_MSEC 500 #define SERVER_REMOVE_GAME_INTERVAL_MSEC 500 #define SERVER_REMOVE_PLAYER_INTERVAL_MSEC 100 -#define SERVER_UPDATE_AVATAR_LOCK_INTERVAL_MSEC 1000 +#define SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC 1000 #define SERVER_PROCESS_SEND_INTERVAL_MSEC 10 -#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar. +#define SERVER_INIT_LOGIN_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar. #define SERVER_INIT_SESSION_TIMEOUT_SEC 60 #define SERVER_TIMEOUT_WARNING_REMAINING_SEC 60 @@ -145,7 +145,7 @@ ServerLobbyThread::ServerLobbyThread(GuiInterface &gui, ServerMode mode, ServerI m_mode(mode), m_playerConfig(playerConfig), m_curGameId(0), m_curUniquePlayerId(0), m_curSessionId(INVALID_SESSION + 1), m_statDataChanged(false), m_removeGameTimer(*ioService), m_removePlayerTimer(*ioService), m_sessionTimeoutTimer(*ioService), m_avatarCleanupTimer(*ioService), - m_saveStatisticsTimer(*ioService), m_avatarLockTimer(*ioService), + m_saveStatisticsTimer(*ioService), m_loginLockTimer(*ioService), m_startTime(boost::posix_time::second_clock::local_time()) { m_internalServerCallback.reset(new InternalServerCallback(*this)); @@ -698,11 +698,11 @@ ServerLobbyThread::RegisterTimers() boost::bind( &ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error)); // Update the avatar upload locks. - m_avatarLockTimer.expires_from_now( - boost::posix_time::milliseconds(SERVER_UPDATE_AVATAR_LOCK_INTERVAL_MSEC)); - m_avatarLockTimer.async_wait( + m_loginLockTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC)); + m_loginLockTimer.async_wait( boost::bind( - &ServerLobbyThread::TimerUpdateClientAvatarLock, shared_from_this(), boost::asio::placeholders::error)); + &ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error)); } void @@ -713,7 +713,7 @@ ServerLobbyThread::CancelTimers() m_sessionTimeoutTimer.cancel(); m_avatarCleanupTimer.cancel(); m_saveStatisticsTimer.cancel(); - m_avatarLockTimer.cancel(); + m_loginLockTimer.cancel(); } void @@ -931,6 +931,23 @@ ServerLobbyThread::HandleNetPacketInit(SessionWrapper session, const InitMessage { LOG_VERBOSE("Received init for session #" << session.sessionData->GetId() << "."); + // Before any other processing, perform some denial of service and + // brute force attack prevention by checking whether the user recently sent an + // Init packet. + bool recentlySentInit = false; + { + boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex); + if (m_timerClientAddressMap.find(session.sessionData->GetClientAddr()) != m_timerClientAddressMap.end()) + recentlySentInit = true; + else + m_timerClientAddressMap[session.sessionData->GetClientAddr()] = boost::timers::portable::microsec_timer(); + } + if (recentlySentInit) + { + SessionError(session, ERR_NET_INIT_BLOCKED); + return; + } + // Check the protocol version. if (initMessage.requestedVersion.major != NET_VERSION_MAJOR || session.playerData) // Has this session already sent an init? @@ -1351,16 +1368,7 @@ ServerLobbyThread::InitAfterLogin(SessionWrapper session) if (!avatarMD5.IsZero() && !GetAvatarManager().GetAvatarFileName(avatarMD5, avatarFileName)) { - bool avatarRecentlyRequested = false; - { - boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); - if (m_timerAvatarClientAddressMap.find(session.sessionData->GetClientAddr()) != m_timerAvatarClientAddressMap.end()) - avatarRecentlyRequested = true; - } - if (avatarRecentlyRequested) - SessionError(session, ERR_NET_AVATAR_UPLOAD_BLOCKED); - else - RequestPlayerAvatar(session); + RequestPlayerAvatar(session); } else { @@ -1429,11 +1437,6 @@ ServerLobbyThread::RequestPlayerAvatar(SessionWrapper session) { if (!session.playerData.get()) throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0); - // Accept no more new avatars from that client for a certain time. - { - boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); - m_timerAvatarClientAddressMap[session.sessionData->GetClientAddr()] = boost::timers::portable::microsec_timer(); - } // Ask the client to send its avatar. boost::shared_ptr packet(new NetPacket(NetPacket::Alloc)); packet->GetMsg()->present = PokerTHMessage_PR_avatarRequestMessage; @@ -1501,29 +1504,29 @@ ServerLobbyThread::TimerRemovePlayer(const boost::system::error_code &ec) } void -ServerLobbyThread::TimerUpdateClientAvatarLock(const boost::system::error_code &ec) +ServerLobbyThread::TimerUpdateClientLoginLock(const boost::system::error_code &ec) { if (!ec) { - boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); + boost::mutex::scoped_lock lock(m_timerClientAddressMapMutex); - TimerClientAddressMap::iterator i = m_timerAvatarClientAddressMap.begin(); - TimerClientAddressMap::iterator end = m_timerAvatarClientAddressMap.end(); + TimerClientAddressMap::iterator i = m_timerClientAddressMap.begin(); + TimerClientAddressMap::iterator end = m_timerClientAddressMap.end(); while (i != end) { TimerClientAddressMap::iterator next = i; ++next; - if (i->second.elapsed().total_seconds() > SERVER_INIT_AVATAR_CLIENT_LOCK_SEC) - m_timerAvatarClientAddressMap.erase(i); + if (i->second.elapsed().total_seconds() > SERVER_INIT_LOGIN_CLIENT_LOCK_SEC) + m_timerClientAddressMap.erase(i); i = next; } // Restart timer - m_avatarLockTimer.expires_from_now( - boost::posix_time::milliseconds(SERVER_UPDATE_AVATAR_LOCK_INTERVAL_MSEC)); - m_avatarLockTimer.async_wait( + m_loginLockTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_UPDATE_LOGIN_LOCK_INTERVAL_MSEC)); + m_loginLockTimer.async_wait( boost::bind( - &ServerLobbyThread::TimerUpdateClientAvatarLock, shared_from_this(), boost::asio::placeholders::error)); + &ServerLobbyThread::TimerUpdateClientLoginLock, shared_from_this(), boost::asio::placeholders::error)); } } diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index b427966d..3e8a117d 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -147,7 +147,7 @@ protected: void RequestPlayerAvatar(SessionWrapper session); void TimerRemoveGame(const boost::system::error_code &ec); void TimerRemovePlayer(const boost::system::error_code &ec); - void TimerUpdateClientAvatarLock(const boost::system::error_code &ec); + void TimerUpdateClientLoginLock(const boost::system::error_code &ec); void TimerCheckSessionTimeouts(const boost::system::error_code &ec); void TimerCleanupAvatarCache(const boost::system::error_code &ec); @@ -200,8 +200,8 @@ private: Gsasl *m_authContext; - TimerClientAddressMap m_timerAvatarClientAddressMap; - mutable boost::mutex m_timerAvatarClientAddressMapMutex; + TimerClientAddressMap m_timerClientAddressMap; + mutable boost::mutex m_timerClientAddressMapMutex; RemoveGameList m_removeGameList; mutable boost::mutex m_removeGameListMutex; @@ -240,7 +240,7 @@ private: boost::asio::deadline_timer m_sessionTimeoutTimer; boost::asio::deadline_timer m_avatarCleanupTimer; boost::asio::deadline_timer m_saveStatisticsTimer; - boost::asio::deadline_timer m_avatarLockTimer; + boost::asio::deadline_timer m_loginLockTimer; const boost::posix_time::ptime m_startTime; diff --git a/src/net/socket_msg.h b/src/net/socket_msg.h index ec129b67..a70547f9 100644 --- a/src/net/socket_msg.h +++ b/src/net/socket_msg.h @@ -83,7 +83,7 @@ #define ERR_NET_GAME_TERMINATION_FAILED 129 #define ERR_NET_INTERNAL_GAME_ERROR 130 #define ERR_NET_DEALER_NOT_FOUND 131 -#define ERR_NET_AVATAR_UPLOAD_BLOCKED 132 +#define ERR_NET_INIT_BLOCKED 132 #define ERR_NET_GSASL_INIT_FAILED 133 #define ERR_NET_GSASL_NO_SCRAM 134 #define ERR_NET_DB_CONNECT_FAILED 135 diff --git a/src/third_party/asn1/ErrorMessage.c b/src/third_party/asn1/ErrorMessage.c index c99ce099..e26eebad 100644 --- a/src/third_party/asn1/ErrorMessage.c +++ b/src/third_party/asn1/ErrorMessage.c @@ -90,8 +90,8 @@ static asn_INTEGER_enum_map_t asn_MAP_errorReason_value2enum_2[] = { { 4, 24, "errorInitPlayerNameInUse" }, { 5, 26, "errorInitInvalidPlayerName" }, { 6, 26, "errorInitServerMaintenance" }, - { 7, 19, "errorAvatarTooLarge" }, - { 8, 24, "errorAvatarUploadBlocked" }, + { 7, 16, "errorInitBlocked" }, + { 8, 19, "errorAvatarTooLarge" }, { 256, 18, "errorInvalidPacket" }, { 257, 17, "errorInvalidState" }, { 258, 21, "errorKickedFromServer" }, @@ -99,10 +99,10 @@ static asn_INTEGER_enum_map_t asn_MAP_errorReason_value2enum_2[] = { { 260, 19, "errorSessionTimeout" } }; static unsigned int asn_MAP_errorReason_enum2value_2[] = { - 7, /* errorAvatarTooLarge(7) */ - 8, /* errorAvatarUploadBlocked(8) */ + 8, /* errorAvatarTooLarge(8) */ 12, /* errorBannedFromServer(259) */ 3, /* errorInitAuthFailure(3) */ + 7, /* errorInitBlocked(7) */ 5, /* errorInitInvalidPlayerName(5) */ 4, /* errorInitPlayerNameInUse(4) */ 2, /* errorInitServerFull(2) */ diff --git a/src/third_party/asn1/ErrorMessage.h b/src/third_party/asn1/ErrorMessage.h index c0898eb8..38d906cd 100644 --- a/src/third_party/asn1/ErrorMessage.h +++ b/src/third_party/asn1/ErrorMessage.h @@ -28,8 +28,8 @@ typedef enum errorReason { errorReason_errorInitPlayerNameInUse = 4, errorReason_errorInitInvalidPlayerName = 5, errorReason_errorInitServerMaintenance = 6, - errorReason_errorAvatarTooLarge = 7, - errorReason_errorAvatarUploadBlocked = 8, + errorReason_errorInitBlocked = 7, + errorReason_errorAvatarTooLarge = 8, errorReason_errorInvalidPacket = 256, errorReason_errorInvalidState = 257, errorReason_errorKickedFromServer = 258,