Commit 2a416f84 authored by Benjamin "Ziirish" SANS's avatar Benjamin "Ziirish" SANS

Merge branch 'master' into 'master'

Improved ldap handling & docs; binds against AD

== New config options
- adds port and encryption options for binding over SSL/TLS
- adds 'searchattr' option, the LDAP attribute to search on, defaults to 'uid'

== LdapLoader.fetch() changes:
- uses python format() to render the search attribute and search value into the search filter
- if search filter is not specified, uses a simple attr=value search filter
- returns DN and CN of the object if found

== LdapLoader.check() changes:
- binds to the LDAP server using the DN found in fetch(). Previously it was trying to bind using 'uid' and the search base which would not work for non-POSIX accounts or objects found in sub-trees.

== LdapUser.__init__() changes:
- records the DN of the user in order to re-bind as that object

== LdapUser.login() changes:
- searches for the user again to prevent binding as a disabled user. In AD, a user may still be able to bind even though their userAccountControl attribute prevents them from logging in to a host. This check overrides the cached result in case the account was disabled since last login.
- disables the user if it can no longer be found in LDAP.

== Overall improvements
- better documentation of the classes and functions

- add better user documentation explaining the new config options and how search filter should be used properly.

See merge request !1
Closes #63
parents f07fc0d6 1676e934
......@@ -10,63 +10,102 @@ except ImportError:
import ConfigParser
class LdapLoader:
The :class:`burpui.misc.auth.ldap.LdapLoader` handles searching for and binding as
a :class:`burpui.misc.auth.ldap.LdapUser` user.
def __init__(self, app=None):
:func:`burpui.misc.auth.ldap.LdapLoader.__init__` establishes a connection to the
LDAP server.
:param app: Instance of the app we are running in
:type app: :class:`burpui.server.BUIServer`
""" = app
conf =['CFG']
c = ConfigParser.ConfigParser({'host': 'localhost', 'binddn': '', 'bindpw': '', 'filter': '', 'base': ''})
defaults = {'host': 'localhost', 'port': None, 'encryption': None, 'binddn': None, 'bindpw': None, 'filter': None, 'base': None, 'searchattr': 'uid'}
mapping = {'host': 'host', 'port': 'port', 'encryption': 'encryption', 'filt': 'filter', 'base': 'base', 'attr': 'searchattr', 'binddn': 'binddn', 'bindpw': 'bindpw'}
c = ConfigParser.ConfigParser(defaults)
with open(conf) as fp:
try: = c.get('LDAP', 'host')
self.filt = c.get('LDAP', 'filter')
self.base = c.get('LDAP', 'base')
self.binddn = c.get('LDAP', 'binddn')
self.bindpw = c.get('LDAP', 'bindpw')
except ConfigParser.NoOptionError, e:
except ConfigParser.NoSectionError, e:
for opt, key in mapping.viewitems():
setattr(self, opt, c.get('LDAP', key))
except ConfigParser.NoOptionError, e:
except ConfigParser.NoSectionError, e:'LDAP host: %s','LDAP port: %s', self.port)'LDAP encryption: %s', self.encryption)'LDAP filter: %s', self.filt)'LDAP base: %s', self.base)'LDAP search attr: %s', self.attr)'LDAP binddn: %s', self.binddn)'LDAP bindpw: %s', '*****' if self.bindpw else 'None')
self.ldap = simpleldap.Connection(, dn=self.binddn, password=self.bindpw)
self.ldap = simpleldap.Connection(, port=self.port, dn=self.binddn, password=self.bindpw, encryption=self.encryption)'OK, connected to LDAP')
except:'Could not connect to LDAP')
self.ldap = None
def __exit__(self, exc_type, exc_value, traceback):
:func:`burpui.misc.auth.ldap.LdapLoader.__exit__` closes the connection to the
LDAP server.
if self.ldap:
def fetch(self, uid=None):
def fetch(self, searchval=None):
:func:`burpui.misc.auth.ldap.LdapLoader.fetch` searches for a user object in the
LDAP server.
:param searchval: attribute value to search for
:type searchval: str
:returns: dictionary of `distinguishedName` and `commonName` attributes for the
user if found, otherwise None.
if self.filt:'filter: %s | base: %s', self.filt, self.base)
r =, base_dn=self.base)
for record in r:
if record['uid'][0] == uid:
return record['uid'][0]
return None
query = self.filt.format(self.attr, searchval)
query = 'uid={0}'.format(uid)'query: %s | base: %s', query, self.base)
r =, base_dn=self.base)
query = '{0}={1}'.format(self.attr, searchval)'filter: %s | base: %s', query, self.base)
r =, base_dn=self.base, attrs=['cn', self.attr])
except Exception, e:'Ooops, LDAP lookup failed: %s', str(e))'Ooops, LDAP lookup failed: {0}'.format(str(e)))
return None
return r[0]['uid'][0]
for record in r:
if searchval in record[self.attr]:'Found DN: {0}'.format(record.dn))
return {'dn': record.dn, 'cn': record['cn'][0]}
def check(self, dn=None, passwd=None):
:func:`burpui.misc.auth.ldap.LdapLoader.check` authenticates a user against the
LDAP server.
def check(self, uid=None, passwd=None):
:param dn: canonical `dn` of the user to authenticate as
:type dn: str
:param passwd: password of the user to authenticate as
:type passwd: str
:returns: True if bind was successful, otherwise False
l = simpleldap.Connection(, dn='uid={0},{1}'.format(uid, self.base), password=passwd)
l = simpleldap.Connection(, dn='{0}'.format(dn), password=passwd)'Bound as user: {0}'.format(dn))
except Exception, e:'Failed to authenticate user: {0}, {1}'.format(dn, str(e)))
return False
......@@ -75,6 +114,9 @@ class LdapLoader:
class UserHandler(BUIhandler):
The :class:`burpui.misc.auth.ldap.UserHandler` class maintains a list of ``Burp-UI`` users.
def __init__(self, app=None):
self.ldap = LdapLoader(app)
self.users = {}
......@@ -87,23 +129,65 @@ class UserHandler(BUIhandler):
class LdapUser(UserMixin, BUIuser):
The :class:`burpui.misc.auth.ldap.LdapUser` class generates a ``Burp-UI`` user from
a user object found in the LDAP server.
def __init__(self, ldap=None, name=None):
:func:`burpui.misc.auth.ldap.LdapUser.__init__` function finds a user in the
LDAP server and stores the DN of the user if found.
:param ldap: an ``LdapLoader`` instance
:type ldap: :class:`burpui.misc.auth.ldap.LdapLoader`
:param name: login name of the user to find in the LDAP server
:param type: str
""" = False
self.ldap = ldap = name
ldapres = self.ldap.fetch(
found = self.ldap.fetch(name)
if ldapres: = ldapres
if found: = found['dn'] = True
def login(self, name=None, passwd=None):
return self.ldap.check(name, passwd)
:func:`burpui.misc.auth.ldap.LdapUser.login` function finds a user in the
LDAP server and authenticates that user using an LDAP bind.
:param name: login name of the user to authenticate as
:type name: str
:param passwd: password to bind to the LDAP server with
:type passwd: str
:returns: True if found and bind was successful;
False if found but bind failed;
otherwise de-activates the user and returns False
if self.ldap.fetch(name):
return self.ldap.check(, passwd)
else: = False
return False
def is_active(self):
:func:`burpui.misc.auth.ldap.LdapUser.is_active` function
:returns: True if user is active, otherwise False
def get_id(self):
:func:`burpui.misc.auth.ldap.LdapUser.get_id` function
:returns: login name of the user
......@@ -59,8 +59,19 @@ refresh: 30
## LDAP host
## LDAP filter to list existing users
##filter: &(burpui=1)
## LDAP port
#port: 389
## Encryption type to LDAP server (none, ssl or tls)
## - try tls if unsure, otherwise ssl on port 636
#encryption: ssl
## Attribute to use when searching the LDAP repository
##searchattr: sAMAccountName
#searchattr: uid
## LDAP filter to find users in the LDAP repository
## - {0} will be replaced by the search attribute
## - {1} will be replaced by the login name
##filter: (&({0}={1})(burpui=1))
##filter: (&({0}={1})(|(userAccountControl=512)(userAccountControl=66048)))
## LDAP base
#base: ou=users,dc=example,dc=com
## Binddn to list existing users
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment