diff options
author | Lance Stout <lancestout@gmail.com> | 2011-08-03 17:00:51 -0700 |
---|---|---|
committer | Lance Stout <lancestout@gmail.com> | 2011-08-03 17:00:51 -0700 |
commit | d4091dbde641dc9796b51e032ea23a0ba5c1fcbb (patch) | |
tree | 4efc7d117b4e40b630c3b09a7f983ba2dbd3cb98 /sleekxmpp/features | |
parent | 9a6eb333e65e58f705aca6a8874d5b65bd52e0e7 (diff) | |
download | slixmpp-d4091dbde641dc9796b51e032ea23a0ba5c1fcbb.tar.gz slixmpp-d4091dbde641dc9796b51e032ea23a0ba5c1fcbb.tar.bz2 slixmpp-d4091dbde641dc9796b51e032ea23a0ba5c1fcbb.tar.xz slixmpp-d4091dbde641dc9796b51e032ea23a0ba5c1fcbb.zip |
Integrate a modified version of Dave Cridland's Suelta SASL library.
Diffstat (limited to 'sleekxmpp/features')
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/mechanisms.py | 86 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/__init__.py | 3 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/auth.py | 12 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/challenge.py | 39 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/failure.py | 2 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/response.py | 39 | ||||
-rw-r--r-- | sleekxmpp/features/feature_mechanisms/stanza/success.py | 4 | ||||
-rw-r--r-- | sleekxmpp/features/sasl_anonymous.py | 31 | ||||
-rw-r--r-- | sleekxmpp/features/sasl_plain.py | 41 |
9 files changed, 139 insertions, 118 deletions
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 diff --git a/sleekxmpp/features/feature_mechanisms/stanza/__init__.py b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py index 0d9135d3..8b80f358 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/__init__.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/__init__.py @@ -11,4 +11,5 @@ from sleekxmpp.features.feature_mechanisms.stanza.mechanisms import Mechanisms from sleekxmpp.features.feature_mechanisms.stanza.auth import Auth from sleekxmpp.features.feature_mechanisms.stanza.success import Success from sleekxmpp.features.feature_mechanisms.stanza.failure import Failure - +from sleekxmpp.features.feature_mechanisms.stanza.challenge import Challenge +from sleekxmpp.features.feature_mechanisms.stanza.response import Response diff --git a/sleekxmpp/features/feature_mechanisms/stanza/auth.py b/sleekxmpp/features/feature_mechanisms/stanza/auth.py index 12208841..e069b57f 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/auth.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/auth.py @@ -6,6 +6,10 @@ See the file LICENSE for copying permission. """ +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + from sleekxmpp.stanza import StreamFeatures from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET from sleekxmpp.xmlstream import register_stanza_plugin @@ -25,11 +29,11 @@ class Auth(StanzaBase): StanzaBase.setup(self, xml) self.xml.tag = self.tag_name() - def set_value(self, value): - self.xml.text = value - def get_value(self): - return self.xml.text + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') def del_value(self): self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/challenge.py b/sleekxmpp/features/feature_mechanisms/stanza/challenge.py new file mode 100644 index 00000000..82af869f --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/challenge.py @@ -0,0 +1,39 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Challenge(StanzaBase): + + """ + """ + + name = 'challenge' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('value',)) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def get_value(self): + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') + + def del_value(self): + self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/failure.py b/sleekxmpp/features/feature_mechanisms/stanza/failure.py index 98a1ab80..027cc5af 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/failure.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/failure.py @@ -45,6 +45,8 @@ class Failure(StanzaBase): #If we had to generate XML then set default values. self['condition'] = 'not-authorized' + self.xml.tag = self.tag_name() + def get_condition(self): """Return the condition element's name.""" for child in self.xml.getchildren(): diff --git a/sleekxmpp/features/feature_mechanisms/stanza/response.py b/sleekxmpp/features/feature_mechanisms/stanza/response.py new file mode 100644 index 00000000..45bb8207 --- /dev/null +++ b/sleekxmpp/features/feature_mechanisms/stanza/response.py @@ -0,0 +1,39 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import base64 + +from sleekxmpp.thirdparty.suelta.util import bytes + +from sleekxmpp.stanza import StreamFeatures +from sleekxmpp.xmlstream import ElementBase, StanzaBase, ET +from sleekxmpp.xmlstream import register_stanza_plugin + + +class Response(StanzaBase): + + """ + """ + + name = 'response' + namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' + interfaces = set(('value',)) + plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() + + def get_value(self): + return base64.b64decode(bytes(self.xml.text)) + + def set_value(self, values): + self.xml.text = bytes(base64.b64encode(values)).decode('utf-8') + + def del_value(self): + self.xml.text = '' diff --git a/sleekxmpp/features/feature_mechanisms/stanza/success.py b/sleekxmpp/features/feature_mechanisms/stanza/success.py index 2c40f56c..028e28a3 100644 --- a/sleekxmpp/features/feature_mechanisms/stanza/success.py +++ b/sleekxmpp/features/feature_mechanisms/stanza/success.py @@ -20,3 +20,7 @@ class Success(StanzaBase): namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' interfaces = set() plugin_attrib = name + + def setup(self, xml): + StanzaBase.setup(self, xml) + self.xml.tag = self.tag_name() diff --git a/sleekxmpp/features/sasl_anonymous.py b/sleekxmpp/features/sasl_anonymous.py deleted file mode 100644 index 98a0d36e..00000000 --- a/sleekxmpp/features/sasl_anonymous.py +++ /dev/null @@ -1,31 +0,0 @@ -import base64 -import sys -import logging - -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class sasl_anonymous(base_plugin): - - def plugin_init(self): - self.name = 'SASL ANONYMOUS' - self.rfc = '6120' - self.description = 'SASL ANONYMOUS Mechanism' - self.stanza = self.xmpp['feature_mechanisms'].stanza - - self.xmpp['feature_mechanisms'].register('ANONYMOUS', - self._handle_anonymous, - priority=self.config.get('priority', 0)) - - def _handle_anonymous(self): - if self.xmpp.boundjid.user: - return False - - resp = self.stanza.Auth(self.xmpp) - resp['mechanism'] = 'ANONYMOUS' - resp.send(now=True) - - return True diff --git a/sleekxmpp/features/sasl_plain.py b/sleekxmpp/features/sasl_plain.py deleted file mode 100644 index 427660ab..00000000 --- a/sleekxmpp/features/sasl_plain.py +++ /dev/null @@ -1,41 +0,0 @@ -import base64 -import sys -import logging - -from sleekxmpp.plugins.base import base_plugin - - -log = logging.getLogger(__name__) - - -class sasl_plain(base_plugin): - - def plugin_init(self): - self.name = 'SASL PLAIN' - self.rfc = '6120' - self.description = 'SASL PLAIN Mechanism' - self.stanza = self.xmpp['feature_mechanisms'].stanza - - self.xmpp['feature_mechanisms'].register('PLAIN', - self._handle_plain, - priority=self.config.get('priority', 1)) - - def _handle_plain(self): - if not self.xmpp.boundjid.user: - return False - - if sys.version_info < (3, 0): - user = bytes(self.xmpp.boundjid.user) - password = bytes(self.xmpp.password) - else: - user = bytes(self.xmpp.boundjid.user, 'utf-8') - password = bytes(self.xmpp.password, 'utf-8') - - auth = base64.b64encode(b'\x00' + user + \ - b'\x00' + password).decode('utf-8') - - resp = self.stanza.Auth(self.xmpp) - resp['mechanism'] = 'PLAIN' - resp['value'] = auth - resp.send(now=True) - return True |