]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_sqloper.cpp
'svn propset -R svn:eol-style CR *' Set to UNIX-style always. Binaries are auto skipp...
[user/henk/code/inspircd.git] / src / modules / extra / m_sqloper.cpp
index 520869e210b3074d1fa02ffe3f6f0a34a7a19d24..4b09ac26eb377607593acf530424192b348dda1f 100644 (file)
@@ -1,283 +1 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
- *
- *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
- *
- * This program is free but copyrighted software; see
- *            the file COPYING for details.
- *
- * ---------------------------------------------------
- */
-
-#include "inspircd.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-#include "configreader.h"
-
-#include "m_sqlv2.h"
-#include "m_sqlutils.h"
-#include "m_hash.h"
-#include "commands/cmd_oper.h"
-
-/* $ModDesc: Allows storage of oper credentials in an SQL table */
-/* $ModDep: m_sqlv2.h m_sqlutils.h */
-
-class ModuleSQLOper : public Module
-{
-       Module* SQLutils;
-       Module* HashModule;
-       std::string databaseid;
-
-public:
-       ModuleSQLOper(InspIRCd* Me)
-       : Module::Module(Me)
-       {
-               ServerInstance->UseInterface("SQLutils");
-               ServerInstance->UseInterface("SQL");
-               ServerInstance->UseInterface("HashRequest");
-
-               /* Attempt to locate the md5 service provider, bail if we can't find it */
-               HashModule = ServerInstance->FindModule("m_md5.so");
-               if (!HashModule)
-                       throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so.");
-
-               SQLutils = ServerInstance->FindModule("m_sqlutils.so");
-               if (!SQLutils)
-                       throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so.");
-
-               OnRehash(NULL,"");
-       }
-
-       virtual ~ModuleSQLOper()
-       {
-               ServerInstance->DoneWithInterface("SQL");
-               ServerInstance->DoneWithInterface("SQLutils");
-               ServerInstance->DoneWithInterface("HashRequest");
-       }
-
-       void Implements(char* List)
-       {
-               List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1;
-       }
-
-       virtual void OnRehash(userrec* user, const std::string &parameter)
-       {
-               ConfigReader Conf(ServerInstance);
-               
-               databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */
-       }
-
-       virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
-       {
-               if ((validated) && (command == "OPER"))
-               {
-                       if (LookupOper(user, parameters[0], parameters[1]))
-                       {       
-                               /* Returning true here just means the query is in progress, or on it's way to being
-                                * in progress. Nothing about the /oper actually being successful..
-                                * If the oper lookup fails later, we pass the command to the original handler
-                                * for /oper by calling its Handle method directly.
-                                */
-                               return 1;
-                       }
-               }
-               return 0;
-       }
-
-       bool LookupOper(userrec* user, const std::string &username, const std::string &password)
-       {
-               Module* target;
-               
-               target = ServerInstance->FindFeature("SQL");
-
-               if (target)
-               {
-                       /* Reset hash module first back to MD5 standard state */
-                       HashResetRequest(this, HashModule).Send();
-                       /* Make an MD5 hash of the password for using in the query */
-                       std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send();
-
-                       /* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function,
-                        * also hashing it in the module and only passing a remote query containing a hash is more secure.
-                        */
-
-                       SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash);
-                       
-                       if (req.Send())
-                       {
-                               /* When we get the query response from the service provider we will be given an ID to play with,
-                                * just an ID number which is unique to this query. We need a way of associating that ID with a userrec
-                                * so we insert it into a map mapping the IDs to users.
-                                * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the
-                                * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling
-                                * us to discard the query.
-                                */
-                               AssociateUser(this, SQLutils, req.id, user).Send();
-
-                               user->Extend("oper_user", strdup(username.c_str()));
-                               user->Extend("oper_pass", strdup(password.c_str()));
-                                       
-                               return true;
-                       }
-                       else
-                       {
-                               return false;
-                       }
-               }
-               else
-               {
-                       ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured");
-                       return false;
-               }
-       }
-       
-       virtual char* OnRequest(Request* request)
-       {
-               if (strcmp(SQLRESID, request->GetId()) == 0)
-               {
-                       SQLresult* res = static_cast<SQLresult*>(request);
-
-                       userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;
-                       UnAssociate(this, SQLutils, res->id).S();
-
-                       char* tried_user = NULL;
-                       char* tried_pass = NULL;
-
-                       user->GetExt("oper_user", tried_user);
-                       user->GetExt("oper_pass", tried_pass);
-                       
-                       if (user)
-                       {
-                               if (res->error.Id() == NO_ERROR)
-                               {
-                                       if (res->Rows())
-                                       {
-                                               /* We got a row in the result, this means there was a record for the oper..
-                                                * now we just need to check if their host matches, and if it does then
-                                                * oper them up.
-                                                * 
-                                                * We now (previous versions of the module didn't) support multiple SQL
-                                                * rows per-oper in the same way the config file does, all rows will be tried
-                                                * until one is found which matches. This is useful to define several different
-                                                * hosts for a single oper.
-                                                * 
-                                                * The for() loop works as SQLresult::GetRowMap() returns an empty map when there
-                                                * are no more rows to return.
-                                                */
-                                               
-                                               for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap())
-                                               {                                                       
-                                                       if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d))
-                                                       {
-                                                               /* If/when one of the rows matches, stop checking and return */
-                                                               return SQLSUCCESS;
-                                                       }
-                                                       if (tried_user && tried_pass)
-                                                       {
-                                                               LoginFail(user, tried_user, tried_pass);
-                                                               free(tried_user);
-                                                               free(tried_pass);
-                                                               user->Shrink("oper_user");
-                                                               user->Shrink("oper_pass");
-                                                       }
-                                               }
-                                       }
-                                       else
-                                       {
-                                               /* No rows in result, this means there was no oper line for the user,
-                                                * we should have already checked the o:lines so now we need an
-                                                * "insufficient awesomeness" (invalid credentials) error
-                                                */
-                                               if (tried_user && tried_pass)
-                                               {
-                                                       LoginFail(user, tried_user, tried_pass);
-                                                       free(tried_user);
-                                                       free(tried_pass);
-                                                       user->Shrink("oper_user");
-                                                       user->Shrink("oper_pass");
-                                               }
-                                       }
-                               }
-                               else
-                               {
-                                       /* This one shouldn't happen, the query failed for some reason.
-                                        * We have to fail the /oper request and give them the same error
-                                        * as above.
-                                        */
-                                       if (tried_user && tried_pass)
-                                       {
-                                               LoginFail(user, tried_user, tried_pass);
-                                               free(tried_user);
-                                               free(tried_pass);
-                                               user->Shrink("oper_user");
-                                               user->Shrink("oper_pass");
-                                       }
-
-                               }
-                       }
-               
-                       return SQLSUCCESS;
-               }
-
-               return NULL;
-       }
-
-       void LoginFail(userrec* user, const std::string &username, const std::string &pass)
-       {
-               command_t* oper_command = ServerInstance->Parser->GetHandler("OPER");
-
-               if (oper_command)
-               {
-                       const char* params[] = { username.c_str(), pass.c_str() };
-                       oper_command->Handle(params, 2, user);
-               }
-               else
-               {
-                       ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!");
-               }
-       }
-
-       bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type)
-       {
-               ConfigReader Conf(ServerInstance);
-               
-               for (int j = 0; j < Conf.Enumerate("type"); j++)
-               {
-                       std::string tname = Conf.ReadValue("type","name",j);
-                       std::string hostname(user->ident);
-
-                       hostname.append("@").append(user->host);
-                                                       
-                       if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str()))
-                       {
-                               /* Opertype and host match, looks like this is it. */
-                               std::string operhost = Conf.ReadValue("type", "host", j);
-
-                               if (operhost.size())
-                                       user->ChangeDisplayedHost(operhost.c_str());
-
-                               ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str());
-                               user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str());
-
-                               if (!user->modes[UM_OPERATOR])
-                                       user->Oper(type);
-
-                               return true;
-                       }
-               }
-               
-               return false;
-       }
-
-       virtual Version GetVersion()
-       {
-               return Version(1,1,1,0,VF_VENDOR,API_VERSION);
-       }
-       
-};
-
-MODULE_INIT(ModuleSQLOper);
-
+/*       +------------------------------------+\r *       | Inspire Internet Relay Chat Daemon |\r *       +------------------------------------+\r *\r *  InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r *            the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r#include "users.h"\r#include "channels.h"\r#include "modules.h"\r#include "configreader.h"\r\r#include "m_sqlv2.h"\r#include "m_sqlutils.h"\r#include "m_hash.h"\r#include "commands/cmd_oper.h"\r\r/* $ModDesc: Allows storage of oper credentials in an SQL table */\r/* $ModDep: m_sqlv2.h m_sqlutils.h */\r\rclass ModuleSQLOper : public Module\r{\r   Module* SQLutils;\r      Module* HashModule;\r    std::string databaseid;\r\rpublic:\r       ModuleSQLOper(InspIRCd* Me)\r    : Module::Module(Me)\r   {\r              ServerInstance->UseInterface("SQLutils");\r              ServerInstance->UseInterface("SQL");\r           ServerInstance->UseInterface("HashRequest");\r\r          /* Attempt to locate the md5 service provider, bail if we can't find it */\r             HashModule = ServerInstance->FindModule("m_md5.so");\r           if (!HashModule)\r                       throw ModuleException("Can't find m_md5.so. Please load m_md5.so before m_sqloper.so.");\r\r              SQLutils = ServerInstance->FindModule("m_sqlutils.so");\r                if (!SQLutils)\r                 throw ModuleException("Can't find m_sqlutils.so. Please load m_sqlutils.so before m_sqloper.so.");\r\r            OnRehash(NULL,"");\r     }\r\r     virtual ~ModuleSQLOper()\r       {\r              ServerInstance->DoneWithInterface("SQL");\r              ServerInstance->DoneWithInterface("SQLutils");\r         ServerInstance->DoneWithInterface("HashRequest");\r      }\r\r     void Implements(char* List)\r    {\r              List[I_OnRequest] = List[I_OnRehash] = List[I_OnPreCommand] = 1;\r       }\r\r     virtual void OnRehash(userrec* user, const std::string &parameter)\r     {\r              ConfigReader Conf(ServerInstance);\r             \r               databaseid = Conf.ReadValue("sqloper", "dbid", 0); /* Database ID of a database configured for the service provider module */\r  }\r\r     virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)\r       {\r              if ((validated) && (command == "OPER"))\r                {\r                      if (LookupOper(user, parameters[0], parameters[1]))\r                    {       \r                               /* Returning true here just means the query is in progress, or on it's way to being\r                             * in progress. Nothing about the /oper actually being successful..\r                             * If the oper lookup fails later, we pass the command to the original handler\r                          * for /oper by calling its Handle method directly.\r                             */\r                            return 1;\r                      }\r              }\r              return 0;\r      }\r\r     bool LookupOper(userrec* user, const std::string &username, const std::string &password)\r       {\r              Module* target;\r                \r               target = ServerInstance->FindFeature("SQL");\r\r          if (target)\r            {\r                      /* Reset hash module first back to MD5 standard state */\r                       HashResetRequest(this, HashModule).Send();\r                     /* Make an MD5 hash of the password for using in the query */\r                  std::string md5_pass_hash = HashSumRequest(this, HashModule, password.c_str()).Send();\r\r                        /* We generate our own MD5 sum here because some database providers (e.g. SQLite) dont have a builtin md5 function,\r                     * also hashing it in the module and only passing a remote query containing a hash is more secure.\r                      */\r\r                   SQLrequest req = SQLreq(this, target, databaseid, "SELECT username, password, hostname, type FROM ircd_opers WHERE username = '?' AND password='?'", username, md5_pass_hash);\r                 \r                       if (req.Send())\r                        {\r                              /* When we get the query response from the service provider we will be given an ID to play with,\r                                * just an ID number which is unique to this query. We need a way of associating that ID with a userrec\r                                 * so we insert it into a map mapping the IDs to users.\r                                 * Thankfully m_sqlutils provides this, it will associate a ID with a user or channel, and if the user quits it removes the\r                             * association. This means that if the user quits during a query we will just get a failed lookup from m_sqlutils - telling\r                             * us to discard the query.\r                             */\r                            AssociateUser(this, SQLutils, req.id, user).Send();\r\r                           user->Extend("oper_user", strdup(username.c_str()));\r                           user->Extend("oper_pass", strdup(password.c_str()));\r                                   \r                               return true;\r                   }\r                      else\r                   {\r                              return false;\r                  }\r              }\r              else\r           {\r                      ServerInstance->Log(SPARSE, "WARNING: Couldn't find SQL provider module. NOBODY will be able to oper up unless their o:line is statically configured");\r                        return false;\r          }\r      }\r      \r       virtual char* OnRequest(Request* request)\r      {\r              if (strcmp(SQLRESID, request->GetId()) == 0)\r           {\r                      SQLresult* res = static_cast<SQLresult*>(request);\r\r                    userrec* user = GetAssocUser(this, SQLutils, res->id).S().user;\r                        UnAssociate(this, SQLutils, res->id).S();\r\r                     char* tried_user = NULL;\r                       char* tried_pass = NULL;\r\r                      user->GetExt("oper_user", tried_user);\r                 user->GetExt("oper_pass", tried_pass);\r                 \r                       if (user)\r                      {\r                              if (res->error.Id() == NO_ERROR)\r                               {\r                                      if (res->Rows())\r                                       {\r                                              /* We got a row in the result, this means there was a record for the oper..\r                                             * now we just need to check if their host matches, and if it does then\r                                                 * oper them up.\r                                                * \r                                             * We now (previous versions of the module didn't) support multiple SQL\r                                                 * rows per-oper in the same way the config file does, all rows will be tried\r                                           * until one is found which matches. This is useful to define several different\r                                                 * hosts for a single oper.\r                                             * \r                                             * The for() loop works as SQLresult::GetRowMap() returns an empty map when there\r                                               * are no more rows to return.\r                                          */\r                                            \r                                               for (SQLfieldMap& row = res->GetRowMap(); row.size(); row = res->GetRowMap())\r                                          {                                                       \r                                                       if (OperUser(user, row["username"].d, row["password"].d, row["hostname"].d, row["type"].d))\r                                                    {\r                                                              /* If/when one of the rows matches, stop checking and return */\r                                                                return SQLSUCCESS;\r                                                     }\r                                                      if (tried_user && tried_pass)\r                                                  {\r                                                              LoginFail(user, tried_user, tried_pass);\r                                                               free(tried_user);\r                                                              free(tried_pass);\r                                                              user->Shrink("oper_user");\r                                                             user->Shrink("oper_pass");\r                                                     }\r                                              }\r                                      }\r                                      else\r                                   {\r                                              /* No rows in result, this means there was no oper line for the user,\r                                           * we should have already checked the o:lines so now we need an\r                                                 * "insufficient awesomeness" (invalid credentials) error\r                                               */\r                                            if (tried_user && tried_pass)\r                                          {\r                                                      LoginFail(user, tried_user, tried_pass);\r                                                       free(tried_user);\r                                                      free(tried_pass);\r                                                      user->Shrink("oper_user");\r                                                     user->Shrink("oper_pass");\r                                             }\r                                      }\r                              }\r                              else\r                           {\r                                      /* This one shouldn't happen, the query failed for some reason.\r                                         * We have to fail the /oper request and give them the same error\r                                       * as above.\r                                    */\r                                    if (tried_user && tried_pass)\r                                  {\r                                              LoginFail(user, tried_user, tried_pass);\r                                               free(tried_user);\r                                              free(tried_pass);\r                                              user->Shrink("oper_user");\r                                             user->Shrink("oper_pass");\r                                     }\r\r                             }\r                      }\r              \r                       return SQLSUCCESS;\r             }\r\r             return NULL;\r   }\r\r     void LoginFail(userrec* user, const std::string &username, const std::string &pass)\r    {\r              command_t* oper_command = ServerInstance->Parser->GetHandler("OPER");\r\r         if (oper_command)\r              {\r                      const char* params[] = { username.c_str(), pass.c_str() };\r                     oper_command->Handle(params, 2, user);\r         }\r              else\r           {\r                      ServerInstance->Log(DEBUG, "BUG: WHAT?! Why do we have no OPER command?!");\r            }\r      }\r\r     bool OperUser(userrec* user, const std::string &username, const std::string &password, const std::string &pattern, const std::string &type)\r    {\r              ConfigReader Conf(ServerInstance);\r             \r               for (int j = 0; j < Conf.Enumerate("type"); j++)\r               {\r                      std::string tname = Conf.ReadValue("type","name",j);\r                   std::string hostname(user->ident);\r\r                    hostname.append("@").append(user->host);\r                                                       \r                       if ((tname == type) && OneOfMatches(hostname.c_str(), user->GetIPString(), pattern.c_str()))\r                   {\r                              /* Opertype and host match, looks like this is it. */\r                          std::string operhost = Conf.ReadValue("type", "host", j);\r\r                             if (operhost.size())\r                                   user->ChangeDisplayedHost(operhost.c_str());\r\r                          ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s", user->nick, user->ident, user->host, type.c_str());\r                            user->WriteServ("381 %s :You are now an IRC operator of type %s", user->nick, type.c_str());\r\r                          if (!user->modes[UM_OPERATOR])\r                                 user->Oper(type);\r\r                             return true;\r                   }\r              }\r              \r               return false;\r  }\r\r     virtual Version GetVersion()\r   {\r              return Version(1,1,1,0,VF_VENDOR,API_VERSION);\r }\r      \r};\r\rMODULE_INIT(ModuleSQLOper);\r\r
\ No newline at end of file