Reading and Interacting With LDAP Servers

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 15

Intermediate

Reading and interacting with LDAP


servers
In this section, you will learn about the LDAP protocol and examine the Python
libraries that deal with LDAP packets.
The LDAP protocol
LDAP is a protocol based on the X.500 standard, which is used to access
information that is stored through a centralized directory that contains the
information of our organization.

LDAP has a client/server architecture, where the server can use a variety of
databases to store a directory, each optimized for fast, high-volume read
operations. When a client application is connected to an LDAP server, most of
the time it will be for queries, although it is also possible to make changes to the
directory entries. If the client application is trying to modify the information in
an LDAP directory, the server will try to verify that the user has the necessary
permissions to update the information.

The biggest advantage of LDAP is that you can consolidate information for an
entire organization within a central repository. For example, instead of managing
user lists for each group within an organization, you can use LDAP as a central
directory, which is accessible from anywhere in the network. Since LDAP
supports Secure Connection Layer (SSL) and Transport Layer Security
(TLS), confidential data can be protected from hackers.

Another advantage of LDAP is that your company can access the LDAP
directory from almost any computing platform, from any of the applications that
is readily available for LDAP. It is also easy to customize your internal business
applications to add LDAP support.
LDAP terminology
LDAP stands for Lightweight Directory Access Protocol. It is an application-
level protocol that allows queries about a directory service to search for
information.

LDAP defines the way to access that directory, that is, it is optimized to carry out
read operations on the directory, such as validating authenticated access to a user
stored in the directory.

A directory service runs the client-server model, so if a client computer wants to


access the directory, it does not access the database directly; instead contacts a
process on the server. The process queries the directory and returns the result of
the operation to the client. Among the main terms when we work with LDAP,
let's highlight the following:

Classes: The objects and their characteristics are defined in classes. For
example, the type of object to be defined and the attributes that it will
contain depend on the type of object. In the scheme, each class is defined
with the attributes that will be obligatory and optional for each created
entry.
Objects: Entries in the directory. Objects are instances that are created from
a certain class or several, depending on the attributes required for an object.
The entire directory will be composed of objects (such as users, groups, or
organizational units).
A directory service is like a database where we organize and store
information with objects of different classes. This hierarchically-organized
structure of the objects is achieved with the implementation of LDAP.
entry: A unit in an LDAP directory. Each entry is identified by its unique
distinguished name (DN).
DN: The distinguished name to uniquely identify a specific object in the
directory. That is, each entry defined is unique throughout the directory. As
we can see, the DN of that object (user type) will be unique throughout the
directory and will uniquely identify you.
Attributes: Pieces of information directly associated with the input. For
example, an organization can be represented as an LDAP entry. The
attributes associated with the organization can be your fax number or your
address, for example. In an LDAP directory, the entries can also be people,
with common attributes such as their telephone number and email
addresses. Some attributes are mandatory while others are optional.
The LDAP Data Exchange Format (LDIF): An ASCII text representation
of LDAP entries. The files used to import data to the LDAP servers must be
in LDIF format.
Introduction to python-ldap
Python's python-ldap (https://www.python-ldap.org/en/latest/) third-party package
provides the necessary functionality to interact with an LDAP server.

You can install this package with the pip command:


$ pip install python-ldap

It is also possible to install python-ldap distributions based on Debian or Ubuntu


with the following commands:
sudo apt-get update
sudo apt-get install python-ldap

To begin, you will have to initialize the LDAP connection, where we can replace
ldap_server with the IP address of the server and the port number:

import ldap
ldap_client = ldap.initialize("ldap://<ldap_server>:port_number/")

This method initializes a new connection object to access the given LDAP
server, and return an LDAP object that's used to perform operations on that
server. The next step is bind/authenticate with a user with appropriate rights:
ldap_client.simple_bind(user,password)

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 to search for users on an LDAP server:
ldap_client.search_s( base_dn, ldap.SCOPE_SUBTREE, filter, attrs)

Here is a complete example to find user information using the LDAP protocol. It
demonstrates how to open a connection to an LDAP server using the ldap module
and invoke a synchronous subtree search.

You can find the following code in the connect_python_ldap.py file:


#!/usr/bin/env python3
import ldap
LDAP_SERVER ="ldap://52.57.162.88:389"
LDAP_BASE_DN = 'ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org'
LDAP_FILTER = '(objectclass=person)'
LDAP_ATTRS = ["cn", "dn", "sn", "givenName"]

def main():
try:
# Open a connection
ldap_client = ldap.initialize(LDAP_SERVER)
# Set LDAPv3 option
ldap_client.set_option(ldap.OPT_PROTOCOL_VERSION,3)
# Bind/authenticate with a user with appropriate rights
ldap_client.simple_bind("admin",'Secret123')
# Get user attributes defined in LDAP_ATTRS
result = ldap_client.search_s(LDAP_BASE_DN,ldap.SCOPE_SUBTREE,LDAP_FILTER, LDAP_ATTRS)
print(result)
except ldap.INVALID_CREDENTIALS as exception:
ldap_client.unbind()
print('Wrong username or password. '+exception)
except ldap.SERVER_DOWN as exception:
print('LDAP server not available. '+exception)

if __name__ == '__main__':
main ()

The previous script verifies credentials for the username and password against a
LDAP server. It returns some of the user attributes on success, or a string that
describes the error on failure. The script will search the LDAP directory subtree
with the ou=ldap3-tutorial,dc=demo1,dc=freeipa,dc=org base DN. The search is limited
to person objects.

We need to define some global variables so that we can establish the URL of the
LDAP server, that is, the base DN to search for users within the LDAP directory
and the user attributes that you want to recover.

First, we need to initialize an instance of the ldap class and define the options that
are required for the connection. Then, try to connect to the server using the
simple_bind function. In case of success, the user's attributes are retrieved using
the search_s function.
The LDAP FreeIPA server
FreeIPA (https://www.freeipa.org/page/Demo) is a fully-featured identity management
solution that provides LDAP server. We can find a free public instance of the
FreeIPA server at https://ipa.demo1.freeipa.org. The FreeIPA domain is configured
with the following users (the password is Secret123 for all of them):

admin: This user has all the privileges and is considered the administrator
account
helpdesk: A regular user with the helpdesk role
employee: A regular user with no special permissions
manager: A regular user, set as the manager of the employee user

In the following screenshot, we can see the active users that are available:

In the following screenshot, we can see the IPA Server configuration:


In the following screenshot, we can see the default user object classes inside the
IPA Server configuration:
Working with LDAP3
ldap3 is a fully-compliant Python LDAP v3 client library. It's written from scratch
to be compatible with Python 2 and Python 3, and can be installed via its
Standard Library with the following command:
pip install ldap3
Accessing the LDAP server
Using ldap3 is straightforward—you define a Server object and a Connection object.
All the importable objects are available in ldap3 namespace. You need to at least
import the Server and the Connection object, and any additional constants you will
use in your LDAP connection:
>>> from ldap3 import Server, Connection, ALL

In this example, we are accessing the LDAP server with an anonymous bind.
The auto_bind=True parameter forces the Bind operation to execute after creating
the Connection object. You can get information with the info property of the
Server object.

You can find the following code in the connect_ldap_server.py file:


#!/usr/bin/env python3

import argparse
from ldap3 import Server, Connection, ALL

def main(address):
# Create the Server object with the given address.
# Get ALL information.
server = Server(address, get_info=ALL)
#Create a connection object, and bind with auto bind set to true.
conn = Connection(server, auto_bind=True)
# Print the LDAP Server Information.
print('******************Server Info**************')
print(server.info)

if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Query LDAP Server')
parser.add_argument('--address', action="store", dest="address", default='ipa.demo1.freeipa.org')
given_args = parser.parse_args()
address = given_args.address
main(address)

The following is the output of the previous script. From this response, we know
that this server is a standalone LDAP server that can hold entries in the dc=demo1,
dc=freeipa, and dc=org contexts:

DSA info (from DSE):


Supported LDAP Versions: 2, 3
Naming Contexts:
cn=changelog
dc=demo1,dc=freeipa,dc=org
o=ipaca
Alternative Servers: None
Supported Controls:
1.2.840.113556.1.4.319 - LDAP Simple Paged Results - Control - RFC2696
1.2.840.113556.1.4.473 - Sort Request - Control - RFC2891
1.3.6.1.1.13.1 - LDAP Pre-read - Control - RFC4527
1.3.6.1.1.13.2 - LDAP Post-read - Control - RFC4527
1.3.6.1.4.1.1466.29539.12 - Chaining loop detect - Control - SUN microsystems
1.3.6.1.4.1.42.2.27.8.5.1 - Password policy - Control - IETF DRAFT behera-ldap-password-policy
1.3.6.1.4.1.42.2.27.9.5.2 - Get effective rights - Control - IETF DRAFT draft-ietf-ldapext-acl-model
1.3.6.1.4.1.42.2.27.9.5.8 - Account usability - Control - SUN microsystems
1.3.6.1.4.1.4203.1.9.1.1 - LDAP content synchronization - Control - RFC4533
1.3.6.1.4.1.4203.666.5.16 - LDAP Dereference - Control - IETF DRAFT draft-masarati-ldap-deref
2.16.840.1.113730.3.4.12 - Proxied Authorization (old) - Control - Netscape
2.16.840.1.113730.3.4.13 - iPlanet Directory Server Replication Update Information - Control - Netsca
2.16.840.1.113730.3.4.14 - Search on specific database - Control - Netscape
2.16.840.1.113730.3.4.15 - Authorization Identity Response Control - Control - RFC3829
2.16.840.1.113730.3.4.16 - Authorization Identity Request Control - Control - RFC3829
2.16.840.1.113730.3.4.17 - Real attribute only request - Control - Netscape
2.16.840.1.113730.3.4.18 - Proxy Authorization Control - Control - RFC6171
2.16.840.1.113730.3.4.19 - Chaining loop detection - Control - Netscape
2.16.840.1.113730.3.4.2 - ManageDsaIT - Control - RFC3296
2.16.840.1.113730.3.4.20 - Mapping Tree Node - Use one backend [extended] - Control - openLDAP
2.16.840.1.113730.3.4.3 - Persistent Search - Control - IETF
2.16.840.1.113730.3.4.4 - Netscape Password Expired - Control - Netscape
2.16.840.1.113730.3.4.5 - Netscape Password Expiring - Control - Netscape
2.16.840.1.113730.3.4.9 - Virtual List View Request - Control - IETF
2.16.840.1.113730.3.8.10.6 - OTP Sync Request - Control - freeIPA
Supported Extensions:
1.3.6.1.4.1.1466.20037 - StartTLS - Extension - RFC4511-RFC4513
1.3.6.1.4.1.4203.1.11.1 - Modify Password - Extension - RFC3062
1.3.6.1.4.1.4203.1.11.3 - Who am I - Extension - RFC4532
2.16.840.1.113730.3.5.10 - Distributed Numeric Assignment Extended Request - Extension - Netscape
2.16.840.1.113730.3.5.12 - Start replication request - Extension - Netscape
2.16.840.1.113730.3.5.3 - Transaction Response Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.4 - iPlanet Replication Response Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.5 - iPlanet End Replication Request Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.6 - iPlanet Replication Entry Request Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.7 - iPlanet Bulk Import Start Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.8 - iPlanet Bulk Import Finished Extended Operation - Extension - Netscape
2.16.840.1.113730.3.5.9 - iPlanet Digest Authentication Calculation Extended Operation - Extension -
2.16.840.1.113730.3.6.5 - Replication CleanAllRUV - Extension - Netscape
2.16.840.1.113730.3.6.6 - Replication Abort CleanAllRUV - Extension - Netscape
2.16.840.1.113730.3.6.7 - Replication CleanAllRUV Retrieve MaxCSN - Extension - Netscape
2.16.840.1.113730.3.6.8 - Replication CleanAllRUV Check Status - Extension - Netscape
2.16.840.1.113730.3.8.10.1 - KeyTab set - Extension - FreeIPA
2.16.840.1.113730.3.8.10.3 - Enrollment join - Extension - FreeIPA
2.16.840.1.113730.3.8.10.5 - KeyTab get - Extension - FreeIPA
Supported SASL Mechanisms:
EXTERNAL, GSS-SPNEGO, GSSAPI, DIGEST-MD5, CRAM-MD5, PLAIN, LOGIN, ANONYMOUS
Schema Entry:
cn=schema
Vendor name: 389 Project
Vendor version: 389-Directory/1.3.3.8 B2015.036.047
Other:
dataversion:
020150912040104020150912040104020150912040104
changeLog:
cn=changelog
lastchangenumber:
3033
firstchangenumber:
1713
lastusn:
8284
defaultnamingcontext:
dc=demo1,dc=freeipa,dc=org
netscapemdsuffix:
cn=ldap://dc=ipa,dc=demo1,dc=freeipa,dc=org:389
objectClass:
top
Finding entries in LDAP
To find entries in the ldap directory, you must use the search operation. This
operation has a number of parameters, but only two of them are mandatory:
conn.search(search_base,search_filter, attributes)

The following are the parameters:

search_base: The location in the ldap directory where the search will start
search_filter: A string that describes what you are searching for
attributes: Attributes to extract

In this script, we are going to search all users in the FreeIPA demo LDAP
server. You can find the following code in the entries_ldap_server.py file:
#!/usr/bin/env python3

from ldap3 import Server, Connection, ObjectDef, AttrDef, Reader, Writer, ALL

LDAP_SERVER ="ipa.demo1.freeipa.org"
LDAP_USER ="uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org"
LDAP_PASSWORD ="Secret123"
LDAP_FILTER = '(objectclass=person)'
LDAP_ATTRS = ["cn", "dn", "sn", "givenName"]

def main():
# Create the Server object with the given address.
server = Server(LDAP_SERVER, get_info=ALL)
#Create a connection object, and bind with the given DN and password.
try:
conn = Connection(server, LDAP_USER, LDAP_PASSWORD, auto_bind=True)
print('LDAP Bind Successful.')
# Perform a search for a pre-defined criteria.
# Mention the search filter / filter type and attributes.
conn.search('dc=demo1,dc=freeipa,dc=org', LDAP_FILTER , attributes=LDAP_ATTRS)
# Print the resulting entries.
for entry in conn.entries:
print(entry)
except core.exceptions.LDAPBindError as e:
# If the LDAP bind failed for reasons such as authentication failure.
print('LDAP Bind Failed: ', e)

if __name__ == '__main__':
main()

This is the execution of the previous script. Here, you request all the entries of
person class , starting from the dc=demo1, dc=freeipa, and dc=org contexts with the
default subtree scope:
[DN: uid=admin,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org
, DN: uid=manager,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org
, DN: uid=employee,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org
, DN: uid=helpdesk,cn=users,cn=accounts,dc=demo1,dc=freeipa,dc=org
]

You might also like