From 45070b06e2865f52b2d255facfa8df5a7bc66f92 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Sun, 31 May 2020 21:52:56 +0100 Subject: [PATCH] Implement authentication detection * pending command to see which instances have never authenticated * authcheck command to see which instances are not currently authenticated --- commands/authcheck.py | 36 ++++++++++++++++++++++++++++++++++++ conf/example/irc.json | 8 ++++++-- conf/help.json | 3 ++- core/bot.py | 21 ++++++++++++++++----- modules/chankeep.py | 2 +- modules/monitor.py | 2 ++ modules/regproc.py | 40 +++++++++++++++++++++++++++++++++------- 7 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 commands/authcheck.py diff --git a/commands/authcheck.py b/commands/authcheck.py new file mode 100644 index 0000000..8ed5575 --- /dev/null +++ b/commands/authcheck.py @@ -0,0 +1,36 @@ +import main + +class AuthcheckCommand: + def __init__(self, *args): + self.authcheck(*args) + + def authcheck(self, addr, authed, data, obj, spl, success, failure, info, incUsage, length): + if authed: + if length == 1: + results = [] + for i in main.IRCPool.keys(): + num = main.IRCPool[i].num + net = main.IRCPool[i].net + if not main.IRCPool[i].authenticated: + results.append("%s - %s: %s" % (net, num, main.alias[num]["nick"])) + info("\n".join(results)) + return + elif length == 2: + if not spl[1] in main.IRCPool.keys(): + failure("No such instance: %s" % spl[1]) + return + results = [] + for i in main.IRCPool.keys(): + num = main.IRCPool[i].num + net = main.IRCPool[i].net + if not net == spl[1]: + continue + if not main.IRCPool[i].authenticated: + results.append("%s - %s: %s" % (net, num, main.alias[num]["nick"])) + info("\n".join(results)) + return + else: + incUsage("authcheck") + return + else: + incUsage(None) diff --git a/conf/example/irc.json b/conf/example/irc.json index e624aa8..680eb58 100644 --- a/conf/example/irc.json +++ b/conf/example/irc.json @@ -2,8 +2,12 @@ "_": { "register": true, "entity": "NickServ", - "email": "{nickname}@example.com", + "email": "{nickname}.irc@domain.com", "register": "REGISTER {password} {email}", - "confirm": "CONFIRM {token}" + "confirm": "CONFIRM {token}", + "check": true, + "checktype": "mode", + "checkmode": "r", + "checkmsg": "Password accepted - you are now recognized." } } diff --git a/conf/help.json b/conf/help.json index ede4b7d..3940fa4 100644 --- a/conf/help.json +++ b/conf/help.json @@ -31,5 +31,6 @@ "exec": "exec ", "reg": "reg ", "confirm": "confirm ", - "pending": "pending []" + "pending": "pending []", + "authcheck": "authcheck []" } diff --git a/core/bot.py b/core/bot.py index 90bf653..8a374f8 100644 --- a/core/bot.py +++ b/core/bot.py @@ -108,7 +108,7 @@ class IRCRelay(IRCClient): def signedOn(self): if not self.isconnected: self.isconnected = True - log("signed on as a relay: %s" % self.num) + #log("signed on as a relay: %s" % self.num) sleeptime = 0 increment = 0.8 for i in self.relayCommands.keys(): @@ -124,6 +124,7 @@ class IRCRelay(IRCClient): class IRCBot(IRCClient): def __init__(self, net, num): self.isconnected = False + self.authenticated = False self.channels = [] self.net = net self.num = num @@ -156,6 +157,8 @@ class IRCBot(IRCClient): self.chanlimit = 0 self.servername = None + self._regAttempt = None + def lineReceived(self, line): if bytes != str and isinstance(line, bytes): # decode bytes from transport to unicode @@ -208,7 +211,7 @@ class IRCBot(IRCClient): return if not {"nick", "ident", "host"}.issubset(set(cast.keys())): cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"]) - if set(["nick", "ident", "host", "msg"]).issubset(set(cast)): + if {"nick", "ident", "host", "msg"}.issubset(set(cast)): if "msg" in cast.keys(): if cast["ident"] == "znc" and cast["host"] == "znc.in": cast["type"] = "znc" @@ -235,6 +238,7 @@ class IRCBot(IRCClient): # channel, but we still want to see them if "user" in cast.keys(): if cast["user"].lower() == self.nickname.lower(): + cast["num"] = self.num castDup = deepcopy(cast) castDup["mtype"] = cast["type"] castDup["type"] = "self" @@ -242,6 +246,7 @@ class IRCBot(IRCClient): self.event(**castDup) if "nick" in cast.keys(): if cast["nick"].lower() == self.nickname.lower(): + cast["num"] = self.num castDup = deepcopy(cast) castDup["mtype"] = cast["type"] castDup["type"] = "self" @@ -251,6 +256,7 @@ class IRCBot(IRCClient): if "msg" in cast.keys() and not cast["type"] == "query": # Don't highlight queries if not cast["msg"] == None: if self.nickname.lower() in cast["msg"].lower(): + cast["num"] = self.num castDup = deepcopy(cast) castDup["mtype"] = cast["type"] castDup["type"] = "highlight" @@ -259,6 +265,9 @@ class IRCBot(IRCClient): if not "net" in cast.keys(): cast["net"] = self.net + if not "num" in cast.keys(): + print("no num", cast) + cast["num"] = self.num counters.event(self.net, cast["type"]) monitor.event(self.net, cast) @@ -474,8 +483,8 @@ class IRCBot(IRCClient): self.isconnected = True if not main.network[self.net].relays[self.num]["registered"]: if main.config["AutoReg"]: - regproc.registerAccount(self.net, self.num) - debug("Attempting to register: %s - %i" % (self.net, self.num)) + self._regAttempt = reactor.callLater(5, regproc.registerAccount, self.net, self.num) + #regproc.registerAccount(self.net, self.num) for i in options: if i.startswith("CHANLIMIT"): if ":" in i: @@ -669,10 +678,11 @@ class IRCBotFactory(ReconnectingClientFactory): userinfo.delChannels(self.net, self.client.channels) if not self.client == None: self.client.isconnected = False + self.client.authenticated = False self.client.channels = [] error = reason.getErrorMessage() - log("%s - %i: connection lost: %s" % (self.net, self.num, error)) if not self.relay: + log("%s - %i: connection lost: %s" % (self.net, self.num, error)) sendAll("%s - %i: connection lost: %s" % (self.net, self.num, error)) sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "lost", "message": error}) self.retry(connector) @@ -681,6 +691,7 @@ class IRCBotFactory(ReconnectingClientFactory): def clientConnectionFailed(self, connector, reason): if not self.client == None: self.client.isconnected = False + self.client.authenticated = False self.client.channels = [] error = reason.getErrorMessage() log("%s - %i: connection failed: %s" % (self.net, self.num, error)) diff --git a/modules/chankeep.py b/modules/chankeep.py index 9c9aac3..05e7c61 100644 --- a/modules/chankeep.py +++ b/modules/chankeep.py @@ -12,7 +12,7 @@ def allRelaysActive(net): for i in main.network[net].relays.keys(): name = net+str(i) if name in main.IRCPool.keys(): - if main.IRCPool[name].isconnected and main.network[net].relays[i]["registered"]: + if main.IRCPool[name].authenticated and main.network[net].relays[i]["registered"]: existNum += 1 if existNum == relayNum: return True diff --git a/modules/monitor.py b/modules/monitor.py index d55c1c5..fcc386b 100644 --- a/modules/monitor.py +++ b/modules/monitor.py @@ -5,6 +5,7 @@ from datetime import datetime import main from core.relay import sendRelayNotification from modules import userinfo +from modules import regproc from utils.dedup import dedup order = ["type", "net", "num", "channel", "msg", "nick", @@ -63,6 +64,7 @@ def event(numName, c): # yes I'm using a short variable because otherwise it goe if dedup(numName, c): return + regproc.registerTest(c) # metadata scraping # need to check if this was received from a relay # in which case, do not do this diff --git a/modules/regproc.py b/modules/regproc.py index 0b006b1..1784d31 100644 --- a/modules/regproc.py +++ b/modules/regproc.py @@ -1,6 +1,7 @@ import main from modules import provision from utils.logging.log import * +from utils.logging.debug import * from copy import deepcopy def selectInst(net): @@ -11,11 +12,10 @@ def selectInst(net): inst[i] = main.irc["_"][i] else: inst = main.irc["_"] - print(inst) - print("hello") return inst def registerAccount(net, num): + debug("Attempting to register: %s - %i" % (net, num)) alias = main.alias[num] nickname = alias["nick"] username = nickname+"/"+net @@ -42,17 +42,43 @@ def confirmAccount(net, num, token): main.IRCPool[name].msg(entity, cmd) enableAuthentication(net, num) +def confirmRegistration(net, num): + obj = main.network[net] + name = net+str(num) + if name in main.IRCPool.keys(): + debug("Relay authenticated: %s - %i" %(net, num)) + main.IRCPool[name].authenticated = True + if obj.relays[num]["registered"]: + return + if name in main.IRCPool.keys(): + if main.IRCPool[name]._regAttempt: + main.IRCPool[name]._regAttempt.cancel() + obj.relays[num]["registered"] = True + main.saveConf("network") + def enableAuthentication(net, num): obj = main.network[net] nick = main.alias[num]["nick"] security = obj.security auth = obj.auth password = obj.aliases[num]["password"] - uname = main.alias[num]["nick"]+"/"+net provision.provisionAuthenticationData(num, nick, net, security, auth, password) # Set up for auth - if obj.relays[num]["registered"]: - warn("Authentication already enabled: %s - %i" % (net, num)) - obj.relays[num]["registered"] = True - main.saveConf("network") main.IRCPool[net+str(num)].msg(main.config["Tweaks"]["ZNC"]["Prefix"]+"status", "Jump") + if selectInst(net)["check"] == False: + confirmRegistration(net, num) + +def registerTest(c): + inst = selectInst(c["net"]) + if inst["check"] == False: + return + if inst["checktype"] == "msg": + if c["type"] == "query": + if inst["checkmsg"] in c["msg"] and c["nick"] == inst["entity"]: + confirmRegistration(c["net"], c["num"]) + return + elif inst["checktype"] == "mode": + if c["type"] == "mode": + if inst["checkmode"] in c["modes"] and c["status"] == True: + confirmRegistration(c["net"], c["num"]) + return