Skip to content

Commit

Permalink
Merge pull request #41 from cannatag/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
cannatag committed Apr 22, 2015
2 parents 90afea0 + 56ad8d3 commit 7f97623
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 4 deletions.
3 changes: 3 additions & 0 deletions _changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
* 0.9.8.3 2015.04.08
- Added support for kerberos sasl - needs the gssapi package (thanks sigmaris and pefoley2)

* 0.9.8.2 2015.04.08
- SaslCred returned as raw bytes (thanks Peter)
- search_paged now properly works in abstract.reader (thanks wazboy)
Expand Down
2 changes: 1 addition & 1 deletion _version.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"url": "https://github.com/cannatag/ldap3",
"description": "A strictly RFC 4511 conforming LDAP V3 pure Python client. Same codebase for Python 2, Python3, PyPy and PyPy 3",
"author": "Giovanni Cannata",
"version": "0.9.8.2",
"version": "0.9.8.3",
"license": "LGPL v3"
}
2 changes: 1 addition & 1 deletion docs/manual/source/connections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ With the connection you can perform all the standard LDAP operations:

* dn: distinguish name of the object whose attributes must be modified

* changes: a dictionary in the form {'attribute1': [(operation, [val1, val2, ...])], 'attribute2': [(operation, [val1, val2, ...])]}, operation is MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT
* changes: a dictionary in the form {'attribute1': (operation, [val1, val2, ...]), 'attribute2': (operation, [val1, val2, ...])}, operation is MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE, MODIFY_INCREMENT

* controls: additional controls to be used in the request

Expand Down
2 changes: 1 addition & 1 deletion ldap3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
SASL = 'SASL'
NTLM = 'NTLM'

SASL_AVAILABLE_MECHANISMS = ['EXTERNAL', 'DIGEST-MD5']
SASL_AVAILABLE_MECHANISMS = ['EXTERNAL', 'DIGEST-MD5', 'GSSAPI']

AUTO_BIND_NONE = 'NONE'
AUTO_BIND_NO_TLS = 'NO_TLS'
Expand Down
3 changes: 3 additions & 0 deletions ldap3/core/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from ..protocol.rfc2849 import operation_to_ldif, add_ldif_header
from ..protocol.sasl.digestMd5 import sasl_digest_md5
from ..protocol.sasl.external import sasl_external
from ..protocol.sasl.kerberos import sasl_gssapi
from ..strategy.async import AsyncStrategy
from ..strategy.ldifProducer import LdifProducerStrategy
from ..strategy.sync import SyncStrategy
Expand Down Expand Up @@ -721,6 +722,8 @@ def do_sasl_bind(self,
result = sasl_external(self, controls)
elif self.sasl_mechanism == 'DIGEST-MD5':
result = sasl_digest_md5(self, controls)
elif self.sasl_mechanism == 'GSSAPI':
result = sasl_gssapi(self, controls)

self.sasl_in_progress = False

Expand Down
2 changes: 1 addition & 1 deletion ldap3/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ class LDAPDefinitionError(LDAPConfigurationError):
pass


class LDAPPackageUnavailableError(LDAPConfigurationError):
class LDAPPackageUnavailableError(LDAPConfigurationError, ImportError):
pass


Expand Down
83 changes: 83 additions & 0 deletions ldap3/protocol/sasl/kerberos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
"""

# Created on 2015.04.08
#
# Author: Giovanni Cannata
#
# Copyright 2015 Giovanni Cannata
#
# This file is part of ldap3.
#
# ldap3 is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ldap3 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ldap3 in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.

# original code by Hugh Cole-Baker, modified by Peter Foley
# it needs the gssapi package

from ...core.exceptions import LDAPPackageUnavailableError, LDAPCommunicationError

try:
import gssapi
except ImportError:
raise LDAPPackageUnavailableError('package gssapi missing')

from .sasl import send_sasl_negotiation, abort_sasl_negotiation

NO_SECURITY_LAYER = 1
INTEGRITY_PROTECTION = 2
CONFIDENTIALITY_PROTECTION = 4


def sasl_gssapi(connection, controls):
"""
Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism
from RFC 4752. Does not support any security layers, only authentication!
"""

target_name = gssapi.Name('ldap@' + connection.server.host, gssapi.NameType.hostbased_service)
ctx = gssapi.SecurityContext(name=target_name, mech=gssapi.MechType.kerberos)
in_token = None
try:
while True:
out_token = ctx.step(in_token)
if out_token is None:
out_token = ''
result = send_sasl_negotiation(connection, controls, out_token)
in_token = result['saslCreds']
try:
ctx.complete # This raises an exception if we haven't completed connecting.
break
except gssapi.exceptions.MissingContextError:
pass

unwrapped_token = ctx.unwrap(in_token)
if len(unwrapped_token.message) != 4:
raise LDAPCommunicationError("Incorrect response from server")

server_security_layers = unwrapped_token.message[0]
if not isinstance(server_security_layers, int):
server_security_layers = ord(server_security_layers)
if server_security_layers in (0, NO_SECURITY_LAYER):
if unwrapped_token.message[1:] != '\x00\x00\x00':
raise LDAPCommunicationError("Server max buffer size must be 0 if no security layer")
if not (server_security_layers & NO_SECURITY_LAYER):
raise LDAPCommunicationError("Server requires a security layer, but this is not implemented")

client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0])
out_token = ctx.wrap(bytes(client_security_layers), False)
return send_sasl_negotiation(connection, controls, out_token.message)
except (gssapi.exceptions.GSSError, LDAPCommunicationError):
abort_sasl_negotiation(connection, controls)
raise

0 comments on commit 7f97623

Please sign in to comment.