changeset 544859c64f78 in /home/hg/repos/python-nbxmpp
details:http://hg.gajim.org/python-nbxmpp?cmd=changeset;node=544859c64f78 description: Add possibility to restrict authentication mechanisms used during login.
Fixes #20
diffstat:
nbxmpp/auth_nb.py | 36 +++++++++++++++++++++++++++++------- nbxmpp/client_nb.py | 19 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-)
diffs (144 lines):
diff -r c79cbe36a94e -r 544859c64f78 nbxmpp/auth_nb.py --- a/nbxmpp/auth_nb.py Wed Feb 26 11:22:41 2014 +0100 +++ b/nbxmpp/auth_nb.py Sat Mar 01 20:53:52 2014 +0100 @@ -91,25 +91,35 @@ """Helper function. Used for SCRAM-SHA-1, SCRAM-SHA-1-PLUS authentication""" return dict(s.split('=', 1) for s in chatter.split(','))
+SASL_AUTHENTICATION_MECHANISMS = \ + set(['ANONYMOUS', 'EXTERNAL', 'GSSAPI', 'SCRAM-SHA-1-PLUS', 'SCRAM-SHA-1', + 'DIGEST-MD5', 'PLAIN', 'X-MESSENGER-OAUTH2']) + class SASL(PlugIn): """ Implements SASL authentication. Can be plugged into NonBlockingClient to start authentication """
- def __init__(self, username, password, on_sasl, channel_binding): + def __init__(self, username, password, on_sasl, channel_binding, + auth_mechs): """ :param username: XMPP username :param password: XMPP password :param on_sasl: Callback, will be called after each SASL auth-step. :param channel_binding: TLS channel binding data, None if the binding data is not available + :param auth_mechs: Set of valid authentication mechanisms. + Possible entries are: + 'ANONYMOUS', 'EXTERNAL', 'GSSAPI', 'SCRAM-SHA-1-PLUS', + 'SCRAM-SHA-1', 'DIGEST-MD5', 'PLAIN', 'X-MESSENGER-OAUTH2' """ PlugIn.__init__(self) self.username = username self.password = password self.on_sasl = on_sasl self.channel_binding = channel_binding + self.enabled_auth_mechs = auth_mechs self.realm = None
def plugin(self, owner): @@ -146,7 +156,7 @@ Start authentication. Result can be obtained via "SASL.startsasl" attribute and will be either SASL_SUCCESS or SASL_FAILURE
- Note that successfull auth will take at least two Dispatcher.Process() + Note that successful auth will take at least two Dispatcher.Process() calls. """ if self.startsasl: @@ -169,10 +179,22 @@ self.startsasl='not-supported' log.info('SASL not supported by server') return - self.mecs = [] - for mec in feats.getTag('mechanisms', namespace=NS_SASL).getTags( - 'mechanism'): - self.mecs.append(mec.getData()) + + self.mecs = set( + mec.getData() + for mec + in feats.getTag('mechanisms', namespace=NS_SASL).getTags('mechanism') + ) & self.enabled_auth_mechs + + # Password based authentication mechanism ordered by strength. + # If the server supports a mechanism disable all weaker mechanisms. + password_auth_mechs_strength = ('SCRAM-SHA-1-PLUS', 'SCRAM-SHA-1', + 'DIGEST-MD5', 'PLAIN', 'X-MESSENGER-OAUTH2') + for i in range(0, len(password_auth_mechs_strength)): + if password_auth_mechs_strength[i] in self.mecs: + for m in password_auth_mechs_strength[i + 1:]: + self.mecs.discard(m) + break
self._owner.RegisterHandler('challenge', self.SASLHandler, xmlns=NS_SASL) @@ -508,7 +530,7 @@
class NonBlockingNonSASL(PlugIn): """ - Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and + Implements old Non-SASL (XEP-0078) authentication used in jabberd1.4 and transport authentication """
diff -r c79cbe36a94e -r 544859c64f78 nbxmpp/client_nb.py --- a/nbxmpp/client_nb.py Wed Feb 26 11:22:41 2014 +0100 +++ b/nbxmpp/client_nb.py Sat Mar 01 20:53:52 2014 +0100 @@ -23,6 +23,7 @@ import socket import transports_nb, dispatcher_nb, auth_nb, roster_nb, protocol, bosh from protocol import NS_TLS +from nbxmpp.auth_nb import SASL_AUTHENTICATION_MECHANISMS
import logging log = logging.getLogger('nbxmpp.client_nb') @@ -472,7 +473,8 @@ ### follows code for authentication, resource bind, session and roster download ###############################################################################
- def auth(self, user, password, resource='', sasl=True, on_auth=None): + def auth(self, user, password, resource='', sasl=True, on_auth=None, + auth_mechs=None): """ Authenticate connnection and bind resource. If resource is not provided random one or library name used @@ -483,6 +485,11 @@ :param sasl: Boolean indicating if SASL shall be used. (default: True) :param on_auth: Callback, called after auth. On auth failure, argument is None. + :param auth_mechs: Set of valid authentification mechanisms. If None all + authentification mechanisms will be allowed. Possible entries are: + 'ANONYMOUS', 'EXTERNAL', 'GSSAPI', 'SCRAM-SHA-1-PLUS', + 'SCRAM-SHA-1', 'DIGEST-MD5', 'PLAIN', 'X-MESSENGER-OAUTH2', + 'XEP-0078' """ self._User, self._Password = user, password self._Resource, self._sasl = resource, sasl @@ -494,6 +501,10 @@ assert (self._channel_binding != None) except NotImplementedError: pass + if auth_mechs == None: + self._auth_mechs = SASL_AUTHENTICATION_MECHANISMS | set(['XEP-0078']) + else: + self._auth_mechs = auth_mechs self.on_auth = on_auth self._on_doc_attrs() return @@ -525,10 +536,12 @@ """ if self._sasl: auth_nb.SASL.get_instance(self._User, self._Password, - self._on_start_sasl, self._channel_binding).PlugIn(self) + self._on_start_sasl, self._channel_binding, + self._auth_mechs).PlugIn(self) if not hasattr(self, 'SASL'): return - if not self._sasl or self.SASL.startsasl == 'not-supported': + if ('XEP-0078' in self._auth_mechs + and (not self._sasl or self.SASL.startsasl == 'not-supported')): if not self._Resource: self._Resource = 'xmpppy' auth_nb.NonBlockingNonSASL.get_instance(self._User, self._Password,