From 634f5d691bab9855deddc4c201389bb60470d76e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 1 Jul 2011 14:45:55 -0700 Subject: Continued reorganization and streamlining. --- .../features/feature_mechanisms/mechanisms.py | 116 +++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 sleekxmpp/features/feature_mechanisms/mechanisms.py (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py new file mode 100644 index 00000000..994c9bed --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -0,0 +1,116 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import logging + +from sleekxmpp.stanza import stream +from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.xmlstream.matcher import * +from sleekxmpp.xmlstream.handler import * +from sleekxmpp.plugins.base import base_plugin + + +log = logging.getLogger(__name__) + + +class feature_mechanisms(base_plugin): + + def plugin_init(self): + self.name = 'SASL Mechanisms' + self.rfc = '6120' + self.description = "SASL Stream Feature" + + self.xmpp.register_stanza(stream.sasl.Success) + self.xmpp.register_stanza(stream.sasl.Failure) + self.xmpp.register_stanza(stream.sasl.Auth) + + self._mechanism_handlers = {} + self._mechanism_priorities = [] + + self.xmpp.register_handler( + Callback('SASL Success', + MatchXPath(stream.sasl.Success.tag_name()), + self._handle_success, + instream=True, + once=True)) + self.xmpp.register_handler( + Callback('SASL Failure', + MatchXPath(stream.sasl.Failure.tag_name()), + self._handle_fail, + instream=True, + once=True)) + + self.xmpp.register_feature('mechanisms', + self._handle_sasl_auth, + restart=True, + order=self.config.get('order', 100)) + + def register_mechanism(self, name, handler, priority=0): + """ + Register a handler for a SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism (all caps) + handler -- The function that will perform the + authentication. The function must + return True if it is able to carry + out the authentication, False if + a required condition is not met. + priority -- An integer value indicating the + preferred ordering for the mechanism. + High values will be attempted first. + """ + self._mechanism_handlers[name] = handler + self._mechanism_priorities.append((priority, name)) + self._mechanism_priorities.sort(reverse=True) + + def remove_mechanism(self, name): + """ + Remove support for a given SASL authentication mechanism. + + Arguments: + name -- The name of the mechanism to remove (all caps) + """ + if name in self._mechanism_handlers: + del self._mechanism_handlers[name] + + p = self._mechanism_priorities + self._mechanism_priorities = [i for i in p if i[1] != name] + + def _handle_sasl_auth(self, features): + """ + Handle authenticating using SASL. + + Arguments: + features -- The stream features stanza. + """ + for priority, mech in self._mechanism_priorities: + if mech in features['mechanisms']: + log.debug('Attempt to use SASL %s' % mech) + if self._mechanism_handlers[mech](): + break + else: + log.error("No appropriate login method.") + self.xmpp.event("no_auth", direct=True) + self.xmpp.disconnect() + + return True + + def _handle_success(self, stanza): + """SASL authentication succeeded. Restart the stream.""" + self.xmpp.authenticated = True + self.xmpp.features.append('mechanisms') + raise RestartStream() + + def _handle_fail(self, stanza): + """SASL authentication failed. Disconnect and shutdown.""" + log.info("Authentication failed.") + self.xmpp.event("failed_auth", direct=True) + self.xmpp.disconnect() + log.debug("Starting SASL Auth") + return True -- cgit v1.2.3 From b0297af38d6dcd9ebfdaa0131ea798c9fe2b8c63 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 21:43:02 -0700 Subject: Finish cleaning up stream feature organization. Fixed missing references that weren't caught due to leftover pyc file allowing tests to keep working when they shouldn't have. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 994c9bed..3cdb1b0a 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,11 +8,11 @@ import logging -from sleekxmpp.stanza import stream from sleekxmpp.xmlstream import RestartStream from sleekxmpp.xmlstream.matcher import * from sleekxmpp.xmlstream.handler import * from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.features.feature_mechanisms import stanza log = logging.getLogger(__name__) @@ -24,23 +24,24 @@ class feature_mechanisms(base_plugin): self.name = 'SASL Mechanisms' self.rfc = '6120' self.description = "SASL Stream Feature" + self.stanza = stanza - self.xmpp.register_stanza(stream.sasl.Success) - self.xmpp.register_stanza(stream.sasl.Failure) - self.xmpp.register_stanza(stream.sasl.Auth) + self.xmpp.register_stanza(stanza.Success) + self.xmpp.register_stanza(stanza.Failure) + self.xmpp.register_stanza(stanza.Auth) self._mechanism_handlers = {} self._mechanism_priorities = [] self.xmpp.register_handler( Callback('SASL Success', - MatchXPath(stream.sasl.Success.tag_name()), + MatchXPath(stanza.Success.tag_name()), self._handle_success, instream=True, once=True)) self.xmpp.register_handler( Callback('SASL Failure', - MatchXPath(stream.sasl.Failure.tag_name()), + MatchXPath(stanza.Failure.tag_name()), self._handle_fail, instream=True, once=True)) -- cgit v1.2.3 From fba235a801a3a1c06d1769cdc944b72dce33f88a Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 21:57:50 -0700 Subject: Simplify SASL mech registration. Moved SASL registration completely to the feature plugin, instead of keeping a portion of it in ClientXMPP. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 3cdb1b0a..a8a046e4 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -51,7 +51,7 @@ class feature_mechanisms(base_plugin): restart=True, order=self.config.get('order', 100)) - def register_mechanism(self, name, handler, priority=0): + def register(self, name, handler, priority=0): """ Register a handler for a SASL authentication mechanism. @@ -70,7 +70,7 @@ class feature_mechanisms(base_plugin): self._mechanism_priorities.append((priority, name)) self._mechanism_priorities.sort(reverse=True) - def remove_mechanism(self, name): + def remove(self, name): """ Remove support for a given SASL authentication mechanism. -- cgit v1.2.3 From b898b14b77d739cb1c118c9e3648aa268348d293 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 22:30:34 -0700 Subject: Use a set to track negotiated features. Added guards to prevent renegotiating STARTTLS or SASL in cases where servers don't behave properly. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index a8a046e4..011010fb 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -90,6 +90,11 @@ class feature_mechanisms(base_plugin): Arguments: features -- The stream features stanza. """ + if 'mechanisms' in self.xmpp.features: + # SASL authentication has already succeeded, but the + # server has incorrectly offered it again. + return False + for priority, mech in self._mechanism_priorities: if mech in features['mechanisms']: log.debug('Attempt to use SASL %s' % mech) @@ -105,7 +110,7 @@ class feature_mechanisms(base_plugin): def _handle_success(self, stanza): """SASL authentication succeeded. Restart the stream.""" self.xmpp.authenticated = True - self.xmpp.features.append('mechanisms') + self.xmpp.features.add('mechanisms') raise RestartStream() def _handle_fail(self, stanza): -- cgit v1.2.3 From 219df582dab2a5dd3c9e2bbfef27d3cfa814841d Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 22:49:34 -0700 Subject: It isn't 2010 anymore. I keep forgetting to update the copyright on new code. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 011010fb..210267fa 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -1,6 +1,6 @@ """ SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz + Copyright (C) 2011 Nathanael C. Fritz This file is part of SleekXMPP. See the file LICENSE for copying permission. -- cgit v1.2.3 From 0224d028e76ba608400fe55602fdb84f8e70f13b Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 2 Jul 2011 23:09:29 -0700 Subject: SASL failure event now includes the failure stanza. Broke SASL stanzas into separate files. Fixed typo in feature_bind. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 210267fa..7a877793 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,7 +8,8 @@ import logging -from sleekxmpp.xmlstream import RestartStream +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin from sleekxmpp.xmlstream.matcher import * from sleekxmpp.xmlstream.handler import * from sleekxmpp.plugins.base import base_plugin @@ -26,6 +27,7 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza + register_stanza_plugin(StreamFeatures, stanza.Mechanisms) self.xmpp.register_stanza(stanza.Success) self.xmpp.register_stanza(stanza.Failure) self.xmpp.register_stanza(stanza.Auth) @@ -115,8 +117,7 @@ class feature_mechanisms(base_plugin): def _handle_fail(self, stanza): """SASL authentication failed. Disconnect and shutdown.""" - log.info("Authentication failed.") - self.xmpp.event("failed_auth", direct=True) + log.info("Authentication failed: %s" % stanza['condition']) + self.xmpp.event("failed_auth", stanza, direct=True) self.xmpp.disconnect() - log.debug("Starting SASL Auth") return True -- cgit v1.2.3 From d4091dbde641dc9796b51e032ea23a0ba5c1fcbb Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 3 Aug 2011 17:00:51 -0700 Subject: Integrate a modified version of Dave Cridland's Suelta SASL library. --- .../features/feature_mechanisms/mechanisms.py | 86 +++++++++++----------- 1 file changed, 45 insertions(+), 41 deletions(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 7a877793..d60818bb 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -8,6 +8,8 @@ import logging +from sleekxmpp.thirdparty import suelta + from sleekxmpp.stanza import StreamFeatures from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin from sleekxmpp.xmlstream.matcher import * @@ -27,13 +29,35 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza + + def tls_active(): + return 'starttls' in self.xmpp.features + + def basic_callback(mech, values): + if 'username' in values: + values['username'] = self.xmpp.boundjid.user + if 'password' in values: + values['password'] = self.xmpp.password + mech.fulfill(values) + + sasl_callback = self.config.get('sasl_callback', None) + if sasl_callback is None: + sasl_callback = basic_callback + + self.mech = None + self.sasl = suelta.SASL(self.xmpp.boundjid.domain, 'xmpp', + username=self.xmpp.boundjid.user, + sec_query=suelta.sec_query_allow, + request_values=sasl_callback, + tls_active=tls_active) + register_stanza_plugin(StreamFeatures, stanza.Mechanisms) + self.xmpp.register_stanza(stanza.Success) self.xmpp.register_stanza(stanza.Failure) self.xmpp.register_stanza(stanza.Auth) - - self._mechanism_handlers = {} - self._mechanism_priorities = [] + self.xmpp.register_stanza(stanza.Challenge) + self.xmpp.register_stanza(stanza.Response) self.xmpp.register_handler( Callback('SASL Success', @@ -47,44 +71,16 @@ class feature_mechanisms(base_plugin): self._handle_fail, instream=True, once=True)) + self.xmpp.register_handler( + Callback('SASL Challenge', + MatchXPath(stanza.Challenge.tag_name()), + self._handle_challenge)) self.xmpp.register_feature('mechanisms', self._handle_sasl_auth, restart=True, order=self.config.get('order', 100)) - def register(self, name, handler, priority=0): - """ - Register a handler for a SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism (all caps) - handler -- The function that will perform the - authentication. The function must - return True if it is able to carry - out the authentication, False if - a required condition is not met. - priority -- An integer value indicating the - preferred ordering for the mechanism. - High values will be attempted first. - """ - self._mechanism_handlers[name] = handler - self._mechanism_priorities.append((priority, name)) - self._mechanism_priorities.sort(reverse=True) - - def remove(self, name): - """ - Remove support for a given SASL authentication mechanism. - - Arguments: - name -- The name of the mechanism to remove (all caps) - """ - if name in self._mechanism_handlers: - del self._mechanism_handlers[name] - - p = self._mechanism_priorities - self._mechanism_priorities = [i for i in p if i[1] != name] - def _handle_sasl_auth(self, features): """ Handle authenticating using SASL. @@ -97,18 +93,26 @@ class feature_mechanisms(base_plugin): # server has incorrectly offered it again. return False - for priority, mech in self._mechanism_priorities: - if mech in features['mechanisms']: - log.debug('Attempt to use SASL %s' % mech) - if self._mechanism_handlers[mech](): - break + mech_list = features['mechanisms'] + self.mech = self.sasl.choose_mechanism(mech_list) + + if self.mech is not None: + resp = stanza.Auth(self.xmpp) + resp['mechanism'] = self.mech.name + resp['value'] = self.mech.process() + resp.send(now=True) else: log.error("No appropriate login method.") self.xmpp.event("no_auth", direct=True) self.xmpp.disconnect() - return True + def _handle_challenge(self, stanza): + """SASL challenge received. Process and send response.""" + resp = self.stanza.Response(self.xmpp) + resp['value'] = self.mech.process(stanza['value']) + resp.send(now=True) + def _handle_success(self, stanza): """SASL authentication succeeded. Restart the stream.""" self.xmpp.authenticated = True -- cgit v1.2.3 From 47bc50d9fbbe8d72b589a6360aa8b5f32d6ba74b Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 4 Aug 2011 22:37:22 -0700 Subject: Cosmetic PEP8 fixes. --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 1 - 1 file changed, 1 deletion(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index d60818bb..2debf3be 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -29,7 +29,6 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza - def tls_active(): return 'starttls' in self.xmpp.features -- cgit v1.2.3 From 572becad44b8cb577a1f8357e04fa93235894536 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 9 Aug 2011 00:51:49 -0700 Subject: Enable forcing a specififc SASL mech: xmpp = ClientXMPP(jid, password, { 'feature_mechanisms': {'use_mech':'PLAIN'}}) --- sleekxmpp/features/feature_mechanisms/mechanisms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/features/feature_mechanisms/mechanisms.py') diff --git a/sleekxmpp/features/feature_mechanisms/mechanisms.py b/sleekxmpp/features/feature_mechanisms/mechanisms.py index 2debf3be..a6cff0a0 100644 --- a/sleekxmpp/features/feature_mechanisms/mechanisms.py +++ b/sleekxmpp/features/feature_mechanisms/mechanisms.py @@ -29,6 +29,8 @@ class feature_mechanisms(base_plugin): self.description = "SASL Stream Feature" self.stanza = stanza + self.use_mech = self.config.get('use_mech', None) + def tls_active(): return 'starttls' in self.xmpp.features @@ -48,7 +50,8 @@ class feature_mechanisms(base_plugin): username=self.xmpp.boundjid.user, sec_query=suelta.sec_query_allow, request_values=sasl_callback, - tls_active=tls_active) + tls_active=tls_active, + mech=self.use_mech) register_stanza_plugin(StreamFeatures, stanza.Mechanisms) -- cgit v1.2.3