Cover V14, i05
may2005.tar

The OpenLDAP Perl Backend

Reinhard E. Voglmaier

OpenLDAP is the reference implementation for the LDAP protocol. The OpenLDAP distribution [1] is not just an LDAP server but also offers a framework containing everything necessary to build an LDAP workbench. And, most importantly for projects with a small budget, it's completely open source and free.

In a previous Sys Admin article ("The OpenLDAP Proxy Server", May 2004 http://www.samag.com/documents/s=9142/sam0405c/), I described how to set up and run the OpenLDAP Proxy Server. In this article, I will present another backend I have found very handy and flexible. Unfortunately, the OpenLDAP project has one weakness -- the documentation is a little lacking. With incomplete documentation, people run into trouble using software, even if it is high quality. I hope to help lower the learning curve a bit so that readers can take full advantage of this high-performing and versatile software without losing too much time.

To begin, I will show you a rough schema of the overall architecture of OpenLDAP. Some of this information was presented in my previous article, but an understanding of the architecture is important for our purposes here. As usual, all examples and code used in this article are available at the Sys Admin Web site: http://www.sysadminmag.com/code/.

The OpenLDAP Architecture

The OpenLDAP server can be considered as consisting of two big blocks: a frontend speaking the LDAP protocol with the clients, and a backend actually providing the data (Figure 1). This is a very rough view; in reality, there are many more components working hand in hand. For our purposes, however, this picture works quite well. I stated that the backend provides the data without telling you from where; this was intentional. The LDAP protocol specifies nothing about how the data has to be held. The OpenLDAP server leaves this up to you, giving you the choice between different backends.

OpenLDAP already ships with a number of different backends. When you compile the OpenLDAP server, you decide which backend(s) you will use. The backend must be compiled into the server in order to be used. You can use more than one backend in parallel. Each backend then serves a different partition of the LDAP database. Figure 2 shows the OpenLDAP server backend with different backends. I assume here that the reader has a basic understanding of LDAP. You can find more on the LDAP protocol in The ABC's of LDAP [2] as well as other books on this subject.

The most frequently used backends are those that store the data in embedded relational databases. The backend uses the API of the embedded RDBMS; therefore, the database software must be installed on the same machine. Frequently used embedded RDBMS are the Berkeley DB distributed as open source software from SleepyCat [3], or GDBM also available as open source software from the Free Software Foundation [4]. There are also other backends, such as the proxy backend I described in my previous article [5]. The proxy backend accesses another LDAP server upon client request and sends the result back to the client.

The Perl backend is very flexible; it allows you to execute Perl code on behalf of client requests. This enables you to get data from nearly every resource you need. You could store the data, for example, on the file system. You could contact a different LDAP server, you could get the data from an Oracle database, or you could mix these three methods. You could use the OpenLDAP server with a custom Perl Backend to easily give your application a standard LDAP interface. The frontend of your custom OpenLDAP server speaks the LDAP protocol; meanwhile, the Perl LDAP backend accesses data in your application. This is a quick and economic way to "LDAP-enable" existing applications without touching the application logic in any way.

The Perl Backend

The LDAP protocol is a message-oriented protocol. A client sends a request to the server and the server answers with a message indicating whether it did what the client wanted and eventually sends the requested data, or the server informs the client of the failure and returns an error code. The LDAP protocol knows the following operations:

  • Interrogation Operations: search, compare
  • Update Operations: add, delete, modifyDN, modify
  • Authentication and Control Operations: bind, unbind, abandon

These operations are mapped by the Perl backend to a Perl object. The OpenLDAP uses further operations for internal purposes, such as the initialization and the configuration of the backend. These operations also have to be implemented in the object; in a moment, we will see how. In reality, all operations are first mapped to C functions; these C functions work as wrappers around calls to Perl. The wrappers use the Perl C API to call Perl code. I will cover this more in the section "Embedding Perl in C". Not all operations are mapped to Perl code. The unbind and abandon operations are actually mapped onto null pointers. Some initialization operations are implemented in C without involving Perl.

The Perl Object must be located into a Perl Module; let's call it PerlBackend.pm. The methods of the object must correspond to the previously mentioned operations. When OpenLDAP starts up, it initializes the Perl backend. During its initialization phase, the Perl backend calls the object constructor followed by a call to the object methods init() to allow further object setup and configure() to read in the configuration parameters from the configuration file. After the Perl backend is loaded, the object methods are ready to use.

Embedding Perl in C

I won't explain here how to embed Perl in C -- that could fill an entire book. But I will give you a little overview. If you want to dig deeper into this subject, take a look at the man pages of Perl [6] or at one of these books: Advanced Perl Programming [7] for embedding Perl in C and Extending and Embedding Perl [8] for a detailed look into the Perl Internals.

Using Perl from C means using its C API. Your C program must make calls to the internal functions and Macros as documented in the Perl manual pages. Once you have written the program, you must compile it using the Perl header files and the Perl library. You need a full Perl installation on your machine to do so. You also need to compile and link your program exactly the same way you compiled and linked Perl itself; otherwise, the compilation will fail or you will get fatal errors at run time. Perl provides a utility that outputs the exact compiler and link loader flags.

All variables passed to Perl are passed via the Perl stack. This means that the variables to be passed to a Perl procedure are pushed onto the stack; meanwhile, the variables you wish to get back are popped from the stack. You have to remember that the stack is used as FIFO, which means you get the values out in the opposite order you put them in. Also note that using the stack is exactly how Perl internally passes variables between subroutines and their callers.

One thing I would like to warn you about is "dynamic loadable C modules". The problem arises if you use modules that are written in C within your Perl object. If you use modules that use C in your embedded Perl code, the interpreter may not know how to load dynamic libraries, and you may run into trouble. Perl as usual, however, helps you out of this situation. The same utility that provides hints about compiler or loader switches can also generate C code to help you bind the needed dynamic modules correctly into your program. We will see the Perl utility a little bit later.

That's all I wanted to tell you about embedding Perl in C; I recommend referring to the Perl man pages if you need more information. The following man pages particularly are of interest: "perlembed", "percall", "perlapi", and "perlguts".

The Module: PerlBackend.pm

Now let's look at an example implementation of the backend Perl object. I will show you an object that does nothing; from this, you can build up the actions you need (see Listing 1). The new method constructs the object. Here you can declare all the data that your object will handle; however, this is more of a help for documenting the object because you can add new data without problems later. You could also give the object a pointer to an empty array:

my $this = {} ;
The init() method allows you to initialize the object -- you may have a further configuration file you can read in or you can get these details from a connection via TCP or a database or whatever you want. The config() method gets its information from the configuration file. Listing 2 shows the part of the configuration file responsible for the Perl backend. The method is called once for every line, so the first call is config("host","authentication1.LdapAbc.org"), the second config("port", 329), and so on.

The bind() method is the one that allows you to authenticate the user. Listing 3 shows an example of how this information is used to authenticate against another LDAP server. You could also connect to a RDBMS or to a Radius server using the user credentials that the bind() method gets as its arguments. The host and port numbers are stored in the object created at the startup of the LDAP server and read from its configuration file. Here you see also how the init() method is used to preload the connection to the LDAP server against which the authentication is made.

Compilation and Configuration of OpenLDAP

Now that I've shown how easy it is to use the Perl backend, let's look at how we get the OpenLDAP server in place. To begin, download the source code from the home page of OpenLDAP [1]. Uncompress it in your working directory. The makefiles must now be built in order to reflect your local environment. You must make sure to get the correct locations of the include files and libraries of your Perl distribution. As I mentioned previously, Perl can help you do this.

Here the utility Perl gives you a hand in getting the locations. The following:

perl -MExtUtils::Embed -ccopts
prints out the compiler options that have been used to compile Perl, and:

perl -MExtUtils::Embed -ldopts
prints out the link loader options. Now you need the instruction of how to add dynamic loadable Perl modules, which themselves use C libraries. Again, Perl itself produces the correct code with the instruction:

perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c
Now, let's look at how to produce makefiles for your environment. This is achieved with the configure utility delivered with the OpenLDAP distribution. This utility is self-explaining using the -h switch, for example:

./configure -h
Here is the configure script I used on my machine to produce the makefiles:

CC=gcc                                             \
./configure --disable-ldbm                         \
                  --disable-bdb                    \
                  --with-threads=no                \
                  --enable-perl                    \
                  --prefix=/usr/local/LdapPerl
This should produce all you need. Check the makefiles created, however, to see whether they contain the correct compiler switches for Perl. It may be that the executable continues to produce core dumps. This is a sign that you should also compile the Perl distribution on your platform and use this freshly compiled Perl environment for your OpenLDAP Perl backend.

Once you have produced the makefiles, you must execute a make depend to resolve all dependencies. After that, launch a make, followed by a make install. Your OpenLDAP distribution will then be installed under the directory configured with the --prefix switch. Now you can begin to configure your OpenLDAP installation.

Edit the slapd.conf file as described in the "Administration Manual" contained in the OpenLDAP distribution. Now, we'll take a look at the options necessary for the Perl backend. You must start the Perl section by stating that Perl is your database for the backend. This is done in line 1. Then you need to define the root of the LDAP tree the Perl backend is serving. You do this with the suffix instruction in line 2. The next two lines define the directory in which the Perl module containing the Perl object lives and define the name of this module. Then you're done. The remaining two lines define parameters needed by the Perl module. Here you can define as many parameters as your Perl object needs. After this, you are ready to write the Perl object and finally use it. Have fun!

Limitations of the Perl Backend and a Brief Look at the New Version

The most critical limitation of this version is that a lot of data on the OpenLDAP server is not available in the Perl object. This data is contained in a number of structures and is transferred from the frontend to the backend. Once arrived in the backend, it can be used by the single backends available in OpenLDAP. Consequently, because the Perl backend has no data describing the actual connection, it cannot handle sessions. It can, however, handle simple connections in a handle-and-forget way.

The OpenLDAP frontend, however, identifies sessions internally by an integer value. Work is actually underway to modify the Perl backend architecture to add the ability to exchange more data structures between C and Perl. Let's take a brief look at the new Perl objects implementing the data structures that should be handled:

Backend -- This structure contains data about the Perl backend itself. It also provides pointers to the functions implemented in Perl. The function pointers used in the C part of the backend are mapped onto the methods of the PerlBackend object.

ConnectionPool -- This structure contains pointers to all connections known by the OpenLDAP server. It also has the capability to implement a connection administration policy. For example, if you decide to drop connections depending on certain conditions or if a connection has been idle for too long, you can close it -- assuming the connection has been left open after an error condition on the client application. Or you can close connections that have used too many resources.

Connection, from C to Perl -- This structure contains data about the actual request, such as information about the time the connection was opened, the time of the last activity, details about the used protocol, such as TCP or UDP, SASL, TLS, authorization and authentication details, and similar data. The structure in C is mapped onto a Perl object. The methods in the Perl object are accessors to the data mapped onto private data in the Perl object.

SlapReply, from Perl to C -- This structure contains the results and all the interesting details traveling back to the client. The structure in C is mapped onto a Perl object. The methods in the Perl object are accessors to the data mapped onto private data in the Perl object.

Trouble also could arise if you use multithreading. For the time being, the Perl backend does not support multithreading, so it is safer to add the --with-threads=no switch to the configure option. These data structures will be mapped directly to Perl objects. Figure 3 shows the new architecture. This method of mapping C structs to Perl is not easy to implement for the C programmer, but once available, it provides an environment that should be familiar to the Perl programmer.

Conclusion

The OpenLDAP server is high-performing, standardized, and economic software. From its first release as University of Michigan (UMich) LDAP server to its actual implementation, it has matured into a product that not only is of academic importance but has proved to possess all the characteristics needed for use in a production environment.

The OpenLDAP implementation however distinguishes itself from all other implementation because it is the only one that offers a family of servers, not just an LDAP server. You get an replication server, a proxy LDAP server, an LDAP server able to access directly a RDBMS, a metadirectory, an LDAP server able to convert the LDAP protocol to Perl, etc., etc., and last but not least, an LDAP server. If this is not enough, you are free to implement a brand new backend to suit whatever needs you have.

In this article, I have shown the actual implementation of the Perl backend. What is the big advantage the Perl backend offers to you? Clearly, you can achieve exactly the same things if you program in C. But can you really? Perl is a programming language that allows rapid prototyping. Once you have a working product, you can decide whether you should port your programs to C. Perl offers a lot of libraries, all of them ready to use for many purposes. It has modules for Oracle, LDAP, various DBMS, Radius, Excel, and much more. Furthermore, you can access Perl via your own application; I don't know any LDAP server implementation that can do this.

An interesting application of the Perl backend could be also one that is CORBA enabled; please let me know if you are playing around with such stuff. Also, if you are interested in the next steps of the Perl backend, check out the home page of the OpenLDAP Perl Backend project [9].

Resources

1. OpenLDAP Home Page -- http://www.openldap.org

2. Voglmaier, Reinhard E. 2003. The ABC's of LDAP. Auerbach Publications.

3. SleepyCat Distribution -- http://www.sleepycat.com

4. Free Software Foundation -- http://www.fsf.org

5. Voglmaier, Reinhard E. "The OpenLDAP Proxy Server," Sys Admin 13(5):18-22.

6. Perl Man Pages -- http://cpan.perl.org

7. Srinivasam, Sriram. 1997. Advanced Perl Programming. O'Reilly & Associates.

8. Jenness, Tim, and Simon Cozens. 2002. Extending and Embedding Perl. Manning.

9. Perl Backend Home Page -- http://sourceforge.com/PerlBackend

Reinhard Voglmaier studied physics at the University of Munich in Germany and graduated from Max Planck Institute for Astrophysics and Extraterrestrial Physics in Munich. After working in the IT department at the German University of the Army in the field of computer architecture, he was employed as a Specialist for Automation in Honeywell and then as a Unix Systems Specialist for performance questions in database/network installations in Siemens Nixdorf. Currently, he is responsible of LDAP Services at GlaxoSmithKline, Italy. He's also the author of The ABC's of LDAP (Auerbach Pub, November 2003). He can be reached at Reinhard.E.Voglmaier@gsk.com.