Understanding LDAP (part 2)

Compile, install and configure OpenLDAP to create an LDAP directory for your organization.

Backtrack

In the first part of this article, I took you on a tour of the theory behind LDAP, the Lightweight Directory Access Protocol. That article was intended to familiarize you with how LDAP works, and give you the theoretical backing necessary to create, manipulate and search an LDAP directory.

With all the theory out of the way, it's time to actually do something with all that information. In this second part, I will be focusing exclusively on practical realities, demonstrating how to configure and use an LDAP directory with the OpenLDAP software suite. Keep reading!

Opening Up

OpenLDAP is an open-source implementation of the LDAP protocol, providing all the tools necessary to set up a fairly sophisticated LDAP directory service on your network. It includes support for version 3 of the LDAP protocol, together with a bunch of other goodies: Unicode support, authentication, referral support, replication and extensibility. It also supports a variety of different database backends for data storage, including Berkeley DB, LDBM and the standard UNIX password database.

The OpenLDAP suite includes both LDAP servers and clients. The server I'll be using throughout this tutorial is named "slapd", the standalone LDAP daemon; the distribution also includes "slurpd", a daemon designed to handle replication between multiple LDAP servers. The client tools include "ldapadd" for adding new entries to the directory, "ldapmodify" for editing entries, "ldapdelete" for deleting entries, and "ldapsearch" for querying the database. Finally, OpenLDAP also supports SSL encryption for greater security, and includes an API that allows developers to access LDAP services from programming languages like PHP and Perl.

Intrigued? Wanna see it in action? Flip the page, and let's get installing!

Building Blocks

The first order of business to install OpenLDAP on your Linux box. Drop by the official Web site at http://www.openldap.org/ and get yourself the latest stable release of the software (this tutorial uses OpenLDAP 2.1.12). Note that you will also need a copy of the SleepyCat Berkeley DB database engine, available from http://www.sleepycat.com/ (this tutorial uses Berkeley DB 4.1.25)

Once you've downloaded the source code archive to your Linux box (mine is named "olympus"), log in as "root".

[me@olympus] $ su -
Password: ****

You'll first need to compile and install Berkeley DB. Extract the source to a temporary directory.

[root@olympus] $ cd /tmp

[root@olympus] $ tar -xzvf db-4.1.25.tar.gz

Next, configure the package using the provided "configure" script,

[root@olympus] $ cd /tmp/db-4.1.25

[root@olympus] $ cd build_unix

[root@olympus] $ ../dist/configure

and compile and install it.

[root@olympus] $ make

[root@olympus] $ make install

Unless you specified a different path to the "configure" script, Berkeley DB will have been installed to the directory "/usr/local/BerkeleyDB.4.1".

Next, you need to install the OpenLDAP software proper. Again, extract the source archive to a temporary directory.

[root@olympus] $ cd /tmp

[root@olympus] $ tar -xzvf openldap-stable-20030107.tar.gz

Next, configure the package using the provided "configure" script,

[root@olympus] $ cd /tmp/openldap-2.1.12

[root@olympus] $ ./configure --prefix=/usr/local/openldap

and compile and install it.

[root@olympus] $ make depend

[root@olympus] $ make

[root@olympus] $ make install

Since I specified a custom path to the "configure" script, OpenLDAP will have been installed to the directory "/usr/local/openldap".

It must be noted that "configure" sometimes barfs when including the Berkeley DB library into OpenLDAP. In case you have this problem, explicitly tell "configure" where to find the files, as follows:

[root@olympus] $ env CPPFLAGS="-I/usr/local/BerkeleyDB.4.1/include" LDFLAGS="-L/usr/local/BerkeleyDB.4.1/lib" ./configure --prefix=/usr/local/openldap

Once you've got OpenLDAP installed, the next step is to configure it.

The Root Of All Evil

Configuration of the "slapd" daemon is handled via a configuration file named "slapd.conf", usually located in your installation's "etc/openldap/" directory. Pop open this file in your favourite text editor, and page down to the end of the file, where the database definitions are stored - you should see something like this:

#######################################################################
# ldbm database definitions
#######################################################################

database        bdb
suffix          "dc=my-domain,dc=com"
rootdn          "cn=Manager,dc=my-domain,dc=com"

# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw          secret

# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd/tools. Mode 700 recommended.
directory       /usr/local/openldap/var/openldap-data

# Indices to maintain
index   objectClass     eq

Update this section to reflect your environment - for example, for the "melonfire.com" domain, I have the following entries in this section:

#######################################################################
# ldbm database definitions
#######################################################################

database    bdb
suffix      "dc=melonfire,dc=com"
rootdn      "cn=root,dc=melonfire,dc=com"

# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw      secret

# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd/tools. Mode 700 recommended.
directory   /usr/local/openldap/var/openldap-data

# Indices to maintain
index   objectClass eq

A quick explanation here: the "suffix" configuration directive tells "slapd" which node to use as the root (or "base DN") of the directory tree, while the "rootdn" and "rootpw" directives tell "slapd" which entry has administrative rights to the database. The "directory" directive tells the system where to store its databases - in this case, in the directory "/usr/local/openldap/var/openldap-data".

The base DN to be specified in this section will serve as the root for the LDAP tree, and therefore must exist as an entry in the LDAP directory before you can begin using the service.

Once you're done with this section, go back up to the top of the file and locate the section which contains the schema definitions to be read by "slapd". By default, this section contains only a single entry:

include         /usr/local/openldap/etc/openldap/schema/core.schema

Alter this to include two additional definitions - these will be needed when you try to create "inetOrgPerson" object instances for your LDAP address book.

include         /usr/local/openldap/etc/openldap/schema/cosine.schema
include         /usr/local/openldap/etc/openldap/schema/inetorgperson.schema

All done? Save the file and exit.

Now, try running "slapd".

[root@olympus] $ /usr/local/openldap/libexec/slapd

If it works, great - flip the page, and find out how to start manipulating your LDAP database. If it doesn't, it's probably because of an error like this:

/usr/local/openldap/libexec/slapd: error in loading shared libraries: libdb-4.1.so: cannot open shared object file: No such file or directory

This is pretty simple to fix - all you need to do is tell your system where the OpenLDAP and Berkeley DB library files are stored. Pop open the "/etc/ld.so.conf" file and add these directories to the end of the directory list:

/usr/local/openldap/lib
/usr/local/BerkeleyDB.4.1/lib

Now, save the file and recreate the system's library database by running "ldconfig".

[root@olympus] $ ldconfig

Try invoking "slapd" again,

[root@olympus] $ /usr/local/openldap/libexec/slapd

and it should start up normally.

A Little Black Book Is Born

Once you've got the server up and running, the next step is to populate the database with a few entries. There are two ways to do this: add entries one at a time using the "ldapadd" command, or add them all at once via an LDIF file (basically, a single text file containing a series of entries in a defined format). I'll show you both techniques here.

Let's try the second method first - create an ASCII text file named "entries.ldif" and place the following data in it:

dn: dc=melonfire, dc=com
objectclass: dcObject
objectclass: organization
o: Melonfire
dc: melonfire.com

dn: mail=root@melonfire-mail.com, dc=melonfire, dc=com
objectclass: inetOrgPerson
cn: Keith
sn: Richards
mail: root@melonfire-mail.com

dn: mail=joe@melonfire-mail.com, dc=melonfire, dc=com
objectclass: inetOrgPerson
cn: Joe
sn: Somebody
mail: joe@melonfire-mail.com

dn: mail=sarah@melonfire-mail.com, dc=melonfire, dc=com
objectclass: inetOrgPerson
cn: Sarah
sn: Nobody
mail: sarah@melonfire-mail.com
telephoneNumber: 23 67 128 5639

Most of this should be familiar to you from the first part of this article - each entry has a DN which uniquely identifies it, an object identifier that indicates which class It belongs to (and therefore which rules it must adhere to), and a series of attribute-value pairs that make up the data for the entry.

Note that the first two entries are essential - these are the entries for the root of the tree and for the database administrator, respectively. It should be noted at this point that the rules for selecting a base DN for the DIT are fairly arbitrary - if your LDAP directory serves a particular domain, you can use an instance of the "dcObject" class for your base DN,

dn: dc=melonfire, dc=com
objectclass: dcObject
dc: melonfire.com

whereas if your LDAP directory serves an entire organization, you could use the "organization" class in your base DN.

dn: o=Melonfire, c=IN
objectclass: organization
o: Melonfire

You can add more entries for other users in the organization to the end of the file (as I said, only the first two are essential) and, when you're done, you can import them all into the database using the following command:

[root@olympus] $ /usr/local/openldap/bin/ldapadd -x -D "cn=root,dc=melonfire,dc=com" -W -f entries.ldif -c

When you're prompted for a password, enter the password value supplied for the "rootpw" configuration directive in "slapd.conf" ("secret", in the example above). This is necessary because adding, removing and editing entries are tasks that can only be performed by the directory administrator.

You should see something like this:

adding new entry "dc=melonfire, dc=com"

adding new entry "mail=root@melonfire-mail.com, dc=melonfire, dc=com"

adding new entry "mail=joe@melonfire-mail.com, dc=melonfire, dc=com"

adding new entry mail=sarah@melonfire-mail.com, dc=melonfire, dc=com

In other words, the LDAP server has accepted and saved your entries to the database.

You can also add entries one at a time by using the "ldapadd" command in interactive mode:

[root@olympus] $ /usr/local/openldap/bin/ldapadd -x -D "cn=root,dc=melonfire,dc=com" -W
Enter LDAP Password: ****
dn: mail=sarah@melonfire-mail.com, dc=melonfire, dc=com
objectclass: inetOrgPerson
cn: Sarah
sn: Nobody
mail: sarah@melonfire-mail.com
telephoneNumber: 23 67 128 5639

adding new entry "mail=saraha@melonfire-mail.com, dc=melonfire, dc=com"

Digging Deep

Now that you've put data in, it's time to get it out - which is where the "ldapsearch" command comes in.

The "ldapsearch" command allows you to query the LDAP database from a specific segment of the directory tree, and look for records matching certain characteristics. These characteristics could be attributes ("fetch me all records containing email addresses beginning with a J"), object classes ("fetch me all records of class 'person'") or any other criteria that you may choose.

Consider the following example, which demonstrates:

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -b 'dc=melonfire,dc=com'

This is a very simple catch-all query - it returns all the records in the database. The "-b" parameter tells the query engine the base at which to begin searching.

Here's the output:

# extended LDIF
#
# LDAPv3
# base <dc=melonfire,dc=com> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# melonfire.com
dn: dc=melonfire,dc=com
objectClass: dcObject
objectClass: organization
o: Melonfire
dc: melonfire.com

# root@melonfire-mail.com, melonfire.com
dn: mail=root@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Keith
sn: Richards
mail: root@melonfire-mail.com

# joe@melonfire-mail.com, melonfire.com
dn: mail=joe@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Joe
sn: Somebody
mail: joe@melonfire-mail.com

# sarah@melonfire-mail.com, melonfire.com
dn: mail=sarah@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Sarah
sn: Nobody
mail: sarah@melonfire-mail.com
telephoneNumber: 23 67 128 5639

# numResponses: 5
# numEntries: 4

Let's try another search, this one a little more focused:

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -u -b 'dc=melonfire,dc=com' '(cn=Joe)'

In this case, I've specified an additional search filter - return only those entries that have a "cn" attribute with the value "Joe". Here's the output:

# extended LDIF
#
# LDAPv3
# base <dc=melonfire,dc=com> with scope sub
# filter: (cn=Joe)
# requesting: ALL
#

# joe@melonfire-mail.com, melonfire.com
dn: mail=joe@melonfire-mail.com,dc=melonfire,dc=com
ufn: joe@melonfire-mail.com, melonfire.com
objectClass: inetOrgPerson
cn: Joe
sn: Somebody
mail: joe@melonfire-mail.com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

How about another one?

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -LLL -b 'dc=melonfire,dc=com' '(mail=root*)'

A different search criteria this time, this one using wildcards to search for users with an email address beginning with "root". The "-LLL" parameter tells the client to display the output in LDIF format, without the additional comments.

Here's the output:

dn: mail=root@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Keith
sn: Richards
mail: root@melonfire-mail.com

Too much information? You can limit the attributes displayed of each entry by specifying them at the end of your command:

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -LLL -b 'dc=melonfire,dc=com' '(mail=root*)' cn sn

In this case, only the "cn" and "sn" attributes of the entry will be displayed:

dn: mail=root@melonfire-mail.com,dc=melonfire,dc=com
cn: Keith
sn: Richards

If you have a large database, you can limit the number of entries returned via the "-z" parameter, which specifies the number of results to display.

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -b 'dc=melonfire,dc=com' -z 2

Here's the output:

# extended LDIF
#
# LDAPv3
# base <dc=melonfire,dc=com> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#

# melonfire.com
dn: dc=melonfire,dc=com
objectClass: dcObject
objectClass: organization
o: Melonfire
dc: melonfire.com

# root@melonfire-mail.com, melonfire.com
dn: mail=root@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Keith
sn: Richards
mail: root@melonfire-mail.com

# search result
search: 2
result: 4 Size limit exceeded

# numResponses: 3
# numEntries: 2

Obviously, there are innumerable ways to search the database, and I don't plan to get into all of them here. The examples above should give you a taste of what is possible - try experimenting on your own, or check out the manual pages for more.

Changing Things Around

Now, how about editing entries in the database? LDAP allows you to do this via the "ldapmodify" command, as demonstrated in the following example:

[root@olympus] $ /usr/local/openldap/bin/ldapmodify -x -D "cn=root,dc=melonfire,dc=com" -W

You'll be prompted for the password - enter it, and then enter the DN for the record being edited, followed by the new attribute-value pairs:

dn: mail=sarah@melonfire-mail.com, dc=melonfire, dc=com
sn: Jones

modifying entry "mail=sarah@melonfire-mail.com,dc=melonfire,dc=com"

Now, when you check the database again with "ldapsearch"

[root@olympus] $ /usr/local/openldap/bin/ldapsearch -b 'dc=melonfire,dc=com' '(cn=Sarah)'

you'll see the revised entry:

# extended LDIF
#
# LDAPv3
# base <dc=melonfire,dc=com> with scope sub
# filter: (cn=Sarah)
# requesting: ALL
#

# sarah@melonfire-mail.com, melonfire.com
dn: mail=sarah@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Sarah
mail: sarah@melonfire-mail.com
telephoneNumber: 23 67 128 5639
sn: Nobody

# search result
search: 1
result: 0 Success

# numResponses: 2
# numEntries: 1

In a similar manner, you can even add a new attribute to an existing entry - here's how:

[root@olympus] $ /usr/local/openldap/bin/ldapmodify -x -D "cn=root,dc=melonfire,dc=com" -W
Enter LDAP Password: ****
dn: mail=sarah@melonfire-mail.com, dc=melonfire, dc=com
carLicense: MFC 437458

modifying entry "mail=sarah@melonfire-mail.com, dc=melonfire, dc=com"

And now, when you run the same search, you'll see that the entry contains your newly-added attribute:

# extended LDIF
#
# LDAPv3
# base <dc=melonfire,dc=com> with scope sub
# filter: (cn=Sarah)
# requesting: ALL
#

# sarah@melonfire-mail.com, melonfire.com
dn: mail=sarah@melonfire-mail.com,dc=melonfire,dc=com
objectClass: inetOrgPerson
cn: Sarah
mail: sarah@melonfire-mail.com
telephoneNumber: 23 67 128 5639
sn: Jones
carLicense: MFC 437458

# search result
search: 1
result: 0 Success

# numResponses: 2
# numEntries: 1

Finally, you can easily remove existing entries from the database via the "ldapdelete" command - simply provide the DN of the entry to be deleted:

[root@olympus] $ /usr/local/openldap/bin/ldapdelete -x -D "cn=root,dc=melonfire,dc=com" -W
Enter LDAP Password: ****
mail=rita@melonfire-mail.com, dc=melonfire, dc=com

You Have Mail

So that takes care of creating and initializing the LDAP directory - you can use the techniques discussed on the previous page to add entries for other members of your organization, providing as much (or as little) detail as you desire for each entry. Once you're done, all you need to do is hook your LDAP-aware clients up to the server, so that you can begin using the directory in your daily activities.

Now, there are a large number of LDAP-aware clients available today - Microsoft and Netscape both support LDAP in their address book applications, as do other email clients like Qualcomm's Eudora Pro. Configuring an LDAP-aware client is pretty simple - all you need to do is provide the name of the LDAP server, together with the base DN to begin searching from, and the client will (usually) do the rest.

In order to demonstrate, I'll quickly hook Eudora up to the LDAP directory created over the previous pages. Pop open your Eudora mail client, and use the Tools -> Directory Services menu to bring up the Directory Services control panel.

Select "LDAP" from the Protocols box, and use the "New Database..." command to configure a new LDAP directory service. In the form that pops up, enter the IP address or name of the host running OpenLDAP,

and use the "Search Options" tab to specify the base from which to begin searching:

Save it, and you're done!

Now, in order to query the LDAP directory, simply select the server from the list at the bottom right of the control panel,

and enter your query in the search box. The results, if any, will appear in the results window:

You can now use this information for your own purposes - in this case, you would probably send the matching user(s) an email messge, or store the contact information in your address book. Other LDAP clients may allow you to do other, more complex things with the returned information - process it for display on a Web page, add it to a database, or scan it for pattern matches.

Link Zone

And that's about it for the moment. In this two-part article, I first took you through a crash course in LDAP theory, explaining the LDAP information model and showing you how LDAP directory entries are structured. I also explained LDAP's object-oriented characteristics, and showed you the basics of dissecting and using a schema definition when creating directory entries.

With the theory out of the way, I then proceeded to a quick-and-dirty implementation of the OpenLDAP software suite, demonstrating how to compile and install the software, initialize the LDAP directory, and use the supplied client tools to add, edit and remove entries from the LDAP directory. I also showed you the basics of searching the LDAP directory using a variety of different filters and expressions, and guided you through the process of hooking your LDAP-aware mail client to the LDAP directory to simplify searching for user contact information.

Of course, all this is just the tip of the iceberg. LDAP is a vast and complex topic, and there are still many miles to traverse before you can claim to be reasonably adept in the subject. Here are a few links to help in this process:

The LDAP specifications, at http://www.ietf.org/rfc/rfc1777.txt and http://www.ietf.org/rfc/rfc2251.txt

LDAP schemas and object classes, at http://www.ietf.org/rfc/rfc2256.txt

The offical OpenLDAP Web site, at http://www.openldap.org/

An Introduction To LDAP, at http://staff.pisoftware.com/bmarshal/publications/intro_ldap/index.htm

The LDAP HOW-TO, at http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html_single/LDAP-Implementation-HOWTO.html

A list of public LDAP servers by country, at http://www.emailman.com/ldap/public.html

LDAP Central, at http://www.ldapcentral.net/

That's about it from me. I hope you enjoyed this article, and that you learnt something from it. Until next time...be good!

Note: Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!

This article was first published on28 Feb 2003.