May 19, 2005

Using LDAP for mail contacts

Author: Nathan Willis

Chances are that your email program supports LDAP among its address book options. This article will show you how to set up a basic LDAP directory for use as an address book server in your home or small office.Many corporate users use company-wide LDAP directories to free them from manually synchronizing and updating their contacts -- a convenience that even a two-PC household could benefit from. We will start by installing and configuring OpenLDAP, the free, open-source LDAP server. It is included with virtually all full-sized Linux distributions, but if you must you can download it from

A cautionary note: technically speaking, what we call an "LDAP server" is actually an X.500 directory server, but one that speaks LDAP: the Lightweight Directory Access Protocol. Defined in RFC 2251, LDAP is a simplified version of the older DAP (I'll let you figure out what that one stands for on your own; thank me later) used for talking to X.500 gateway systems. But DAP did not run over TCP/IP and used significantly higher overhead. An LDAP server can do much more than maintain contact info directories, notably network authentication but a host of other services too.

Both for this reason and due to its pre-TCP heritage, you will come face to face with extraneous configuration options and documentation geared at enterprise-level IT shops. Feel free to stop and exclaim "if this is the lightweight protocol, I'd hate to see what it replaced" as often as necessary. The syntax is not complicated, but it is highly abbreviated and not designed to be self-explanatory. But that's what this guide is for: trimming down the distractions and getting the job done.

OpenLDAP consists of two daemons: slapd (the stand-alone LDAP daemon) and slurpd (the stand-alone LDAP update replication daemon). You only need slapd; slurpd is used for coordinating multiple LDAP servers so they cooperate in a hierarchy, much like DNS servers.

Configuring slapd for fun and profit

Slapd reads in parameters from a config file, by default at /etc/openldap/slapd.conf. We will make minimal changes to the default supplied by OpenLDAP. The first few non-comment lines are includes pointing to the .schema files that dictate what type of information our directory will hold. The Fedora package I use includes core, COSINE, inetOrgPerson and NIS schema. I commented out the NIS schema since I know I'm not interested in it, but that is optional. inetOrgPerson and COSINE define the interesting address book data that we will use, and core contains fundamental definitions, so all three of these must stay.

And they have inheritance: inetOrgPerson inherits from COSINE, which inherits from core. If you want to define additional info to track in your address book, you can add a schema that inherits from inetOrgPerson and adds the interesting parts. Since I use Mozilla Thunderbird as my email client, I downloaded a copy of the
MozillaOrgPerson schema
and put it in the LDAP directory with the others. There are also schema defined for Outlook and other popular email clients, though they are all just icing on the cake. inetOrgPerson gives you the basic name, address, email and phone number information.

Skip to the end of the slapd.conf file, and you will find a section labeled database definitions. Here is my example definition:

database bdb

suffix "dc=glyphography,dc=com"

rootdn "cn=AddressManager,dc=glyphography,dc=com"

rootpw NotOnYourLife


directory /var/lib/ldap/local

For database you have two basic choices: ldbm and bdb -- ldbm stores its data in LDIF (LDAP Data Interchange Format) files, bdb in binary files. Using bdb is faster.

The next line is suffix and is our first encounter with some of that abbreviation-happy LDAP syntax. Here are a few of the common abbreviations we will see again and again:

  • dn: distinguished name, the unique identifier of an entry in the database. No duplicates are allowed, so a well-planned naming scheme is required.
  • cn: common name, the regular name of a person or entry -- it's okay for two people to have the same common name (since some people do), but not the same distinguished name.
  • o: organization, be it a company name or something generic like localnetwork
  • ou: organizational unit, for a subcategory of an organization, be it family, accounting department or whatever.
  • dc: domain component, part of an Internet domain name -- in the example, has two components.

The suffix you declare here denotes the scope of this database and is where all searches. We can define multiple databases in the slapd.conf file, but they must have different suffixes. It can be a compound of two or more parts, like the example. It can be anything syntactically valid, just remember what it is! Some people use their organization as their suffix, such as "o=newsforge" or "o=MyHouse" -- the convention of using your domain name (even though it must be split up as two DC's) comes from the hierarchical origins of LDAP. If I ever decided to link my LDAP directory up with another one, they might have chosen "o=MyDirectory" but they won't have chosen my domain name. Plus, if I ever need to run a second OpenLDAP server I can define it as a sub-domain such as "dc=maui,dc=glyphography,dc=com".

The rootdn line defines the DN of the superuser account for this database. Here it is composed of a descriptive CN and the suffix of the database itself -- it is unique like all good DNs, but clearly descriptive as well. The rootpw sets a password for simple authentication.

Last but not least, the directory line specifies where the actual bdb database will be created and stored. I chose to create a new directory, /var/lib/ldap/local, instead of the default /var/lib/ldap, just to make testing less hazardous.

With slapd.conf all configured and chomping at the bit, launch it with /etc/rc.d/init.d/ldap start. You are ready to pull a few addresses out of your existing, old-fashioned address book and add them to the LDAP directory.

LDIF Autopsy

In most email clients, you can export your existing address book as an LDIF file. Almost any such client export is going to require some tweaking, so whether it is faster to create a simple LDIF file or to fix the automatically-generated one depends on how many entries you have. I for one steadfastly refuse to do things by hand even when it is far easier, and as a result I've spent some time learning the ins and outs of Mozilla's LDIF export. As with the schema, my examples come from Thunderbird, but similar steps would work for other clients.

Take a deep breath and open the newly-minted LDIF file in your editor of choice. Each address book entry has been dumped into a block of text offset from its neighbors by blank lines. They look something like this:

dn: cn=Tom Delay,

objectclass: top

objectclass: person

objectclass: organizationalPerson

objectclass: inetOrgPerson

objectclass: mozillaAbPersonObsolete

givenName: Tom

sn: Delay

cn: Tom Delay

xmozillanickname: tommy


modifytimestamp: 0Z

The first line is the DN of the entry, which as you recall must serve as its unique key in the database. Mozilla's LDIF export creates DN's by slapping together the CN and MAIL fields from elsewhere in the entry -- presumably reasoning that any two people could share an email address or coincidentally have the same name, but not both. Regrettably OpenLDAP runs afoul of these DNs. The reason it technical, but suffice it to say that concatenating random things is not the proper way to build DNs. At the very simplest, we can substitute our database's suffix from slapd.conf and all will be well.

So to make these entries readable to OpenLDAP and thus useful for our address book, we'll have to scrub every offending DN ... and a couple of other stray fields while we're at it. Say goodbye to the objectclass: mozillaAbPersonObsolete line, the xmozillanickname line and the modifytimestamp: 0Z as well; the former two are non-standard and undefined and the latter is supposed to be automatically-generated by the database -- importing one is just begging for trouble.

The other lines in each stanza hold either internal-structure LDAP fields (like objectclass) or our contact's data. Of particular note is the objectclass: inetOrgPerson line, which points OpenLDAP to the inetOrg schema we loaded in slapd.conf. Earlier I mentioned that I used the mozillaOrgPerson schema and loaded it in slapd.conf; here is where can we tell OpenLDAP to reference it.

Fix the LDIF file with a simple Perl script, like the example listed below. There are a variety of such scripts available on the Internet since Mozilla's LDIF problems are well-studied. This one strips out the useless modifytimestamp and mozillanickname entries, substitutes a domain-based suffix in each DN, and replaces mozillaAbPersonObsolete with mozillaOrgPerson. If you don't want to bother with mozillaOrgPerson, you should remove the mozillaAbPersonObsolete altogether.

#!/usr/bin/perl -pi

#; a space-age script for polishing up Mozilla LDIF export for OpenLDAP use




s/objectclass: mozillaAbPersonObsolete/objectclass: mozillaOrgPerson/;

The modern world is filled with people that know more about Perl than I do, and there is certainly more tweaking that could be done, but for me this was sufficient. Execute the script with mycontacts.ldif. It is now ready to load into the database. First, however, we will make another LDIF file that contains no contact info but just our directory definitions:

dn: dc=glyphography,dc=com

objectclass: top

objectclass: dcObject

objectclass: organization

dc: glyphography

o: Glyphography


dn: ou=personal,dc=glyphography,dc=com

objectclass: top

objectclass: organizationalUnit

ou: personal

description: Personal Addressbook

All that these entries do is define and describe the organization referenced in the DC elements of our slapd.conf file. Notice that the DN is the same as the suffix we assigned in slapd.conf -- it is what identifies our database. You could put this info at the beginning of the mycontacts.ldif file instead, but separating it makes it easier to update the contacts. Note also that I define an OU for personal contacts -- that is just in case I want to add a second OU for work contacts and maintain them separately.

Load 'em up

So now you have a running slapd server and a pair of LDIF files: one holding your contact information and one defining your directory. From a command line, load the directory definition file first:

ldapadd -xv -D 'cn=AddressManager,dc=glyphography,dc=com" -f directory_def.ldif -W

The x flag tells the server to use simple (password) authentication and the v flag generates verbose output. After the D flag we specify the DN under which we want to make a connection -- the same root DN that we defined in slapd.conf. The W flag prompts us for the password, also from slapd.conf.

If you see error messages, check your syntax and especially double-check the formats of your DN's, as misspelled words are the biggest pitfall. When you are ready to proceed, load the contact information file in the same manner:

ldapadd -xv -D 'cn=AddressManager,dc=glyphography,dc=com" -f mycontacts.ldif -W

Again, if all goes according to plan, you will see output reporting to you as each of your contacts is added. The ldapadd command will stop on any errors, so just look at the line of output that generated the error and fix the entry in your LDIF file. It's a good idea to stop slapd, delete the database files, and restart slapd when you have to do this -- I find it makes tracking down problematic LDIF entries easier.

Now you can connect to your OpenLDAP server with your email client! In Thunderbird, open the Address Book and select New LDAP Directory from the File menu. In the hostname field, type localhost, and in the Base DN field give the suffix you chose in slapd.conf (in my example, dc=glyphography,dc=com).

LDAP is based on search queries; by default you don't see any entries in the Address Book pane in Thunderbird. To find someone, just start typing in the search box, and your results will pop up in the list below. To see everyone (or at least everyone with an email address), type in an @ sign. There is an Offline tab suggesting that you can download a copy of the entire directory, but this has never worked in any Mozilla client, so just ignore it.

This next part may come as a surprise to people, but Mozilla Thunderbird does not let you edit LDAP entries directly. There is a feature request in Bugzilla to add this functionality, but since LDAP has not historically been a "home user" feature, it has been slow coming. Since you are running your OpenLDAP server locally, however, you can edit your entries with another tool such as Luma (a nice and straightforward GUI program) or phpLDAPadmin if you happen to be running a local web server too.

You can help advance Mozilla's LDAP functionality by cast votes for the appropriate Bugzilla entries. There are three that relate to what I have described here: 86405 is the LDAP editing feature and 231965 is the offline directory-download bug. 116692 is the mozillaOrgPerson schema, which is not a bug, but more of a feature tracker -- work is ongoing, and going well. Having a well-defined LDAP schema is important to using Mozilla in enterprise environments.

Enjoy your LDAP bliss, and don't be shy about lording it over friends and coworkers still struggling with their cluttered, unsynchronized contacts. Assuming they have your email address, they can always ask you for help.