]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Move the <disabled> tag out of the core to a new module.
authorPeter Powell <petpow@saberuk.com>
Thu, 24 Jan 2019 15:10:02 +0000 (15:10 +0000)
committerPeter Powell <petpow@saberuk.com>
Thu, 24 Jan 2019 15:10:02 +0000 (15:10 +0000)
docs/conf/inspircd.conf.example
docs/conf/modules.conf.example
include/configreader.h
include/ctables.h
src/command_parse.cpp
src/configreader.cpp
src/inspircd.cpp
src/mode.cpp
src/modules/m_conn_umodes.cpp
src/modules/m_disable.cpp [new file with mode: 0644]
src/modules/m_samode.cpp

index f2db745002dee9f85aa6af9d3c005392da5f9b25..890c9cc4cbf61684d36ffdb969b5325e46af01cd 100644 (file)
 # up to 100 entries.
 <maxlist chan="*" limit="100">
 
-#-#-#-#-#-#-#-#-#-#-#-  DISABLED FEATURES  -#-#-#-#-#-#-#-#-#-#-#-#-#-#
-#                                                                     #
-# This tag is optional, and specifies one or more features which are  #
-# not available to non-operators.                                     #
-#                                                                     #
-# For example you may wish to disable NICK and prevent non-opers from #
-# changing their nicknames.                                           #
-# Note that any disabled commands take effect only after the user has #
-# 'registered' (e.g. after the initial USER/NICK/PASS on connection)  #
-# so for example disabling NICK will not cripple your network.        #
-#                                                                     #
-# You can also define if you want to disable any channelmodes         #
-# or usermodes from your users.                                       #
-#                                                                     #
-# `fakenonexistant' will make the ircd pretend that nonexistant       #
-# commands simply don't exist to non-opers ("no such command").       #
-#                                                                     #
-#<disabled commands="TOPIC MODE" usermodes="" chanmodes="" fakenonexistant="yes">
-
 #-#-#-#-#-#-#-#-#-#-#-#-#-  SERVER OPTIONS   -#-#-#-#-#-#-#-#-#-#-#-#-#
 #                                                                     #
 #   Settings to define which features are usable on your server.      #
index 0647f77c07baa08d332bc10f01d4bb9e74db8ccc..980d0d6a56c7a64dafd118d642175debb1c57f17 100644 (file)
 #<goodchan name="#funtimes">                                          #
 # Glob masks are accepted here also.                                  #
 
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Disable module: Provides support for disabling commands and modes.  #
+#<module name="disable">
+#
+#-#-#-#-#-#-#-#-#-#-#-#- DISABLE CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
+#                                                                     #
+# If you have the disable module loaded then you need to specify the  #
+# commands and modes that you want disabled. Users who have not fully #
+# connected yet are exempt from this module so you can e.g. disable   #
+# the NICK command but still allow users to connect to the server.    #
+#                                                                     #
+# commands - A space-delimited list of commands that can not be used  #
+#            by users. You can exempt server operators from this with #
+#            the servers/use-disabled-commands privilege.             #
+#                                                                     #
+# chanmodes - One or more channel modes that can not be added/removed #
+#             by users. You can exempt server operators from this     #
+#             with the servers/use-disabled-commands privilege.       #
+#                                                                     #
+# usermodes - One or more user modes that can not be added/removed by #
+#             users. You can exempt server operators from this with   #
+#             the servers/use-disabled-commands privilege.            #
+#                                                                     #
+# fakenonexistent - Whether to pretend that a disabled command/mode   #
+#                   does not exist when executed/changed by a user.   #
+#                   Defaults to no.                                   #
+#                                                                     #
+# notifyopers - Whether to send a notice to snomask `a` when a user   #
+#               is prevented from using a disabled command/mode.      #
+#               Defaults to no.                                       #
+#                                                                     #
+#<disabled commands="KICK TOPIC"                                      #
+#          chanmodes="kp"                                             #
+#          usermodes="iw"                                             #
+#          fakenonexistent="yes"                                      #
+#          notifyopers="no">                                          #
+
 #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 # DNS blacklist module: Provides support for looking up IPs on one or #
 # more blacklists.                                                    #
index fd9401c0fa9cf7a2b9810ebf13f972e5e7c0d6d2..511bedbee2618ecfb42977844d4455aed4096939 100644 (file)
@@ -332,18 +332,6 @@ class CoreExport ServerConfig
         */
        std::string ServerDesc;
 
-       /** Pretend disabled commands don't exist.
-        */
-       bool DisabledDontExist;
-
-       /** This variable identifies which usermodes have been diabled.
-        */
-       std::bitset<64> DisabledUModes;
-
-       /** This variable identifies which chanmodes have been disabled.
-        */
-       std::bitset<64> DisabledCModes;
-
        /** How to treat a user in a channel who is banned. */
        BannedUserTreatment RestrictBannedUsers;
 
@@ -466,9 +454,6 @@ class CoreExport ServerConfig
 
        void Fill();
 
-       /** Disables the commands specified in <disabled:commands>. */
-       bool ApplyDisabledCommands();
-
        /** Escapes a value for storage in a configuration key.
         * @param str The string to escape.
         * @param xml Are we using the XML config format?
index 8be40cc54d597350b21a2b51ff845cc8b641c973..1c7d5b4bd5dadd1e45d619323ebab6411b71ca72 100644 (file)
@@ -163,10 +163,6 @@ class CoreExport CommandBase : public ServiceProvider
         */
        unsigned long use_count;
 
-       /** True if the command is disabled to non-opers
-        */
-       bool disabled;
-
        /** True if the command can be issued before registering
         */
        bool works_before_reg;
@@ -212,23 +208,6 @@ class CoreExport CommandBase : public ServiceProvider
         */
        virtual void EncodeParameter(std::string& parameter, unsigned int index);
 
-       /** Disable or enable this command.
-        * @param setting True to disable the command.
-        */
-       void Disable(bool setting)
-       {
-               disabled = setting;
-       }
-
-       /** Obtain this command's disable state.
-        * @return true if the command is currently disabled
-        * (disabled commands can be used only by operators)
-        */
-       bool IsDisabled()
-       {
-               return disabled;
-       }
-
        /** @return true if the command works before registration.
         */
        bool WorksBeforeReg()
index c2ae39d494e3a75579d4635f36f62394d5c95671..7a732f8b5c7acc43242f8a1b1b4538a8bcf88f5c 100644 (file)
@@ -266,24 +266,6 @@ void CommandParser::ProcessCommand(LocalUser* user, std::string& command, Comman
                }
        }
 
-       if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
-       {
-               /* command is disabled! */
-               user->CommandFloodPenalty += failpenalty;
-               if (ServerInstance->Config->DisabledDontExist)
-               {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
-               }
-               else
-               {
-                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "This command has been disabled.");
-               }
-
-               ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
-                               command.c_str(), user->nick.c_str(), user->ident.c_str(), user->GetRealHost().c_str());
-               return;
-       }
-
        if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param))
                command_p.pop_back();
 
@@ -333,7 +315,6 @@ CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpa
        , min_params(minpara)
        , max_params(maxpara)
        , use_count(0)
-       , disabled(false)
        , works_before_reg(false)
        , allow_empty_last_param(true)
        , Penalty(1)
index ce03f1d9eb62d0faf85d7ee703cdeb42b9e7136d..5a0ceff06b9e0462e38a5ae2dfa72fa5a4cab486 100644 (file)
@@ -72,31 +72,6 @@ ServerConfig::~ServerConfig()
        delete EmptyTag;
 }
 
-bool ServerConfig::ApplyDisabledCommands()
-{
-       // Enable everything first.
-       const CommandParser::CommandMap& commands = ServerInstance->Parser.GetCommands();
-       for (CommandParser::CommandMap::const_iterator x = commands.begin(); x != commands.end(); ++x)
-               x->second->Disable(false);
-
-       // Now disable the commands specified in the config.
-       std::string command;
-       irc::spacesepstream commandlist(ConfValue("disabled")->getString("commands"));
-       while (commandlist.GetToken(command))
-       {
-               Command* handler = ServerInstance->Parser.GetHandler(command);
-               if (!handler)
-               {
-                       ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Unable to disable the %s command as it does not exist!", command.c_str());
-                       continue;
-               }
-
-               ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "The %s command has been disabled", command.c_str());
-               handler->Disable(true);
-       }
-       return true;
-}
-
 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
 {
        ConfigTagList tags = conf->ConfTags(tag);
@@ -378,7 +353,6 @@ void ServerConfig::Fill()
        ServerDesc = server->getString("description", "Configure Me");
        Network = server->getString("network", "Network");
        NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
-       DisabledDontExist = ConfValue("disabled")->getBool("fakenonexistant");
        CustomVersion = security->getString("customversion");
        HideBans = security->getBool("hidebans");
        HideServer = security->getString("hideserver", security->getString("hidewhois"));
@@ -429,25 +403,6 @@ void ServerConfig::Fill()
                RestrictBannedUsers =  ServerConfig::BUT_RESTRICT_NOTIFY;
        else
                throw CoreException(restrictbannedusers + " is an invalid <options:restrictbannedusers> value, at " + options->getTagLocation());
-
-       DisabledUModes.reset();
-       std::string modes = ConfValue("disabled")->getString("usermodes");
-       for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
-       {
-               // Complain when the character is not a valid mode character.
-               if (!ModeParser::IsModeChar(*p))
-                       throw CoreException("Invalid usermode " + std::string(1, *p) + " was found.");
-               DisabledUModes.set(*p - 'A');
-       }
-
-       DisabledCModes.reset();
-       modes = ConfValue("disabled")->getString("chanmodes");
-       for (std::string::const_iterator p = modes.begin(); p != modes.end(); ++p)
-       {
-               if (!ModeParser::IsModeChar(*p))
-                       throw CoreException("Invalid chanmode " + std::string(1, *p) + " was found.");
-               DisabledCModes.set(*p - 'A');
-       }
 }
 
 // WARNING: it is not safe to use most of the codebase in this function, as it
@@ -723,7 +678,6 @@ void ConfigReaderThread::Finish()
                ServerInstance->Users.RehashCloneCounts();
                ServerInstance->XLines->CheckELines();
                ServerInstance->XLines->ApplyLines();
-               Config->ApplyDisabledCommands();
                User* user = ServerInstance->FindNick(TheUserUID);
 
                ConfigStatus status(user);
index 7e299700be31e0fc2a7a97e9de1d46a56d488e56..09e48ea1fa2840e1a2a77d4a32646ea9b9d056ca 100644 (file)
@@ -442,7 +442,6 @@ InspIRCd::InspIRCd(int argc, char** argv) :
 
        // Build ISupport as ModuleManager::LoadAll() does not do it
        this->ISupport.Build();
-       Config->ApplyDisabledCommands();
 
        if (!pl.empty())
        {
index 5793b4e665083d284f44f3cbe76ede8748f001b7..459eb21c8ed8ed12ec66dbc157bd79202b1b22f4 100644 (file)
@@ -312,17 +312,6 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Mode
                }
        }
 
-       if (IS_LOCAL(user) && !user->IsOper())
-       {
-               const std::bitset<64>& disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes;
-               if (disabled.test(modechar - 'A'))
-               {
-                       user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c has been locked by the administrator",
-                               type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
-                       return MODEACTION_DENY;
-               }
-       }
-
        if ((adding) && (IS_LOCAL(user)) && (mh->NeedsOper()) && (!user->HasModePermission(mh)))
        {
                /* It's an oper only mode, and they don't have access to it. */
index 1f2fe745531f9e4a7463b790a9c3e37f7b0b2141..3132aed4016685a9cc97948ecb1f3f5139acbbc9 100644 (file)
@@ -38,10 +38,6 @@ class ModuleModesOnConnect : public Module
 
        void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
        {
-               // Backup and zero out the disabled usermodes, so that we can override them here.
-               const std::bitset<64> save = ServerInstance->Config->DisabledUModes;
-               ServerInstance->Config->DisabledUModes.reset();
-
                ConfigTag* tag = user->MyClass->config;
                std::string ThisModes = tag->getString("modes");
                if (!ThisModes.empty())
@@ -58,8 +54,6 @@ class ModuleModesOnConnect : public Module
 
                        ServerInstance->Parser.CallHandler("MODE", modes, user);
                }
-
-               ServerInstance->Config->DisabledUModes = save;
        }
 };
 
diff --git a/src/modules/m_disable.cpp b/src/modules/m_disable.cpp
new file mode 100644 (file)
index 0000000..203f9ee
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2019 Peter Powell <petpow@saberuk.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+enum
+{
+       // From ircu.
+       ERR_DISABLED = 517
+};
+
+// Holds a list of disabled commands.
+typedef std::vector<std::string> CommandList;
+
+// Holds whether modes are disabled or not.
+typedef std::bitset<64> ModeStatus;
+
+class ModuleDisable : public Module
+{
+ private:
+       CommandList commands;
+       ModeStatus chanmodes;
+       bool fakenonexistent;
+       bool notifyopers;
+       ModeStatus usermodes;
+
+       void ReadModes(ConfigTag* tag, const std::string& field, ModeType type, ModeStatus& status)
+       {
+               const std::string modes = tag->getString(field);
+               for (std::string::const_iterator iter = modes.begin(); iter != modes.end(); ++iter)
+               {
+                       const char& chr = *iter;
+
+                       // Check that the character is a valid mode letter.
+                       if (!ModeParser::IsModeChar(chr))
+                               throw ModuleException(InspIRCd::Format("Invalid mode '%c' was specified in <disabled:%s> at %s",
+                                       chr, field.c_str(), tag->getTagLocation().c_str()));
+
+                       // Check that the mode actually exists.
+                       ModeHandler* mh = ServerInstance->Modes->FindMode(chr, type);
+                       if (!chr)
+                               throw ModuleException(InspIRCd::Format("Non-existent mode '%c' was specified in <disabled:%s> at %s",
+                                       chr, field.c_str(), tag->getTagLocation().c_str()));
+
+                       // Disable the mode.
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "The %c (%s) %s mode has been disabled",
+                               mh->GetModeChar(), mh->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user");
+                       status.set(chr - 'A');
+               }
+       }
+
+       void WriteLog(const char* message, ...) CUSTOM_PRINTF(2, 3)
+       {
+               std::string buffer;
+               VAFORMAT(buffer, message, message);
+
+               if (notifyopers)
+                       ServerInstance->SNO->WriteToSnoMask('a', buffer);
+               else
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, buffer);
+       }
+
+ public:
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("disabled");
+
+               // Parse the disabled commands.
+               CommandList newcommands;
+               irc::spacesepstream commandlist(tag->getString("commands"));
+               for (std::string command; commandlist.GetToken(command); )
+               {
+                       // Check that the command actually exists.
+                       Command* handler = ServerInstance->Parser.GetHandler(command);
+                       if (!handler)
+                               throw ModuleException(InspIRCd::Format("Non-existent command '%s' was specified in <disabled:commands> at %s",
+                                       command.c_str(), tag->getTagLocation().c_str()));
+
+                       // Prevent admins from disabling COMMANDS and MODULES for transparency reasons.
+                       if (handler->name == "COMMANDS" || handler->name == "MODULES")
+                               continue;
+
+                       // Disable the command.
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "The %s command has been disabled", handler->name.c_str());
+                       newcommands.push_back(handler->name);
+               }
+
+               // Parse the disabled channel modes.
+               ModeStatus newchanmodes;
+               ReadModes(tag, "chanmodes", MODETYPE_CHANNEL, newchanmodes);
+
+               // Parse the disabled user modes.
+               ModeStatus newusermodes;
+               ReadModes(tag, "usermodes", MODETYPE_USER, newusermodes);
+
+               // The server config was valid so we can use these now.
+               chanmodes = newchanmodes;
+               usermodes = newusermodes;
+               commands.swap(newcommands);
+
+               // Whether we should fake the non-existence of disabled things.
+               fakenonexistent = tag->getBool("fakenonexistent", tag->getBool("fakenonexistant"));
+
+               // Whether to notify server operators via snomask `a` about the attempted use of disabled commands/modes.
+               notifyopers = tag->getBool("notifyopers");
+       }
+
+       ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
+       {
+               // If a command is unvalidated or the source is not registered we do nothing.
+               if (!validated || user->registered != REG_ALL)
+                       return MOD_RES_PASSTHRU;
+
+               // If the command is not disabled or the user has the servers/use-disabled-commands priv we do nothing.
+               if (!stdalgo::isin(commands, command) || user->HasPrivPermission("servers/use-disabled-commands"))
+                       return MOD_RES_PASSTHRU;
+
+               // The user has tried to execute a disabled command!
+               user->CommandFloodPenalty += 2000;
+               WriteLog("%s was blocked from executing the disabled %s command", user->GetFullRealHost().c_str(), command.c_str());
+
+               if (fakenonexistent)
+               {
+                       // The server administrator has specified that disabled commands should be
+                       // treated as if they do not exist.
+                       user->WriteNumeric(ERR_UNKNOWNCOMMAND, command, "Unknown command");
+                       ServerInstance->stats.Unknown++;
+                       return MOD_RES_DENY;
+               }
+
+               // Inform the user that the command they executed has been disabled.
+               user->WriteNumeric(ERR_DISABLED, command, "Command disabled");
+               return MOD_RES_DENY;
+       }
+
+       ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE
+       {
+               // If a mode change is remote or the source is not registered we do nothing.
+               if (!IS_LOCAL(user) || user->registered != REG_ALL)
+                       return MOD_RES_PASSTHRU;
+
+               // If the mode is not disabled or the user has the servers/use-disabled-modes priv we do nothing.
+               const std::bitset<64>& disabled = (mh->GetModeType() == MODETYPE_CHANNEL) ? chanmodes : usermodes;
+               if (!disabled.test(mh->GetModeChar() - 'A') || user->HasPrivPermission("servers/use-disabled-modes"))
+                       return MOD_RES_PASSTHRU;
+
+               // The user has tried to change a disabled mode!
+               const char* what = mh->GetModeType() == MODETYPE_CHANNEL ? "channel" : "user";
+               WriteLog("%s was blocked from executing the disabled %s mode %c (%s)",
+                       user->GetFullRealHost().c_str(), what, mh->GetModeChar(), mh->name.c_str());
+
+               if (fakenonexistent)
+               {
+                       // The server administrator has specified that disabled modes should be
+                       // treated as if they do not exist.
+                       user->WriteNumeric(mh->GetModeType() == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK,
+                               mh->GetModeChar(), "is unknown mode char to me");
+                       return MOD_RES_DENY;
+               }
+
+               // Inform the user that the mode they changed has been disabled.
+               user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - %s mode %c (%s) is disabled",
+                       what, mh->GetModeChar(), mh->name.c_str()));
+               return MOD_RES_DENY;
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides support for disabling commands and modes", VF_VENDOR);
+       }
+};
+
+MODULE_INIT(ModuleDisable)
index 8f28a7e9cbee8825e7952973415e4f56beb01bad..db3345b98920f95f39bd21424c0b5f6ec3a55d48 100644 (file)
@@ -124,6 +124,9 @@ class ModuleSaMode : public Module
 
        void Prioritize() CXX11_OVERRIDE
        {
+               Module* disabled = ServerInstance->Modules->Find("m_disabled.so");
+               ServerInstance->Modules->SetPriority(this, I_OnRawMode, PRIORITY_BEFORE, disabled);
+
                Module *override = ServerInstance->Modules->Find("m_override.so");
                ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_BEFORE, override);
        }