Getting Started with LDAP

This article was much more difficult than I expected. I initially began with an
in-depth explanation of LDAP as a protocol, but realized that the real
goal here is to be able to work with LDAP right now, not after reading
50
pages of abstract explanations.

So with that goal in mind,
we're going to start working with LDAP in a semi-real work
environment.
Specifically, we're going to set up a basic LDAP directory to store
Unix user accounts, along with a script to pull those accounts
to a Unix system -- that is one of the things for which you can and
should
use LDAP. This will also be useful to demonstrate that even
if
your version of Unix
can't authenticate directly off LDAP, you can still store your users
in LDAP and get all the benefits that come with that.

The goal

As mentioned in my previous article, LDAP was developed as a method of
consolidating access, authentication, and authorization (AAA, or
Triple-A)
information. By itself, this is useful, because you are maintaining
all
of the information in one place rather than many. However, you could
have
accomplished the same thing using any old database. What makes LDAP
especially suited to store your AAA information is that all
LDAP operations take place within the context of the AAA information,
rather than forcing the application to supply or interpret the
context. Operations fail or succeed with no need for the application
to understand the rules involved.

If you attempted to put all of the same AAA information into a database
(which would be somewhat difficult because you would have to define all the standards for storage of the information, which LDAP has already
done
for you), then every one of your applications would have to parse that
information and take it into account for each AAA operation. If you
use LDAP, however, there are already methods for storage of the AAA
information, although they are not yet RFC-defined, and the LDAP
server rather than the application applies all of the AAA rules.
This not only makes the lives of the application
developers easier, it also eliminates the chance of rogue
applications or users bypassing the AAA rules to directly access
and modify the directory contents, except of course through traditional
security compromises.

So, our goal here is to build such an AAA infrastructure, with LDAP
at its core. The majority of our authentication information already
exists, in the form of user accounts, and for the purposes of this
article, we will assume that those user accounts are on Unix machines.
Our
goal, then, is to put that user account information into
LDAP, manage it entirely from LDAP, and then use it as the root of
AAA operations in other applications. Once we have the authentication
information in place, we must then add the access and authorization
information.

This article will deal with the first task, replacing the standard
methods of maintaining Unix accounts with methods using LDAP.
Later,
we will hopefully provide examples of web-based maintenance of our LDAP
data and some applications which might take further advantage of this
newly centralized data.

The tools

As mentioned in the previous article, nearly every modern language has
an LDAP API; as such, there is a near infinite availability of tools.
Fortunately, because of the simplicity of the protocol, most of the
APIs
work quite similarly. To get started immediately, we're going
to start with the command-line tools (of which there are multiple
versions),
because they're straightforward and ubiquitous.

There are only three basic types of LDAP operations, and each basic
type
has a few subtypes: interrogation (search, compare),
updating (add, modify, rename, delete), and binding/control (bind,
unbind,
abandon). Notice there is no "read" operation; if you want to read
an entry, you use a search operation to retrieve it.

To move our Unix accounts to LDAP, we must convert them into
a form the LDAP server can understand.
Because of the simplicity of conversion from passwd
file to LDIF (see below), we will leave the conversion as an exercise
for the reader. Instead, we will begin with an already converted
user account and add it to LDAP:

This operation uses two of the three basic LDAP operations, an update
operation and a binding operation, along with an implicit unbind.
We must bind as a privileged user in order to modify the directory
(this example uses a special user which all iPlanet
Directory Servers have).

We then
provide the data for the entry we want to add, which is in LDAP
Date Interchange Format (LDIF), the standard text format for LDAP
data. Notice that each line has an attribute name, then a colon and a
space,
and the value of the attribute. It certainly makes sense that LDAP
search
operations would return this format, so that it could be immediately
fed
back into an LDAP server, but some tools -- including those shipped with
Solaris -- do not print standard LDIF, stupidly requiring that we
convert.

Components of an LDAP entry

Now that we see what an LDAP entry looks like, let's go through the
various pieces to understand it.

Distinguished Names

The first line is
what is called the "Distinguished Name", or dn. Because this is
how we tell
the server what object we are working with, the dn line must always be
specified first.

The dn is how an entry
is uniquely referred to within an LDAP server, similar to an absolute
path name or a fully qualified domain name. Notice that the dn
is represented similarly to DNS names, with the most specific
information
first, as opposed to path names, which have the least specific information
first. Contrary to how it looks, the root of this LDAP tree, also
called the "naming context," is dc=domain,dc=com, not
dc=com.

There are no
requirements about what you name the root of your LDAP tree, but there
are two standards: either the standard I've followed here, which breaks
a domain into its various domain components, or one where an
organization
is referred to at the top level (for example, o=domain.com). Which one
you should follow will be answered differently by everyone you ask, and
why you should follow it will also be answered differently. I have
chosen
to follow the domain component standard because it seems to be more
popular these days -- Sun and Microsoft have both begun
recommending/requiring
it. Pick the
one that seems easiest and fits best with how you plan on using the
data.

The rest of the dn consists of a
branch in the tree, ou=People, and an attribute-value pair
uniquely identifying this entry, uid=luke. This attribute-value
pair, when
separate from the dn,
is called the "Relative Distinguished Name," or rdn, and it
uniquely
identifies this object at this level of the tree.

Because path names and DNS names always refer to the name of the object
in question,
all they need to specify is that value, but LDAP can use
any attribute to create an rdn, and as a result both the attribute and
its
value must be specified.
The dn for this entry could also be
cn=Luke A. Kanies,ou=People,dc=domain,dc=com, as long as there
is no one else in ou=People with "Luke A. Kanies" as a value of
cn.

We choose uid here, though, because we will always guarantee
that
it is unique for each entry -- anything else would not function correctly
on our Unix systems, and conveniently iPlanet's Directory Server can
be set up to disallow duplicate uid values (or any other
attribute).
Apparently Microsoft's Active Directory is requiring that cn be
used
to create the rdn, which is annoying because the value of cn is
almost guaranteed not to be unique, as it is the user's full name.
Thanks!

Object classes

Next in our entry are two objectclass attributes. These
attributes
define what type of object the entry is. However, the concept of an
object
in LDAP is extremely simple: It merely defines what attributes an entry
must have and what attributes an entry is allowed to have. All
object classes
inherit requirements from their parent object classes and add their own.
The objectclass attribute isn't a special attribute, though -- in
all
LDAP operations it is treated exactly like other LDAP attributes, but
modifying object classes does determine whether the object
will be acceptable to the server after the operation.

The above definition of an LDAP object is important, because it is
difficult
to convince yourself how simple this definition really is. Again, an
object
merely defines what attributes must or can be stored with an entry. I
can
create an object which has both the posixAccount object class and
a
printer object class; this combination may seem quite contradictory
to you
and
me, but to LDAP, the data is just data, and has no meaning on its own.
One
of the things that makes LDAP great is that the attributes mean
something
to humans, but it is up to the humans working with the data to retain
that
meaning by naming object classes and attributes intelligently and then
creating objects that actually make sense. This is more difficult
than
it sounds, and deciding how all of this will be done is the first step
to using LDAP. Fortunately, most of the object classes you will need
are part of the LDAP specification (although posixAccount is
part of a later RFC), so at least initially you won't have to worry too
much about that.

Note that you can't just willy-nilly make up object classes and add them
to an object; the
server must have each object class defined for it, in what is called its
schema. If you try to add an entry with an undefined
object class,
you will get a schema violation and the operation will fail. How
you define an object class for the server varies from server to server,
but
most of them document it quite well.

In this case,
we have declared that our entry will be of types top and
posixAccount. The top object class
merely requires that the objectclass attribute be present, and
by definition it is the parent object class for
all other LDAP object classes. Given this fact, and the fact that
objects
always list the object classes, they are an instance of along with all
of their parent object classes, every object in an LDAP database will
list top as an object class. The posixAccount object class
is defined in an RFC by Luke Howard, who has done a significant amount
of the work involved in allowing LDAP to store Unix accounts, and
provides
a means to store all of the information from a passwd file in LDAP.