The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

m_cgiirc.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 #include "inspircd.h"
00015 
00016 #ifndef WINDOWS
00017 #include <sys/socket.h>
00018 #include <netinet/in.h>
00019 #include <arpa/inet.h>
00020 #endif
00021 
00022 /* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */
00023 
00024 enum CGItype { INVALID, PASS, IDENT, PASSFIRST, IDENTFIRST, WEBIRC };
00025 
00026 
00029 class CGIhost : public classbase
00030 {
00031 public:
00032         std::string hostmask;
00033         CGItype type;
00034         std::string password;
00035 
00036         CGIhost(const std::string &mask = "", CGItype t = IDENTFIRST, const std::string &spassword ="")
00037         : hostmask(mask), type(t), password(spassword)
00038         {
00039         }
00040 };
00041 typedef std::vector<CGIhost> CGIHostlist;
00042 
00043 /*
00044  * WEBIRC
00045  *  This is used for the webirc method of CGIIRC auth, and is (really) the best way to do these things.
00046  *  Syntax: WEBIRC password client hostname ip
00047  *  Where password is a shared key, client is the name of the "client" and version (e.g. cgiirc), hostname
00048  *  is the resolved host of the client issuing the command and IP is the real IP of the client.
00049  *
00050  * How it works:
00051  *  To tie in with the rest of cgiirc module, and to avoid race conditions, /webirc is only processed locally
00052  *  and simply sets metadata on the user, which is later decoded on full connect to give something meaningful.
00053  */
00054 class CommandWebirc : public Command
00055 {
00056         CGIHostlist Hosts;
00057         bool notify;
00058         public:
00059                 CommandWebirc(InspIRCd* Instance, CGIHostlist &cHosts, bool bnotify) : Command(Instance, "WEBIRC", 0, 4, true), Hosts(cHosts), notify(bnotify)
00060                 {
00061                         this->source = "m_cgiirc.so";
00062                         this->syntax = "password client hostname ip";
00063                 }
00064                 CmdResult Handle(const std::vector<std::string> &parameters, User *user)
00065                 {
00066                         if(user->registered == REG_ALL)
00067                                 return CMD_FAILURE;
00068 
00069                         for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
00070                         {
00071                                 if(InspIRCd::Match(user->host, iter->hostmask) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask))
00072                                 {
00073                                         if(iter->type == WEBIRC && parameters[0] == iter->password)
00074                                         {
00075                                                 user->Extend("cgiirc_realhost", new std::string(user->host));
00076                                                 user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
00077                                                 if (notify)
00078                                                         ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", user->nick.c_str(), user->host.c_str(), parameters[2].c_str(), user->host.c_str());
00079                                                 user->Extend("cgiirc_webirc_hostname", new std::string(parameters[2]));
00080                                                 user->Extend("cgiirc_webirc_ip", new std::string(parameters[3]));
00081                                                 return CMD_LOCALONLY;
00082                                         }
00083                                 }
00084                         }
00085                         return CMD_FAILURE;
00086                 }
00087 };
00088 
00089 
00092 class CGIResolver : public Resolver
00093 {
00094         std::string typ;
00095         int theirfd;
00096         User* them;
00097         bool notify;
00098  public:
00099         CGIResolver(Module* me, InspIRCd* Instance, bool NotifyOpers, const std::string &source, bool forward, User* u, int userfd, const std::string &type, bool &cached)
00100                 : Resolver(Instance, source, forward ? DNS_QUERY_A : DNS_QUERY_PTR4, cached, me), typ(type), theirfd(userfd), them(u), notify(NotifyOpers) { }
00101 
00102         virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached, int resultnum = 0)
00103         {
00104                 if (resultnum)
00105                         return;
00106 
00107                 /* Check the user still exists */
00108                 if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
00109                 {
00110                         if (notify)
00111                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), result.c_str(), typ.c_str());
00112 
00113                         them->host.assign(result,0, 64);
00114                         them->dhost.assign(result, 0, 64);
00115                         if (querytype)
00116                                 them->SetSockAddr(them->GetProtocolFamily(), result.c_str(), them->GetPort());
00117                         them->ident.assign("~cgiirc", 0, 8);
00118                         them->InvalidateCache();
00119                         them->CheckLines(true);
00120                 }
00121         }
00122 
00123         virtual void OnError(ResolverError e, const std::string &errormessage)
00124         {
00125                 if ((them) && (them == ServerInstance->SE->GetRef(theirfd)))
00126                 {
00127                         if (notify)
00128                                 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.c_str(), them->host.c_str(), typ.c_str());
00129                 }
00130         }
00131 
00132         virtual ~CGIResolver()
00133         {
00134         }
00135 };
00136 
00137 class ModuleCgiIRC : public Module
00138 {
00139         CommandWebirc* mycommand;
00140         bool NotifyOpers;
00141         CGIHostlist Hosts;
00142 public:
00143         ModuleCgiIRC(InspIRCd* Me) : Module(Me)
00144         {
00145 
00146                 OnRehash(NULL,"");
00147                 mycommand = new CommandWebirc(Me, Hosts, NotifyOpers);
00148                 ServerInstance->AddCommand(mycommand);
00149 
00150                 Implementation eventlist[] = { I_OnRehash, I_OnUserRegister, I_OnCleanup, I_OnSyncUserMetaData, I_OnDecodeMetaData, I_OnUserDisconnect, I_OnUserConnect };
00151                 ServerInstance->Modules->Attach(eventlist, this, 7);
00152         }
00153 
00154 
00155         virtual void Prioritize()
00156         {
00157                 ServerInstance->Modules->SetPriority(this, I_OnUserConnect, PRIO_FIRST);
00158         }
00159 
00160         virtual void OnRehash(User* user, const std::string &parameter)
00161         {
00162                 ConfigReader Conf(ServerInstance);
00163                 Hosts.clear();
00164 
00165                 NotifyOpers = Conf.ReadFlag("cgiirc", "opernotice", 0); // If we send an oper notice when a CGI:IRC has their host changed.
00166 
00167                 if(Conf.GetError() == CONF_VALUE_NOT_FOUND)
00168                         NotifyOpers = true;
00169 
00170                 for(int i = 0; i < Conf.Enumerate("cgihost"); i++)
00171                 {
00172                         std::string hostmask = Conf.ReadValue("cgihost", "mask", i); // An allowed CGI:IRC host
00173                         std::string type = Conf.ReadValue("cgihost", "type", i); // What type of user-munging we do on this host.
00174                         std::string password = Conf.ReadValue("cgihost", "password", i);
00175 
00176                         if(hostmask.length())
00177                         {
00178                                 if (type == "webirc" && !password.length()) {
00179                                                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc: Missing password in config: %s", hostmask.c_str());
00180                                 }
00181                                 else
00182                                 {
00183                                         CGItype cgitype = INVALID;
00184                                         if (type == "pass")
00185                                                 cgitype = PASS;
00186                                         else if (type == "ident")
00187                                                 cgitype = IDENT;
00188                                         else if (type == "passfirst")
00189                                                 cgitype = PASSFIRST;
00190                                         else if (type == "webirc")
00191                                         {
00192                                                 cgitype = WEBIRC;
00193                                         }
00194 
00195                                         if (cgitype == INVALID)
00196                                                 cgitype = PASS;
00197 
00198                                         Hosts.push_back(CGIhost(hostmask,cgitype, password.length() ? password : "" ));
00199                                 }
00200                         }
00201                         else
00202                         {
00203                                 ServerInstance->Logs->Log("CONFIG",DEFAULT, "m_cgiirc.so: Invalid <cgihost:mask> value in config: %s", hostmask.c_str());
00204                                 continue;
00205                         }
00206                 }
00207         }
00208 
00209         virtual void OnCleanup(int target_type, void* item)
00210         {
00211                 if(target_type == TYPE_USER)
00212                 {
00213                         User* user = (User*)item;
00214                         std::string* realhost;
00215                         std::string* realip;
00216 
00217                         if(user->GetExt("cgiirc_realhost", realhost))
00218                         {
00219                                 delete realhost;
00220                                 user->Shrink("cgiirc_realhost");
00221                         }
00222 
00223                         if(user->GetExt("cgiirc_realip", realip))
00224                         {
00225                                 delete realip;
00226                                 user->Shrink("cgiirc_realip");
00227                         }
00228                 }
00229         }
00230 
00231         virtual void OnSyncUserMetaData(User* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
00232         {
00233                 if((extname == "cgiirc_realhost") || (extname == "cgiirc_realip"))
00234                 {
00235                         std::string* data;
00236 
00237                         if(user->GetExt(extname, data))
00238                         {
00239                                 proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, *data);
00240                         }
00241                 }
00242         }
00243 
00244         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
00245         {
00246                 if(target_type == TYPE_USER)
00247                 {
00248                         User* dest = (User*)target;
00249                         std::string* bleh;
00250                         if(((extname == "cgiirc_realhost") || (extname == "cgiirc_realip")) && (!dest->GetExt(extname, bleh)))
00251                         {
00252                                 dest->Extend(extname, new std::string(extdata));
00253                         }
00254                 }
00255         }
00256 
00257         virtual void OnUserDisconnect(User* user)
00258         {
00259                 OnCleanup(TYPE_USER, user);
00260         }
00261 
00262 
00263         virtual int OnUserRegister(User* user)
00264         {
00265                 for(CGIHostlist::iterator iter = Hosts.begin(); iter != Hosts.end(); iter++)
00266                 {
00267                         if(InspIRCd::Match(user->host, iter->hostmask) || InspIRCd::MatchCIDR(user->GetIPString(), iter->hostmask))
00268                         {
00269                                 // Deal with it...
00270                                 if(iter->type == PASS)
00271                                 {
00272                                         CheckPass(user); // We do nothing if it fails so...
00273                                         user->CheckLines(true);
00274                                 }
00275                                 else if(iter->type == PASSFIRST && !CheckPass(user))
00276                                 {
00277                                         // If the password lookup failed, try the ident
00278                                         CheckIdent(user);       // If this fails too, do nothing
00279                                         user->CheckLines(true);
00280                                 }
00281                                 else if(iter->type == IDENT)
00282                                 {
00283                                         CheckIdent(user); // Nothing on failure.
00284                                         user->CheckLines(true);
00285                                 }
00286                                 else if(iter->type == IDENTFIRST && !CheckIdent(user))
00287                                 {
00288                                         // If the ident lookup fails, try the password.
00289                                         CheckPass(user);
00290                                         user->CheckLines(true);
00291                                 }
00292                                 else if(iter->type == WEBIRC)
00293                                 {
00294                                         // We don't need to do anything here
00295                                 }
00296                                 return 0;
00297                         }
00298                 }
00299                 return 0;
00300         }
00301 
00302         virtual void OnUserConnect(User* user)
00303         {
00304                 std::string *webirc_hostname, *webirc_ip;
00305                 if(user->GetExt("cgiirc_webirc_hostname", webirc_hostname))
00306                 {
00307                         user->host.assign(*webirc_hostname, 0, 64);
00308                         user->dhost.assign(*webirc_hostname, 0, 64);
00309                         delete webirc_hostname;
00310                         user->InvalidateCache();
00311                         user->Shrink("cgiirc_webirc_hostname");
00312                 }
00313                 if(user->GetExt("cgiirc_webirc_ip", webirc_ip))
00314                 {
00315                         ServerInstance->Users->RemoveCloneCounts(user);
00316                         user->SetSockAddr(user->GetProtocolFamily(), webirc_ip->c_str(), user->GetPort());
00317                         delete webirc_ip;
00318                         user->InvalidateCache();
00319                         user->Shrink("cgiirc_webirc_ip");
00320                         ServerInstance->Users->AddLocalClone(user);
00321                         ServerInstance->Users->AddGlobalClone(user);
00322                         user->CheckClass();
00323                         user->CheckLines(true);
00324                 }
00325         }
00326 
00327         bool CheckPass(User* user)
00328         {
00329                 if(IsValidHost(user->password))
00330                 {
00331                         user->Extend("cgiirc_realhost", new std::string(user->host));
00332                         user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
00333                         user->host.assign(user->password, 0, 64);
00334                         user->dhost.assign(user->password, 0, 64);
00335                         user->InvalidateCache();
00336 
00337                         bool valid = false;
00338                         ServerInstance->Users->RemoveCloneCounts(user);
00339 #ifdef IPV6
00340                         if (user->GetProtocolFamily() == AF_INET6)
00341                                 valid = (inet_pton(AF_INET6, user->password.c_str(), &((sockaddr_in6*)user->ip)->sin6_addr) > 0);
00342                         else
00343                                 valid = (inet_aton(user->password.c_str(), &((sockaddr_in*)user->ip)->sin_addr));
00344 #else
00345                         if (inet_aton(user->password.c_str(), &((sockaddr_in*)user->ip)->sin_addr))
00346                                 valid = true;
00347 #endif
00348                         ServerInstance->Users->AddLocalClone(user);
00349                         ServerInstance->Users->AddGlobalClone(user);
00350                         user->CheckClass();
00351 
00352                         if (valid)
00353                         {
00354                                 /* We were given a IP in the password, we don't do DNS so they get this is as their host as well. */
00355                                 if(NotifyOpers)
00356                                         ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from PASS", user->nick.c_str(), user->host.c_str(), user->password.c_str());
00357                         }
00358                         else
00359                         {
00360                                 /* We got as resolved hostname in the password. */
00361                                 try
00362                                 {
00363 
00364                                         bool cached;
00365                                         CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, user->password, false, user, user->GetFd(), "PASS", cached);
00366                                         ServerInstance->AddResolver(r, cached);
00367                                 }
00368                                 catch (...)
00369                                 {
00370                                         if (NotifyOpers)
00371                                                 ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str());
00372                                 }
00373                         }
00374 
00375                         user->password.clear();
00376                         return true;
00377                 }
00378 
00379                 return false;
00380         }
00381 
00382         bool CheckIdent(User* user)
00383         {
00384                 int ip[4];
00385                 const char* ident;
00386                 char newip[16];
00387                 int len = user->ident.length();
00388 
00389                 if(len == 8)
00390                         ident = user->ident.c_str();
00391                 else if(len == 9 && user->ident[0] == '~')
00392                         ident = user->ident.c_str() + 1;
00393                 else
00394                         return false;
00395 
00396                 for(int i = 0; i < 4; i++)
00397                         if(!HexToInt(ip[i], ident + i*2))
00398                                 return false;
00399 
00400                 snprintf(newip, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
00401 
00402                 user->Extend("cgiirc_realhost", new std::string(user->host));
00403                 user->Extend("cgiirc_realip", new std::string(user->GetIPString()));
00404                 ServerInstance->Users->RemoveCloneCounts(user);
00405                 user->SetSockAddr(user->GetProtocolFamily(), newip, user->GetPort());
00406                 ServerInstance->Users->AddLocalClone(user);
00407                 ServerInstance->Users->AddGlobalClone(user);
00408                 user->CheckClass();
00409                 try
00410                 {
00411                         user->host.assign(newip, 0, 16);
00412                         user->dhost.assign(newip, 0, 16);
00413                         user->ident.assign("~cgiirc", 0, 8);
00414 
00415                         bool cached;
00416                         CGIResolver* r = new CGIResolver(this, ServerInstance, NotifyOpers, newip, false, user, user->GetFd(), "IDENT", cached);
00417                         ServerInstance->AddResolver(r, cached);
00418                 }
00419                 catch (...)
00420                 {
00421                         user->host.assign(newip, 0, 16);
00422                         user->dhost.assign(newip, 0, 16);
00423                         user->ident.assign("~cgiirc", 0, 8);
00424                         user->InvalidateCache();
00425 
00426                         if(NotifyOpers)
00427                                  ServerInstance->SNO->WriteToSnoMask('A', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str());
00428                 }
00429 
00430                 return true;
00431         }
00432 
00433         bool IsValidHost(const std::string &host)
00434         {
00435                 if(!host.size())
00436                         return false;
00437 
00438                 for(unsigned int i = 0; i < host.size(); i++)
00439                 {
00440                         if(     ((host[i] >= '0') && (host[i] <= '9')) ||
00441                                         ((host[i] >= 'A') && (host[i] <= 'Z')) ||
00442                                         ((host[i] >= 'a') && (host[i] <= 'z')) ||
00443                                         ((host[i] == '-') && (i > 0) && (i+1 < host.size()) && (host[i-1] != '.') && (host[i+1] != '.')) ||
00444                                         ((host[i] == '.') && (i > 0) && (i+1 < host.size())) )
00445 
00446                                 continue;
00447                         else
00448                                 return false;
00449                 }
00450 
00451                 return true;
00452         }
00453 
00454         bool IsValidIP(const std::string &ip)
00455         {
00456                 if(ip.size() < 7 || ip.size() > 15)
00457                         return false;
00458 
00459                 short sincedot = 0;
00460                 short dots = 0;
00461 
00462                 for(unsigned int i = 0; i < ip.size(); i++)
00463                 {
00464                         if((dots <= 3) && (sincedot <= 3))
00465                         {
00466                                 if((ip[i] >= '0') && (ip[i] <= '9'))
00467                                 {
00468                                         sincedot++;
00469                                 }
00470                                 else if(ip[i] == '.')
00471                                 {
00472                                         sincedot = 0;
00473                                         dots++;
00474                                 }
00475                         }
00476                         else
00477                         {
00478                                 return false;
00479 
00480                         }
00481                 }
00482 
00483                 if(dots != 3)
00484                         return false;
00485 
00486                 return true;
00487         }
00488 
00489         bool HexToInt(int &out, const char* in)
00490         {
00491                 char ip[3];
00492                 ip[0] = in[0];
00493                 ip[1] = in[1];
00494                 ip[2] = 0;
00495                 out = strtol(ip, NULL, 16);
00496 
00497                 if(out > 255 || out < 0)
00498                         return false;
00499 
00500                 return true;
00501         }
00502 
00503         virtual ~ModuleCgiIRC()
00504         {
00505         }
00506 
00507         virtual Version GetVersion()
00508         {
00509                 return Version("$Id: m_cgiirc.cpp 10607 2008-09-29 08:13:49Z special $",VF_VENDOR,API_VERSION);
00510         }
00511 
00512 };
00513 
00514 MODULE_INIT(ModuleCgiIRC)