summaryrefslogtreecommitdiff
path: root/sleekxmpp/features
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2011-08-03 17:00:51 -0700
committerLance Stout <lancestout@gmail.com>2011-08-03 17:00:51 -0700
commitd4091dbde641dc9796b51e032ea23a0ba5c1fcbb (patch)
tree4efc7d117b4e40b630c3b09a7f983ba2dbd3cb98 /sleekxmpp/features
parent9a6eb333e65e58f705aca6a8874d5b65bd52e0e7 (diff)
downloadslixmpp-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.py86
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/__init__.py3
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/auth.py12
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/challenge.py39
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/failure.py2
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/response.py39
-rw-r--r--sleekxmpp/features/feature_mechanisms/stanza/success.py4
-rw-r--r--sleekxmpp/features/sasl_anonymous.py31
-rw-r--r--sleekxmpp/features/sasl_plain.py41
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