From c5b6658200a987b9b2db2a021605cc22adc1d9d1 Mon Sep 17 00:00:00 2001 From: James Lu Date: Mon, 14 Sep 2015 17:44:07 -0700 Subject: [PATCH] bots: more validation in "MODE" to prevent bad things from happening This adds a new "allow_forceset_usermodes" attribute to protocol modules, which determines whether the IRCd allows us to force usermode changes on other servers' clients. Also, make sure our target is a valid nick/UID/channel, and that the parsed modes are not empty! --- classes.py | 3 +++ plugins/bots.py | 30 +++++++++++++++++------------- protocols/inspircd.py | 3 +++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/classes.py b/classes.py index 9e5e6f5..e156370 100644 --- a/classes.py +++ b/classes.py @@ -396,6 +396,9 @@ class Protocol(): self.casemapping = 'rfc1459' self.hook_map = {} + # Whether the IRCd allows forcing user mode changes on other servers' clients. + self.allow_forceset_usermodes = False + class FakeProto(Protocol): """Dummy protocol module for testing purposes.""" def handle_events(self, data): diff --git a/plugins/bots.py b/plugins/bots.py index 771621d..82d594c 100644 --- a/plugins/bots.py +++ b/plugins/bots.py @@ -138,30 +138,34 @@ def kick(irc, source, args): def mode(irc, source, args): """ - Admin-only. Sets modes on from , where is either the nick of a PyLink client, or the SID of a PyLink server.""" + Admin-only. Sets modes on from , where is either the nick of a PyLink client, or the SID of a PyLink server. can be either a nick or a channel.""" utils.checkAuthenticated(irc, source, allowOper=False) try: modesource, target, modes = args[0], args[1], args[2:] except IndexError: irc.msg(source, 'Error: Not enough arguments. Needs 3: source nick, target, modes to set.') return - if not modes: - irc.msg(source, "Error: No modes given to set!") - return - parsedmodes = utils.parseModes(irc, target, modes) - targetuid = utils.nickToUid(irc, target) - if targetuid: - target = targetuid - elif not utils.isChannel(target): + target = utils.nickToUid(irc, target) or target + if not (target in irc.users or target in irc.channels): irc.msg(source, "Error: Invalid channel or nick %r." % target) return + elif target in irc.users and not irc.proto.allow_forceset_usermodes: + irc.msg(source, "Error: this IRCd does not allow forcing user mode " + "changes on other servers' users!") + return + parsedmodes = utils.parseModes(irc, target, modes) + if not parsedmodes: + irc.msg(source, "Error: No valid modes were given.") + return if utils.isInternalServer(irc, modesource): + # Setting modes from a server. irc.proto.modeServer(modesource, target, parsedmodes) - irc.callHooks([modesource, 'PYLINK_BOTSPLUGIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) else: - sourceuid = utils.nickToUid(irc, modesource) - irc.proto.modeClient(sourceuid, target, parsedmodes) - irc.callHooks([sourceuid, 'PYLINK_BOTSPLUGIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) + # Setting modes from a client. + modesource = utils.nickToUid(irc, modesource) + irc.proto.modeClient(modesource, target, parsedmodes) + irc.callHooks([modesource, 'PYLINK_BOTSPLUGIN_MODE', + {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) @utils.add_cmd def msg(irc, source, args): diff --git a/protocols/inspircd.py b/protocols/inspircd.py index f59e4df..c64055c 100644 --- a/protocols/inspircd.py +++ b/protocols/inspircd.py @@ -27,6 +27,9 @@ class InspIRCdProtocol(TS6BaseProtocol): self.sidgen = utils.TS6SIDGenerator(self.irc.serverdata["sidrange"]) self.uidgen = {} + # Whether the IRCd allows forcing user mode changes on other servers' clients. + self.allow_forceset_usermodes = True + def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None): """Spawns a client with nick on the given IRC connection.