Introduction to OpenLDAP

Ken Hughes - 09/10/02

Contents


Synopsis

This presentaion is designed to give a general overview of the LDAP protocol, as well as how to install and configure OpenLDAP, an open source implementation of LDAP. We will also discuss extending OpenLDAP through the use of various schemas, and end with a short section on authenticating users through LDAP.

All configuration examples and data templates will be included for your own use.


LDAP Overview

LDAP stands for Lightweight Directory Access Protocol and is a widely used tool for providing directory access to clients. Common implementations can be seen in directory services such as Yahoo!, Netscape, Bigfoot, and many others. The RFC for LDAP v3 is RFC 2251.

LDAP is modeled after the larger X.500 DAP and was originally designed to be a gateway to X.500 DAP servers. X.500 is an enormous topic in itself and won't be covered here beyond what I've already said. LDAP is client-server in nature. An LDAP client (e.g. Netscape) contacts an LDAP server with a query and the server responds with a result set based on the query.

Information stored in an LDAP entry can include names, e-mail addresses, phone numbers, and even encrypted passwords for authentication. Entries are defined within special files called schemas. There are many schemas available for use in OpenLDAP, and they can even be extended with custom attribute types and object classes.

Why would you want to run an LDAP server? Many businesses depend on having managed, centralized directories of employees, customers, and vendors. If you are responsible for managing these resources, OpenLDAP is a powerful and inexpensive way to achieve this. It is also a popular way to incorporate user authentication. In this day and age of home networking, the ease of setting up an LDAP server makes creating centralized e-mail directories at home a cinch. So the question becomes, why wouldn't you want to run an LDAP server? ;^)

OpenLDAP components

A typical OpenLDAP installation consists of the following:

slapd - The main LDAP server binary
slurpd - The binary for replication to primary LDAP servers
slapd.conf - The config file for the slapd daemon
ldap.conf - Global defaults for LDAP clients

There are typically other config files for things like search preferences and filters, but those are beyond the scope of this paper (i.e. I don't really understand them fully myself so I'm not qualified to discourse on them ;^).

The only components you need to get your LDAP server running are slapd, ldap.conf, and slapd.conf. This will not give you a very functional LDAP server, but it will give you a running LDAP server. To really get some use out of your server you will need schemas and a list of entries for your server, which we'll cover in the next two sections. First you will need to install OpenLDAP either off of your Linux CD's or download and compile it from ftp://ftp.openldap.org/pub/openldap/openldap-release. As of this writing version 2.1.6 is current. If you choose to download and compile the current release, you will need a C compiler and POSIX REGEX software. OpenSSL and Cyrus SASL are recommended but not required. To compile, simply extract the openldap-2.1.6.tgz file into a temporary directory and run:

./configure ; make

Depending on the processor speed compiling may take a few minutes, but not an inordinate amount of time. Once things are done you can either type 'make install' to finish the install, or 'make test' to test the compile. I strongly recommend the latter. It is rather time consuming but it will test all aspects of the program and alert you if things aren't working properly. After the test complete successfully type 'make install'. I tested the previous compile and install on a SuSE 8.0 installation without errors.

Getting OpenLDAP running

LDAP defines the data it serves through the use of attributes. These attributes are defined in special files called schemas. The first step in configuring OpenLDAP is to "include", or refer to a special schema called core.schema. The primary configuration file for slapd is slapd.conf, which is located in either /etc/openldap (if you installed from a distro CD), or /usr/local/etc/openldap (if you installed from source). Open this file in your favorite editor and make sure the following lines are there:

 include /etc/openldap/schema/core.schema
 include /etc/openldap/schema/cosine.schema
 include /etc/openldap/schema/inetorgperson.schema

You'll notice that we added two additional schemas, cosine and inetorgperson. These schemas are not required but are very useful and therefore recommended. You can optionally add the following entries:

 pidfile /var/run/slapd.pid # file that hold the process id of the slapd daemon
 argsfile /var/run/slapd.args # file that hold the command line arguments to the slapd daemon

Again the above two entries are not required, but some find them helpful. The next step is the database definitions. For this example we will use foo.org and FooBar Inc. for our databases. Make the following entries in slapd.conf:

 database     ldbm
 suffix     "dc=foo,dc=org"
 suffix     "o=FooBar Inc.,c=US"

First we told slapd that we want the database to be in ldbm format, and then we define foo.org as a valid suffix through the use of the attribute type dc, which is short for "domain component". Lastly we also define the organizationName (o) as FooBar Inc. and the countryName (c) as US. Next we need to specify a rootdn for the server. The rootdn is basically the "superuser" of the server, and is not subject to access control or other restrictions. The default is an empty rootdn (no root access) which mean you must grant access to populate the database and create entries. While this is the more secure method, we will declare a rootdn to make populating the database easier. Add the following lines to slapd.conf:

 rootdn     "cn=manager,dc=foo,dc=org"
 rootpw     insecure

Here we've set the rootdn to "manager" and granted a password of "insecure". Hopefully you can see the inherent problems with this method. Your only method of preventing someone from taking over your ldap server is to make slapd.conf non-world readable. This is a Bad Thing in its classic form. A better method would be to at least create a password hash:

 rootpw     {SHA}ojN+jsbELZbRJeRb0qj9+MMjPUs=

If you don't know how to create password hashes, I'll provide links at the end of this paper where you can download a handy script to do this. If you install openldap from sources you will have a utility called slappasswd that will create password hashes too.

Our last step is to tell slapd where to store the database and create an index:

 directory     /var/lib/ldap
 index     objectClass     eq

This tells slapd to keep the database files in /var/lib/ldap and create an equality index on the attribute type "objectClass". There are other types of indexes you can create, but which are beyond the scope of this paper.

You have now completed the initial configuration of your openldap server! Save slapd.conf and start the server:

 /usr/lib/openldap/slapd    # This is for SuSE Linux
 /usr/local/etc/libexec/slapd     # This is typically for from-source installs
 rcldap start # Many distributions will provide their own rc-style script

You should now have a running slapd process listening for connections on port 389, the default port for LDAP. Now we'll move on to entering some useful data into the database...

Entering data

Now that the server is running, lets give it some data for us to query against. You will remember how we set the rootdn to "manager" in slapd.conf so the first entry we should make is one for "manager". Create a file called "foobar_db.ldif" and make the following entries:

 dn: dc=foo,dc=org
 objectclass: organization
 o: foo.org
 description: The Foo Organization

 dn: cn=manager,dc=foo,dc=org
 objectclass: organizationalRole
 cn: manager
 description: Directory Manager

This creates an organization entry for "foo.org" and adds an entry for "manager". Analyzing the entries, the first line specifies a "distinguished name" (dn:) with a "domain component" (dc) of foo and org. The second line specifies that this has an objectclass of "organization". The third line declares the organization (o) name is "foo.org". Line four is an optional description. The second entry is similar with the exception of a "cn:" instead of an "o:". "cn:" is short for "canonical name" and is used for person and role entries.

Now to get this information into the ldap server, we use the 'ldapadd' command. 'ldapadd' is actually just a hard link to 'ldapmodify -a', but it is usually there by default so we'll use it:

  /usr/bin/ldapadd -f /etc/openldap/foobar_db.ldif -x -D "cn=manager,dc=foo,dc=org" -w insecure

To break down the above command, we tell 'ldapadd' to read entries from the file (-f) foobar_db.ldif, use simple authenication (-x), bind to the server as "manager" (-D "....") with the password (-w) of "insecure". For the security conscious you can alternatively use -W instead of -w to have ldapadd prompt for the password. This will keep your ldap password out of your .history file. ;)

We can create further entries following the above examples:

 dn: cn=Ken Hughes,dc=foo,dc=org
 cn: Ken
 surname: Hughes
 objectclass: organizationalperson

Now we change things a bit. This is a normal entry with no special role (like the "manager") so we've made them a simple "organizationalperson". Lets add a little more information to this entry:

 dn: cn=Ken Hughes,dc=foo,dc=org
 cn: Ken Hughes
 givenname: Ken
 surname: Hughes
 objectclass: inetorgperson
 mail: ken@foo.org

Notice we changed this entry to "inetorgperson"? The reason for this is that the attributes "givenname" and "mail" are referenced in the "inetorgperson" schema and therefore are not allowed under the default "core" schema. Feel free to browse through the various schemas and experiment with different attributes.

Now that we've made some entries in the server, let's query the server to see our listing. Type the following to get a listing of all entries:

  ldapsearch -x -b 'dc=foo,dc=org' '(objectclass=*)'

Again we use simple authentication (-x) and specify a searchbase (-b) of all entries (objectclass=*) in foo.org (dc=foo,dc=org).

That should be enough to get you started on populating your LDAP server. There is a wealth of information on the web related to LDAP on Linux, but I have included a few links at the bottom of this paper that I personally found helpful. Remember that Google is Your Friend. Next I'll touch briefly on authenticating Linux to an LDAP server.

LDAP authentication

Configuring Linux to authenticate to a central LDAP server is fairly straightforward, although there are a few packages you will need to make sure you have, and also edit several config files. You will need to make sure you have installed pam_ldap and nss_ldap. If your Linux distribution does not supply these packages, you can get them here:

 http://www.padl.com/download/pam_ldap.tgz
 http://www.padl.com/download/nss_ldap.tgz

There are a handful of helpful migration tools also available here:

 http://www.padl.com/download/MigrationTools.tgz

These tools will do things like extract your user account information from /etc/passwd into ldap-readable ldif format. You can then import those entries using the same methods detailed previously.

Once you have these packages installed and your passwords entered in your ldap server, you will need to make sure the following entries are added to your slapd.conf:

 defaultaccess read

 access to attr=userpassword
  by self write
  by * read

 access to *
  by self write
  by dn=".+" read
  by * read

Then edit /etc/pam.d/login, and /etc/pam.d/ssh (for remote access authentication) and add the following:

 #%PAM-1.0
 auth   sufficient   /lib/security/pam_ldap.so
 account   sufficient   /lib/security/pam_ldap.so
 password   sufficient   /lib/security/pam_ldap.so
 session   sufficient   /lib/security/pam_ldap.so

Next edit /etc/ldap.conf and put the following in:

 BASE dc=foo,dc=org # Change this to reflect your ldap server settings
 HOST ldap # Change this to the hostname of your ldap server
 pam_crypt local # This tells pam to encrypt the password before sending

Lastly you will need to edit /etc/nsswitch and make the following changes:

 passwd: ldap files nis
 shadow: ldap files nis
 group: ldap files nis

As a side note, many major Linux distributions provide mechanisms to automatically make the above changes to enable LDAP authentication. If you are using one of these distributions (i.e. SuSE) then you may wish to use these mechanisms instead of editing the files manually.

You should now have the mAd SkiLL7 required to install, configure, and run a fully functional OpenLDAP server!


Links

http://www.openldap.org/doc/admin21/
http://staff.pisoftware.com/bmarshal/publications/system_auth/sage-au/system_auth.html
http://www.tldp.org/HOWTO/LDAP-HOWTO/
http://www.skills-1st.co.uk/papers/security-with-ldap-jan-2002/security-with-ldap.html
http://www.direct-to-linux.com/TUTORIALS/LinuxTutorialLDAP.html

Scripts

hashit.pl - This script requires the perl modules "Digest::SHA1" and "Digest::MD5" to create those hashes

--begin hashit.pl-- #!/usr/bin/perl -I /usr/lib/perl5/site_perl/5.005/i386-linux

# Perl code to create and print SHA1 and MD5 hashes of passwords
# shamelessly stolen from the openLDAP Faq-O-Matic
# written 19-Jul-01 by Ed Truitt
# Updated 12-Oct-01 to include a {crypt} version of the password

$theGoodWord = $ARGV[0];
chomp($theGoodWord);

# First, print the clear text version

print "\n" ;
print "The Good Word is ==> $theGoodWord \n " ;
print "\n" ;

# Now generate and print the SHA1 hash

use Digest::SHA1;
use MIME::Base64;
$ctx = Digest::SHA1->new;
$ctx->add($theGoodWord);
$hashedSHAPasswd = '{SHA}' . encode_base64($ctx->digest,'');
print 'userPassword: ' . $hashedSHAPasswd . "\n";

# Now generate and print the MD5 hash

use Digest::MD5;
use MIME::Base64;
$ctx = Digest::MD5->new;
$ctx->add($theGoodWord);
$hashedMD5Passwd = '{MD5}' . encode_base64($ctx->digest,'');
print 'userPassword: ' . $hashedMD5Passwd . "\n";

# Now generate and print the CRYPT version
# first we need to generate the salt

@chars = ("A" .. "Z", "a" .. "z", 0 .. 9, qw(. /) );
$salt = join("", @chars[ map { rand @chars} ( 1 .. 4) ]);

# now to generate the password itself

$cryptPasswd = '{crypt}' . crypt($theGoodWord,$salt);
print 'userPassword: ' . $cryptPasswd . "\n";
print "\n";

--end hashit.pl--

hashit.sh - Simple 5 liner to make generating multiple passwords easy.

--begin hashit.sh--

#!/bin/sh
for x in $@
     do
     ./hashit.pl ${x}
done

--end hashit.sh--

Don't forget to chmod +x hashit.pl hashit.sh. Stick these two scripts in the same directory and run:

 ./hashit.sh password1 password2 password3 ...

Sample configuration file

--begin slapd.conf--

include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema

pidfile /var/run/slapd.pid
argsfile /var/run/slapd.args

database     ldbm
suffix     "dc=foo,dc=org"
suffix     "o=FooBar Inc.,c=US"
rootdn     "cn=manager,dc=foo,dc=org"
rootpw     insecure
directory     /var/lib/ldap
index     objectClass     eq

--end slapd.conf--