]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_cloaking.cpp
Remove legacy code, mostly related to 1.2 compatibility
[user/henk/code/inspircd.git] / src / modules / m_cloaking.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
7  *   Copyright (C) 2003-2008 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
11  *
12  * This file is part of InspIRCd.  InspIRCd is free software: you can
13  * redistribute it and/or modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25
26 #include "inspircd.h"
27 #include "hash.h"
28
29 /* $ModDesc: Provides masking of user hostnames */
30
31 enum CloakMode
32 {
33         /** 2.0 cloak of "half" of the hostname plus the full IP hash */
34         MODE_HALF_CLOAK,
35         /** 2.0 cloak of IP hash, split at 2 common CIDR range points */
36         MODE_OPAQUE
37 };
38
39 // lowercase-only encoding similar to base64, used for hash output
40 static const char base32[] = "0123456789abcdefghijklmnopqrstuv";
41
42 /** Handles user mode +x
43  */
44 class CloakUser : public ModeHandler
45 {
46  public:
47         LocalStringExt ext;
48
49         std::string debounce_uid;
50         time_t debounce_ts;
51         int debounce_count;
52
53         CloakUser(Module* source)
54                 : ModeHandler(source, "cloak", 'x', PARAM_NONE, MODETYPE_USER),
55                 ext("cloaked_host", source), debounce_ts(0), debounce_count(0)
56         {
57         }
58
59         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
60         {
61                 LocalUser* user = IS_LOCAL(dest);
62
63                 /* For remote clients, we don't take any action, we just allow it.
64                  * The local server where they are will set their cloak instead.
65                  * This is fine, as we will receive it later.
66                  */
67                 if (!user)
68                 {
69                         dest->SetMode('x',adding);
70                         return MODEACTION_ALLOW;
71                 }
72
73                 if (user->uuid == debounce_uid && debounce_ts == ServerInstance->Time())
74                 {
75                         // prevent spamming using /mode user +x-x+x-x+x-x
76                         if (++debounce_count > 2)
77                                 return MODEACTION_DENY;
78                 }
79                 else
80                 {
81                         debounce_uid = user->uuid;
82                         debounce_count = 1;
83                         debounce_ts = ServerInstance->Time();
84                 }
85
86                 if (adding == user->IsModeSet('x'))
87                         return MODEACTION_DENY;
88
89                 /* don't allow this user to spam modechanges */
90                 if (source == dest)
91                         user->CommandFloodPenalty += 5000;
92
93                 if (adding)
94                 {
95                         std::string* cloak = ext.get(user);
96
97                         if (!cloak)
98                         {
99                                 /* Force creation of missing cloak */
100                                 creator->OnUserConnect(user);
101                                 cloak = ext.get(user);
102                         }
103                         if (cloak)
104                         {
105                                 user->ChangeDisplayedHost(cloak->c_str());
106                                 user->SetMode('x',true);
107                                 return MODEACTION_ALLOW;
108                         }
109                         else
110                                 return MODEACTION_DENY;
111                 }
112                 else
113                 {
114                         /* User is removing the mode, so restore their real host
115                          * and make it match the displayed one.
116                          */
117                         user->SetMode('x',false);
118                         user->ChangeDisplayedHost(user->host.c_str());
119                         return MODEACTION_ALLOW;
120                 }
121         }
122
123 };
124
125 class CommandCloak : public Command
126 {
127  public:
128         CommandCloak(Module* Creator) : Command(Creator, "CLOAK", 1)
129         {
130                 flags_needed = 'o';
131                 syntax = "<host>";
132         }
133
134         CmdResult Handle(const std::vector<std::string> &parameters, User *user);
135 };
136
137 class ModuleCloaking : public Module
138 {
139  public:
140         CloakUser cu;
141         CloakMode mode;
142         CommandCloak ck;
143         std::string prefix;
144         std::string suffix;
145         std::string key;
146         const char* xtab[4];
147         dynamic_reference<HashProvider> Hash;
148
149         ModuleCloaking() : cu(this), mode(MODE_OPAQUE), ck(this), Hash(this, "hash/md5")
150         {
151         }
152
153         void init()
154         {
155                 OnRehash(NULL);
156
157                 ServerInstance->Modules->AddService(cu);
158                 ServerInstance->Modules->AddService(ck);
159                 ServerInstance->Modules->AddService(cu.ext);
160
161                 Implementation eventlist[] = { I_OnRehash, I_OnCheckBan, I_OnUserConnect, I_OnChangeHost };
162                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
163         }
164
165         /** This function takes a domain name string and returns just the last two domain parts,
166          * or the last domain part if only two are available. Failing that it just returns what it was given.
167          *
168          * For example, if it is passed "svn.inspircd.org" it will return ".inspircd.org".
169          * If it is passed "brainbox.winbot.co.uk" it will return ".co.uk",
170          * and if it is passed "localhost.localdomain" it will return ".localdomain".
171          *
172          * This is used to ensure a significant part of the host is always cloaked (see Bug #216)
173          */
174         std::string LastTwoDomainParts(const std::string &host)
175         {
176                 int dots = 0;
177                 std::string::size_type splitdot = host.length();
178
179                 for (std::string::size_type x = host.length() - 1; x; --x)
180                 {
181                         if (host[x] == '.')
182                         {
183                                 splitdot = x;
184                                 dots++;
185                         }
186                         if (dots >= 3)
187                                 break;
188                 }
189
190                 if (splitdot == host.length())
191                         return "";
192                 else
193                         return host.substr(splitdot);
194         }
195
196         /**
197          * 2.0-style cloaking function
198          * @param item The item to cloak (part of an IP or hostname)
199          * @param id A unique ID for this type of item (to make it unique if the item matches)
200          * @param len The length of the output. Maximum for MD5 is 16 characters.
201          */
202         std::string SegmentCloak(const std::string& item, char id, int len)
203         {
204                 std::string input;
205                 input.reserve(key.length() + 3 + item.length());
206                 input.append(1, id);
207                 input.append(key);
208                 input.append(1, '\0'); // null does not terminate a C++ string
209                 input.append(item);
210
211                 std::string rv = Hash->sum(input).substr(0,len);
212                 for(int i=0; i < len; i++)
213                 {
214                         // this discards 3 bits per byte. We have an
215                         // overabundance of bits in the hash output, doesn't
216                         // matter which ones we are discarding.
217                         rv[i] = base32[rv[i] & 0x1F];
218                 }
219                 return rv;
220         }
221
222         std::string SegmentIP(const irc::sockets::sockaddrs& ip, bool full)
223         {
224                 std::string bindata;
225                 int hop1, hop2, hop3;
226                 int len1, len2;
227                 std::string rv;
228                 if (ip.sa.sa_family == AF_INET6)
229                 {
230                         bindata = std::string((const char*)ip.in6.sin6_addr.s6_addr, 16);
231                         hop1 = 8;
232                         hop2 = 6;
233                         hop3 = 4;
234                         len1 = 6;
235                         len2 = 4;
236                         // pfx s1.s2.s3. (xxxx.xxxx or s4) sfx
237                         //     6  4  4    9/6
238                         rv.reserve(prefix.length() + 26 + suffix.length());
239                 }
240                 else
241                 {
242                         bindata = std::string((const char*)&ip.in4.sin_addr, 4);
243                         hop1 = 3;
244                         hop2 = 0;
245                         hop3 = 2;
246                         len1 = len2 = 3;
247                         // pfx s1.s2. (xxx.xxx or s3) sfx
248                         rv.reserve(prefix.length() + 15 + suffix.length());
249                 }
250
251                 rv.append(prefix);
252                 rv.append(SegmentCloak(bindata, 10, len1));
253                 rv.append(1, '.');
254                 bindata.erase(hop1);
255                 rv.append(SegmentCloak(bindata, 11, len2));
256                 if (hop2)
257                 {
258                         rv.append(1, '.');
259                         bindata.erase(hop2);
260                         rv.append(SegmentCloak(bindata, 12, len2));
261                 }
262
263                 if (full)
264                 {
265                         rv.append(1, '.');
266                         bindata.erase(hop3);
267                         rv.append(SegmentCloak(bindata, 13, 6));
268                         rv.append(suffix);
269                 }
270                 else
271                 {
272                         char buf[50];
273                         if (ip.sa.sa_family == AF_INET6)
274                         {
275                                 snprintf(buf, 50, ".%02x%02x.%02x%02x%s",
276                                         ip.in6.sin6_addr.s6_addr[2], ip.in6.sin6_addr.s6_addr[3],
277                                         ip.in6.sin6_addr.s6_addr[0], ip.in6.sin6_addr.s6_addr[1], suffix.c_str());
278                         }
279                         else
280                         {
281                                 const unsigned char* ip4 = (const unsigned char*)&ip.in4.sin_addr;
282                                 snprintf(buf, 50, ".%d.%d%s", ip4[1], ip4[0], suffix.c_str());
283                         }
284                         rv.append(buf);
285                 }
286                 return rv;
287         }
288
289         ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask)
290         {
291                 LocalUser* lu = IS_LOCAL(user);
292                 if (!lu)
293                         return MOD_RES_PASSTHRU;
294
295                 OnUserConnect(lu);
296                 std::string* cloak = cu.ext.get(user);
297                 /* Check if they have a cloaked host, but are not using it */
298                 if (cloak && *cloak != user->dhost)
299                 {
300                         char cmask[MAXBUF];
301                         snprintf(cmask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), cloak->c_str());
302                         if (InspIRCd::Match(cmask,mask))
303                                 return MOD_RES_DENY;
304                 }
305                 return MOD_RES_PASSTHRU;
306         }
307
308         void Prioritize()
309         {
310                 /* Needs to be after m_banexception etc. */
311                 ServerInstance->Modules->SetPriority(this, I_OnCheckBan, PRIORITY_LAST);
312         }
313
314         // this unsets umode +x on every host change. If we are actually doing a +x
315         // mode change, we will call SetMode back to true AFTER the host change is done.
316         void OnChangeHost(User* u, const std::string& host)
317         {
318                 if(u->IsModeSet('x'))
319                 {
320                         u->SetMode('x', false);
321                         u->WriteServ("MODE %s -x", u->nick.c_str());
322                 }
323         }
324
325         ~ModuleCloaking()
326         {
327         }
328
329         Version GetVersion()
330         {
331                 std::string testcloak = "broken";
332                 if (Hash)
333                 {
334                         switch (mode)
335                         {
336                                 case MODE_HALF_CLOAK:
337                                         testcloak = prefix + SegmentCloak("*", 3, 8) + suffix;
338                                         break;
339                                 case MODE_OPAQUE:
340                                         testcloak = prefix + SegmentCloak("*", 4, 8) + suffix;
341                         }
342                 }
343                 return Version("Provides masking of user hostnames", VF_COMMON|VF_VENDOR, testcloak);
344         }
345
346         void OnRehash(User* user)
347         {
348                 ConfigTag* tag = ServerInstance->Config->ConfValue("cloak");
349                 prefix = tag->getString("prefix");
350                 suffix = tag->getString("suffix", ".IP");
351
352                 std::string modestr = tag->getString("mode");
353                 if (modestr == "half")
354                         mode = MODE_HALF_CLOAK;
355                 else if (modestr == "full")
356                         mode = MODE_OPAQUE;
357                 else
358                         throw ModuleException("Bad value for <cloak:mode>; must be half or full");
359
360                 key = tag->getString("key");
361                 if (key.empty() || key == "secret")
362                         throw ModuleException("You have not defined cloak keys for m_cloaking. Define <cloak:key> as a network-wide secret.");
363         }
364
365         std::string GenCloak(const irc::sockets::sockaddrs& ip, const std::string& ipstr, const std::string& host)
366         {
367                 std::string chost;
368
369                 switch (mode)
370                 {
371                         case MODE_HALF_CLOAK:
372                         {
373                                 if (ipstr != host)
374                                         chost = prefix + SegmentCloak(host, 1, 6) + LastTwoDomainParts(host);
375                                 if (chost.empty() || chost.length() > 50)
376                                         chost = SegmentIP(ip, false);
377                                 break;
378                         }
379                         case MODE_OPAQUE:
380                         default:
381                                 chost = SegmentIP(ip, true);
382                 }
383                 return chost;
384         }
385
386         void OnUserConnect(LocalUser* dest)
387         {
388                 std::string* cloak = cu.ext.get(dest);
389                 if (cloak)
390                         return;
391
392                 cu.ext.set(dest, GenCloak(dest->client_sa, dest->GetIPString(), dest->host));
393         }
394 };
395
396 CmdResult CommandCloak::Handle(const std::vector<std::string> &parameters, User *user)
397 {
398         ModuleCloaking* mod = (ModuleCloaking*)(Module*)creator;
399         irc::sockets::sockaddrs sa;
400         std::string cloak;
401
402         if (irc::sockets::aptosa(parameters[0], 0, sa))
403                 cloak = mod->GenCloak(sa, parameters[0], parameters[0]);
404         else
405                 cloak = mod->GenCloak(sa, "", parameters[0]);
406
407         user->WriteServ("NOTICE %s :*** Cloak for %s is %s", user->nick.c_str(), parameters[0].c_str(), cloak.c_str());
408
409         return CMD_SUCCESS;
410 }
411
412 MODULE_INIT(ModuleCloaking)