]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_cgiirc.cpp
Extra debug
[user/henk/code/inspircd.git] / src / modules / m_cgiirc.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15
16 #ifndef WINDOWS
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #endif
21
22 /* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
23
24 enum CGItype { INVALID, PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
25
26
27 /** Holds a CGI site's details
28  */
29 class CGIhost : public classbase
30 {
31 public:
32         std::string hostmask;
33         CGItype type;
34         std::string password;
35
36         CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &spassword ="")
37         : hostmask(mask), type(t), password(spassword)
38         {
39         }
40 };
41 typedef std::vector<CGIhost> CGIHostlist;
42
43 /*
44  * WEBIRC
45  *  This is used for the webirc method of CGIIRC auth, and is (really) the best way to do these things.
46  *  Syntax: WEBIRC password client hostname ip
47  *  Where password is a shared key, client is the name of the "client" and version (e.g. cgiirc), hostname
48  *  is the resolved host of the client issuing the command and IP is the real IP of the client.
49  *
50  * How it works:
51  *  To tie in with the rest of cgiirc module, and to avoid race conditions, /webirc is only processed locally
52  *  and simply sets metadata on the user, which is later decoded on full connect to give something meaningful.
53  */
54 class CommandWebirc : public Command
55 {
56         CGIHostlist Hosts;
57         bool notify;
58         public:
59                 CommandWebirc(InspIRCd* Instance, CGIHostlist &cHosts, bool bnotify) : Command(Instance, "WEBIRC", 0, 4, true), Hosts(cHosts), notify(bnotify)
60                 {
61                         this->source = "m_cgiirc.so";
62                         this->syntax = "password client hostname ip";
63                 }
64                 CmdResult Handle(const char* const* parameters, int pcnt, User *user)
65                 {
66                         if(user->registered == REG_ALL)
67                                 return CMD_FAILURE;
68                         
69                         for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
70                         {
71                                 if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
72                                 {
73                                         if(iter->type == WEBIRC && parameters[0] == iter->password)
74                                         {
75                                                 user->Extend("cgiirc_realhost", new std::string(user->host));
76                                                 user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
77                                                 if (notify)
78                                                         ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick, user->host, parameters[2], user->host);
79                                                 user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2]));
80                                                 user->Extend("cgiirc_webirc_ip", new std::string(parameters[3]));
81                                                 return CMD_LOCALONLY;
82                                         }
83                                 }
84                         }
85                         return CMD_FAILURE;
86                 }
87 };
88
89
90 /** Resolver for CGI:IRC hostnames encoded in ident/GECOS
91  */
92 class CGIResolver : public Resolver
93 {
94         std::string typ;
95         int theirfd;
96         User* them;
97         bool notify;
98  public:
99         CGIResolver(Module* me, InspIRCd* Instance, bool NotifyOpers, const std::string &source, bool forward, User* u, int userfd, const std::string &type, bool &cached)
100                 : Resolver(Instance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { }
101
102         virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached, int resultnum = 0)
103         {
104                 if (resultnum)
105                         return;
106
107                 /* Check the user still exists */
108                 if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
109                 {
110                         if (notify)
111                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick, them->host, result.c_str(), typ.c_str());
112
113                         strlcpy(them->host, result.c_str(), 63);
114                         strlcpy(them->dhost, result.c_str(), 63);
115                         strlcpy(them->ident, "~cgiirc", 8);
116                         them->InvalidateCache();
117                 }
118         }
119
120         virtual void OnError(ResolverError e, const std::string &errormessage)
121         {
122                 if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
123                 {
124                         if (notify)
125                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), but their host can't be resolved from their %s!", them->nick, them->host,typ.c_str());
126                 }
127         }
128
129         virtual ~CGIResolver()
130         {
131         }
132 };
133
134 class ModuleCgiIRC : public Module
135 {
136         CommandWebirc* mycommand;
137         bool NotifyOpers;
138         CGIHostlist Hosts;
139 public:
140         ModuleCgiIRC(InspIRCd* Me) : Module(Me)
141         {
142                 
143                 OnRehash(NULL,"");
144                 mycommand = new CommandWebirc(Me, Hosts, NotifyOpers);
145                 ServerInstance->AddCommand(mycommand);
146
147                 Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnCleanup, I_OnSyncUserMetaData, I_OnDecodeMetaData, I_OnUserDisconnect, I_OnUserConnect };
148                 ServerInstance->Modules->Attach(eventlist, this, 7);
149         }
150
151         
152         virtual void Prioritize()
153         {
154                 ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIO_FIRST);
155         }
156
157         virtual void OnRehash(User* user, const std::string &parameter)
158         {
159                 ConfigReader Conf(ServerInstance);
160                 Hosts.clear();
161                 
162                 NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed.
163                 
164                 if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
165                         NotifyOpers = true;
166                 
167                 for(int i = 0; i < Conf.Enumerate("cgihost"); i++)
168                 {
169                         std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host
170                         std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host.
171                         std::string password = Conf.ReadValue("cgihost", "password", i);
172                         
173                         if(hostmask.length())
174                         {
175                                 if (type == "webirc" && !password.length()) {
176                                                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
177                                 }
178                                 else
179                                 {
180                                         CGItype cgitype = INVALID;
181                                         if (type == "pass")
182                                                 cgitype = PASS;
183                                         else if (type == "ident")
184                                                 cgitype = IDENT;
185                                         else if (type == "passfirst")
186                                                 cgitype = PASSFIRST;
187                                         else if (type == "webirc")
188                                         {
189                                                 cgitype = WEBIRC;
190                                         }
191
192                                         if (cgitype == INVALID)
193                                                 cgitype = PASS;
194
195                                         Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" ));
196                                 }
197                         }
198                         else
199                         {
200                                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
201                                 continue;
202                         }
203                 }
204         }
205
206         virtual void OnCleanup(int target_type, void* item)
207         {
208                 if(target_type == TYPE_USER)
209                 {
210                         User* user = (User*)item;
211                         std::string* realhost;
212                         std::string* realip;
213                         
214                         if(user->GetExt("cgiirc_realhost", realhost))
215                         {
216                                 delete realhost;
217                                 user->Shrink("cgiirc_realhost");
218                         }
219                         
220                         if(user->GetExt("cgiirc_realip", realip))
221                         {
222                                 delete realip;
223                                 user->Shrink("cgiirc_realip");
224                         }
225                 }
226         }
227         
228         virtual void OnSyncUserMetaData(User* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
229         {
230                 if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip"))
231                 {
232                         std::string* data;
233                         
234                         if(user->GetExt(extname, data))
235                         {
236                                 proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data);
237                         }
238                 }
239         }
240
241         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
242         {
243                 if(target_type == TYPE_USER)
244                 {
245                         User* dest = (User*)target;
246                         std::string* bleh;
247                         if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh)))
248                         {
249                                 dest->Extend(extname, new std::string(extdata));
250                         }
251                 }
252         }
253
254         virtual void OnUserDisconnect(User* user)
255         {
256                 OnCleanup(TYPE_USER, user);
257         }
258         
259
260         virtual int OnUserRegister(User* user)
261         {       
262                 for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
263                 {                       
264                         if(ServerInstance->MatchText(user->host, iter->hostmask) || ServerInstance->MatchText(user->GetIPString(), iter->hostmask))
265                         {
266                                 // Deal with it...
267                                 if(iter->type == PASS)
268                                 {
269                                         CheckPass(user); // We do nothing if it fails so...
270                                 }
271                                 else if(iter->type == PASSFIRST && !CheckPass(user))
272                                 {
273                                         // If the password lookup failed, try the ident
274                                         CheckIdent(user);       // If this fails too, do nothing
275                                 }
276                                 else if(iter->type == IDENT)
277                                 {
278                                         CheckIdent(user); // Nothing on failure.
279                                 }
280                                 else if(iter->type == IDENTFIRST && !CheckIdent(user))
281                                 {
282                                         // If the ident lookup fails, try the password.
283                                         CheckPass(user);
284                                 }
285                                 else if(iter->type == WEBIRC)
286                                 {
287                                         // We don't need to do anything here
288                                 }
289                                 return 0;
290                         }
291                 }
292                 return 0;
293         }
294
295         virtual void OnUserConnect(User* user)
296         {
297                 std::string *webirc_hostname, *webirc_ip;
298                 if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname))
299                 {
300                         strlcpy(user->host,webirc_hostname->c_str(),63);
301                         strlcpy(user->dhost,webirc_hostname->c_str(),63);
302                         delete webirc_hostname;
303                         user->InvalidateCache();
304                         user->Shrink("cgiirc_webirc_hostname");
305                 }
306                 if(user->GetExt("cgiirc_webirc_ip", webirc_ip))
307                 {
308                         bool valid=false;
309                         ServerInstance->Users->RemoveCloneCounts(user);
310 #ifdef IPV6
311                         valid = (inet_pton(AF_INET6, webirc_ip->c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0); 
312
313                         if(!valid)
314                                 valid = (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr));
315 #else
316                         if (inet_aton(webirc_ip->c_str(), &((sockaddr_in*)user->ip)->sin_addr))
317                                 valid = true;
318 #endif
319
320                         delete webirc_ip;
321                         user->InvalidateCache();
322                         user->Shrink("cgiirc_webirc_ip");
323                         ServerInstance->Users->AddLocalClone(user);
324                         ServerInstance->Users->AddGlobalClone(user);
325                         user->CheckClass();
326                 }
327         }
328
329         bool CheckPass(User* user)
330         {
331                 if(IsValidHost(user->password))
332                 {
333                         user->Extend("cgiirc_realhost", new std::string(user->host));
334                         user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
335                         strlcpy(user->host, user->password, 64);
336                         strlcpy(user->dhost, user->password, 64);
337                         user->InvalidateCache();
338
339                         bool valid = false;
340                         ServerInstance->Users->RemoveCloneCounts(user);
341 #ifdef IPV6
342                         if (user->GetProtocolFamily() == AF_INET6)
343                                 valid = (inet_pton(AF_INET6, user->password, &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
344                         else
345                                 valid = (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr));
346 #else
347                         if (inet_aton(user->password, &((sockaddr_in*)user->ip)->sin_addr))
348                                 valid = true;
349 #endif
350                         ServerInstance->Users->AddLocalClone(user);
351                         ServerInstance->Users->AddGlobalClone(user);
352                         user->CheckClass();
353
354                         if (valid)
355                         {
356                                 /* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */
357                                 if(NotifyOpers)
358                                         ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);
359                         }
360                         else
361                         {
362                                 /* We got as resolved hostname in the password. */
363                                 try
364                                 {
365
366                                         bool cached;
367                                         CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached);
368                                         ServerInstance->AddResolver(r, cached);
369                                 }
370                                 catch (...)
371                                 {
372                                         if (NotifyOpers)
373                                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
374                                 }
375                         }
376                         
377                         *user->password = 0;
378
379                         /*if(NotifyOpers)
380                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick, user->host, user->password);*/
381
382                         return true;
383                 }
384                 
385                 return false;
386         }
387         
388         bool CheckIdent(User* user)
389         {
390                 int ip[4];
391                 char* ident;
392                 char newip[16];
393                 int len = strlen(user->ident);
394                 
395                 if(len == 8)
396                         ident = user->ident;
397                 else if(len == 9 && *user->ident == '~')
398                         ident = user->ident+1;
399                 else
400                         return false;
401         
402                 for(int i = 0; i < 4; i++)
403                         if(!HexToInt(ip[i], ident + i*2))
404                                 return false;
405
406                 snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
407                         
408                 user->Extend("cgiirc_realhost", new std::string(user->host));
409                 user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
410                 ServerInstance->Users->RemoveCloneCounts(user);
411 #ifdef IPV6
412                 if (user->GetProtocolFamily() == AF_INET6)
413                         inet_pton(AF_INET6, newip, &((sockaddr_in6*)user->ip)->sin6_addr);
414                 else
415 #endif
416                 inet_aton(newip, &((sockaddr_in*)user->ip)->sin_addr);
417                 ServerInstance->Users->AddLocalClone(user);
418                 ServerInstance->Users->AddGlobalClone(user);
419                 user->CheckClass();
420                 try
421                 {
422                         strlcpy(user->host, newip, 16);
423                         strlcpy(user->dhost, newip, 16);
424                         strlcpy(user->ident, "~cgiirc", 8);
425
426                         bool cached;
427                         CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached);
428                         ServerInstance->AddResolver(r, cached);
429                 }
430                 catch (...)
431                 {
432                         strlcpy(user->host, newip, 16);
433                         strlcpy(user->dhost, newip, 16);
434                         strlcpy(user->ident, "~cgiirc", 8);
435                         user->InvalidateCache();
436
437                         if(NotifyOpers)
438                                  ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), but i could not resolve their hostname!", user->nick, user->host);
439                 }
440                 /*strlcpy(user->host, newip, 16);
441                 strlcpy(user->dhost, newip, 16);
442                 strlcpy(user->ident, "~cgiirc", 8);*/
443
444                 return true;
445         }
446         
447         bool IsValidHost(const std::string &host)
448         {
449                 if(!host.size())
450                         return false;
451         
452                 for(unsigned int i = 0; i < host.size(); i++)
453                 {
454                         if(     ((host[i] >= '0') && (host[i] <= '9')) ||
455                                         ((host[i] >= 'A') && (host[i] <= 'Z')) ||
456                                         ((host[i] >= 'a') && (host[i] <= 'z')) ||
457                                         ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
458                                         ((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
459                                         
460                                 continue;
461                         else
462                                 return false;
463                 }
464                 
465                 return true;
466         }
467
468         bool IsValidIP(const std::string &ip)
469         {
470                 if(ip.size() < 7 || ip.size() > 15)
471                         return false;
472         
473                 short sincedot = 0;
474                 short dots = 0;
475         
476                 for(unsigned int i = 0; i < ip.size(); i++)
477                 {
478                         if((dots <= 3) && (sincedot <= 3))
479                         {
480                                 if((ip[i] >= '0') && (ip[i] <= '9'))
481                                 {
482                                         sincedot++;
483                                 }
484                                 else if(ip[i] == '.')
485                                 {
486                                         sincedot = 0;
487                                         dots++;
488                                 }
489                         }
490                         else
491                         {
492                                 return false;
493                         
494                         }
495                 }
496                 
497                 if(dots != 3)
498                         return false;
499                 
500                 return true;
501         }
502         
503         bool HexToInt(int &out, const char* in)
504         {
505                 char ip[3];
506                 ip[0] = in[0];
507                 ip[1] = in[1];
508                 ip[2] = 0;
509                 out = strtol(ip, NULL, 16);
510                 
511                 if(out > 255 || out < 0)
512                         return false;
513
514                 return true;
515         }
516         
517         virtual ~ModuleCgiIRC()
518         {
519         }
520          
521         virtual Version GetVersion()
522         {
523                 return Version(1,2,0,0,VF_VENDOR,API_VERSION);
524         }
525         
526 };
527
528 MODULE_INIT(ModuleCgiIRC)