LDAP has been used for a long time for accessing and managing distributed directory information. This is an application level protocol that works over the IP network. Directory service is heavily used in organizations for managing the information about the users, the computer systems, the networks, the applications, and so on. The LDAP protocol contains plenty of technical jargon. It is a client/server-based protocol. So, the LDAP client will make a request to a properly configured LDAP server. After initializing the LDAP connection, the connection will need to be authenticated by using a few parameters. A simple BIND operation will establish an LDAP session. In a simple case, you can set up a simple anonymous BIND that would not need no password or any other credentials.
If you a run a simple LDAP query with the help of ldapsearch
, then you will see results such as:
# ldapsearch -x -b "dc=localdomain,dc=loc" -h 10.0.2.15 -p 389 # extended LDIF # # LDAPv3 # base <dc=localdomain,dc=loc> with scope subtree # filter: (objectclass=*) # requesting: ALL # # localdomain.loc dn: dc=localdomain,dc=loc objectClass: top objectClass: dcObject objectClass: organization o: localdomain.loc dc: localdomain # admin, localdomain.loc dn: cn=admin,dc=localdomain,dc=loc objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator # groups, localdomain.loc dn: ou=groups,dc=localdomain,dc=loc ou: groups objectClass: organizationalUnit objectClass: top # users, localdomain.loc dn: ou=users,dc=localdomain,dc=loc ou: users objectClass: organizationalUnit objectClass: top # admin, groups, localdomain.loc dn: cn=admin,ou=groups,dc=localdomain,dc=loc cn: admin gidNumber: 501 objectClass: posixGroup # Faruque Sarker, users, localdomain.loc dn: cn=Faruque Sarker,ou=users,dc=localdomain,dc=loc givenName: Faruque sn: Sarker cn: Faruque Sarker uid: fsarker uidNumber: 1001 gidNumber: 501 homeDirectory: /home/users/fsarker loginShell: /bin/sh objectClass: inetOrgPerson objectClass: posixAccount # search result search: 2 result: 0 Success # numResponses: 7 # numEntries: 6
The preceding communication can be captured with the help of Wireshark. You need to capture the packets on port 389. As shown in the following screenshot, the LDAP client-server communication will be established after a bindRequest
has been successfully sent. It's not secure to communicate anonymously with the LDAP server. For the sake of simplicity, in the following example the search has been done without binding with any of the credentials.
The Python's third-party python-ldap
package provides the necessary functionality for interacting with an LDAP server. You can install this package with the help of pip
.
$ pip install python-ldap
To begin with, you will have to initialize the LDAP connection:
import ldap ldap_client = ldap.initialize("ldap://10.0.2.15:389/")
Then the following code will show how a simple BIND operation can be performed:
ldap_client.simple_bind("dc=localdomain,dc=loc")
Then you can perform an ldap search. It requires you to specify the necessary parameters, such as base DN, filter, and attributes. Here is an example of the syntax that is required for searching for the users on an LDAP server:
ldap_client.search_s( base_dn, ldap.SCOPE_SUBTREE, filter, attrs )
Here is a complete example for finding user information by using the LDAP protocol:
import ldap # Open a connection ldap_client = ldap.initialize("ldap://10.0.2.15:389/") # Bind/authenticate with a user with apropriate rights to add objects ldap_client.simple_bind("dc=localdomain,dc=loc") base_dn = 'ou=users,dc=localdomain,dc=loc' filter = '(objectclass=person)' attrs = ['sn'] result = ldap_client.search_s( base_dn, ldap.SCOPE_SUBTREE, filter, attrs ) print(result)
The preceding code will search the LDAP directory subtree with the ou=users,dc=localdomain,dc=loc
base DN
and the [sn]
attributes. The search is limited to the person objects.
If we analyze the communication between the LDAP client and the server, then we can see the format of the LDAP search request and response. The parameters that we have used in our code have a direct relationship with the searchRequest
section of an LDAP packet. As shown in the following screenshot produced by Wireshark, it contains data, such as baseObject
, scope
and Filter
.
The LDAP search request generates a server response, which has been shown here:
When the LDAP server returns the search response, we can see the format of the response. As shown in the preceding screenshot, it contains the result of the search and the associated attributes.
Here is an example of searching a user from an LDAP server:
#!/usr/bin/env python import ldap import ldap.modlist as modlist LDAP_URI = "ldap://10.0.2.15:389/" BIND_TO = "dc=localdomain,dc=loc" BASE_DN = 'ou=users,dc=localdomain,dc=loc' SEARCH_FILTER = '(objectclass=person)' SEARCH_FILTER = ['sn'] if __name__ == '__main__': # Open a connection l = ldap.initialize(LDAP_URI) # bind to the server l.simple_bind(BIND_TO) result = l.search_s( BASE_DN, ldap.SCOPE_SUBTREE, SEARCH_FILTER, SEARCH_FILTER ) print(result)
In a properly configured LDAP machine, the preceding script will return a result that will be similar to the following:
$ python 5_5_ldap_read_record.py [('cn=Faruque Sarker,ou=users,dc=localdomain,dc=loc', {'sn': ['Sarker']})]