Managing an LDAP Database with Perl and Apache
Reinhard Erich Voglmaier
One of the most tedious activities in systems administration is password management. This means not only to create, modify, and delete accounts, but also to handle requests from users who forgot their passwords and, even more time consuming, to explain what password has to be used on which system under what application. In Sys Admin, June 2000, Lizmari Brignoni described the basics behind the LDAP protocol (Lightweight Directory Access Protocol). This protocol is widely used for user authentication. It allows one application to access information maintained by another one. A good example is the use of data stored in an email system by the company telephone book made accessible via a Web application.
The strength of the LDAP protocol is that it is a standard protocol implemented by a large number of big players in the software arena, such as: Netscape, Oracle, Sun, and Microsoft. Furthermore, commercial solutions are available as well as free source code implementations such as OpenLDAP, which is based on the University of Michigan Implementation.
While the Brignoni article described the LDAP protocol and basic design questions, I will examine application development using data from LDAP. Specifically, I will discuss a self-service application that permits users to subscribe themselves to an application. Once they are subscribed, they also can modify their data such as phone number, address, and password. In this application, I use the following software: Apache Web server 1.3.12 (required > 1.3) with mod_aut_ldap, OpenLDAP 1.2.8, and Perl 5.6 (required > 5.xxxx). In this article, I will explain the use of the Perl and Apache LDAP APIs but I'll only scratch the surface. For more information, see the documentation that comes with the software distribution for the Perl and for the Apache API.
The First Step after Installation -- Loading Data After you've chosen, installed, and configured the server software, you must consider how to load the data in the database. There are several possibilities, depending on your local requirements. The first is an import utility, suitable if you have to add a large number of entries. It uses the standard input format LDIF (LDAP Data Interchange Format). The following line imports an input file written in LDIF format in the database:
ldif2ldbm -i LDAP.ldif
If you only need to add one entry or modify something, there are also command-line utilities available. The most important one to know is ldapmodify. This utility is used to add or modify an entry. The second utility is ldapsearch, which is used to retrieve entries from the database. The following is an example of ldapmodify:
ldapmodify -a -h ldap.myorganisation.com -p \
389 -D "uid=RVoglmaier, ou=IT, o=GlaxoWellcome" \
-w "Password" -f Inputfile
where InputFile reads:
dn: uid=MyName, ou=Department1, o=GlaxoWellcome
cn: My Name
sn: Name
givenname: My
A brief example of ldapsearch:
ldapsearch -h ldap.myorganisation.com -p \
389 -D "uid=RVoglmaier, ou=IT, o=GlaxoWellcome" \
-w "Password" -b "o=GlaxoWellcome" "(sn = Name)"
After you have an LDAP server running and armed with these tools, you can store information in your database, search, and update it. For occasional use by experienced LDAP users, this may not seem too bad. However, for the administrator in his everyday job or even the end user, it is not so comfortable. Furthermore, we wanted an application that is accessible via a Web browser, and occasionally used by employees who are unskilled in IT.
Toward a More Robust Application Because we wanted a Web-enabled application and command- line tools are not very safe in this environment, we needed another mechanism. Most LDAP implementations have APIs for a number of programming languages (e.g., C, Java, PHP, and Perl, which I used for this article). Here is the basic functionality requested from our application:
1. Add a new entry.
2. Modify an existing entry.
3. Search for an entry.
However, since there will be an administrator who must erase old entries, there must be a delete entry as well as other maintenance tools, which I'll address later in this article.
A Command-Line Search Utility Listing 1 explains the very basics of the LDAP Perl Interface. (All listings for this article are available from Sys Admin's Web site: http://www.sysadminmag.com.) It's object oriented and requires Perl 5.000 or above. Before connecting with an LDAP server, you need an LDAP object containing the necessary data structures. This does the new call. After creating an instance of the LDAP class, you can connect with the LDAP server. In LDAP parlance, this is called binding, so it's no wonder the method is called bind.
Remember LDAP is a protocol based on TCP/IP, which means you can connect not only to a local LDAP server, but also to a remote LDAP server. Furthermore, there are two ways to bind to an LDAP server -- one unprivileged way, called anonymous, and another way using UserId and Password.
Once bound to the LDAP server, you can search. Be careful because the LDAP Perl API calls return result objects, regardless of whether the method call failed or not. To understand whether everything's okay, you can use the code method ($Result->code) which returns the error code, where 0 means OK. If it's true (not equal 0) in the listing we print the error message via the method error.
Format the Search Results After finishing the search call, you have a handy tool to work with. In the previous example, you used the tool to plot out (in plain text) everything the object contains, but maybe that's not really what you wanted. Assume you only want the name, phone number, and email of the persons satisfying the query. In Perl there's always more than one way to do it. The first is to print out only what you wanted and substitute the dump method in the for each loop with the following:
foreach $entry ($Result->all_entries) {
printf("Name: %s\n",$entry->get('cn')) ;
printf("Name: %s\n",$entry->get('given \
name')) ;
}
The second way is to resctrict the search by using the @attrs parameter in the search call. (See listing 2.) This parameter is a pointer to an array and indicates the entries in which we are interesed in the database. This way allows you to get from the found entries all attributes except for the Distinct Name (DN). To get the DN, you have to use the method dn, as shown in the example.
There are a couple of other useful search methods. You can get the number of found entries with the count method:
printf (" Found : %d records\n",$Result->count()) ;
You can also put the whole result of your query in a complex data structure. This is achieved with the method as_struct. The call as_struct gives you back a pointer to a hash. The keys of this hash are the Distinct Names (DN). Meanwhile, the values of the hash are pointer to Hashes containing the key/value pairs of the data.
Note that in LDAP a query is called a filter. A filter is the condition all entries must satisfy in order to be elected by the query. Another term is base, which is the node in the hierarchical object tree where the search begins.
Entering Data Previously, I mentioned a self-service application where the user enters data. To do this, there's the add method, but to use it you must understand a little detail of LDAP's architecture. LDAP is a database with the knowledge of objects and inheritance. This means that every entry knows exactly where it fits in the hierarchy. If you want to insert a new entry, you must explain this structure. Do this by using an array, such as:
top -> person -> organizationalperson \
-> inetOrgperson
This means, that an inetOrgperson is a kind of organizationalperson, which gets properties from person and from the top object. In Perl this sounds like:
@objectclasses = ['top','person', \
'organizationalperson','inetOrgperson'] ;
As above, we create a new LDAP object, call the bind method on it, and insert the new object in the database using the add method. At the end, disconnect from the database by using the method unbind. Listing 3 shows how to insert a new object in the database.
Keep Data Up-to-Date It frequently happens that some user data, such as email, changes or that a user simply wishes to change her password. Obviously this happens via Web, so you need two things:
An application consisting of one HTML page generated on the fly and a CGI script to actually modify the user data.
An authentication mechanism to guarantee that the user can see and change data only regarding herself.
HTML Page/CGI The HTML page is easily produced by a CGI script using the CGI module from Lincoln Stein:
http://stein.cshl.org/WWW/CGI
As shown in Listing 4, it's just one query to put user data building the LDAP object into the HTML page. The LDAP object is found using the unique userid as a filter. The userid is obtained from the authentication mechanism, which will be covered later. Once the user has finished the modification of her data and clicks on the submit button, the second CGI script (Listing 4) comes into action -- it simply uses the ldapmodify method to update the user data.
You may have noticed that ldapmodify needs to specify what type of change it has to apply on the object. There are three possibilities: add, modify, and delete. Add means you add a new property to the object; modify means you change an existing one; delete means you erase a property. For details, see Graham Barr's module Bundle::Net::LDAP, which you can easily find with the search utility of CPAN:
http://theoryx5.unwinnipeg.ca/ \
CPAN/cpan-search.html
Authentication Fortunately, Apache has the mod_ldap_auth module that does exactly what we want. See Listing 5 for the configuration. Line 3 specifies the directory that contains the application. Line 10 tells the Web server where the LDAP server is listening. Line 11 specifies that only valid users can connect to this particular directory. We could also require that only memebers of a particular user group can connect (e.g., require group admin) and so on. See:
http://www.rudedog.org/mod_ldap
(Dave Carrigan: mod_ldap) for more information about the API and the location of software download. The Web server also puts the userid in the environment variable for later use. This is necessary for the CGI script in Listing 4. See the line:
$Uid=$ENV{REMOTE_USER'}
Data Maintenance The self-service application allows the user to subscribe herself, change her data, change password, and launch applications after previous authentication. Once authenticated, the Web server can produce dynamic HTML pages, depending on the user access rights (e.g., using PHP). With the help of the Perl libraries, we can easily build the scripts necessary for the administration: an AddUser script, a ModifyUser script, a DeleteUser script, and as mentioned previously, a SearchUser script.
Another important utility is the reset password script. The reset password script may have various forms, depending on your security requirements. You might provide a forget password? button on the authentication request page, or when you notice that authentication failed. When the user clicks on this button, you can automatically send the password to her email address, or you can set the password to a determined value (such as the UserId, or a randomly generated value). The user should be instructed to then change her password immediately. You can set up a password reset robot that, on receiving email, launches the Reset Password utility.
For some reason, you might wish to export and then reimport your database. Listing 6 shows the example for the export and import utility.
Password Encoding Sometimes users don't want to have a lot of passwords, and it's common for your users to have one password for several accounts. That password should not be stored in plain text in the database. When your application inserts new data into the database, it must convert the user-supplied password in an encrypted form. You can use the Digest::SHA1 module available from the CPAN and choose the format you want to use. When you put the encrypted password into the database, you don't have to convert the password every time the user authenticates herself. LDAP is smart enough to understand if, and how, the password has been encrypted.
Conclusion LDAP is a standard protocol that permits the sharing of information between different applications. It allows the centralization of authentication, thereby avoiding password proliferation. It has powerful interfaces in a number of languages, including Perl, which is one of the most versatile scripting languages. In Perl + Net::LDAP, you can not only write user-friendly administration scripts, but also entire, safe Web applications to insert and update entries in the database.
Regarding the self-service application and how to use the Net::LDAP library, I have two observations -- this application is only a basic example and is far from complete. You may not want to suddenly activate the user account, but only do so after having controlled some user data. Second, as Larry Wall says, there's more than one way to do things. There is, for example, the Netscape LDAP SDK delivered with the Netscape Directory Server. The SDK is a comfortable Perl library that allows you to do the same things in slightly different syntax.
For more information about LDAP, the software development kits, and the API, refer to Programming LDAP, by Mark Wilcox (Wrox Press).
About the Author
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 the Internet and Intranet Manager at GlaxoWellcome, Italy. He can be reached at: rv33100@GlaxoWellcome.co.uk.
|