Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:

5 changed files:

Changes:

  • README.md
    ... ... @@ -11,6 +11,10 @@
    11 11
     - precis-i18n
    
    12 12
     - idna
    
    13 13
     
    
    14
    +## Optional Runtime Requirements
    
    15
    +
    
    16
    +- python-gssapi (for GSSAPI authentication)
    
    17
    +
    
    14 18
     ## Features
    
    15 19
     
    
    16 20
     * List of [supported XEPs](https://dev.gajim.org/gajim/python-nbxmpp/-/wikis/Supported-XEPs-in-python-nbxmpp/)
    

  • nbxmpp/auth.py
    ... ... @@ -29,17 +29,16 @@ from nbxmpp.protocol import SASL_AUTH_MECHS
    29 29
     from nbxmpp.util import b64decode
    
    30 30
     from nbxmpp.util import b64encode
    
    31 31
     from nbxmpp.util import LogAdapter
    
    32
    -from nbxmpp.const import GSSAPIState
    
    33 32
     from nbxmpp.const import StreamState
    
    34 33
     
    
    35 34
     
    
    36 35
     log = logging.getLogger('nbxmpp.auth')
    
    37 36
     
    
    38 37
     try:
    
    39
    -    kerberos = __import__('kerberos')
    
    40
    -    KERBEROS_AVAILABLE = True
    
    38
    +    gssapi = __import__('gssapi')
    
    39
    +    GSSAPI_AVAILABLE = True
    
    41 40
     except ImportError:
    
    42
    -    KERBEROS_AVAILABLE = False
    
    41
    +    GSSAPI_AVAILABLE = False
    
    43 42
     
    
    44 43
     
    
    45 44
     class SASL:
    
    ... ... @@ -91,7 +90,7 @@ class SASL:
    91 90
             self._enabled_mechs.discard('SCRAM-SHA-256-PLUS')
    
    92 91
             # channel_binding_data = None
    
    93 92
     
    
    94
    -        if not KERBEROS_AVAILABLE:
    
    93
    +        if not GSSAPI_AVAILABLE:
    
    95 94
                 self._enabled_mechs.discard('GSSAPI')
    
    96 95
     
    
    97 96
             available_mechs = features.get_mechs() & self._enabled_mechs
    
    ... ... @@ -156,9 +155,16 @@ class SASL:
    156 155
     
    
    157 156
             elif chosen_mechanism == 'GSSAPI':
    
    158 157
                 self._method = GSSAPI(self._client)
    
    159
    -            self._method.initiate(domain_based_name or
    
    160
    -                                  self._client.domain)
    
    161
    -
    
    158
    +            if domain_based_name:
    
    159
    +                hostname = domain_based_name
    
    160
    +            else:
    
    161
    +                hostname = self._client.domain
    
    162
    +            try:
    
    163
    +                self._method.initiate(hostname)
    
    164
    +            except AuthFail as error:
    
    165
    +                self._log.error(error)
    
    166
    +                self._abort_auth()
    
    167
    +                return
    
    162 168
             else:
    
    163 169
                 self._log.error('Unknown auth mech')
    
    164 170
     
    
    ... ... @@ -258,39 +264,41 @@ class ANONYMOUS:
    258 264
     
    
    259 265
     class GSSAPI:
    
    260 266
     
    
    267
    +    # See https://tools.ietf.org/html/rfc4752#section-3.1
    
    268
    +
    
    261 269
         _mechanism = 'GSSAPI'
    
    262 270
     
    
    263 271
         def __init__(self, client):
    
    264 272
             self._client = client
    
    265
    -        self._gss_vc = None
    
    266
    -        self._state = GSSAPIState.STEP
    
    267 273
     
    
    268 274
         def initiate(self, hostname):
    
    269
    -        self._gss_vc = kerberos.authGSSClientInit('xmpp@%s' % hostname)[1]
    
    270
    -        kerberos.authGSSClientStep(self._gss_vc, '')
    
    271
    -        response = kerberos.authGSSClientResponse(self._gss_vc)
    
    275
    +        service = gssapi.Name(
    
    276
    +            'xmpp@%s' % hostname, name_type=gssapi.NameType.hostbased_service)
    
    277
    +        try:
    
    278
    +            self.ctx = gssapi.SecurityContext(
    
    279
    +                name=service, usage="initiate",
    
    280
    +                flags=gssapi.RequirementFlag.integrity)
    
    281
    +            token = self.ctx.step()
    
    282
    +        except (gssapi.exceptions.GeneralError, gssapi.raw.misc.GSSError) as e:
    
    283
    +            raise AuthFail(e)
    
    272 284
             node = Node('auth',
    
    273 285
                         attrs={'xmlns': Namespace.SASL, 'mechanism': 'GSSAPI'},
    
    274
    -                    payload=(response or ''))
    
    286
    +                    payload=b64encode(token))
    
    275 287
             self._client.send_nonza(node)
    
    276 288
     
    
    277 289
         def response(self, server_message, *args, **kwargs):
    
    278 290
             server_message = b64decode(server_message, bytes)
    
    279
    -        if self._state == GSSAPIState.STEP:
    
    280
    -            rc = kerberos.authGSSClientStep(self._gss_vc, server_message)
    
    281
    -            if rc != kerberos.AUTH_GSS_CONTINUE:
    
    282
    -                self._state = GSSAPIState.WRAP
    
    283
    -        elif self._state == GSSAPIState.WRAP:
    
    284
    -            rc = kerberos.authGSSClientUnwrap(self._gss_vc, server_message)
    
    285
    -            response = kerberos.authGSSClientResponse(self._gss_vc)
    
    286
    -            rc = kerberos.authGSSClientWrap(
    
    287
    -                self._gss_vc,
    
    288
    -                response,
    
    289
    -                kerberos.authGSSClientUserName(self._gss_vc))
    
    290
    -        response = kerberos.authGSSClientResponse(self._gss_vc)
    
    291
    -        if not response:
    
    292
    -            response = ''
    
    293
    -
    
    291
    +        try:
    
    292
    +            if not self.ctx.complete:
    
    293
    +                output_token = self.ctx.step(server_message)
    
    294
    +            else:
    
    295
    +                result = self.ctx.unwrap(server_message)
    
    296
    +                # TODO(jelmer): Log result.message
    
    297
    +                data = b'\x00\x00\x00\x00' + bytes(self.ctx.initiator_name)
    
    298
    +                output_token = self.ctx.wrap(data, False).message
    
    299
    +        except (gssapi.exceptions.GeneralError, gssapi.raw.misc.GSSError) as e:
    
    300
    +            raise AuthFail(e)
    
    301
    +        response = b64encode(output_token)
    
    294 302
             node = Node('response',
    
    295 303
                         attrs={'xmlns': Namespace.SASL},
    
    296 304
                         payload=response)
    

  • nbxmpp/client.py
    ... ... @@ -279,6 +279,7 @@ class Client(Observable):
    279 279
         def mechs(self):
    
    280 280
             return set(self._allowed_mechs or set(['SCRAM-SHA-256',
    
    281 281
                                                    'SCRAM-SHA-1',
    
    282
    +                                               'GSSAPI',
    
    282 283
                                                    # 'SCRAM-SHA-256-PLUS',
    
    283 284
                                                    # 'SCRAM-SHA-1-PLUS',
    
    284 285
                                                    'PLAIN']))
    

  • nbxmpp/const.py
    ... ... @@ -21,10 +21,6 @@ from functools import total_ordering
    21 21
     
    
    22 22
     from gi.repository import Gio
    
    23 23
     
    
    24
    -class GSSAPIState(IntEnum):
    
    25
    -    STEP = 0
    
    26
    -    WRAP = 1
    
    27
    -
    
    28 24
     
    
    29 25
     class IqType(Enum):
    
    30 26
         GET = 'get'
    

  • nbxmpp/protocol.py
    ... ... @@ -40,8 +40,8 @@ SASL_AUTH_MECHS = [
    40 40
         'SCRAM-SHA-256',
    
    41 41
         'SCRAM-SHA-1-PLUS',
    
    42 42
         'SCRAM-SHA-1',
    
    43
    -    'PLAIN',
    
    44 43
         'GSSAPI',
    
    44
    +    'PLAIN',
    
    45 45
         'EXTERNAL',
    
    46 46
         'ANONYMOUS',
    
    47 47
     ]