[Twisted-Python] LDAP binds appears to succeed with no password
I'm using LDAP to authenticate users, and when I give it an empty password, it appears to succeed! Can anyone see what I'm doing wrong? I've added comments by the log messages that appear in the output. Peter. from twisted.internet import reactor, defer from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector, ldaperrors from ldaptor.protocols import pureldap as L import log class LDAPAuthenticationSource(object): def __init__(self, base_dn, hostname=None, attr='uid', bind_dn='', bind_pw='', filter=L.LDAPFilter_present('cn')): self.hostname = hostname self.base_dn = base_dn self.attr = attr self.bind_dn = bind_dn self.bind_pw = bind_pw self.filter = filter self.cli = None @defer.inlineCallbacks def authenticate(self, username, password): if not password: log.info('Empty password!!!!') ####### appears else: log.debug('Attempting to login as', username) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) self.client = yield c.connect(self.base_dn) yield ldapsyntax.LDAPEntry(self.client, self.bind_dn).bind(self.bind_pw) entries = [] base = ldapsyntax.LDAPEntry(self.client, self.base_dn) yield base.search( filterObject=L.LDAPFilter_and( [L.LDAPFilter_equalityMatch( attributeDesc=L.LDAPAttributeDescription(self.attr), assertionValue=L.LDAPAssertionValue(username) ), self.filter, ]), attributes = (self.attr,), # No need to read the whole entry! callback=entries.append ) n_entries = len(entries) if n_entries == 0: self.client.unbind() log.debug('Failed login for %s: no search results' % (username,)) raise Exception('No search results!') elif n_entries > 1: self.client.unbind() log.debug('Failed login as %s: %d search results for unique entry!' % (username, n_entries)) raise Exception('%d search results for unique entry!' % n_entries) else: # The password matches if we can bind as this DN with it. try: if not password: log.info('Empty password') ##### appears user_entry = yield entries[0].bind(password) if not password: log.info('Succeeded with an empty password!') ##### appears log.info('user_entry:') ##### appears log.info(user_entry) ##### appears log.info('Login:', username) except ldaperrors.LDAPInvalidCredentials: log.info('Failed login for %s: invalid credentials' % username) raise except Exception, e: log.rep(e, 'Error while binding with user password') self.client.unbind() raise self.client.unbind() defer.returnValue(user_entry)
On 7/30/10 12:54 PM, Peter Westlake wrote:
I'm using LDAP to authenticate users, and when I give it an empty password, it appears to succeed! Can anyone see what I'm doing wrong? I've added comments by the log messages that appear in the output.
Please post the full code, including how you're using such class. Also: - log the entry you've found and that you're using to re-bind. It might not be what you're expecting. - try the very same query using something like ldapsearch or Apache LDAP Studio, and see whether the result differs. -- Alan Franzoni contact me at public@[mysurname].eu
On Fri, 30 Jul 2010 13:55 +0200, "Alan Franzoni" <mailing@franzoni.eu> wrote:
On 7/30/10 12:54 PM, Peter Westlake wrote:
I'm using LDAP to authenticate users, and when I give it an empty password, it appears to succeed! Can anyone see what I'm doing wrong? I've added comments by the log messages that appear in the output.
Please post the full code, including how you're using such class. Also:
The rest of it is below.
- log the entry you've found and that you're using to re-bind. It might not be what you're expecting.
It is, though - it's my directory entry. See the output, below.
- try the very same query using something like ldapsearch or Apache LDAP Studio, and see whether the result differs.
With ldapsearch, it fails as expected. That's a bit different to my code, though, because it only does a bind and a search. It isn't trying to re-bind using the entry it finds. LDAP Studio is a great find, thank you! And thank you for your help. Peter. Here's the test program: ----------------------------------------------------------------------- auth.py: from lda import LDAPAuthenticator import getpass from twisted.internet import reactor import log username = raw_input('Username: ') password = getpass.getpass('Password: ') def _success(entry): log.info('Successful authentication as %r' % username) return username def _failure(f): log.info('Failed authentication as %r' % username, f) raise Exception('Authentication failed') def stop(result): print 'Result', result reactor.stop() authenticator= LDAPAuthenticator( hostname=LDAP_HOST, base_dn=LDAP_ROOT, attr='sAMAccountName', bind_dn=LDAP_BIND_DN, bind_pw=LDAP_BIND_PW ) try: print 'Before authenticate' d = authenticator.authenticate(username, password) print 'Called authenticate' except Exception, e: print 'Exception was:' print e reactor.stop() d.addCallbacks(_success, _failure) d.addBoth(stop) reactor.run() -------------------------------------------------------------------------------- log.py: # Simplified log for test purposes. def info(*message): print message def debug(*message): print message def rep(e, *message): print message print 'Exception is:', e ------------------------------------------------------------------------------- The output of the program: $ python auth.py Username: peterw Password: Before authenticate ('Empty password!!!!',) Called authenticate ('Empty password',) ('Succeeded with an empty password!',) ('user_entry:',) (LDAPEntryWithClient(dn='CN=Peter Westlake,OU=....', attributes={'sAMAccountName': JournaledLDAPAttributeSet('sAMAccountName', ['peterw'])}),) ('Login:', 'peterw') ("Successful authentication as 'peterw'",) Result peterw --------------------------------------------------------------------------------- The file I posted last time, for completeness and ease of reference: lda.py: from twisted.internet import reactor, defer from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector, ldaperrors from ldaptor.protocols import pureldap as L import log class LDAPAuthenticator(object): def __init__(self, base_dn, hostname=None, attr='uid', bind_dn='', bind_pw='', filter=L.LDAPFilter_present('cn')): self.hostname = hostname self.base_dn = base_dn self.attr = attr self.bind_dn = bind_dn self.bind_pw = bind_pw self.filter = filter self.cli = None @defer.inlineCallbacks def authenticate(self, username, password): if not password: log.info('Empty password!!!!') else: log.debug('Attempting to login as', username) c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) self.client = yield c.connect(self.base_dn) yield ldapsyntax.LDAPEntry(self.client, self.bind_dn).bind(self.bind_pw) entries = [] base = ldapsyntax.LDAPEntry(self.client, self.base_dn) yield base.search( filterObject=L.LDAPFilter_and( [L.LDAPFilter_equalityMatch( attributeDesc=L.LDAPAttributeDescription(self.attr), assertionValue=L.LDAPAssertionValue(username) ), self.filter, ]), attributes = (self.attr,), # No need to read the whole entry! callback=entries.append ) n_entries = len(entries) if n_entries == 0: self.client.unbind() log.debug('Failed login for %s: no search results' % (username,)) raise Exception('No search results!') elif n_entries > 1: self.client.unbind() log.debug('Failed login as %s: %d search results for unique entry!' % (username, n_entries)) raise Exception('%d search results for unique entry!' % n_entries) else: # The password matches if we can bind as this DN with it. try: if not password: log.info('Empty password') user_entry = yield entries[0].bind(password) if not password: log.info('Succeeded with an empty password!') log.info('user_entry:') log.info(user_entry) log.info('Login:', username) except ldaperrors.LDAPInvalidCredentials: log.info('Failed login for %s: invalid credentials' % username) raise except Exception, e: log.rep(e, 'Error while binding with user password') self.client.unbind() raise self.client.unbind() defer.returnValue(user_entry) --------END-----------
participants (2)
-
Alan Franzoni
-
Peter Westlake