Implement authentication detection

* pending command to see which instances have never authenticated
* authcheck command to see which instances are not currently
  authenticated
This commit is contained in:
Mark Veidemanis 2020-05-31 21:52:56 +01:00
parent 586a337ea4
commit 097f100ec5
7 changed files with 96 additions and 16 deletions

36
commands/authcheck.py Normal file
View File

@ -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)

View File

@ -2,8 +2,12 @@
"_": { "_": {
"register": true, "register": true,
"entity": "NickServ", "entity": "NickServ",
"email": "{nickname}@example.com", "email": "{nickname}.irc@domain.com",
"register": "REGISTER {password} {email}", "register": "REGISTER {password} {email}",
"confirm": "CONFIRM {token}" "confirm": "CONFIRM {token}",
"check": true,
"checktype": "mode",
"checkmode": "r",
"checkmsg": "Password accepted - you are now recognized."
} }
} }

View File

@ -31,5 +31,6 @@
"exec": "exec <expr ...>", "exec": "exec <expr ...>",
"reg": "reg <network> <num>", "reg": "reg <network> <num>",
"confirm": "confirm <network> <num> <token>", "confirm": "confirm <network> <num> <token>",
"pending": "pending [<network>]" "pending": "pending [<network>]",
"authcheck": "authcheck [<network>]"
} }

View File

@ -108,7 +108,7 @@ class IRCRelay(IRCClient):
def signedOn(self): def signedOn(self):
if not self.isconnected: if not self.isconnected:
self.isconnected = True self.isconnected = True
log("signed on as a relay: %s" % self.num) #log("signed on as a relay: %s" % self.num)
sleeptime = 0 sleeptime = 0
increment = 0.8 increment = 0.8
for i in self.relayCommands.keys(): for i in self.relayCommands.keys():
@ -124,6 +124,7 @@ class IRCRelay(IRCClient):
class IRCBot(IRCClient): class IRCBot(IRCClient):
def __init__(self, net, num): def __init__(self, net, num):
self.isconnected = False self.isconnected = False
self.authenticated = False
self.channels = [] self.channels = []
self.net = net self.net = net
self.num = num self.num = num
@ -156,6 +157,8 @@ class IRCBot(IRCClient):
self.chanlimit = 0 self.chanlimit = 0
self.servername = None self.servername = None
self._regAttempt = None
def lineReceived(self, line): def lineReceived(self, line):
if bytes != str and isinstance(line, bytes): if bytes != str and isinstance(line, bytes):
# decode bytes from transport to unicode # decode bytes from transport to unicode
@ -208,7 +211,7 @@ class IRCBot(IRCClient):
return return
if not {"nick", "ident", "host"}.issubset(set(cast.keys())): if not {"nick", "ident", "host"}.issubset(set(cast.keys())):
cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"]) 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 "msg" in cast.keys():
if cast["ident"] == "znc" and cast["host"] == "znc.in": if cast["ident"] == "znc" and cast["host"] == "znc.in":
cast["type"] = "znc" cast["type"] = "znc"
@ -235,6 +238,7 @@ class IRCBot(IRCClient):
# channel, but we still want to see them # channel, but we still want to see them
if "user" in cast.keys(): if "user" in cast.keys():
if cast["user"].lower() == self.nickname.lower(): if cast["user"].lower() == self.nickname.lower():
cast["num"] = self.num
castDup = deepcopy(cast) castDup = deepcopy(cast)
castDup["mtype"] = cast["type"] castDup["mtype"] = cast["type"]
castDup["type"] = "self" castDup["type"] = "self"
@ -242,6 +246,7 @@ class IRCBot(IRCClient):
self.event(**castDup) self.event(**castDup)
if "nick" in cast.keys(): if "nick" in cast.keys():
if cast["nick"].lower() == self.nickname.lower(): if cast["nick"].lower() == self.nickname.lower():
cast["num"] = self.num
castDup = deepcopy(cast) castDup = deepcopy(cast)
castDup["mtype"] = cast["type"] castDup["mtype"] = cast["type"]
castDup["type"] = "self" 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 "msg" in cast.keys() and not cast["type"] == "query": # Don't highlight queries
if not cast["msg"] == None: if not cast["msg"] == None:
if self.nickname.lower() in cast["msg"].lower(): if self.nickname.lower() in cast["msg"].lower():
cast["num"] = self.num
castDup = deepcopy(cast) castDup = deepcopy(cast)
castDup["mtype"] = cast["type"] castDup["mtype"] = cast["type"]
castDup["type"] = "highlight" castDup["type"] = "highlight"
@ -259,6 +265,9 @@ class IRCBot(IRCClient):
if not "net" in cast.keys(): if not "net" in cast.keys():
cast["net"] = self.net cast["net"] = self.net
if not "num" in cast.keys():
print("no num", cast)
cast["num"] = self.num
counters.event(self.net, cast["type"]) counters.event(self.net, cast["type"])
monitor.event(self.net, cast) monitor.event(self.net, cast)
@ -474,8 +483,8 @@ class IRCBot(IRCClient):
self.isconnected = True self.isconnected = True
if not main.network[self.net].relays[self.num]["registered"]: if not main.network[self.net].relays[self.num]["registered"]:
if main.config["AutoReg"]: if main.config["AutoReg"]:
regproc.registerAccount(self.net, self.num) self._regAttempt = reactor.callLater(5, regproc.registerAccount, self.net, self.num)
debug("Attempting to register: %s - %i" % (self.net, self.num)) #regproc.registerAccount(self.net, self.num)
for i in options: for i in options:
if i.startswith("CHANLIMIT"): if i.startswith("CHANLIMIT"):
if ":" in i: if ":" in i:
@ -669,10 +678,11 @@ class IRCBotFactory(ReconnectingClientFactory):
userinfo.delChannels(self.net, self.client.channels) userinfo.delChannels(self.net, self.client.channels)
if not self.client == None: if not self.client == None:
self.client.isconnected = False self.client.isconnected = False
self.client.authenticated = False
self.client.channels = [] self.client.channels = []
error = reason.getErrorMessage() error = reason.getErrorMessage()
log("%s - %i: connection lost: %s" % (self.net, self.num, error))
if not self.relay: 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)) sendAll("%s - %i: connection lost: %s" % (self.net, self.num, error))
sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "lost", "message": error}) sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "lost", "message": error})
self.retry(connector) self.retry(connector)
@ -681,6 +691,7 @@ class IRCBotFactory(ReconnectingClientFactory):
def clientConnectionFailed(self, connector, reason): def clientConnectionFailed(self, connector, reason):
if not self.client == None: if not self.client == None:
self.client.isconnected = False self.client.isconnected = False
self.client.authenticated = False
self.client.channels = [] self.client.channels = []
error = reason.getErrorMessage() error = reason.getErrorMessage()
log("%s - %i: connection failed: %s" % (self.net, self.num, error)) log("%s - %i: connection failed: %s" % (self.net, self.num, error))

View File

@ -12,7 +12,7 @@ def allRelaysActive(net):
for i in main.network[net].relays.keys(): for i in main.network[net].relays.keys():
name = net+str(i) name = net+str(i)
if name in main.IRCPool.keys(): 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 existNum += 1
if existNum == relayNum: if existNum == relayNum:
return True return True

View File

@ -5,6 +5,7 @@ from datetime import datetime
import main import main
from core.relay import sendRelayNotification from core.relay import sendRelayNotification
from modules import userinfo from modules import userinfo
from modules import regproc
from utils.dedup import dedup from utils.dedup import dedup
order = ["type", "net", "num", "channel", "msg", "nick", 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): if dedup(numName, c):
return return
regproc.registerTest(c)
# metadata scraping # metadata scraping
# need to check if this was received from a relay # need to check if this was received from a relay
# in which case, do not do this # in which case, do not do this

View File

@ -1,6 +1,7 @@
import main import main
from modules import provision from modules import provision
from utils.logging.log import * from utils.logging.log import *
from utils.logging.debug import *
from copy import deepcopy from copy import deepcopy
def selectInst(net): def selectInst(net):
@ -11,11 +12,10 @@ def selectInst(net):
inst[i] = main.irc["_"][i] inst[i] = main.irc["_"][i]
else: else:
inst = main.irc["_"] inst = main.irc["_"]
print(inst)
print("hello")
return inst return inst
def registerAccount(net, num): def registerAccount(net, num):
debug("Attempting to register: %s - %i" % (net, num))
alias = main.alias[num] alias = main.alias[num]
nickname = alias["nick"] nickname = alias["nick"]
username = nickname+"/"+net username = nickname+"/"+net
@ -42,17 +42,43 @@ def confirmAccount(net, num, token):
main.IRCPool[name].msg(entity, cmd) main.IRCPool[name].msg(entity, cmd)
enableAuthentication(net, num) 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): def enableAuthentication(net, num):
obj = main.network[net] obj = main.network[net]
nick = main.alias[num]["nick"] nick = main.alias[num]["nick"]
security = obj.security security = obj.security
auth = obj.auth auth = obj.auth
password = obj.aliases[num]["password"] password = obj.aliases[num]["password"]
uname = main.alias[num]["nick"]+"/"+net uname = main.alias[num]["nick"]+"/"+net
provision.provisionAuthenticationData(num, nick, net, security, auth, password) # Set up for auth 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") 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