Building Sendmail Mailing Lists in LDAP

If you already keep e-mail addresses in an LDAP database, it’s possible to tweak things a bit so that sendmail can lookup mailing list addresses in it.

Paul Heinlein
First published on April 21, 2002
Last updated on June 4, 2004

At home, I’ve slowly been migrating all address-book data to an LDAP directory running on my Linux box. It’s not a huge directory (a couple hundred entries), but it’s comprehensive enough that I rely on it fairly heavily.

In particular, I use it for e-mail address lookups from within pine, while my wife does the same from within Eudora on her Windows box.

I’ve thought for some time that it’d be just too cool to be able to build mailing lists using live LDAP data. It’d be fairly easy to build a sendmail aliases file, or even a pine .addressbook, using a perl or shell script – but that seemed like a needless extra step.

For some time, sendmail has supported using LDAP for maps, aliases, and classes, so I finally decided it was time to learn how to implement at least the aliases stuff on my home system.

Now that I’ve got it working successfully, I thought I’d pass along how I did it, just so you don’t have to repeat all the doc-wading and mistake-making that I did.

My LDAP directory

I have just two main subtrees in my LDAP directory, people and hosts:

+ dc=heinlein, dc=net
  |
  + ou=people
  | |
  | + cn=Bill Gates
  | |
  | + cn=Steve Ballmer
  | |
  | + ...
  |
  + ou=hosts
    |
    + ...

From the beginning of my LDAP explorations, I’ve tried to use standard schemata instead of home-brewed ones, a habit that proved handy when it came time to transition from OpenLDAP 1.x to the stricter 2.x. The “people” subtree of my home directory is based on the inetOrgPerson object class, e.g.,

dn: cn=Bill Gates, ou=people, dc=heinlein, dc=net
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
ou: people
cn: Bill Gates
cn: William Gates, III
cn: Borg Boy
givenName: William
sn: Gates
mail: billg@microsoft.com
telephoneNumber: +1 425 882 8080
postalAddress: One Microsoft Way $ Redmond, WA 98052-6399

dn: cn=Steven Ballmer, ou=people, dc=heinlein, dc=net
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
ou: people
cn: Steven Ballmer
cn: Steve Ballmer
cn: Fred Astaire NOT
givenName: Steven
sn: Ballmer
mail: dancingmonkeyboy@microsoft.com
telephoneNumber: +1 425 882 8080
postalAddress: One Microsoft Way $ Redmond, WA 98052-6399

The sendmail.schema shortfall

Sendmail includes a .schema file that defines some object classes and attribute types that could be used to build LDAP-based alias maps. Here’s the example from the current cf/README file:

dn: sendmailMTAKey=sendmail-list, dc=sendmail, dc=org
objectClass: sendmailMTA
objectClass: sendmailMTAAlias
objectClass: sendmailMTAAliasObject
sendmailMTAAliasGrouping: aliases
sendmailMTAHost: etrn.sendmail.org
sendmailMTAKey: sendmail-list
sendmailMTAAliasValue: ca@example.org
sendmailMTAAliasValue: eric
sendmailMTAAliasValue: gshapiro@example.com

In this example, the sendmail-list alias would get expanded to ca@example.org, eric, and gshapiro@example.com.

This would work well if you maintained mailing lists that were largely unrelated to any other data in your LDAP directory.

For me, however, the sendmail schemata are problematic because I don’t want to have to edit the same e-mail address in two places. Let’s say that I built a microsoft-execs list and included

sendmailMTAAliasValue: billg@microsoft.com
sendmailMTAAliasValue: dancingmonkeyboy@microsoft.com

If Bill were to change his e-mail address, I’d have to touch it in two places: my original people record and the microsoft-execs alias record.

In some instances, this might be a good thing. Someone might want to receive different alias mailings at different e-mail addresses. In that case, the sendmail way of doing things is much more flexible than what I wanted.

A different goal

My goal was to be able to build mailing lists from LDAP records I already have on hand in such a way that changing the original people record would automatically update all the aliases associated with that person. So, for instance, if I had my parents’ record associated with a bunch of aliases (all-friends, all-family, nuclear-family); then when they finally ditched AOL (fat chance!), I’d only have to update the one record for all the aliases to be accurate.

Getting it done

The steps to accomplish this were relatively straightforward, once I waded through a mound of Google searches and sendmail READMEs.

Oh, I probably ought to note that I did all this on an x86 box running Red Hat 7.1 and the standard sendmail and openldap packages. I didn’t have to rebuild anything (though I’ve hand-built both sendmail and OpenLDAP in other contexts).

  1. Make sure your sendmail is compiled with LDAP support. Run the following command:

    $ sendmail -d0.1 -bv postmaster

    You should see “LDAPMAP” in the “Compiled with:” section. If you don’t, you’ll have to rebuild sendmail, an operation outside the scope of this document.

  2. Make sure your ldap server is running properly (as if you’d read this far if it weren’t…).

  3. The tricky part: add/use the appropriate object classes and attribute types to add a mailing list entry to your people records.

    I’ll tell you what I did – and you’re of course welcome to copy it – but your solution may be somewhat different than mine.

    I created a file called /etc/openldap/schema/heinlein.schema with two OID entries:

    # Paul Heinlein's LDAP schemata
    #
    # Note: I, ahem, borrowed part of the Sendmail Private Enterprise
    #       Number namespace for these OIDs.
    #
    
    attributetype ( 1.3.6.1.4.1.6152.945.2.1
      NAME 'mailingListName'
      SUP  name )
    
    objectclass ( 1.3.6.1.4.1.6152.945.1.1
      NAME 'mailingListPerson'
      SUP  inetOrgPerson
      STRUCTURAL
      MAY mailingListName )
    
    #
    # eof
    #
    

    The objectclass mailingListPerson builds on the inetOrgPerson class and adds just one attribute: mailingListName. I tried to be as unintrusive as possible when it comes to adding new LDAP definitions.

  4. Add the new schemata to your slapd.conf file. If you’re running OpenLDAP 2.x, you’ll find a bunch of include directives toward the top of the configuration file. Just add another line with the path to the new .schema file you created.

  5. Restart slapd to let the new schemata take effect.

  6. Alter your person records. For each person entry, I added

    objectClass: mailingListPerson

    Then I added a mailingListName attribute for each alias. To make Bill and Steve part of the microsoft-execs alias, you’d add the following attribute to each of their entries:

    mailingListName: microsoft-execs

  7. Rebuild your sendmail.cf file. I’ll admit it, I’d rather use the m4 macros than do this by hand – so I’m only going to show you the two additional lines I added to my sendmail.mc file:

    define(`ALIAS_FILE',
           `/etc/aliases,ldap: -v mail -k (mailingListName=%0)')dnl
    define(`confLDAP_DEFAULT_SPEC',
           `-h localhost -b dc=heinlein,dc=net')dnl
    

    The second line tells sendmail to use the LDAP server running on localhost and to make all searches with the base dn of dc=heinlein,dc=net. (Note: you can specify multiple hosts behind the -h switch; if the first server times out, sendmail will check the second one, and so on.)

    The first line tells sendmail to look for aliases first in the traditional /etc/aliases dbm file. If it’s not there, then use LDAP: search for all records that include a mailingListName entry for the alias in question (%0 gets expanded, obviously). In each of the returned records, find the recipient’s e-mail address in the attribute called “mail.”

    Once you’ve edited the .mc file, then rebuild sendmail.cf and restart sendmail.

That should do it. You ought to be able to send mail to microsoft-execs@your.host.name and have it delivered to Messrs. Gates and Ballmer. (Bug reports would likely be appreciated :-)