MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 http://www.macdevcenter.com/pub/a/mac/2004/06/18/ldap.html
 See this if you're having trouble printing code examples


More LDAP in Mac OS X Server

by Tony Williams
06/18/2004

After the last article in this series, you now have your OpenLDAP server running smoothly, and it's providing authentication to any Mac OS X computer in your network. Kerberos is up and running, so you get single sign-on for file sharing. What next?

First, how about getting all those addresses working properly with Address Book and SquirrelMail? Due to another slew of "features" in Apple's integration of WorkGroup Manager and LDAP, the records it creates in our LDAP server are far from ideal to use in email applications. For starters, they don't have email addresses. While this isn't a problem in Mail (if your user's "short name" and email address are the same and you all live in the one domain), it is a problem with other email clients, including SquirrelMail.

In my last article, I suggested that you could fix the problems in the information Apple's WorkGroup Manager writes to the LDAP server by editing the records by hand and fixing the sn and givenName attributes. Well, you need a little more than that to get it all working well with both SquirrelMail and Address Book. To solve the problem, I delved into my toolbox and wrote a Perl script to give you a hand. In my company, a person's user name is their first name and last name joined by an underscore, so I could do it all in one go with my script. If you have some other system, then you'll need to make some small changes to my script, and perhaps do some editing by hand.

To get the script working, you'll need to use cpan to load the Net::LDAP module. Using sudo cpan -i Net::LDAP at a terminal prompt should get that done. Watch it, though, as it may well ask if you'd like to load one or two other modules on which it depends. If it does then just say yes -- none are terribly large or hard for the Mac OS X Server to digest.


#!/usr/bin/perl

use Net::LDAP;

#config here
$server = "ldap.example.com";
$base=cn=users,dc=example,dc=com;
# server part of mail address
$mail = "@example.com";
# end config

$ldap = Net::LDAP->new( $server ) or die "$@";
$mesg = $ldap->bind('uid=admin,cn=users,dc=example,dc=com',
					password => 'secret');
$mesg->code && die $mesg->error;
$mesg = $ldap->search( base => $base, 
	filter=> '(uid=*)',
	);
	
$mesg->code && die $mesg->error;

foreach $entry ($mesg->all_entries) { 
	$id = $entry->get_value('uid');

	# if uid is First_Last use next line
	($first, $last) = split('_', $id);

	# if givenName and sn are set use next line
	# $first = $entry->get_value('givenName'); $last = $entry->get_value('sn');
	
	# edit this according to your desires
	# if you have already fixed sn and givenName then comment out
	# the appropriate lines below
	# I use cn="$last $first" as that sorts properly in SquirrelMail
	
	$mesg = $ldap->modify( $entry, changes => [
		replace => [
			sn  => $last,
			cn => "$last $first"
		],
		add => [ 
			givenName => $first,
			mail => "$id$mail"
		]
	]);
	print "Modified $id\n"; 
	}

$mesg = $ldap->unbind;   # take down session

Save the code as ldap.pl, alter to taste, and then enter perl ldap.pl at the terminal prompt. I left in a line to print something to the terminal for each user so you can watch it go.

Even once you've done that, though, you still don't have SquirrelMail and Panther Server talking. The default configuration in Panther Server is a little too paranoid for us; it only allows logins using version 3 of the LDAP protocol. And SquirrelMail wants to use version 2.

To fix this, edit /etc/opendlap/slapd.conf and add the line allows bind_v2 just under all of the schema include lines. At this point, you can either reboot your server or restart slapd by hand. To restart by hand, enter sudo kill -INT `cat /var/run/slapd.pid` (those are back quotes -- found just under the Esc key) to kill it, then /usr/libexec/slapd to run it again.

Now we just need to get SquirrelMail pointed to our mail server, configured to talk to the LDAP server, and running.

Configuring SquirrelMail

Configuring SquirrelMail is accomplished by using a Perl script that you can find at /etc/squirrelmail/config/conf.pl. This script provides a primitive menu system to change the settings. To set the mail server, select "2" and update the IMAP settings:


IMAP Settings
--------------
4.  IMAP Server            : mail.junpacific.com
5.  IMAP Port              : 143
6.  Authentication type    : login
7.  Secure IMAP (TLS)      : false
8.  Server software        : other
9.  Delimiter              : detect

Then, your SMTP settings:

SMTP Settings
-------------
4.   SMTP Server           : mail.junpacific.com
5.   SMTP Port             : 25
6.   POP before SMTP       : false
7.   SMTP Authentication   : none
8.   Secure SMTP (TLS)     : false

For SMTP authentication, you may have to check what your server supports. If you want, the configure program will attempt to work it out for you.

Then you need to set up the link to your LDAP server. This is done from the main menu selection "6". This brings you to another menu with only two choices. Leave Use Javascript Address Book Search set to false, and select "1" to change the LDAP servers.

This choice runs a small subsystem that allows you to add or delete LDAP servers from the list, but not change the settings on an existing one. To add our server, press "+" and answer the questions as they come up.

hostname: ldap.example.com
base: cn=users,dc=example,dc=com
name: Staff

The default is fine for all other settings. Since we are only searching, we can leave binddn blank, for anonymous bind.

Starting SquirrelMail

Starting SquirrelMail is not difficult. Run Server Admin and select Web. Then select Settings and Sites, which brings up a list of the web sites you have defined. If you edit the main site under Options, you'll see a tick box for WebMail that turns it on and off. Once it is turned on, you can access it as http://www.example.com/webmail.

Log in using your email ID and password, and you will see your mailbox. One caveat: the Addresses link at the top of the page shows your personal address book, and so doesn't reflect all of our work. To get to the addresses from the LDAP server, you need to click on the Addresses button on the Compose form. This allows you to list all of the addresses, search them, and choose the one you want to use. You can see the Addresses button just below "Priority: Normal" in the Compose form below.

Extending the System

Now that you have the email addresses of all of the staff available in both the Address Book and SquirrelMail, how do we extend that so we that can share other mail addresses? We can do that easily with a little help.

The secret is that Mail, Address Book, and SquirrelMail can all be set up to check more than one LDAP server. But we can use the same LDAP server with two different search bases. Remember that we used cn=users,dc=example,dc=com as our base, so if we add a reference to the same LDAP server but with a base of, say, cn=customers,dc=example,dc=com, we can have a separate mail list. In fact, we could have many lists; say, one for customers and one for suppliers, if desired.

The easiest way to add the records is to use a small Perl script. I've written one that reads the names from standard input with the fields separated by tabs. Write your file like so (those blanks are single tabs):


Tony          Williams          tonyw@honestpuck.com
Peter	      Parker            spiderman@comics.com
Tim           O'Reilly          tim@oreilly.com

Then cat address.txt | perl ldap_add.pl at the command line will do the job. Here's the script:


#!/usr/bin/perl

use Net::LDAP;

#the 'cn' container that will hold the addresses
$container = 'customers';
# DN for a user able to write to LDAP server
$bind = 'uid=admin,cn=users,dc=example,dc=com';
$passwd = 'secret';

$ldap = Net::LDAP->new( 'ldap.example.com ) or die "$@";
$mesg = $ldap->bind($bind, password => $passwd);
$mesg->code && die $mesg->error;
while (<>) {
	chomp;
	($first, $last, $mail) = split('\t', $_);	
	$cn = "$last $first";
	$dn = "$cn,cn=$container,dc=example,dc=com";
	$mesg = $ldap->add( $dn, 
		attrs => [
			objectClass => 'inetOrgPerson',
			cn => $cn,
			givenName => $first,
			sn => $last,
			mail => $mail
		]
	);		
	$mesg->code && die $mesg->error;	
}
$mesg = $ldap->unbind;   # take down session

If you don't want to use something like this script, then you can of course do it by hand using phpLDAPadmin or your favorite LDAP editor. You can also extend the script to support more fields for Address Book, if you wish. Just add the fields into the text file and script.

Now just add the "new" LDAP server to Address Book, Mail, and SquirrelMail in exactly the same way as the first one -- just replace the base cn=users,dc=example,dc=com with the new one, cn=customers,dc=example,dc=com. I've established a customers container, a suppliers container, and a misc container for each of our three state offices, and then set Mail and Address Book to search only the containers the user requires. This keeps the total list of addresses searched by the user as short as possible. Unfortunately, SquirrelMail does not support an individual list of LDAP servers, so users have a longer list here, though they can easily select just one server.

In the illustration above, you can see the pop-up menu (right next to the Search button) that allows the user to select where to search. The choices are All, Address Book (which is the list of the user's personal addresses saved in SquirrelMail) followed by the "name" field from each of the LDAP servers you set in the SquirrelMail preferences.

LDAP and Apache

For our next trick, we are going to need some serious mojo. After doing a lot of research and testing, I've found a module for Apache 1.13.x that will allow us to provide user authentication for Apache basic authorization, though with some problems. There are several (I found five) modules available that should do this, but none works perfectly. So I took one (mod_LDAPauth from Piet Ruyssinck) that worked fairly well, and did some hacking to lengthen the user ID and make it support Apple's model of group membership. Go to my web site to grab a copy of the hacked version.

You'll be happy to read that building, installing, and configuring Apache for it are trivial. Download the source file into a folder on your server, and then type sudo apxs -lldap -llber -i -a -c mod_LDAPauth.c at a terminal prompt. This will run the Apache extension tool to build and install the module. Once you've done that, sudo apachectl restart will get Apache to read the new configuration, and you are good to go.

You can secure a directory by adding a <Directory> block to your Apache config in /etc/httpd/httpd.conf, but I prefer to get everything working using a .htaccess file in the directory itself, and then later transfer it to the configuration file. Create a directory named /Library/WebServer/Documents/test_secure and place a small HTML file in it called index.html. Here's mine:


<html>
<head>
        <title>Testing Security</title>
</head>
<body>
<p>
This is a secured page
</p>
</body>
</html>
 

Then create the .htaccess file to test it, and drop that in the same directory.


AuthName "IT Staff only"
AuthType Basic
LDAP_server ldap://ldap.example.com/
LDAP_base_dn cn=users,dc=example,dc=com
LDAP_scope subtree
# require valid-user
require user admin tony_williams marvin_martian
# LDAP_group_base_dn cn=groups,dc=example,dc=com
# require group Group_Aliens Group_Accounts

All you need to do is replace the list admin tony_williams marvin_martian with your own list of users, and all should work fine. You can also change the entire line to require valid-user, and then anyone listed as user in your LDAP server can log on.

You can see from the example above how to use valid-user and group. Note that if you have a require group line, then you need to specify a distinguished name for the container that holds your groups using the LDAP_group_base_dn line.

Final Thoughts

Now we have completed all of the work from the previous article. After this tutorial, our users can log onto any machine in the network and get their home folders and preferences; log on automatically to Mail and any volume on our server thanks to Kerberos single sign-on; list any email address on our LDAP server in Mail, Address Book, and SquirrelMail; and log onto protected areas of our web server using their login ID and password.

Not bad for a couple of hours of work.

Tony Williams is currently a desktop support consultant at a major Australian university, specializing in Macintosh computers. He describes himself as a "professional Mac geek."


Return to the Mac DevCenter

Copyright © 2007 O'Reilly Media, Inc.