|
|
590d18 |
From 96cadaec3c16b296627991ed517d7015fbe2882f Mon Sep 17 00:00:00 2001
|
|
|
590d18 |
From: Jan Cholasta <jcholast@redhat.com>
|
|
|
590d18 |
Date: Wed, 2 Sep 2015 14:04:17 +0200
|
|
|
590d18 |
Subject: [PATCH] ldap: Make ldap2 connection management thread-safe again
|
|
|
590d18 |
|
|
|
590d18 |
This fixes the connection code in LDAPClient to not store the LDAP connection
|
|
|
590d18 |
in an attribute of the object, which in combination with ldap2's per-thread
|
|
|
590d18 |
connections lead to race conditions resulting in connection failures. ldap2
|
|
|
590d18 |
code was updated accordingly.
|
|
|
590d18 |
|
|
|
590d18 |
https://fedorahosted.org/freeipa/ticket/5268
|
|
|
590d18 |
|
|
|
590d18 |
Reviewed-By: Tomas Babej <tbabej@redhat.com>
|
|
|
590d18 |
---
|
|
|
590d18 |
ipapython/ipaldap.py | 32 +++++++++-----------------------
|
|
|
590d18 |
ipaserver/plugins/ldap2.py | 29 +++++++++++++----------------
|
|
|
590d18 |
2 files changed, 22 insertions(+), 39 deletions(-)
|
|
|
590d18 |
|
|
|
590d18 |
diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
|
|
|
590d18 |
index ef7c41a37c1f46290afbb11f912d7a4b8ea224c9..4443db03bcee25033abf63786016a7931f7eed20 100644
|
|
|
590d18 |
--- a/ipapython/ipaldap.py
|
|
|
590d18 |
+++ b/ipapython/ipaldap.py
|
|
|
590d18 |
@@ -710,11 +710,10 @@ class LDAPClient(object):
|
|
|
590d18 |
self._decode_attrs = decode_attrs
|
|
|
590d18 |
|
|
|
590d18 |
self.log = log_mgr.get_logger(self)
|
|
|
590d18 |
- self._conn = None
|
|
|
590d18 |
self._has_schema = False
|
|
|
590d18 |
self._schema = None
|
|
|
590d18 |
|
|
|
590d18 |
- self._connect()
|
|
|
590d18 |
+ self._conn = self._connect()
|
|
|
590d18 |
|
|
|
590d18 |
@property
|
|
|
590d18 |
def conn(self):
|
|
|
590d18 |
@@ -1023,29 +1022,16 @@ class LDAPClient(object):
|
|
|
590d18 |
"""
|
|
|
590d18 |
Close the connection.
|
|
|
590d18 |
"""
|
|
|
590d18 |
- if self._conn is not None:
|
|
|
590d18 |
- self._disconnect()
|
|
|
590d18 |
+ self._conn = None
|
|
|
590d18 |
|
|
|
590d18 |
def _connect(self):
|
|
|
590d18 |
- if self._conn is not None:
|
|
|
590d18 |
- raise errors.DatabaseError(
|
|
|
590d18 |
- desc="Can't connect to server", info="Already connected")
|
|
|
590d18 |
-
|
|
|
590d18 |
with self.error_handler():
|
|
|
590d18 |
- # bypass ldap2's locking
|
|
|
590d18 |
- object.__setattr__(self, '_conn',
|
|
|
590d18 |
- ldap.initialize(self.ldap_uri))
|
|
|
590d18 |
+ conn = ldap.initialize(self.ldap_uri)
|
|
|
590d18 |
|
|
|
590d18 |
if self._start_tls:
|
|
|
590d18 |
- self._conn.start_tls_s()
|
|
|
590d18 |
-
|
|
|
590d18 |
- def _disconnect(self):
|
|
|
590d18 |
- if self._conn is None:
|
|
|
590d18 |
- raise errors.DatabaseError(
|
|
|
590d18 |
- desc="Can't disconnect from server", info="Not connected")
|
|
|
590d18 |
+ conn.start_tls_s()
|
|
|
590d18 |
|
|
|
590d18 |
- # bypass ldap2's locking
|
|
|
590d18 |
- object.__setattr__(self, '_conn', None)
|
|
|
590d18 |
+ return conn
|
|
|
590d18 |
|
|
|
590d18 |
def simple_bind(self, bind_dn, bind_password, server_controls=None,
|
|
|
590d18 |
client_controls=None):
|
|
|
590d18 |
@@ -1059,7 +1045,7 @@ class LDAPClient(object):
|
|
|
590d18 |
assert isinstance(bind_dn, DN)
|
|
|
590d18 |
bind_dn = str(bind_dn)
|
|
|
590d18 |
bind_password = self.encode(bind_password)
|
|
|
590d18 |
- self._conn.simple_bind_s(
|
|
|
590d18 |
+ self.conn.simple_bind_s(
|
|
|
590d18 |
bind_dn, bind_password, server_controls, client_controls)
|
|
|
590d18 |
|
|
|
590d18 |
def external_bind(self, user_name, server_controls=None,
|
|
|
590d18 |
@@ -1070,7 +1056,7 @@ class LDAPClient(object):
|
|
|
590d18 |
with self.error_handler():
|
|
|
590d18 |
auth_tokens = ldap.sasl.external(user_name)
|
|
|
590d18 |
self._flush_schema()
|
|
|
590d18 |
- self._conn.sasl_interactive_bind_s(
|
|
|
590d18 |
+ self.conn.sasl_interactive_bind_s(
|
|
|
590d18 |
'', auth_tokens, server_controls, client_controls)
|
|
|
590d18 |
|
|
|
590d18 |
def gssapi_bind(self, server_controls=None, client_controls=None):
|
|
|
590d18 |
@@ -1080,7 +1066,7 @@ class LDAPClient(object):
|
|
|
590d18 |
with self.error_handler():
|
|
|
590d18 |
auth_tokens = ldap.sasl.sasl({}, 'GSSAPI')
|
|
|
590d18 |
self._flush_schema()
|
|
|
590d18 |
- self._conn.sasl_interactive_bind_s(
|
|
|
590d18 |
+ self.conn.sasl_interactive_bind_s(
|
|
|
590d18 |
'', auth_tokens, server_controls, client_controls)
|
|
|
590d18 |
|
|
|
590d18 |
def unbind(self):
|
|
|
590d18 |
@@ -1089,7 +1075,7 @@ class LDAPClient(object):
|
|
|
590d18 |
"""
|
|
|
590d18 |
with self.error_handler():
|
|
|
590d18 |
self._flush_schema()
|
|
|
590d18 |
- self._conn.unbind_s()
|
|
|
590d18 |
+ self.conn.unbind_s()
|
|
|
590d18 |
|
|
|
590d18 |
def make_dn_from_attr(self, attr, value, parent_dn=None):
|
|
|
590d18 |
"""
|
|
|
590d18 |
diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
|
|
|
590d18 |
index 68feee4f09eb12e50867dfbe3c482a359838aa82..deb0592ab68ab8eb712a6d29fdffd8776e2e289a 100644
|
|
|
590d18 |
--- a/ipaserver/plugins/ldap2.py
|
|
|
590d18 |
+++ b/ipaserver/plugins/ldap2.py
|
|
|
590d18 |
@@ -76,10 +76,7 @@ class ldap2(CrudBackend, LDAPClient):
|
|
|
590d18 |
# do not set it
|
|
|
590d18 |
pass
|
|
|
590d18 |
|
|
|
590d18 |
- def _disconnect(self):
|
|
|
590d18 |
- pass
|
|
|
590d18 |
-
|
|
|
590d18 |
- def __del__(self):
|
|
|
590d18 |
+ def close(self):
|
|
|
590d18 |
if self.isconnected():
|
|
|
590d18 |
self.disconnect()
|
|
|
590d18 |
|
|
|
590d18 |
@@ -118,10 +115,11 @@ class ldap2(CrudBackend, LDAPClient):
|
|
|
590d18 |
if debug_level:
|
|
|
590d18 |
_ldap.set_option(_ldap.OPT_DEBUG_LEVEL, debug_level)
|
|
|
590d18 |
|
|
|
590d18 |
- LDAPClient._connect(self)
|
|
|
590d18 |
- conn = self._conn
|
|
|
590d18 |
+ client = LDAPClient(self.ldap_uri,
|
|
|
590d18 |
+ force_schema_updates=self._force_schema_updates)
|
|
|
590d18 |
+ conn = client._conn
|
|
|
590d18 |
|
|
|
590d18 |
- with self.error_handler():
|
|
|
590d18 |
+ with client.error_handler():
|
|
|
590d18 |
if self.ldap_uri.startswith('ldapi://') and ccache:
|
|
|
590d18 |
conn.set_option(_ldap.OPT_HOST_NAME, self.api.env.host)
|
|
|
590d18 |
minssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MIN)
|
|
|
590d18 |
@@ -147,29 +145,28 @@ class ldap2(CrudBackend, LDAPClient):
|
|
|
590d18 |
context=krbV.default_context()).principal().name
|
|
|
590d18 |
|
|
|
590d18 |
os.environ['KRB5CCNAME'] = ccache
|
|
|
590d18 |
- self.gssapi_bind(server_controls=serverctrls,
|
|
|
590d18 |
- client_controls=clientctrls)
|
|
|
590d18 |
+ client.gssapi_bind(server_controls=serverctrls,
|
|
|
590d18 |
+ client_controls=clientctrls)
|
|
|
590d18 |
setattr(context, 'principal', principal)
|
|
|
590d18 |
else:
|
|
|
590d18 |
# no kerberos ccache, use simple bind or external sasl
|
|
|
590d18 |
if autobind:
|
|
|
590d18 |
pent = pwd.getpwuid(os.geteuid())
|
|
|
590d18 |
- self.external_bind(pent.pw_name,
|
|
|
590d18 |
+ client.external_bind(pent.pw_name,
|
|
|
590d18 |
+ server_controls=serverctrls,
|
|
|
590d18 |
+ client_controls=clientctrls)
|
|
|
590d18 |
+ else:
|
|
|
590d18 |
+ client.simple_bind(bind_dn, bind_pw,
|
|
|
590d18 |
server_controls=serverctrls,
|
|
|
590d18 |
client_controls=clientctrls)
|
|
|
590d18 |
- else:
|
|
|
590d18 |
- self.simple_bind(bind_dn, bind_pw,
|
|
|
590d18 |
- server_controls=serverctrls,
|
|
|
590d18 |
- client_controls=clientctrls)
|
|
|
590d18 |
|
|
|
590d18 |
return conn
|
|
|
590d18 |
|
|
|
590d18 |
def destroy_connection(self):
|
|
|
590d18 |
"""Disconnect from LDAP server."""
|
|
|
590d18 |
try:
|
|
|
590d18 |
- if self._conn is not None:
|
|
|
590d18 |
+ if self.conn is not None:
|
|
|
590d18 |
self.unbind()
|
|
|
590d18 |
- LDAPClient._disconnect(self)
|
|
|
590d18 |
except errors.PublicError:
|
|
|
590d18 |
# ignore when trying to unbind multiple times
|
|
|
590d18 |
pass
|
|
|
590d18 |
--
|
|
|
590d18 |
2.5.1
|
|
|
590d18 |
|