| ... |
... |
@@ -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)
|