Using libldap, the LDAP Client Library
Pages: 1, 2
Compiling the Example
To compile the code, you may need to tell gcc where the LDAP
header and library files are.
You will also need to link with the LDAP and BER libraries:
$ gcc -o ldap_add ldap_add.c -I/usr/local/include/ -L/usr/local/lib -lldap -llber
Run the example with:
$ ./ldap_add
If the program compiles and links but does not run, you may need to add
/use/local/lib to your $LD_LIBRARY_PATH, or else pass
-Wl,rpath,/usr/local/lib to the gcc command line.
Thanks to Halvard Furuseth for pointing this out.
Verifying the Add Operation
To verify that the user has been added into the directory, we can use the
ldapsearch utility:
$ ldapsearch -h localhost -x -LL -b 'dc=example,dc=com' '(objectClass=inetOrgPerson)'
dn: cn=Rory Winston,ou=Developers,dc=example,dc=com
cn: Rory Winston
sn: Winston
givenName: Rory
uid: rwinston
title: Internet Developer
objectClass: inetOrgPerson
ou: Development
The -b flag means "search using the base DN
dc=example,dc=com." The -LL flag means "display the
results in LDIF format, without comments." The -x flag signifies
that we will be using simple authentication.
We could also use a graphical LDAP browser, such as the Java-based LDAP Browser shown in Figure 1. LDAP Explorer Tool is another good open source LDAP explorer.

Figure 1. A graphical LDAP Browser
Searching the Directory
There are four basic operations we can invoke on an LDAP directory server:
add, delete, search, and modify. We'll tackle searching next. From our point of
view, the libldap API provides three basic methods of searching:
synchronous, synchronous with timeout, and asynchronous. For simplicity, we'll
use the synchronous search method provided by
ldap_search_s.
The ldap_search_s function is defined as follows:
int ldap_search_s(LDAP* ld, char* base, int scope, char* filter,
char* attrs[], int attrsonly, LDAPMessage** res)
where base is the base DN to search on, and scope
specifies the depth of the search. scope can be LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, or
LDAP_SCOPE_SUBTREE, as we saw with the ldapsearch
utility. The filter paramter is the actual search filter; e.g.,
(objectClass=*). The attrs parameter is a
NULL-terminated array of attribute types to return from any matching entries
that are returned by the search filter. If NULL is specified here, then all
attributes are returned. The attrsonly parameter can be
0 or 1. A value of 1 signifies that we
want to return attribute types only, where a value of 0 means that
we want to return both attribute types and attribute values. The final
parameter is an LDAPMessage**, a pointer to a structure that
contains details about an individual LDAP directory entry. The LDAP library
will allocate memory for these structures itself. It is up to the caller to
free the memory when they are finished.
Actually performing the search is simple:
/* search from this point */
char* base="ou=developers,dc=example,dc=com";
/* return everything */
char* filter = "(objectClass=*)";
if (ldap_search_s(ld, base, LDAP_SCOPE_SUBTREE, filter, NULL, 0, &msg)
!= LDAP_SUCCESS) {
ldap_perror( ld, "ldap_add_s" );
}
Processing LDAP Search Results
A successful search of the directory will return one or more
LDAPMessage objects. The number of LDAPMessage
objects returned can be obtained using the ldap_count_entries
function, as shown below:
int num_entries_returned = ldap_count_entries(ld, msg);
In order to process the returned entries, the standard idiom is to use a
for loop:
for (entry = ldap_first_entry(ld, msg); entry != NULL;
entry = ldap_next_entry(ld, entry)) {
/* process entry here ... */
}
The first entry returned is given by ldap_first_entry(). Each
subsequent entry is given by ldap_next_entry, and we keep looping
until we encounter a NULL entry, which signifies the end of the
result set.
The Distinguished Name (DN) of an entry can be extracted using the
ldap_get_dn() function.
We can extract the attributes from a directory entry using a similar
mechanism that we use to get the entries themselves, using the
ldap_first_attribute and ldap_next_attribute
functions. A sample loop to process all of the attributes for an LDAP entry
resembles:
for( attr = ldap_first_attribute(ld, entry, &ber); attr != NULL;
attr = ldap_next_attribute(ld, entry, ber))
{
/* process attributes here ... */
}
The ldap_first_attribute() and
ldap_next_attribute() functions take an extra parameter: an object
of type BerElement**. A BERElement represents data
encoded using the Basic Encoding Rules (BER), a binary transfer syntax used by LDAP.
The LDAP library uses a BerElement object internally to keep track
of where it is in the attribute list. Even though the LDAP library will
allocate memory for the BerElement, it is up to you to free the
memory using ber_free().
Finally, we need to retrieve the actual attribute values
themselves. This is done by calling the ldap_get_values()
function. This will initialize a NULL-terminated array of values that we can
use, like so:
if ((vals = ldap_get_values(ld, entry, attr)) != NULL) {
for(i = 0; vals[i] != NULL; i++) {
/* process the current value */
printf("%s:%s\n", attr, vals[i]);
}
}
In our trivial example program, we just print out the current attribute name
and value. It is important to remember to clean up the values array when you
are finished, using ldap_value_free(), and also the attribute
itself, using ldap_memfree(). The full sample code can be found
here.
Modifying Entries
The next major operation that we need to cover is the modify operation. The
modify operation is conceptually very similar to the add operation --
define the DN to be modified, define the attribute(s) to be modified, added, or
deleted within the entry, and then call ldap_modify_s() with the
relevant parameters. We'll modify the title attribute. Here are
the relevant code snippets:
/* DN to be modified */
char *user_dn = "cn=Rory Winston, ou=Developers, dc=example, dc=com";
/* Replacement value for title attribute */
char *title_values[] = {"Internet Development Manager", NULL};
LDAPMod title;
LDAPMod *mods[2];
/* Initialize the attribute, specifying 'REPLACE' as the operation */
title.mod_op = LDAP_MOD_REPLACE;
title.mod_type = "title";
title.mod_values = title_values;
/* Fill the attributes array (remember it must be NULL-terminated) */
mods[0] = &title;
mods[1] = NULL;
/* ....initialize connection, etc. */
if (ldap_modify_s(ld, user_dn, mods) != LDAP_SUCCESS) {
ldap_perror( ld, "ldap_modify_s" );
}
See the code for more information.
Deleting Entries
Deleting entries is trivial -- simply provide the DN of the object to
be deleted to the ldap_delete_s() call for a synchronous delete,
or to ldap_delete() for asynchronous operation. The
ldap_delete() function takes two parameters, the LDAP connection
context and a DN. A simple delete is:
LDAP* ld;
char* dn = "cn=Rory Winston,ou=Developers,dc=example,dc=com";
/* ...LDAP connection and initialization */
if (ldap_delete_s(ld, dn) != LDAP_SUCCESS) {
ldap_perror( ld, "ldap_delete" );
}
It's as simple as that. See the accompanying code for a full listing.
Asynchronous Operations
So far we have been using synchronous directory operations, for simplicity.
Now we will examine an asynchronous search operation using
ldap_search(). In libldap, whenever an asynchronous operation is
commenced, the library returns to the caller a cookie, or message ID, that uniquely identifies that operation throughout the client session. This ID can
later be passed to the ldap_result() function to get the status of
the operation and any result(s) that may have been returned. Figure 2 shows
the basic flow:

Figure 2. Asynchronous operation flow
Let's take a simple search operation as an example. The prototype for
ldap_search is as follows:
int ldap_search(LDAP* ld, char* base, int scope,
char* filter, char* attrs[], int attrsonly);
This is very similar to the ldap_search_s() function, except
we don't pass in any LDAPMessage structures to be initialized with
the search results. After all, this is an asynchronous operation, and we will
retrieve the results later. The return value of ldap_search(), if
successful, is a unique ID for the operation that we may use later to retrieve
any results that have been returned.
The ldap_result() function prototype looks like:
int ldap_result(LDAP* ld, int msgid, int all, struct timeval* timeout,
LDAPMessage** result)
msgid is an ID that we have previously retrieved from a call
to an asynchronous operation. result is an
LDAPMessage** parameter that will be allocated by the LDAP
library if there are any results to return, exactly as in
ldap_search_s().
The timeout parameter is a pointer to a struct
timeval. This specifies whether the call to ldap_result()
should block or not, and if it does, how long to block. Passing a
NULL pointer here will tell ldap_result() to block
indefinitely until the specified asynchronous operation identified by
msgid has completed. Otherwise, we can fill in the
timeval structure with values specifying how long we want to wait. This behavior is related to the fact that ldap_result() uses
select() as its underlying asynchronous notification mechanism.
For more details, see man select and man ldap_result.
For our example, we will just pass a NULL pointer for the
timeout parameter, telling ldap_result() to block
until the search operation has completed.
The last parameter is all. This flag only has meaning for
search operations. It allows you to retrieve multiple search entries from a
search query via multiple calls to ldap_result(). Each call to
ldap_result() will retrieve a single entry. In our example, we set
all to 0, signifying that we want ldap_result() to
return the entire result set when the search operation has completed.
Here is a short asynchronous search example:
/*
*
* Perform the asynchronous search
* ldap_search() returns -1 if there is an error, otherwise the msgid
*
*/
if ((msgid = ldap_search(ld, base, LDAP_SCOPE_SUBTREE, filter, NULL, 0)) == -1)
{
ldap_perror( ld, "ldap_search" );
}
/* ... some time later */
result = ldap_result(ld, msgid, 1, NULL, &msg);
switch(result)
{
case(-1):
ldap_perror(ld, "ldap_result");
break;
case(0):
printf("Timeout exceeded in ldap_result()");
break;
case(LDAP_RES_SEARCH_RESULT):
printf("Search result returned\n");
break;
default:
printf("result : %x\n", result);
break;
}
In case of an error, ldap_result() returns -1. If we have
exceeded the timeout specified in the timeval parameter,
ldap_result() returns 0. In our case, we are looking for a return
result of LDAP_RES_SEARCH_RESULT, which means that our search
operation has completed. The results are contained in the msg
parameter.
Wrapping Up
This brings us to the end of our quick introduction to libldap.
As you can see, once you learn a few basic operations, it is quite simple to
use. Our examples were written in C, but there are LDAP client libraries for
many languages, including Java and Perl. Using C gives us power and speed, at
the cost of manual memory management and added complexity.
Acknowledgments
Thanks to Hallvard Furuseth for his invaluable help and editorial and technical comments while reviewing this article.
Related Links
- The OpenLDAP homepage
- LDAP v3 Protocol
- Java-based LDAP Browser
- LDAP Explorer
- LDAP C API Specification
Rory Winston is a solutions architect with Checkfree i-Solutions, one of the world's leading EBPP providers. He specializes in J2EE technologies on Linux and BSD platforms .
Return to the Linux DevCenter.