From d3c41f3137ffdf591ed1b26fd54928717b4bdcf7 Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 20:19:04 +0200 Subject: Enable discovering of real user DN to break loose from the uid=%s,%s pattern. --- Authenticators/LDAP/LDAPauth.ini | 2 ++ Authenticators/LDAP/LDAPauth.py | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Authenticators/LDAP/LDAPauth.ini b/Authenticators/LDAP/LDAPauth.ini index 13d065e..efc6cdf 100644 --- a/Authenticators/LDAP/LDAPauth.ini +++ b/Authenticators/LDAP/LDAPauth.ini @@ -22,6 +22,8 @@ watchdog = 30 ; bind_dn = ; bind_pass = users_dn = ou=Users,dc=example,dc=com +; Use discover_dn to find the user DN by searching +discover_dn = false username_attr = uid number_attr = roomNumber display_attr = displayName diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index f323ac1..c2068dd 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -137,6 +137,7 @@ default = { 'ldap':(('ldap_uri', str, 'ldap://127.0.0.1'), ('bind_dn', str, ''), ('bind_pass', str, ''), ('users_dn', str, 'ou=Users,dc=example,dc=org'), + ('discover_dn', x2bool, True), ('username_attr', str, 'uid'), ('number_attr', str, 'RoomNumber'), ('display_attr', str, 'displayName'), @@ -452,6 +453,15 @@ def do_main_program(): ldap_conn.unbind() warning('Invalid credentials for bind_dn=' + bind_dn) return (AUTH_REFUSED, None, None) + elif cfg.ldap.discover_dn: + # Use anonymous bind to discover the DN + try: + ldap_conn.bind_s() + except ldap.INVALID_CREDENTIALS: + ldap_conn.unbind() + warning('Failed anomymous bind for discovering DN') + return (AUTH_REFUSED, None, None) + else: # Prevent anonymous authentication. if not pw: @@ -481,6 +491,7 @@ def do_main_program(): # Parse the user information. uid = int(match[1][cfg.ldap.number_attr][0]) displayName = match[1][cfg.ldap.display_attr][0] + user_dn = match[0] debug('User match found, display "' + displayName + '" with UID ' + repr(uid)) # Optionally check groups. @@ -488,21 +499,21 @@ def do_main_program(): debug('Checking group membership for ' + name) #Search for user in group - res = ldap_conn.search_s(cfg.ldap.group_cn, ldap.SCOPE_SUBTREE, '(%s=%s=%s,%s)' % (cfg.ldap.group_attr, cfg.ldap.username_attr, name, cfg.ldap.users_dn), [cfg.ldap.number_attr, cfg.ldap.display_attr]) + res = ldap_conn.search_s(cfg.ldap.group_cn, ldap.SCOPE_SUBTREE, user_dn, [cfg.ldap.number_attr, cfg.ldap.display_attr]) # Check if the user is a member of the group if len(res) < 1: debug('User ' + name + ' failed with no group membership') return (AUTH_REFUSED, None, None) - # Second bind to test user credentials if using bind_dn. - if cfg.ldap.bind_dn: + # Second bind to test user credentials if using bind_dn or discover_dn. + if cfg.ldap.bind_dn or cfg.ldap.discover_dn: # Prevent anonymous authentication. if not pw: warning("No password supplied for user " + name) return (AUTH_REFUSED, None, None) - bind_dn = "%s=%s,%s" % (cfg.ldap.username_attr, name, cfg.ldap.users_dn) + bind_dn = user_dn bind_pass = pw try: ldap_conn.bind_s(bind_dn, bind_pass) -- cgit v1.2.3 From e0e944af0d6cfb86d95e0a2345493b49be072a50 Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 20:25:50 +0200 Subject: Update comments in LDAPAuth to reflect new discover_dn feature --- Authenticators/LDAP/LDAPauth.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index c2068dd..bd57cda 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -3,6 +3,7 @@ # Copyright (C) 2011 Benjamin Jemlich # Copyright (C) 2011 Nathaniel Kofalt # Copyright (C) 2013 Stefan Hacker +# Copyright (C) 2014 Dominik George # # All rights reserved. # @@ -51,10 +52,8 @@ # This is largely due to the numerous ways you can store user information in LDAP. # The example configuration is probably not the best way to do things; it's just a simple setup. # -# Further, this script has only been tested while storing the users in a single OU. -# Storing users in an OU tree will work, but probably will break group membership checking. -# The group-membership code will have to be expanded if you want to use multiple OUs with groups. -# Or if you want multiple groups allowed, etc. This is just a simple example. +# The group-membership code will have to be expanded if you want multiple groups allowed, etc. +# This is just a simple example. # # In this configuration, I use a really simple groupOfUniqueNames and OU of inetOrgPersons. # The tree already uses the "uid" attribute for usernames, so roomNumber was used to store UID. -- cgit v1.2.3 From fb4f211f8edbef447f81f523e8c929ccf3e18c79 Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 21:08:24 +0200 Subject: Use configured bind_dn for id search --- Authenticators/LDAP/LDAPauth.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index bd57cda..dda9810 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -559,7 +559,14 @@ def do_main_program(): debug("nameToId %s (cache) -> %d", name, uid) return uid - ldap_conn = ldap.initialize(cfg.ldap.ldap_uri, 0) #Anon search + ldap_conn = ldap.initialize(cfg.ldap.ldap_uri, 0) + + # Bind if configured, else do explicit anonymous bind + if cfg.ldap.bind_dn and cfg.ldap.bind_pass: + ldap_conn.simple_bind_s(cfg.ldap.bind_dn, cfg.ldap.bind_pass) + else: + ldap_conn.simple_bind_s() + res = ldap_conn.search_s(cfg.ldap.users_dn, ldap.SCOPE_SUBTREE, '(%s=%s)' % (cfg.ldap.display_attr, name), [cfg.ldap.number_attr]) #If user found, return the ID -- cgit v1.2.3 From b90fc0b2b2c42c021f8bfc58cd0e548e7f95f31f Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 21:20:33 +0200 Subject: Implement getInfo, optionally enabled through config --- Authenticators/LDAP/LDAPauth.ini | 3 +++ Authenticators/LDAP/LDAPauth.py | 41 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Authenticators/LDAP/LDAPauth.ini b/Authenticators/LDAP/LDAPauth.ini index efc6cdf..cd0ddf6 100644 --- a/Authenticators/LDAP/LDAPauth.ini +++ b/Authenticators/LDAP/LDAPauth.ini @@ -29,6 +29,9 @@ number_attr = roomNumber display_attr = displayName group_cn = cn=mumble,ou=Groups,dc=example,dc=com group_attr = uniqueMember +; Uncomment and set below to provide more info from LDAP +; provide_info = true +; mail_attr = mail ;Murmur configuration [murmur] diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index dda9810..2be0166 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -141,7 +141,9 @@ default = { 'ldap':(('ldap_uri', str, 'ldap://127.0.0.1'), ('number_attr', str, 'RoomNumber'), ('display_attr', str, 'displayName'), ('group_cn', str, 'ou=Groups,dc=example,dc=org'), - ('group_attr', str, 'member')), + ('group_attr', str, 'member'), + ('provide_info', x2bool, False), + ('mail_attr', str, 'mail')), 'user':(('id_offset', int, 1000000000), ('reject_on_error', x2bool, True), @@ -537,9 +539,40 @@ def do_main_program(): Gets called to fetch user specific information """ - # We do not expose any additional information so always fall through - debug('getInfo for %d -> denied', id) - return (False, None) + if not cfg.ldap.provide_info: + # We do not expose any additional information so always fall through + debug('getInfo for %d -> denied', id) + return (False, None) + + ldap_conn = ldap.initialize(cfg.ldap.ldap_uri, 0) + + # Bind if configured, else do explicit anonymous bind + if cfg.ldap.bind_dn and cfg.ldap.bind_pass: + ldap_conn.simple_bind_s(cfg.ldap.bind_dn, cfg.ldap.bind_pass) + else: + ldap_conn.simple_bind_s() + + name = self.idToName(id) + + res = ldap_conn.search_s(cfg.ldap.users_dn, + ldap.SCOPE_SUBTREE, + '(%s=%s)' % (cfg.ldap.display_attr, name), + [cfg.ldap.display_attr, + cfg.ldap.mail_attr + ]) + + #If user found, return info + if len(res) == 1: + info = {} + + if cfg.ldap.mail_attr in res[0][1]: + info['UserEmail'] = res[0][1][cfg.ldap.mail_attr][0] + + debug('nameToId %s -> %s', name, repr(info)) + else: + debug('nameToId %s -> ?', name) + return (False, None) + @fortifyIceFu(-2) -- cgit v1.2.3 From 700ebecc03313c4cdb2bb28503519b55878e724b Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 21:45:44 +0200 Subject: Fix the last commit --- Authenticators/LDAP/LDAPauth.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index 2be0166..79b4b39 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -552,7 +552,7 @@ def do_main_program(): else: ldap_conn.simple_bind_s() - name = self.idToName(id) + name = self.idToName(id, current) res = ldap_conn.search_s(cfg.ldap.users_dn, ldap.SCOPE_SUBTREE, @@ -566,11 +566,12 @@ def do_main_program(): info = {} if cfg.ldap.mail_attr in res[0][1]: - info['UserEmail'] = res[0][1][cfg.ldap.mail_attr][0] + info[Murmur.UserInfo.UserEmail] = res[0][1][cfg.ldap.mail_attr][0] - debug('nameToId %s -> %s', name, repr(info)) + debug('getInfo %s -> %s', name, repr(info)) + return (True, info) else: - debug('nameToId %s -> ?', name) + debug('getInfo %s -> ?', name) return (False, None) -- cgit v1.2.3 From b14ce4d54c9c315a47d6b990b2d7049722be4b92 Mon Sep 17 00:00:00 2001 From: Dominik George Date: Mon, 4 Aug 2014 22:49:15 +0200 Subject: Implementet getRegisteredUsers --- Authenticators/LDAP/LDAPauth.ini | 2 ++ Authenticators/LDAP/LDAPauth.py | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Authenticators/LDAP/LDAPauth.ini b/Authenticators/LDAP/LDAPauth.ini index cd0ddf6..fee5b7d 100644 --- a/Authenticators/LDAP/LDAPauth.ini +++ b/Authenticators/LDAP/LDAPauth.ini @@ -32,6 +32,8 @@ group_attr = uniqueMember ; Uncomment and set below to provide more info from LDAP ; provide_info = true ; mail_attr = mail +; Uncomment to provide list of registered users from LDAP +; provide_users = true ;Murmur configuration [murmur] diff --git a/Authenticators/LDAP/LDAPauth.py b/Authenticators/LDAP/LDAPauth.py index 79b4b39..45bb0c6 100644 --- a/Authenticators/LDAP/LDAPauth.py +++ b/Authenticators/LDAP/LDAPauth.py @@ -143,7 +143,8 @@ default = { 'ldap':(('ldap_uri', str, 'ldap://127.0.0.1'), ('group_cn', str, 'ou=Groups,dc=example,dc=org'), ('group_attr', str, 'member'), ('provide_info', x2bool, False), - ('mail_attr', str, 'mail')), + ('mail_attr', str, 'mail'), + ('provide_users', x2bool, False)), 'user':(('id_offset', int, 1000000000), ('reject_on_error', x2bool, True), @@ -683,11 +684,37 @@ def do_main_program(): def getRegisteredUsers(self, filter, current = None): """ Returns a list of usernames in the LDAP directory which contain - filter as a substring. Currently not implemented + filter as a substring. """ FALL_THROUGH = {} - debug('getRegisteredUsers -> fall through') - return FALL_THROUGH + + if not cfg.ldap.provide_users: + # Fall through if not configured to provide user list + debug('getRegisteredUsers -> fall through') + return FALL_THROUGH + + ldap_conn = ldap.initialize(cfg.ldap.ldap_uri, 0) + + # Bind if configured, else do explicit anonymous bind + if cfg.ldap.bind_dn and cfg.ldap.bind_pass: + ldap_conn.simple_bind_s(cfg.ldap.bind_dn, cfg.ldap.bind_pass) + else: + ldap_conn.simple_bind_s() + + if filter: + res = ldap_conn.search_s(cfg.ldap.users_dn, ldap.SCOPE_SUBTREE, '(&(uid=*)(%s=*%s*))' % (cfg.ldap.display_attr, filter), [cfg.ldap.number_attr, cfg.ldap.display_attr]) + else: + res = ldap_conn.search_s(cfg.ldap.users_dn, ldap.SCOPE_SUBTREE, '(uid=*)', [cfg.ldap.number_attr, cfg.ldap.display_attr]) + + # Build result dict + users = {} + for dn, attrs in res: + if cfg.ldap.number_attr in attrs and cfg.ldap.display_attr in attrs: + uid = int(attrs[cfg.ldap.number_attr][0]) + cfg.user.id_offset + name = attrs[cfg.ldap.display_attr][0] + users[uid] = name + debug('getRegisteredUsers %s -> %s', filter, repr(users)) + return users @fortifyIceFu(-1) @checkSecret -- cgit v1.2.3