summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--setup.py1
-rw-r--r--sleekxmpp/plugins/__init__.py2
-rw-r--r--sleekxmpp/plugins/xep_0078.py72
-rw-r--r--sleekxmpp/plugins/xep_0078/__init__.py12
-rw-r--r--sleekxmpp/plugins/xep_0078/legacyauth.py108
-rw-r--r--sleekxmpp/plugins/xep_0078/stanza.py43
-rw-r--r--sleekxmpp/stanza/stream_features.py2
7 files changed, 168 insertions, 72 deletions
diff --git a/setup.py b/setup.py
index b997106c..e3b3aa9a 100644
--- a/setup.py
+++ b/setup.py
@@ -58,6 +58,7 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0060',
'sleekxmpp/plugins/xep_0060/stanza',
'sleekxmpp/plugins/xep_0066',
+ 'sleekxmpp/plugins/xep_0078',
'sleekxmpp/plugins/xep_0085',
'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0092',
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 7fa031ef..c0b1121b 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -9,3 +9,5 @@ __all__ = ['xep_0004', 'xep_0009', 'xep_0012', 'xep_0030', 'xep_0033',
'xep_0045', 'xep_0050', 'xep_0060', 'xep_0066', 'xep_0082',
'xep_0085', 'xep_0086', 'xep_0092', 'xep_0128', 'xep_0199',
'xep_0203', 'xep_0224', 'xep_0249', 'gmail_notify']
+
+# Don't automatically load xep_0078
diff --git a/sleekxmpp/plugins/xep_0078.py b/sleekxmpp/plugins/xep_0078.py
deleted file mode 100644
index bb6a4632..00000000
--- a/sleekxmpp/plugins/xep_0078.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-from __future__ import with_statement
-from xml.etree import cElementTree as ET
-import logging
-import hashlib
-from . import base
-
-
-log = logging.getLogger(__name__)
-
-
-class xep_0078(base.base_plugin):
- """
- XEP-0078 NON-SASL Authentication
- """
- def plugin_init(self):
- self.description = "Non-SASL Authentication (broken)"
- self.xep = "0078"
- self.xmpp.add_event_handler("session_start", self.check_stream)
- #disabling until I fix conflict with PLAIN
- #self.xmpp.registerFeature("<auth xmlns='http://jabber.org/features/iq-auth'/>", self.auth)
- self.streamid = ''
-
- def check_stream(self, xml):
- self.streamid = xml.attrib['id']
- if xml.get('version', '0') != '1.0':
- self.auth()
-
- def auth(self, xml=None):
- log.debug("Starting jabber:iq:auth Authentication")
- auth_request = self.xmpp.makeIqGet()
- auth_request_query = ET.Element('{jabber:iq:auth}query')
- auth_request.attrib['to'] = self.xmpp.boundjid.host
- username = ET.Element('username')
- username.text = self.xmpp.username
- auth_request_query.append(username)
- auth_request.append(auth_request_query)
- result = auth_request.send()
- rquery = result.find('{jabber:iq:auth}query')
- attempt = self.xmpp.makeIqSet()
- query = ET.Element('{jabber:iq:auth}query')
- resource = ET.Element('resource')
- resource.text = self.xmpp.resource
- query.append(username)
- query.append(resource)
- if rquery.find('{jabber:iq:auth}digest') is None:
- log.warning("Authenticating via jabber:iq:auth Plain.")
- password = ET.Element('password')
- password.text = self.xmpp.password
- query.append(password)
- else:
- log.debug("Authenticating via jabber:iq:auth Digest")
- digest = ET.Element('digest')
- digest.text = hashlib.sha1(b"%s%s" % (self.streamid, self.xmpp.password)).hexdigest()
- query.append(digest)
- attempt.append(query)
- result = attempt.send()
- if result.attrib['type'] == 'result':
- with self.xmpp.lock:
- self.xmpp.authenticated = True
- self.xmpp.sessionstarted = True
- self.xmpp.event("session_start")
- else:
- log.info("Authentication failed")
- self.xmpp.disconnect()
- self.xmpp.event("failed_auth")
diff --git a/sleekxmpp/plugins/xep_0078/__init__.py b/sleekxmpp/plugins/xep_0078/__init__.py
new file mode 100644
index 00000000..5a2bda77
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0078/__init__.py
@@ -0,0 +1,12 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.xep_0078 import stanza
+from sleekxmpp.plugins.xep_0078.stanza import IqAuth, AuthFeature
+from sleekxmpp.plugins.xep_0078.legacyauth import xep_0078
+
diff --git a/sleekxmpp/plugins/xep_0078/legacyauth.py b/sleekxmpp/plugins/xep_0078/legacyauth.py
new file mode 100644
index 00000000..bdd2df67
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0078/legacyauth.py
@@ -0,0 +1,108 @@
+"""
+ 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 logging
+import hashlib
+import random
+
+from sleekxmpp.stanza import Iq, StreamFeatures
+from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
+from sleekxmpp.plugins.base import base_plugin
+from sleekxmpp.plugins.xep_0078 import stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class xep_0078(base_plugin):
+
+ """
+ XEP-0078 NON-SASL Authentication
+
+ This XEP is OBSOLETE in favor of using SASL, so DO NOT use this plugin
+ unless you are forced to use an old XMPP server implementation.
+ """
+
+ def plugin_init(self):
+ self.xep = "0078"
+ self.description = "Non-SASL Authentication"
+ self.stanza = stanza
+
+ self.xmpp.register_feature('auth',
+ self._handle_auth,
+ restart=False,
+ order=self.config.get('order', 15))
+
+ register_stanza_plugin(Iq, stanza.IqAuth)
+ register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
+
+
+ def _handle_auth(self, features):
+ # If we can or have already authenticated with SASL, do nothing.
+ if 'mechanisms' in features['features']:
+ return False
+ if self.xmpp.authenticated:
+ return False
+
+ log.debug("Starting jabber:iq:auth Authentication")
+
+ # Step 1: Request the auth form
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq['to'] = self.xmpp.boundjid.host
+ iq['auth']['username'] = self.xmpp.boundjid.user
+ resp = iq.send(now=True)
+
+ if resp is None or resp['type'] != 'result':
+ log.info("Authentication failed: %s" % resp['error']['condition'])
+ self.xmpp.event('failed_auth', resp, direct=True)
+ self.xmpp.disconnect()
+ return True
+
+ # Step 2: Fill out auth form for either password or digest auth
+ iq = self.xmpp.Iq()
+ iq['type'] = 'set'
+ iq['auth']['username'] = self.xmpp.boundjid.user
+
+ # A resource is required, so create a random one if necessary
+ if self.xmpp.boundjid.resource:
+ iq['auth']['resource'] = self.xmpp.boundjid.resource
+ else:
+ iq['auth']['resource'] = '%s' % random.random()
+
+ if 'digest' in resp['auth']['fields']:
+ log.debug('Authenticating via jabber:iq:auth Digest')
+ if sys.version_info < (3, 0):
+ stream_id = bytes(self.xmpp.stream_id)
+ password = bytes(self.xmpp.password)
+ else:
+ stream_id = bytes(self.xmpp.stream_id, encoding='utf-8')
+ password = bytes(self.xmpp.password, encoding='utf-8')
+
+ digest = hashlib.sha1(b'%s%s' % (stream_id, password)).hexdigest()
+ iq['auth']['digest'] = digest
+ else:
+ log.warning('Authenticating via jabber:iq:auth Plain.')
+ iq['auth']['password'] = self.xmpp.password
+
+ # Step 3: Send credentials
+ result = iq.send(now=True)
+ if result is not None and result.attrib['type'] == 'result':
+ self.xmpp.features.add('auth')
+
+ self.xmpp.authenticated = True
+ log.debug("Established Session")
+ self.xmpp.sessionstarted = True
+ self.xmpp.session_started_event.set()
+ self.xmpp.event('session_start')
+ else:
+ log.info("Authentication failed")
+ self.xmpp.disconnect()
+ self.xmpp.event("failed_auth")
+
+ return True
diff --git a/sleekxmpp/plugins/xep_0078/stanza.py b/sleekxmpp/plugins/xep_0078/stanza.py
new file mode 100644
index 00000000..86ba09ad
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0078/stanza.py
@@ -0,0 +1,43 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
+
+
+class IqAuth(ElementBase):
+ namespace = 'jabber:iq:auth'
+ name = 'query'
+ plugin_attrib = 'auth'
+ interfaces = set(('fields', 'username', 'password', 'resource', 'digest'))
+ sub_interfaces = set(('username', 'password', 'resource', 'digest'))
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
+
+ def get_fields(self):
+ fields = set()
+ for field in self.sub_interfaces:
+ if self.xml.find('{%s}%s' % (self.namespace, field)) is not None:
+ fields.add(field)
+ return fields
+
+ def set_resource(self, value):
+ self._set_sub_text('resource', value, keep=True)
+
+ def set_password(self, value):
+ self._set_sub_text('password', value, keep=True)
+
+
+class AuthFeature(ElementBase):
+ namespace = 'http://jabber.org/features/iq-auth'
+ name = 'auth'
+ plugin_attrib = 'auth'
+ interfaces = set()
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
+
+
diff --git a/sleekxmpp/stanza/stream_features.py b/sleekxmpp/stanza/stream_features.py
index 5be2e55f..b800011f 100644
--- a/sleekxmpp/stanza/stream_features.py
+++ b/sleekxmpp/stanza/stream_features.py
@@ -19,6 +19,8 @@ class StreamFeatures(StanzaBase):
namespace = 'http://etherx.jabber.org/streams'
interfaces = set(('features', 'required', 'optional'))
sub_interfaces = interfaces
+ plugin_tag_map = {}
+ plugin_attrib_map = {}
def setup(self, xml):
StanzaBase.setup(self, xml)