Improve authentication detection

Add a negative check in the event we are authenticated and registered,
but not confirmed, as this fools other checks.
This commit is contained in:
Mark Veidemanis 2020-10-31 16:49:37 +00:00
parent eaeb4b72c2
commit d60d89dbf6
6 changed files with 125 additions and 31 deletions

View File

@ -16,8 +16,8 @@ class AuthcheckCommand:
info("\n".join(results)) info("\n".join(results))
return return
elif length == 2: elif length == 2:
if not spl[1] in main.IRCPool.keys(): if not spl[1] in main.network.keys():
failure("No such instance: %s" % spl[1]) failure("No such network: %s" % spl[1])
return return
results = [] results = []
for i in main.IRCPool.keys(): for i in main.IRCPool.keys():

36
commands/recheckauth.py Normal file
View File

@ -0,0 +1,36 @@
import main
class RecheckauthCommand:
def __init__(self, *args):
self.recheckauth(*args)
def recheckauth(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
main.IRCPool[i].authenticated = False
main.IRCPool[i].regPing()
success("Successfully reset authentication status")
return
elif length == 2:
if not spl[1] in main.network.keys():
failure("No such network: %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
main.IRCPool[i].authenticated = False
main.IRCPool[i].regPing()
success("Successfully reset authentication status on %s" % spl[1])
return
else:
incUsage("recheckauth")
return
else:
incUsage(None)

View File

@ -3,14 +3,21 @@
"register": true, "register": true,
"entity": "NickServ", "entity": "NickServ",
"email": "{nickname}@domain.com", "email": "{nickname}@domain.com",
"register": "REGISTER {password} {email}", "registermsg": "REGISTER {password} {email}",
"confirm": "CONFIRM {token}", "confirm": "CONFIRM {token}",
"check": true, "check": true,
"ping": true, "ping": true,
"pingmsg": "STATUS {nickname}", "negative": true,
"pingmsg": "STATUS",
"negativemsg": "INFO {nickname}",
"checktype": "mode", "checktype": "mode",
"checkmode": "r", "checkmode": "r",
"checkmsg": "Password accepted - you are now recognized.", "checkmsg": "Password accepted - you are now recognized.",
"checkmsg2": "You are logged in as" "checkmsg2": "You are logged in as",
"checknegativemsg": "has \u0002NOT COMPLETED\u0002 registration verification",
"checkendnegative": "End of Info"
},
"freenode": {
"confirm": "VERIFY REGISTER {nickname} {token}"
} }
} }

View File

@ -31,5 +31,6 @@
"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>]" "authcheck": "authcheck [<network>]",
"recheckauth": "recheckauth [<network>]"
} }

View File

@ -160,6 +160,7 @@ class IRCBot(IRCClient):
self.servername = None self.servername = None
self._regAttempt = None self._regAttempt = None
self._negativePass = None
def lineReceived(self, line): def lineReceived(self, line):
if bytes != str and isinstance(line, bytes): if bytes != str and isinstance(line, bytes):
@ -167,7 +168,6 @@ class IRCBot(IRCClient):
line = line.decode("utf-8", "replace") line = line.decode("utf-8", "replace")
line = lowDequote(line) line = lowDequote(line)
trace(self.net, self.num, line)
try: try:
prefix, command, params = parsemsg(line) prefix, command, params = parsemsg(line)
if command in numeric_to_symbolic: if command in numeric_to_symbolic:
@ -618,14 +618,46 @@ class IRCBot(IRCClient):
self.topicUpdated(prefix, channel, newtopic) self.topicUpdated(prefix, channel, newtopic)
# End of Twisted hackery # End of Twisted hackery
def regPing(self, negativepass=None):
if self.authenticated:
return
sinst = regproc.substitute(self.net, self.num)
if not self._negativePass == True:
if negativepass == False:
self._negativePass = False
return
if negativepass == True:
if self._negativePass == None:
self._negativePass = True
debug("Positive registration check - %s - %i" % (self.net, self.num))
if sinst["ping"]:
debug("Sending ping - %s - %i" % (self.net, self.num))
self.msg(sinst["entity"], sinst["pingmsg"])
return
else:
debug("Negative registration for %s - %i" % (self.net, self.num))
return
if sinst["check"]:
if sinst["negative"]:
self._negativePass = None
self.msg(sinst["entity"], sinst["negativemsg"])
return
else:
self._negativePass = True
if sinst["ping"]:
self.msg(sinst["entity"], sinst["pingmsg"])
return
else:
self.authenticated = True
warn("Won't send registration ping for %s - %i" % (self.net, self.num))
def signedOn(self): def signedOn(self):
log("signed on: %s - %i" % (self.net, self.num)) log("signed on: %s - %i" % (self.net, self.num))
ctime = str(datetime.now().isoformat()) ctime = str(datetime.now().isoformat())
sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "signedon", "time": ctime}) sendRelayNotification({"type": "conn", "net": self.net, "num": self.num, "status": "signedon", "time": ctime})
if not self.authenticated: if not self.authenticated:
inst = regproc.selectInst(self.net) reactor.callLater(10, self.regPing)
if inst["ping"] and inst["check"]:
self.msg(inst["entity"], inst["pingmsg"])
def joined(self, channel): def joined(self, channel):
if not channel in self.channels: if not channel in self.channels:

View File

@ -22,38 +22,45 @@ def selectInst(net):
inst = main.irc["_"] inst = main.irc["_"]
return inst return inst
def registerAccount(net, num): def substitute(net, num, token=None):
debug("Attempting to register: %s - %i" % (net, num)) inst = selectInst(net)
alias = main.alias[num] alias = main.alias[num]
nickname = alias["nick"] nickname = alias["nick"]
username = nickname+"/"+net username = nickname+"/"+net
password = main.network[net].aliases[num]["password"] password = main.network[net].aliases[num]["password"]
inst = selectInst(net) inst["email"] = inst["email"].replace("{nickname}", nickname)
for i in inst.keys():
if not isinstance(inst[i], str):
continue
inst[i] = inst[i].replace("{nickname}", nickname)
inst[i] = inst[i].replace("{password}", password)
inst[i] = inst[i].replace("{email}", inst["email"])
if token:
inst[i] = inst[i].replace("{token}", token)
return inst
def registerAccount(net, num):
debug("Attempting to register: %s - %i" % (net, num))
sinst = substitute(net, num)
if not inst["register"]: if not inst["register"]:
error("Cannot register for %s: function disabled" % (net)) error("Cannot register for %s: function disabled" % (net))
return False return False
entity = inst["entity"]
email = inst["email"]
cmd = inst["register"]
email = email.replace("{nickname}", nickname)
cmd = cmd.replace("{password}", password)
cmd = cmd.replace("{email}", email)
name = net+str(num) name = net+str(num)
main.IRCPool[name].msg(entity, cmd) main.IRCPool[name].msg(sinst["entity"], sinst["registermsg"])
def confirmAccount(net, num, token): def confirmAccount(net, num, token):
inst = selectInst(net) sinst = substitute(net, num, token=token)
entity = inst["entity"]
cmd = inst["confirm"]
cmd = cmd.replace("{token}", token)
name = net+str(num) name = net+str(num)
main.IRCPool[name].msg(entity, cmd) main.IRCPool[name].msg(sinst["entity"], sinst["confirm"])
enableAuthentication(net, num) enableAuthentication(net, num)
def confirmRegistration(net, num): def confirmRegistration(net, num, negativepass=None):
obj = main.network[net] obj = main.network[net]
name = net+str(num) name = net+str(num)
if name in main.IRCPool.keys(): if name in main.IRCPool.keys():
if not negativepass == None:
main.IRCPool[name].regPing(negativepass=negativepass)
return
debug("Relay authenticated: %s - %i" %(net, num)) debug("Relay authenticated: %s - %i" %(net, num))
main.IRCPool[name].authenticated = True main.IRCPool[name].authenticated = True
main.IRCPool[name].recheckList() main.IRCPool[name].recheckList()
@ -79,16 +86,27 @@ def enableAuthentication(net, num):
def registerTest(c): def registerTest(c):
inst = selectInst(c["net"]) inst = selectInst(c["net"])
name = c["net"]+str(c["num"])
if inst["check"] == False: if inst["check"] == False:
return return
if "msg" in c.keys() and not c["msg"] == None: if "msg" in c.keys() and not c["msg"] == None:
if inst["checktype"] == "msg": if inst["negative"]:
if c["type"] == "query": if name in main.IRCPool.keys():
if inst["checkmsg"] in c["msg"] and c["nick"] == inst["entity"]: if not main.IRCPool[name]._negativePass == True:
confirmRegistration(c["net"], c["num"]) if c["type"] == "query" and c["nick"] == inst["entity"]:
return if inst["checknegativemsg"] in c["msg"]:
confirmRegistration(c["net"], c["num"], negativepass=False) # Not passed negative check, report back
return
if inst["checkendnegative"] in c["msg"]:
confirmRegistration(c["net"], c["num"], negativepass=True) # Passed the negative check, report back
return
if inst["ping"]: if inst["ping"]:
if inst["checkmsg2"] in c["msg"] and c["nick"] == inst["entity"]: if inst["checkmsg2"] in c["msg"] and c["nick"] == inst["entity"]:
print("CHECKMSG2 SUCCEEDED", c["num"])
confirmRegistration(c["net"], c["num"])
return
if inst["checktype"] == "msg":
if inst["checkmsg"] in c["msg"]:
confirmRegistration(c["net"], c["num"]) confirmRegistration(c["net"], c["num"])
return return
elif inst["checktype"] == "mode": elif inst["checktype"] == "mode":