summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Powell <petpow@saberuk.com>2019-01-24 15:10:02 +0000
committerPeter Powell <petpow@saberuk.com>2019-01-24 15:10:02 +0000
commit4047a143fc1d16350db70c94b9ea77d79de05714 (patch)
tree854169605c3b134d5edc39067d0298f7d1e9c64b
parentcbef0241a04eafe5250b75ebb3f7ef8c32ecb260 (diff)
Move the <disabled> tag out of the core to a new module.
-rw-r--r--docs/conf/inspircd.conf.example19
-rw-r--r--docs/conf/modules.conf.example37
-rw-r--r--include/configreader.h15
-rw-r--r--include/ctables.h21
-rw-r--r--src/command_parse.cpp19
-rw-r--r--src/configreader.cpp46
-rw-r--r--src/inspircd.cpp1
-rw-r--r--src/mode.cpp11
-rw-r--r--src/modules/m_conn_umodes.cpp6
-rw-r--r--src/modules/m_disable.cpp189
-rw-r--r--src/modules/m_samode.cpp3
11 files changed, 229 insertions, 138 deletions
diff --git a/docs/conf/inspircd.conf.example b/docs/conf/inspircd.conf.example
index f2db74500..890c9cc4c 100644
--- a/docs/conf/inspircd.conf.example
+++ b/docs/conf/inspircd.conf.example
@@ -520,25 +520,6 @@
# 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. #
diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example
index 0647f77c0..980d0d6a5 100644
--- a/docs/conf/modules.conf.example
+++ b/docs/conf/modules.conf.example
@@ -765,6 +765,43 @@
# 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. #
#<module name="dnsbl"> #
diff --git a/include/configreader.h b/include/configreader.h
index fd9401c0f..511bedbee 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -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?
diff --git a/include/ctables.h b/include/ctables.h
index 8be40cc54..1c7d5b4bd 100644
--- a/include/ctables.h
+++ b/include/ctables.h
@@ -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()
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index c2ae39d49..7a732f8b5 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -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)
diff --git a/src/configreader.cpp b/src/configreader.cpp
index ce03f1d9e..5a0ceff06 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -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);
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index 7e299700b..09e48ea1f 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -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())
{
diff --git a/src/mode.cpp b/src/mode.cpp
index 5793b4e66..459eb21c8 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -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. */
diff --git a/src/modules/m_conn_umodes.cpp b/src/modules/m_conn_umodes.cpp
index 1f2fe7455..3132aed40 100644
--- a/src/modules/m_conn_umodes.cpp
+++ b/src/modules/m_conn_umodes.cpp
@@ -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
index 000000000..203f9ee64
--- /dev/null
+++ b/src/modules/m_disable.cpp
@@ -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)
diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp
index 8f28a7e9c..db3345b98 100644
--- a/src/modules/m_samode.cpp
+++ b/src/modules/m_samode.cpp
@@ -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);
}