1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2014 Googolplexed <googol@googolplexed.net>
6  *   Copyright (C) 2013, 2017-2021 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
8  *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
9  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
10  *   Copyright (C) 2008, 2010 Craig Edwards <brain@inspircd.org>
11  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
12  *
13  * This file is part of InspIRCd.  InspIRCd is free software: you can
14  * redistribute it and/or modify it under the terms of the GNU General Public
15  * License as published by the Free Software Foundation, version 2.
16  *
17  * This program is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 #include "inspircd.h"
28 #include "xline.h"
29 #include "modules/server.h"
30 #include "modules/webirc.h"
31 
32 class ModuleConnectBan CXX11_FINAL
33 	: public Module
34 	, public ServerProtocol::LinkEventListener
35 	, public WebIRC::EventListener
36 {
37  private:
38 	typedef std::map<irc::sockets::cidr_mask, unsigned int> ConnectMap;
39 
40 	ConnectMap connects;
41 	unsigned int threshold;
42 	unsigned int banduration;
43 	unsigned int ipv4_cidr;
44 	unsigned int ipv6_cidr;
45 	unsigned long bootwait;
46 	unsigned long splitwait;
47 	time_t ignoreuntil;
48 	std::string banmessage;
49 
GetRange(LocalUser * user)50 	unsigned char GetRange(LocalUser* user)
51 	{
52 		int family = user->client_sa.family();
53 		switch (family)
54 		{
55 			case AF_INET:
56 				return ipv4_cidr;
57 
58 			case AF_INET6:
59 				return ipv6_cidr;
60 
61 			case AF_UNIX:
62 				// Ranges for UNIX sockets are ignored entirely.
63 				return 0;
64 		}
65 
66 		// If we have reached this point then we have encountered a bug.
67 		ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: ModuleConnectBan::GetRange(): socket type %d is unknown!", family);
68 		return 0;
69 	}
70 
IsExempt(LocalUser * user)71 	static bool IsExempt(LocalUser* user)
72 	{
73 		// E-lined and already banned users shouldn't be hit.
74 		if (user->exempt || user->quitting)
75 			return true;
76 
77 		// Users in an exempt class shouldn't be hit.
78 		return user->GetClass() && !user->GetClass()->config->getBool("useconnectban", true);
79 	}
80 
81  public:
82 	// Stop GCC warnings about the deprecated OnServerSplit event.
83 	using ServerProtocol::LinkEventListener::OnServerSplit;
84 
ModuleConnectBan()85 	ModuleConnectBan()
86 		: ServerProtocol::LinkEventListener(this)
87 		, WebIRC::EventListener(this)
88 		, ignoreuntil(0)
89 	{
90 	}
91 
Prioritize()92 	void Prioritize() CXX11_OVERRIDE
93 	{
94 		Module* corexline = ServerInstance->Modules->Find("core_xline");
95 		ServerInstance->Modules->SetPriority(this, I_OnSetUserIP, PRIORITY_AFTER, corexline);
96 	}
97 
GetVersion()98 	Version GetVersion() CXX11_OVERRIDE
99 	{
100 		return Version("Z-lines IP addresses which make excessive connections to the server.", VF_VENDOR);
101 	}
102 
ReadConfig(ConfigStatus & status)103 	void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
104 	{
105 		ConfigTag* tag = ServerInstance->Config->ConfValue("connectban");
106 
107 		ipv4_cidr = tag->getUInt("ipv4cidr", 32, 1, 32);
108 		ipv6_cidr = tag->getUInt("ipv6cidr", 128, 1, 128);
109 		threshold = tag->getUInt("threshold", 10, 1);
110 		bootwait = tag->getDuration("bootwait", 60*2);
111 		splitwait = tag->getDuration("splitwait", 60*2);
112 		banduration = tag->getDuration("duration", 10*60, 1);
113 		banmessage = tag->getString("banmessage", "Your IP range has been attempting to connect too many times in too short a duration. Wait a while, and you will be able to connect.");
114 
115 		if (status.initial)
116 			ignoreuntil = ServerInstance->Time() + bootwait;
117 	}
118 
OnWebIRCAuth(LocalUser * user,const WebIRC::FlagMap * flags)119 	void OnWebIRCAuth(LocalUser* user, const WebIRC::FlagMap* flags) CXX11_OVERRIDE
120 	{
121 		if (IsExempt(user))
122 			return;
123 
124 		// HACK: Lower the connection attempts for the gateway IP address. The user
125 		// will be rechecked for connect spamming shortly after when their IP address
126 		// is changed and OnSetUserIP is called.
127 		irc::sockets::cidr_mask mask(user->client_sa, GetRange(user));
128 		ConnectMap::iterator iter = connects.find(mask);
129 		if (iter != connects.end() && iter->second)
130 			iter->second--;
131 	}
132 
OnServerSplit(const Server * server,bool error)133 	void OnServerSplit(const Server* server, bool error) CXX11_OVERRIDE
134 	{
135 		if (splitwait)
136 			ignoreuntil = std::max<time_t>(ignoreuntil, ServerInstance->Time() + splitwait);
137 	}
138 
OnSetUserIP(LocalUser * u)139 	void OnSetUserIP(LocalUser* u) CXX11_OVERRIDE
140 	{
141 		if (IsExempt(u) || ignoreuntil > ServerInstance->Time())
142 			return;
143 
144 		irc::sockets::cidr_mask mask(u->client_sa, GetRange(u));
145 		ConnectMap::iterator i = connects.find(mask);
146 
147 		if (i != connects.end())
148 		{
149 			i->second++;
150 
151 			if (i->second >= threshold)
152 			{
153 				// Create Z-line for set duration.
154 				ZLine* zl = new ZLine(ServerInstance->Time(), banduration, ServerInstance->Config->ServerName, banmessage, mask.str());
155 				if (!ServerInstance->XLines->AddLine(zl, NULL))
156 				{
157 					delete zl;
158 					return;
159 				}
160 				ServerInstance->XLines->ApplyLines();
161 				std::string maskstr = mask.str();
162 				ServerInstance->SNO->WriteGlobalSno('x', "Z-line added by module m_connectban on %s to expire in %s (on %s): Connect flooding",
163 					maskstr.c_str(), InspIRCd::DurationString(zl->duration).c_str(), InspIRCd::TimeString(zl->expiry).c_str());
164 				ServerInstance->SNO->WriteGlobalSno('a', "Connect flooding from IP range %s (%d)", maskstr.c_str(), threshold);
165 				connects.erase(i);
166 			}
167 		}
168 		else
169 		{
170 			connects[mask] = 1;
171 		}
172 	}
173 
OnGarbageCollect()174 	void OnGarbageCollect() CXX11_OVERRIDE
175 	{
176 		ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Clearing map.");
177 		connects.clear();
178 	}
179 };
180 
181 MODULE_INIT(ModuleConnectBan)
182