changeset 1f3eb954483c in /home/hg/repos/python-nbxmpp

details:http://hg.gajim.org/python-nbxmpp?cmd=changeset;node=1f3eb954483c
description: SCRAM-SHA-1-PLUS authentication mechanism
        Fixes #16

diffstat:

 nbxmpp/auth_nb.py   |  38 ++++++++++++++++++++++++++++----------
 nbxmpp/client_nb.py |  10 +++++++++-
 nbxmpp/tls_nb.py    |  10 ++++++++++
 3 files changed, 47 insertions(+), 11 deletions(-)

diffs (152 lines):

diff -r b92ebcc55cb4 -r 1f3eb954483c nbxmpp/auth_nb.py
--- a/nbxmpp/auth_nb.py Tue Feb 25 16:36:56 2014 +0100
+++ b/nbxmpp/auth_nb.py Wed Feb 26 10:18:08 2014 +0100
@@ -88,7 +88,7 @@
     return dict_
 
 def _scram_parse(chatter):
-    """Helper function. Used for SCRAM-SHA-1 authentication"""
+    """Helper function. Used for SCRAM-SHA-1, SCRAM-SHA-1-PLUS 
authentication"""
     return dict(s.split('=', 1) for s in chatter.split(','))
 
 class SASL(PlugIn):
@@ -97,16 +97,19 @@
     to start authentication
     """
 
-    def __init__(self, username, password, on_sasl):
+    def __init__(self, username, password, on_sasl, channel_binding):
         """
         :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
         """
         PlugIn.__init__(self)
         self.username = username
         self.password = password
         self.on_sasl = on_sasl
+        self.channel_binding = channel_binding
         self.realm = None
 
     def plugin(self, owner):
@@ -213,6 +216,13 @@
                 raise NodeProcessed
             except kerberos.GSSError, e:
                 log.info('GSSAPI authentication failed: %s' % str(e))
+        if 'SCRAM-SHA-1-PLUS' in self.mecs and self.channel_binding != None:
+            self.mecs.remove('SCRAM-SHA-1-PLUS')
+            self.mechanism = 'SCRAM-SHA-1-PLUS'
+            self._owner._caller.get_password(self.set_password, self.mechanism)
+            self.scram_step = 0
+            self.startsasl = SASL_IN_PROCESS
+            raise NodeProcessed
         if 'SCRAM-SHA-1' in self.mecs:
             self.mecs.remove('SCRAM-SHA-1')
             self.mechanism = 'SCRAM-SHA-1'
@@ -241,8 +251,9 @@
             self.startsasl = SASL_IN_PROCESS
             raise NodeProcessed
         self.startsasl = SASL_FAILURE
-        log.info('I can only use EXTERNAL, SCRAM-SHA-1, DIGEST-MD5, GSSAPI and 
'
-            'PLAIN mecanisms.')
+        log.info('I can only use ANONYMOUS, EXTERNAL, GSSAPI, 
SCRAM-SHA-1-PLUS,'
+                 ' SCRAM-SHA-1, DIGEST-MD5, PLAIN and X-MESSENGER-OAUTH2'
+                 ' mechanisms.')
         if self.on_sasl:
             self.on_sasl()
         return
@@ -283,7 +294,7 @@
                 reason = challenge
             on_auth_fail(reason)
         elif challenge.getName() == 'success':
-            if self.mechanism == 'SCRAM-SHA-1':
+            if self.mechanism in ('SCRAM-SHA-1', 'SCRAM-SHA-1-PLUS'):
                 # check data-with-success
                 data = _scram_parse(data)
                 if data['v'] != scram_base64(self.scram_ServerSignature):
@@ -327,7 +338,7 @@
             self._owner.send(Node('response', attrs={'xmlns': NS_SASL},
                 payload=response).__str__())
             raise NodeProcessed
-        if self.mechanism == 'SCRAM-SHA-1':
+        if self.mechanism in ('SCRAM-SHA-1', 'SCRAM-SHA-1-PLUS'):
             hashfn = hashlib.sha1
 
             def HMAC(k, s):
@@ -363,8 +374,12 @@
                 if (data['r'][:len(self.client_nonce)] != self.client_nonce):
                     on_auth_fail('Server nonce is incorrect')
                     raise NodeProcessed
-                # TODO: Channel binding data goes in here too.
-                r = 'c=' + scram_base64(self.scram_gs2)
+                if self.mechanism == 'SCRAM-SHA-1':
+                    r = 'c=' + scram_base64(self.scram_gs2)
+                else:
+                    # Channel binding data goes in here too.
+                    r = 'c=' + scram_base64(self.scram_gs2
+                                             + self.channel_binding)           
        
                 r += ',r=' + data['r']
                 self.scram_soup += r
                 salt = data['s'].decode('base64')
@@ -437,10 +452,13 @@
 
     def set_password(self, password):
         self.password = '' if password is None else password
-        if self.mechanism == 'SCRAM-SHA-1':
+        if self.mechanism in ('SCRAM-SHA-1', 'SCRAM-SHA-1-PLUS'):
             self.client_nonce = '%x' % rndg.getrandbits(196)
             self.scram_soup = 'n=' + self.username + ',r=' + self.client_nonce
-            self.scram_gs2 = 'n,,' # No CB yet.
+            if self.mechanism == 'SCRAM-SHA-1':
+                self.scram_gs2 = 'n,,' # No CB.
+            else:
+                self.scram_gs2 = 'p=tls-unique,,'
             sasl_data = (self.scram_gs2 + self.scram_soup).encode('base64').\
                 replace('\n', '')
             node = Node('auth', attrs={'xmlns': NS_SASL,
diff -r b92ebcc55cb4 -r 1f3eb954483c nbxmpp/client_nb.py
--- a/nbxmpp/client_nb.py       Tue Feb 25 16:36:56 2014 +0100
+++ b/nbxmpp/client_nb.py       Wed Feb 26 10:18:08 2014 +0100
@@ -486,6 +486,14 @@
         """
         self._User, self._Password = user, password
         self._Resource, self._sasl = resource, sasl
+        self._channel_binding = None
+        if self.connected in ('ssl', 'tls'):
+            try:
+                self._channel_binding = 
self.Connection.NonBlockingTLS.get_channel_binding()
+                # TLS handshake is finished so channel binding data muss exist
+                assert (self._channel_binding != None)
+            except NotImplementedError:
+                pass
         self.on_auth = on_auth
         self._on_doc_attrs()
         return
@@ -517,7 +525,7 @@
         """
         if self._sasl:
             auth_nb.SASL.get_instance(self._User, self._Password,
-                    self._on_start_sasl).PlugIn(self)
+                    self._on_start_sasl, self._channel_binding).PlugIn(self)
         if not hasattr(self, 'SASL'):
             return
         if not self._sasl or self.SASL.startsasl == 'not-supported':
diff -r b92ebcc55cb4 -r 1f3eb954483c nbxmpp/tls_nb.py
--- a/nbxmpp/tls_nb.py  Tue Feb 25 16:36:56 2014 +0100
+++ b/nbxmpp/tls_nb.py  Wed Feb 26 10:18:08 2014 +0100
@@ -478,3 +478,13 @@
             log.error("Exception caught in _ssl_info_callback:", exc_info=True)
             # Make sure something is printed, even if log is disabled.
             traceback.print_exc()
+
+    def get_channel_binding(self):
+        """
+        Get channel binding data. RFC 5929
+        """
+        sslObj = self._owner._sslObj
+        try:       
+            return sslObj.get_channel_binding()
+        except AttributeError:
+            raise NotImplementedError
_______________________________________________
Commits mailing list
[email protected]
http://lists.gajim.org/cgi-bin/listinfo/commits

Reply via email to