[Twisted-Python] LDAP binds appears to succeed with no password
Peter Westlake
peter.westlake at pobox.com
Fri Jul 30 07:31:00 MDT 2010
On Fri, 30 Jul 2010 13:55 +0200, "Alan Franzoni" <mailing at 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-----------
More information about the Twisted-Python
mailing list