From 2ae72d6723aba0eb63cd3b62e4e49238c935e587 Mon Sep 17 00:00:00 2001 From: James Lu Date: Sun, 13 Jun 2021 01:00:41 -0700 Subject: [PATCH] Expose SSL/TLS state in UID hooks when available (#169) --- docs/technical/hooks-reference.md | 21 ++++++++++++--------- protocols/hybrid.py | 4 ++-- protocols/inspircd.py | 3 ++- protocols/p10.py | 2 +- protocols/ts6.py | 4 ++-- protocols/unreal.py | 4 ++-- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/docs/technical/hooks-reference.md b/docs/technical/hooks-reference.md index ec88435..7a94936 100644 --- a/docs/technical/hooks-reference.md +++ b/docs/technical/hooks-reference.md @@ -1,6 +1,6 @@ # PyLink hooks reference -***Last updated for 2.1-alpha2 (2019-07-01).*** +***Last updated for 3.1-dev (2021-06-13).*** In PyLink, protocol modules communicate with plugins through a system of hooks. This has the benefit of being IRCd-independent, allowing most plugins to function regardless of the IRCd being used. Each hook payload is formatted as a Python `list`, with three arguments: `numeric`, `command`, and `args`. @@ -100,9 +100,10 @@ The following hooks represent regular IRC commands sent between servers. - `oldtopic` denotes the original topic, and `text` indicates the new one being set. - `setter` is the raw sender field given to us by the IRCd; it may be a `nick!user@host`, a UID, a SID, a server name, or a nick. This is not processed at the protocol level. -- **UID**: `{'uid': 'UID1', 'ts': 1234567891, 'nick': 'supercoder', 'realhost': 'localhost', 'host': 'admin.testnet.local', 'ident': ident, 'ip': '127.0.0.1'}` +- **UID**: `{'uid': 'UID1', 'ts': 1234567891, 'nick': 'supercoder', 'realhost': 'localhost', 'host': 'admin.testnet.local', 'ident': ident, 'ip': '127.0.0.1', 'secure': True}` - This command is used to introduce users; the sender of the message should be the server bursting or announcing the connection. - `ts` refers to the user's signon time. + - `secure` is a ternary value (True/False/None) that determines whether the user is connected over a secure connection (SSL/TLS). This value is only available on some IRCds: currently UnrealIRCd, P10, Charybdis TS6, and Hybrid; on other servers this will be `None`. ### Extra commands (where supported) @@ -163,15 +164,17 @@ At this time, commands that are handled by protocol modules without returning an ## Changes +* 2021-06-13 (3.1-dev) + - Added the `secure` field to `UID` hooks. * 2019-07-01 (2.1-alpha2) - - KILL and QUIT hooks now always include a non-empty `userdata` key. Now, if a QUIT message for a killed user is received before the corresponding KILL (or vice versa), only the first message received will have the corresponding hook payload broadcasted. + - KILL and QUIT hooks now always include a non-empty `userdata` key. Now, if a QUIT message for a killed user is received before the corresponding KILL (or vice versa), only the first message received will have the corresponding hook payload broadcasted. * 2018-12-27 (2.1-dev) - - Add the `affected_servers` argument to SQUIT hooks. + - Add the `affected_servers` argument to SQUIT hooks. * 2018-07-11 (2.0.0) - - Version bump for 2.0 stable release; no meaningful content changes. + - Version bump for 2.0 stable release; no meaningful content changes. * 2018-01-13 (2.0-alpha2) - - Replace `IrcChannel`, `IrcUser`, and `IrcServer` with their new class names (`classes.Channel`, `classes.User`, and `classes.Server`) - - Replace `irc.fullVersion()` with `irc.version()` - - Various minor wording tweaks. + - Replace `IrcChannel`, `IrcUser`, and `IrcServer` with their new class names (`classes.Channel`, `classes.User`, and `classes.Server`) + - Replace `irc.fullVersion()` with `irc.version()` + - Various minor wording tweaks. * 2017-02-24 (1.2-dev) - - The `was_successful` key was added to PYLINK_DISCONNECT. + - The `was_successful` key was added to PYLINK_DISCONNECT. diff --git a/protocols/hybrid.py b/protocols/hybrid.py index 591a1da..f20e358 100644 --- a/protocols/hybrid.py +++ b/protocols/hybrid.py @@ -212,13 +212,13 @@ class HybridProtocol(TS6Protocol): self._check_oper_status_change(uid, parsedmodes) # Track SSL/TLS status - self.users[uid].ssl = ('+S', None) in parsedmodes + has_ssl = self.users[uid].ssl = ('+S', None) in parsedmodes # Set the account name if present if account: self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}]) - return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip} + return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip, 'secure': has_ssl} def handle_tburst(self, numeric, command, args): """Handles incoming topic burst (TBURST) commands.""" diff --git a/protocols/inspircd.py b/protocols/inspircd.py index 0fedfaa..be0cd67 100644 --- a/protocols/inspircd.py +++ b/protocols/inspircd.py @@ -777,7 +777,8 @@ class InspIRCdProtocol(TS6BaseProtocol): self._check_oper_status_change(uid, parsedmodes) self.servers[numeric].users.add(uid) - return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip} + # InspIRCd sends SSL status in the metadata command, so the info is not known at this point + return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip, 'secure': None} def handle_server(self, source, command, args): """Handles incoming SERVER commands (introduction of servers).""" diff --git a/protocols/p10.py b/protocols/p10.py index ba3eedd..79263ec 100644 --- a/protocols/p10.py +++ b/protocols/p10.py @@ -971,7 +971,7 @@ class P10Protocol(IRCS2SProtocol): self._check_cloak_change(uid) - return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip, 'parse_as': 'UID'} + return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip, 'parse_as': 'UID', 'secure': uobj.ssl} else: # <- ABAAA N jlu5_ 1460753763 diff --git a/protocols/ts6.py b/protocols/ts6.py index 96c3c67..5f31b15 100644 --- a/protocols/ts6.py +++ b/protocols/ts6.py @@ -607,9 +607,9 @@ class TS6Protocol(TS6BaseProtocol): # charybdis and derivatives have a usermode (+Z) to mark SSL connections # ratbox doesn't appear to have this - self.users[uid].ssl = ('+%s' % self.umodes.get('ssl'), None) in parsedmodes + has_ssl = self.users[uid].ssl = ('+%s' % self.umodes.get('ssl'), None) in parsedmodes - return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip} + return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip, 'secure': has_ssl} def handle_uid(self, numeric, command, args): """Handles legacy user introductions (UID).""" diff --git a/protocols/unreal.py b/protocols/unreal.py index 796f3f4..84e55ac 100644 --- a/protocols/unreal.py +++ b/protocols/unreal.py @@ -509,7 +509,7 @@ class UnrealProtocol(TS6BaseProtocol): accountname = nick # Track SSL/TLS status - self.users[uid].ssl = ('+z', None) in parsedmodes + has_ssl = self.users[uid].ssl = ('+z', None) in parsedmodes if not accountname.isdigit(): self.call_hooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}]) @@ -517,7 +517,7 @@ class UnrealProtocol(TS6BaseProtocol): # parse_as is used here to prevent legacy user introduction from being confused # with a nick change. return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, - 'ident': ident, 'ip': ip, 'parse_as': 'UID'} + 'ident': ident, 'ip': ip, 'parse_as': 'UID', 'secure': has_ssl} def handle_pass(self, numeric, command, args): # <- PASS :abcdefg