aboutsummaryrefslogtreecommitdiff
path: root/modules/sasl.py
diff options
context:
space:
mode:
authorGravatar jesopo2019-02-05 15:54:20 +0000
committerGravatar jesopo2019-02-05 15:54:20 +0000
commit1fe20a2c98ed5e4042d10c415d7923aebfaa362d (patch)
tree677385905a109b6760258842b18643b8bf7c9fd8 /modules/sasl.py
parentSwitch to using __init__.py as main file of directory modules, so they behave (diff)
Move sasl.py to a directory module and move SCRAM logic to a different file,
move `github/module.py` to `github/__init__.py`
Diffstat (limited to 'modules/sasl.py')
-rw-r--r--modules/sasl.py142
1 files changed, 0 insertions, 142 deletions
diff --git a/modules/sasl.py b/modules/sasl.py
deleted file mode 100644
index ed4faabf..00000000
--- a/modules/sasl.py
+++ /dev/null
@@ -1,142 +0,0 @@
-import base64, hashlib, hmac, uuid
-from src import ModuleManager, utils
-
-def _validate(self, s):
- mechanism = s
- if " " in s:
- mechanism, arguments = s.split(" ", 1)
- return {"mechanism": mechanism, "args": arguments}
-
-def _scram_nonce():
- return str(uuid.uuid4().hex)
-def _scram_escape(s):
- return s.replace("=", "=3D").replace(",", "=2C")
-def _scram_unescape(s):
- return s.replace("=3D", "=").replace("=2C", ",")
-def _scram_xor(s1, s2):
- return bytes(a ^ b for a, b in zip(s1, s2))
-
-@utils.export("serverset", {"setting": "sasl",
- "help": "Set the sasl username/password for this server",
- "validate": _validate})
-class Module(ModuleManager.BaseModule):
- @utils.hook("received.cap.ls")
- def on_cap(self, event):
- has_sasl = "sasl" in event["capabilities"]
- our_sasl = event["server"].get_setting("sasl", None)
-
- do_sasl = False
- if has_sasl and our_sasl:
- if not event["capabilities"]["sasl"] == None:
- our_mechanism = our_sasl["mechanism"].upper()
- do_sasl = our_mechanism in event["capabilities"
- ]["sasl"].split(",")
- else:
- do_sasl = True
-
- if do_sasl:
- event["server"].queue_capability("sasl")
-
- @utils.hook("received.cap.ack")
- def on_cap_ack(self, event):
- if "sasl" in event["capabilities"]:
- sasl = event["server"].get_setting("sasl")
- event["server"].send_authenticate(sasl["mechanism"].upper())
- event["server"].wait_for_capability("sasl")
-
- @utils.hook("received.authenticate")
- def on_authenticate(self, event):
- sasl = event["server"].get_setting("sasl")
- mechanism = sasl["mechanism"].upper()
-
- if mechanism == "PLAIN":
- if event["message"] != "+":
- event["server"].send_authenticate("*")
- else:
- sasl_username, sasl_password = sasl["args"].split(":", 1)
- auth_text = ("%s\0%s\0%s" % (
- sasl_username, sasl_username, sasl_password)).encode("utf8")
-
- elif mechanism == "EXTERNAL":
- if event["message"] != "+":
- event["server"].send_authenticate("*")
- else:
- auth_text = "+"
-
- elif mechanism.startswith("SCRAM-"):
- algo = mechanism.split("SCRAM-", 1)[1].replace("-", "")
- sasl_username, sasl_password = sasl["args"].split(":", 1)
- if event["message"] == "+":
- # start SCRAM handshake
- first_base = "n=%s,r=%s" % (
- _scram_escape(sasl_username), _scram_nonce())
- first_withchannel = "n,,%s" % first_base
- auth_text = first_withchannel.encode("utf8")
- event["server"]._scram_first = first_base.encode("utf8")
- else:
- data = base64.b64decode(event["message"]).decode("utf8")
- pieces = dict(piece.split("=", 1) for piece in data.split(","))
- if "s" in pieces:
- # server-first-message
- nonce = pieces["r"].encode("utf8")
- salt = base64.b64decode(pieces["s"])
- iterations = pieces["i"]
- password = sasl_password.encode("utf8")
-
- salted_password = hashlib.pbkdf2_hmac(algo, password, salt,
- int(iterations), dklen=None)
- event["server"]._scram_salted_password = salted_password
-
- client_key = hmac.digest(salted_password, b"Client Key",
- algo)
- stored_key = hashlib.new(algo, client_key).digest()
-
- channel = base64.b64encode(b"n,,")
- auth_noproof = b"c=%s,r=%s" % (channel, nonce)
- auth_message = b"%s,%s,%s" % (event["server"]._scram_first,
- data.encode("utf8"), auth_noproof)
- event["server"]._scram_auth_message = auth_message
-
- client_signature = hmac.digest(stored_key, auth_message,
- algo)
- client_proof = base64.b64encode(
- _scram_xor(client_key, client_signature))
-
- auth_text = auth_noproof + (b",p=%s" % client_proof)
- elif "v" in pieces:
- # server-final-message
- verifier = pieces["v"]
-
- salted_password = event["server"]._scram_salted_password
- auth_message = event["server"]._scram_auth_message
- server_key = hmac.digest(salted_password, b"Server Key",
- algo)
- server_signature = hmac.digest(server_key, auth_message,
- algo)
-
- del event["server"]._scram_first
- del event["server"]._scram_salted_password
- del event["server"]._scram_auth_message
-
- if server_signature != base64.b64decode(verifier):
- raise ValueError("SCRAM %s authentication failed "
- % algo)
- event["server"].disconnect()
- auth_text = "+"
-
- else:
- raise ValueError("unknown sasl mechanism '%s'" % mechanism)
-
- if not auth_text == "+":
- auth_text = base64.b64encode(auth_text)
- auth_text = auth_text.decode("utf8")
- event["server"].send_authenticate(auth_text)
-
- def _end_sasl(self, server):
- server.capability_done("sasl")
- @utils.hook("received.numeric.903")
- def sasl_success(self, event):
- self._end_sasl(event["server"])
- @utils.hook("received.numeric.904")
- def sasl_failure(self, event):
- self._end_sasl(event["server"])