diff options
| author | 2019-02-05 15:54:20 +0000 | |
|---|---|---|
| committer | 2019-02-05 15:54:20 +0000 | |
| commit | 1fe20a2c98ed5e4042d10c415d7923aebfaa362d (patch) | |
| tree | 677385905a109b6760258842b18643b8bf7c9fd8 /modules/sasl/__init__.py | |
| parent | Switch 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/__init__.py')
| -rw-r--r-- | modules/sasl/__init__.py | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/modules/sasl/__init__.py b/modules/sasl/__init__.py new file mode 100644 index 00000000..b961ba00 --- /dev/null +++ b/modules/sasl/__init__.py @@ -0,0 +1,98 @@ +import base64, hashlib, hmac, uuid +from src import ModuleManager, utils +from . import scram + +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 + event["server"]._scram = scram.SCRAM( + algo, sasl_username, sasl_password) + auth_text = event["server"]._scram.client_first() + print(auth_text) + else: + current_scram = event["server"]._scram + if current_scram.state == scram.SCRAMState.ClientFirst: + auth_text = current_scram.server_first(event["message"]) + elif current_scram.state == scram.SCRAMState.ClientFinal: + auth_text = current_scram.server_final(event["message"]) + del event["server"]._scram + 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"]) |
