fix ldappool bad password retry logic

This patch fixes a bug in ldappool which causes a bind attempt
utilizing a bad password to be retried until the retry limit has been
reached. Instead ldappool will now break out of the retry loop if the
ldap connection try block catches a ldap.INVALID_PASSWORD exception.

Previously ldappool would attempt to catch ldap.LDAPError which is
the base exception class for all ldap errors in the python-ldap
library. This is an issue because Keystone by default enables
ldappool and configures the default retry value to be 3. An LDAP
server with a password lockout threshold of 3 bad passwords will
lock out a user after a single bad password attempt through Keystone.

Change-Id: I2a9b850ce977260d4df1e9edf86417b8042a6fb8
Closes-Bug: #1785898
This commit is contained in:
Nick Wilburn 2018-08-11 14:21:11 -07:00
parent 459e1b1399
commit 459000d7aa
2 changed files with 32 additions and 0 deletions

View File

@ -252,6 +252,11 @@ class ConnectionManager(object):
conn.timeout = self.timeout
self._bind(conn, bind, passwd)
connected = True
except ldap.INVALID_CREDENTIALS as error:
exc = error
log.error('Invalid credentials. Cancelling retry',
exc_info=True)
break
except ldap.LDAPError as error:
exc = error
time.sleep(self.retry_delay)

View File

@ -55,6 +55,10 @@ def _bind_fails2(self, who='', cred='', **kw):
raise ldap.SERVER_DOWN('LDAP connection invalid')
def _bind_fails_invalid_credentials(self, who='', cred='', **kw):
raise ldap.INVALID_CREDENTIALS('LDAP connection invalid')
def _start_tls_s(self):
if self.start_tls_already_called_flag:
raise ldap.LOCAL_ERROR
@ -157,3 +161,26 @@ class TestLDAPConnection(unittest.TestCase):
pass
else:
raise AssertionError()
def test_simple_bind_fails_invalid_credentials(self):
unbinds = []
def _unbind(self):
unbinds.append(1)
# the binding fails with an LDAPError
ldappool.StateConnector.simple_bind_s = _bind_fails_invalid_credentials
ldappool.StateConnector.unbind_s = _unbind
uri = ''
dn = 'uid=adminuser,ou=logins,dc=mozilla'
passwd = 'adminuser'
cm = ldappool.ConnectionManager(uri, dn, passwd, use_pool=True, size=2)
self.assertEqual(len(cm), 0)
try:
with cm.connection('dn', 'pass'):
pass
except ldap.INVALID_CREDENTIALS:
pass
else:
raise AssertionError()