From 8ce43110b618a4dcad1ceaca75d3a8f5341455d5 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 11 Jan 2017 00:10:46 +0100 Subject: [PATCH] Working support of scram-sha-256. --- src/conf.py | 3 ++- src/irclib.py | 30 ++++++++++++++++++++++++------ src/registry.py | 2 +- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/conf.py b/src/conf.py index 7fc4e1719..42ecc0d83 100644 --- a/src/conf.py +++ b/src/conf.py @@ -312,7 +312,8 @@ class SpaceSeparatedSetOfChannels(registry.SpaceSeparatedListOf): return None class ValidSaslMechanism(registry.OnlySomeStrings): - validStrings = ('ecdsa-nist256p-challenge', 'external', 'plain') + validStrings = ('ecdsa-nist256p-challenge', 'external', 'plain', + 'scram-sha-256') class SpaceSeparatedListOfSaslMechanisms(registry.SpaceSeparatedListOf): Value = ValidSaslMechanism diff --git a/src/irclib.py b/src/irclib.py index 4f0b15b26..3a329d180 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -1116,10 +1116,16 @@ class Irc(IrcCommandDispatcher, log.Firewalled): step = self.sasl_scram_state['step'] try: if step == 'uninitialized': - self.doAuthenticateScramFirst() + log.debug('%s: starting SCRAM.', + self.network) + self.doAuthenticateScramFirst(mechanism) elif step == 'first-sent': + log.debug('%s: received SCRAM challenge.', + self.network) self.doAuthenticateScramChallenge(string) elif step == 'final-sent': + log.debug('%s: finishing SCRAM.', + self.network) self.doAuthenticateScramFinish(string) else: assert False @@ -1149,11 +1155,17 @@ class Irc(IrcCommandDispatcher, log.Firewalled): args=('*',))) self.tryNextSaslMechanism() - def doAuthenticateScramFirst(self): + def doAuthenticateScramFirst(self, mechanism): """Handle sending the client-first message of SCRAM auth.""" hash_name = mechanism[len('scram-'):] if hash_name.endswith('-plus'): hash_name = hash_name[:-len('-plus')] + hash_name = hash_name.upper() + if hash_name not in scram.HASH_FACTORIES: + log.debug('%s: SCRAM hash %r not supported, aborting.', + self.network, hash_name) + self.tryNextSaslMechanism() + return authenticator = scram.SCRAMClientAuthenticator(hash_name, channel_binding=False) self.sasl_scram_state['authenticator'] = authenticator @@ -1167,13 +1179,19 @@ class Irc(IrcCommandDispatcher, log.Firewalled): def doAuthenticateScramChallenge(self, challenge): client_final = self.sasl_scram_state['authenticator'] \ .challenge(challenge) + self.sendSaslString(client_final) self.sasl_scram_state['step'] = 'final-sent' def doAuthenticateScramFinish(self, data): - # TODO: do something with BadSuccessException - res = self.sasl_scram_state['authenticator'] \ - .finish(data) - self.sasl_scram_state['step'] = 'authenticated' + try: + res = self.sasl_scram_state['authenticator'] \ + .finish(data) + except scram.BadSuccessException as e: + log.warning('%s: SASL authentication failed with SCRAM error: %e', + self.network, e) + self.tryNextSaslMechanism() + else: + self.sasl_scram_state['step'] = 'authenticated' def do903(self, msg): log.info('%s: SASL authentication successful', self.network) diff --git a/src/registry.py b/src/registry.py index c924066f3..0404cf188 100644 --- a/src/registry.py +++ b/src/registry.py @@ -508,7 +508,7 @@ class Probability(Float): class String(Value): """Value is not a valid Python string.""" - errormsg = _('Value is not a valid Python string, not %r.') + errormsg = _('Value should be a valid Python string, not %r.') def set(self, s): v = s if not v: