summaryrefslogtreecommitdiff
path: root/sleekxmpp/features/feature_mechanisms
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/features/feature_mechanisms')
-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
7 files changed, 139 insertions, 46 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()