Permalink
Browse files

Added login-delay for server

  • Loading branch information...
bear101 committed Feb 11, 2019
1 parent 4588815 commit 6e3fc8684374aacef05bc9237c1cea9b1ebec11c
@@ -1372,6 +1372,15 @@ public struct ServerProperties
/** @brief The version of the server's protocol. */
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = TeamTalkBase.TT_STRLEN)]
public string szServerProtocolVersion;
/** @brief Number of msec before an IP-address can make
* another login attempt. If less than this amount then
* TeamTalkBase.DoLogin() will result in
* #CMDERR_MAX_LOGINS_PER_IPADDRESS_EXCEEDED. Zero means
* disabled.
*
* Also checkout @c nMaxLoginAttempts and @c
* nMaxLoginsPerIPAddress. */
public int nLoginDelayMSec;
}

/**
@@ -869,6 +869,7 @@ void setServerProperties(JNIEnv* env, ServerProperties& srvprop, jobject lpServe
jfieldID fid_motdraw = env->GetFieldID(cls_srv, "szMOTDRaw", "Ljava/lang/String;");
jfieldID fid_maxusers = env->GetFieldID(cls_srv, "nMaxUsers", "I");
jfieldID fid_maxattempts = env->GetFieldID(cls_srv, "nMaxLoginAttempts", "I");
jfieldID fid_logindelay = env->GetFieldID(cls_srv, "nLoginDelayMSec", "I");
jfieldID fid_iplogins = env->GetFieldID(cls_srv, "nMaxLoginsPerIPAddress", "I");
jfieldID fid_voicetx = env->GetFieldID(cls_srv, "nMaxVoiceTxPerSecond", "I");
jfieldID fid_vidcaptx = env->GetFieldID(cls_srv, "nMaxVideoCaptureTxPerSecond", "I");
@@ -887,6 +888,7 @@ void setServerProperties(JNIEnv* env, ServerProperties& srvprop, jobject lpServe
assert(fid_motdraw);
assert(fid_maxusers);
assert(fid_maxattempts);
assert(fid_logindelay);
assert(fid_iplogins);
assert(fid_voicetx);
assert(fid_vidcaptx);
@@ -907,6 +909,7 @@ void setServerProperties(JNIEnv* env, ServerProperties& srvprop, jobject lpServe
env->SetObjectField(lpServerProperties, fid_motdraw, NEW_JSTRING(env, srvprop.szMOTDRaw));
env->SetIntField(lpServerProperties, fid_maxusers, srvprop.nMaxUsers);
env->SetIntField(lpServerProperties, fid_maxattempts, srvprop.nMaxLoginAttempts);
env->SetIntField(lpServerProperties, fid_logindelay, srvprop.nLoginDelayMSec);
env->SetIntField(lpServerProperties, fid_iplogins, srvprop.nMaxLoginsPerIPAddress);
env->SetIntField(lpServerProperties, fid_voicetx, srvprop.nMaxVoiceTxPerSecond);
env->SetIntField(lpServerProperties, fid_vidcaptx, srvprop.nMaxVideoCaptureTxPerSecond);
@@ -929,6 +932,7 @@ void setServerProperties(JNIEnv* env, ServerProperties& srvprop, jobject lpServe
srvprop.nMaxUsers = env->GetIntField(lpServerProperties, fid_maxusers);
srvprop.nMaxLoginAttempts = env->GetIntField(lpServerProperties, fid_maxattempts);
srvprop.nMaxLoginsPerIPAddress = env->GetIntField(lpServerProperties, fid_iplogins);
srvprop.nLoginDelayMSec = env->GetIntField(lpServerProperties, fid_logindelay);
srvprop.nMaxVoiceTxPerSecond = env->GetIntField(lpServerProperties, fid_voicetx);
srvprop.nMaxVideoCaptureTxPerSecond = env->GetIntField(lpServerProperties, fid_vidcaptx);
srvprop.nMaxMediaFileTxPerSecond = env->GetIntField(lpServerProperties, fid_mftx);
@@ -31,6 +31,7 @@
public int nMaxUsers;
public int nMaxLoginAttempts;
public int nMaxLoginsPerIPAddress;
public int nLoginDelayMSec;
public int nMaxVoiceTxPerSecond;
public int nMaxVideoCaptureTxPerSecond;
public int nMaxMediaFileTxPerSecond;
@@ -1883,6 +1883,45 @@ public void testAbusePrevention() {
assertTrue("do text message after cmd-timeout", waitCmdSuccess(ttclient, ttclient.doTextMessage(txtmsg), DEF_WAIT));
}

public void testLoginDelay() throws InterruptedException {
String USERNAME = "tt_test", PASSWORD = "tt_test", NICKNAME = "jUnit - " + getCurrentMethod();
int USERRIGHTS = UserRight.USERRIGHT_CREATE_TEMPORARY_CHANNEL | UserRight.USERRIGHT_MULTI_LOGIN;

TTMessage msg = new TTMessage();

TeamTalkBase ttadmin = newClientInstance();
connect(ttadmin);
login(ttadmin, ADMIN_NICKNAME, ADMIN_USERNAME, ADMIN_PASSWORD);

ServerProperties srvprop = new ServerProperties();
assertTrue(ttadmin.getServerProperties(srvprop));
srvprop.nLoginDelayMSec = 1000;
assertTrue(waitCmdSuccess(ttadmin, ttadmin.doUpdateServer(srvprop), DEF_WAIT));

UserAccount account = new UserAccount();
account.szUsername = USERNAME;
account.szPassword = PASSWORD;
account.uUserType = UserType.USERTYPE_DEFAULT;
account.uUserRights = USERRIGHTS;

assertTrue("create account", waitCmdSuccess(ttadmin, ttadmin.doNewUserAccount(account), DEF_WAIT));

TeamTalkBase ttclient1 = newClientInstance();
TeamTalkBase ttclient2 = newClientInstance();

connect(ttclient1);
login(ttclient1, NICKNAME, USERNAME, PASSWORD);
connect(ttclient2);

assertTrue("login failure", waitCmdError(ttclient2, ttclient2.doLogin(NICKNAME, USERNAME, PASSWORD), DEF_WAIT));

Thread.sleep(2000);
login(ttclient2, NICKNAME, USERNAME, PASSWORD);

srvprop.nLoginDelayMSec = 0;
assertTrue(waitCmdSuccess(ttadmin, ttadmin.doUpdateServer(srvprop), DEF_WAIT));
}

public void testLoginAttempts() {

TeamTalkBase ttadmin = newClientInstance();
@@ -1221,6 +1221,7 @@ void Convert(const teamtalk::ServerProperties& srvprop, ServerProperties& result
result.bAutoSave = srvprop.autosave;
result.nMaxUsers = srvprop.maxusers;
result.nUserTimeout = srvprop.usertimeout;
result.nLoginDelayMSec = srvprop.logindelay;
ACE_OS::strsncpy(result.szServerVersion, srvprop.version.c_str(), TT_STRLEN);
}

@@ -1265,6 +1266,7 @@ void Convert(const ServerProperties& srvprop, teamtalk::ServerProperties& result
result.totaltxlimit = srvprop.nMaxTotalTxPerSecond;
result.autosave = srvprop.bAutoSave;
result.usertimeout = srvprop.nUserTimeout;
result.logindelay = srvprop.nLoginDelayMSec;
}

void Convert(const ServerProperties& srvprop, teamtalk::ServerInfo& result)
@@ -815,7 +815,7 @@ void RunWizard(ServerXML& xmlSettings)
int maxusers, max_logins_per_ip = 0;
bool autosave = true;
_INT64 diskquota = 0, maxdiskusage = 0, log_maxsize = 0;
int tcpport = DEFAULT_TCPPORT, udpport = DEFAULT_UDPPORT, max_login_attempts = 0;
int tcpport = DEFAULT_TCPPORT, udpport = DEFAULT_UDPPORT, max_login_attempts = 0, logindelay = 0;

servername = Utf8ToUnicode(xmlSettings.GetServerName().c_str());
motd = Utf8ToUnicode(xmlSettings.GetMessageOfTheDay().c_str());
@@ -831,6 +831,7 @@ void RunWizard(ServerXML& xmlSettings)
maxdiskusage = xmlSettings.GetMaxDiskUsage();
max_login_attempts = xmlSettings.GetMaxLoginAttempts();
max_logins_per_ip = xmlSettings.GetMaxLoginsPerIP();
logindelay = xmlSettings.GetLoginDelay();

#if defined(ENABLE_TEAMTALKPRO)
certfile = Utf8ToUnicode(xmlSettings.GetCertificateFile().c_str());
@@ -1189,6 +1190,9 @@ void RunWizard(ServerXML& xmlSettings)
cout << "Maximum number of logins per IP-address, 0 = disabled: ";
max_logins_per_ip = printGetInt(max_logins_per_ip);

cout << "Delay in milliseconds before an IP-address can make another login, 0 = disabled: ";
logindelay = printGetInt(logindelay);

cout << endl << endl;
cout << "Your " << TEAMTALK_NAME << " is now configured with the following settings:" << endl;
cout << endl;
@@ -1228,6 +1232,8 @@ void RunWizard(ServerXML& xmlSettings)
else
cout << "Max logins per IP-address: " << "disabled" << endl;

cout << "Users wait for " << logindelay << " msec before attempting login again." << endl;

#if defined(ENABLE_TEAMTALKPRO)
cout << "Server certificate file for encryption: " << certfile << endl;
cout << "Server private key file for encryption: " << keyfile << endl;
@@ -1260,6 +1266,7 @@ void RunWizard(ServerXML& xmlSettings)
xmlSettings.SetServerLogMaxSize(log_maxsize);
xmlSettings.SetMaxLoginAttempts(max_login_attempts);
xmlSettings.SetMaxLoginsPerIP(max_logins_per_ip);
xmlSettings.SetLoginDelay(logindelay);
xmlSettings.SaveFile();

cout << "Changes saved." << endl;
@@ -489,6 +489,7 @@ void ServerGuard::OnSaveConfiguration(ServerNode& servernode, const ServerUser*
m_settings.SetMaxUsers(properties.maxusers);
m_settings.SetMaxLoginAttempts(properties.maxloginattempts);
m_settings.SetMaxLoginsPerIP(properties.max_logins_per_ipaddr);
m_settings.SetLoginDelay(properties.logindelay);
m_settings.SetUserTimeout(properties.usertimeout);
m_settings.SetVoiceTxLimit(properties.voicetxlimit);
m_settings.SetVideoCaptureTxLimit(properties.videotxlimit);
@@ -897,6 +898,7 @@ namespace teamtalk {
properties.maxusers = xmlSettings.GetMaxUsers() == UNDEFINED? MAX_USERS : xmlSettings.GetMaxUsers();
properties.max_logins_per_ipaddr = xmlSettings.GetMaxLoginsPerIP();
properties.maxloginattempts = xmlSettings.GetMaxLoginAttempts();
properties.logindelay = xmlSettings.GetLoginDelay();
properties.usertimeout = xmlSettings.GetUserTimeout();
properties.filesroot = Utf8ToUnicode(xmlSettings.GetFilesRoot().c_str());
properties.diskquota = xmlSettings.GetDefaultDiskQuota();
@@ -452,6 +452,26 @@ namespace teamtalk{
return nValue;
}

bool ServerXML::SetLoginDelay(int delaymsec)
{
TiXmlElement* parent = GetGeneralElement();
if(parent)
{
PutInteger(*parent, "login-delay-msec", delaymsec);
return true;
}
else
return false;
}

int ServerXML::GetLoginDelay()
{
int nValue = 0;
TiXmlElement* parent = GetGeneralElement();
if(parent)
GetInteger(*parent, "login-delay-msec", nValue);
return nValue;
}

bool ServerXML::SetUserTimeout(int nTimeoutSec)
{
@@ -87,6 +87,9 @@ namespace teamtalk {
bool SetMaxLoginsPerIP(int max_ip_logins);
int GetMaxLoginsPerIP();

bool SetLoginDelay(int delaymsec);
int GetLoginDelay();

/***** <bandwidth-limits> *****/

bool SetVoiceTxLimit(int tx_bytes_per_sec);
@@ -300,6 +300,11 @@ ACE_TString UptimeHours(const ACE_Time_Value& value)
return buf;
}

ACE_Time_Value ToTimeValue(int msec)
{
return ACE_Time_Value(msec / 1000, (msec % 1000) * 1000);
}

strings_t tokenize(const ACE_TString& source, const ACE_TString& delimeters)
{
vector<ACE_TString> tokens;
@@ -67,6 +67,8 @@ strings_t tokenize(const ACE_TString& source, const ACE_TString& delimeters);
ACE_TString KeyToHexString(const unsigned char* key, int length);
void HexStringToKey(const ACE_TString& crypt_key, unsigned char* key);

ACE_Time_Value ToTimeValue(int msec);

ACE_TString UptimeHours(const ACE_Time_Value& value);

#if defined(UNICODE)
@@ -32,7 +32,7 @@
#include <ace/SString.h>
#include "Common.h"

#define TEAMTALK_PROTOCOL_VERSION ACE_TEXT("5.4")
#define TEAMTALK_PROTOCOL_VERSION ACE_TEXT("5.5")

/* parameter names */
#define TT_USERID ACE_TEXT("userid")
@@ -129,6 +129,7 @@
#define TT_TRANSMITQUEUE ACE_TEXT("transmitqueue") // v5.2
#define TT_CMDFLOOD ACE_TEXT("cmdflood") // v5.3
#define TT_BANTYPE ACE_TEXT("type") // v5.3
#define TT_LOGINDELAY ACE_TEXT("logindelay") // v5.5

// Client ---> Server
// -------------------------
@@ -46,6 +46,7 @@ namespace teamtalk {
int maxusers;
int maxloginattempts; //max login attempts with wrong password
int max_logins_per_ipaddr;
int logindelay = 0; // msec before IP-address can log in again
ACE_INT64 diskquota; //max bytes for each channel to store files
ACE_INT64 maxdiskusage; //max bytes to use for storage of files
int usertimeout;
@@ -4278,6 +4278,7 @@ int ClientNode::DoUpdateServer(const ServerInfo& serverprop)
AppendProperty(TT_MAXUSERS, serverprop.maxusers, command);
AppendProperty(TT_MAXLOGINATTEMPTS, serverprop.maxloginattempts, command);
AppendProperty(TT_MAXLOGINSPERIP, serverprop.max_logins_per_ipaddr, command);
AppendProperty(TT_LOGINDELAY, serverprop.logindelay, command);
AppendProperty(TT_AUTOSAVE, serverprop.autosave, command);
if (serverprop.hostaddrs.size())
{
@@ -4770,6 +4771,7 @@ void ClientNode::HandleServerUpdate(const mstrings_t& properties)
GetProperty(properties, TT_MAXUSERS, m_serverinfo.maxusers);
GetProperty(properties, TT_MAXLOGINATTEMPTS, m_serverinfo.maxloginattempts);
GetProperty(properties, TT_MAXLOGINSPERIP, m_serverinfo.max_logins_per_ipaddr);
GetProperty(properties, TT_LOGINDELAY, m_serverinfo.logindelay);
GetProperty(properties, TT_USERTIMEOUT, m_serverinfo.usertimeout);
GetProperty(properties, TT_AUTOSAVE, m_serverinfo.autosave);
GetProperty(properties, TT_VOICETXLIMIT, m_serverinfo.voicetxlimit);
@@ -567,6 +567,15 @@ int ServerNode::TimerEvent(ACE_UINT32 timer_event_id, long userdata)

UpdateSoloTransmitChannels();

// erase login delays
ACE_Time_Value now = ACE_OS::gettimeofday();
for (auto it = m_logindelay.begin();it != m_logindelay.end();)
{
if (now > it->second + ToTimeValue(m_properties.logindelay * 2))
m_logindelay.erase(it++);
else
++it;
}
break;
}
case TIMER_DESKTOPACKPACKET_ID :
@@ -1004,7 +1013,8 @@ void ServerNode::StopServer(bool docallback)
}

TTASSERT(m_admins.empty());
m_mLoginAttempts.clear();
m_failedlogins.clear();
m_logindelay.clear();
m_filetransfers.clear();
m_updUserIPs.clear();

@@ -1225,27 +1235,43 @@ void ServerNode::IncLoginAttempt(const ServerUser& user)
ASSERT_REACTOR_LOCKED(this);

//ban user's IP if logged in to many times
mapiptime_t::iterator ite = m_mLoginAttempts.find(user.GetIpAddress());
if(ite == m_mLoginAttempts.end())
m_failedlogins[user.GetIpAddress()].push_back(ACE_OS::gettimeofday());

if (m_properties.maxloginattempts > 0 &&
m_failedlogins[user.GetIpAddress()].size() >= (size_t)m_properties.maxloginattempts)
{
vector<ACE_Time_Value> attempts;
attempts.push_back(ACE_OS::gettimeofday());
m_mLoginAttempts[user.GetIpAddress()] = attempts;
BannedUser ban;
ban.bantype = BANTYPE_DEFAULT;
ban.ipaddr = user.GetIpAddress();
m_srvguard->AddUserBan(user, ban);
if(IsAutoSaving())
m_srvguard->OnSaveConfiguration(*this, &user);
}
else
}

bool ServerNode::LoginsExceeded(const ServerUser& user)
{
ASSERT_REACTOR_LOCKED(this);

if (m_properties.logindelay == 0)
return false;

ACE_Time_Value now = ACE_OS::gettimeofday();
if (m_logindelay.find(user.GetIpAddress()) == m_logindelay.end())
{
ite->second.push_back(ACE_OS::gettimeofday());
if( m_properties.maxloginattempts > 0 &&
ite->second.size() >= (size_t)m_properties.maxloginattempts)
{
BannedUser ban;
ban.bantype = BANTYPE_DEFAULT;
ban.ipaddr = user.GetIpAddress();
m_srvguard->AddUserBan(user, ban);
if(IsAutoSaving())
m_srvguard->OnSaveConfiguration(*this, &user);
}
m_logindelay[user.GetIpAddress()] = now;
return false;
}

ACE_Time_Value delay = ToTimeValue(m_properties.logindelay);
if (m_logindelay[user.GetIpAddress()] + delay > now)
{
m_logindelay[user.GetIpAddress()] = now;
return true;
}
m_logindelay[user.GetIpAddress()] = now;

return false;
}

int ServerNode::SendPacket(const FieldPacket& packet,
@@ -2577,7 +2603,11 @@ ErrorMsg ServerNode::UserLogin(int userid, const ACE_TString& username,
switch(err.errorno)
{
case TT_CMDERR_SUCCESS :
{
if (LoginsExceeded(*user))
return TT_CMDERR_COMMAND_FLOOD;
break;
}
case TT_CMDERR_SERVER_BANNED :
m_srvguard->OnUserLoginBanned(*user);
return err; //banned from server
@@ -2636,7 +2666,7 @@ ErrorMsg ServerNode::UserLogin(int userid, const ACE_TString& username,
m_admins.push_back(user);

//clear any wrong logins
m_mLoginAttempts.erase(user->GetIpAddress());
m_failedlogins.erase(user->GetIpAddress());

//do connect accepted
user->DoAccepted(useraccount);
Oops, something went wrong.

0 comments on commit 6e3fc86

Please sign in to comment.