Cooking with sendmail

Editor's note: sendmail Cookbook offers hundreds of step-by-step solutions to configuration problems just like the one in today's excerpt, on routing mail with LDAP. If you're an administrator, you know you can't spend hours tracking down the answer to every problem; the solutions and configuration code included with each recipe in the book can be implemented immediately. Check out this recipe and see for yourself.

Recipe 5.9: Routing Mail with LDAP

Problem

Your enterprise wants to use the IETF Internet Draft LDAP Schema for Intranet Mail Routing. You have been asked to configure sendmail to use the IETF draft schema and to read internal mail routing information from the LDAP server.

Solution

This solution requires collaboration between the LDAP administrator and the
sendmail administrator.

Instructions for the LDAP administrator

Locate a copy of the IETF draft schema for intranet mail routing. The schema
is documented in the draft-lachman-laser-ldap-mail-routing-02 file available from the IETF. The schema is defined as part of the misc.schema file provided with the OpenLDAP distribution. Include the schema file in the LDAP configuration by adding the following include command to the slapd.conf file:

include /etc/openldap/schema/misc.schema

See the Discussion section for information on a possible conflict between the
misc.schema file and other schema.

Additionally, the sendmail.schema file should be
copied to the proper path and included in the LDAP configuration, as described
in Recipe 1.3.

Restart LDAP to ensure that the IETF draft schema and the sendmail schema are
available for use. Here is an example:

Create an LDIF file containing the mail routing information. Use the
inetLocalMailRecipient object class from the IETF draft schema to
format the mail routing data in the LDIF file. Run ldapadd to add the data to the LDAP database. The Discussion section shows an example of this step.

sendmail applies LDAP routing to a recipient address when the host portion of
that address is listed in class $={LDAPRoute}. To store the
$={LDAPRoute} class values on the LDAP server, create an LDIF file
containing a sendmailMTAClass record, which is an object class
defined in the sendmail.schema file. Set the
sendmailMTAClassName attribute to LDAPRoute and define
the values for that class using sendmailMTAClassValue attributes.
Use ldapadd to convert the LDIF file and add the data to
the LDAP database.

Instructions for the sendmail administrator

Direct sendmail to use LDAP intranet mail routing by adding the ldap_routing feature to the configuration. Add the LDAPROUTE_DOMAIN_FILE macro to load class $={LDAPRoute}, which can be loaded from the LDAP server. Lines, such as the following, should be added to the sendmail configuration:

Rebuild and reinstall the sendmail.cf file, and
restart sendmail as described in Recipe 1.8.

Discussion

This discussion covers both LDAP and sendmail configuration.

LDAP configuration

For a query to succeed, sendmail and LDAP must
use and understand the same schema. The ldap_routing
feature depends on a mail routing schema defined in a draft IETF document. That
schema, which is available in the misc.schema file
provided with the OpenLDAP distribution, must be included in the LDAP
configuration in order for that LDAP to understand the mail routing queries
coming from sendmail.

NOTE: A problem may occur when you attempt to restart LDAP after including the misc.schema file in the configuration. The error might look something like the following:

In this case, the include command for the misc.schema file was inserted into the slapd.conf file before an rfc822-MailMember.schemainclude command. When LDAP attempted to process the rfc822-MailMember.schema file, it found that the attribute type had already been declared - in this case, by the misc.schema file. If you have such a problem, you need to resolve the conflict. In this case, the resolution is straight-forward - simply remove the rfc822-MailMember.schemainclude command from the slapd.conf file. The include command can be safely removed because the rfc822-MailMember.schema file exactly duplicates the last two entries of the misc.schema file. This specific problem occurred on Red Hat Linux systems because the conflict is with a schema file provided by Red Hat. However, conflicts are possible any time multiple schema files are included in the LDAP configuration.

Include the sendmail.schema file in the LDAP
configuration to provide support for the other LDAP records used by sendmail.
For example, the sendmail configuration in this recipe reads LDAP records to
load the $={LDAPRoute} class, which requires the sendmail schema.
After the schema files are added to the slapd.conf file,
restart slapd to ensure that the schema files have been
read and are ready to be used.

Add the records required by the ldap_routing feature
to the LDAP database using the draft IETF schema. This example shows three
possible variations of the LDAP routing record:

The object class of the records is inetLocalMailRecipient. Each
record contains a mailLocalAddress attribute that sendmail uses as
the database key. Records may contain one or both of the attributes
mailHost and mailRoutingAddress, which are the values
returned to sendmail by the LDAP database. The example shows how these values
are first used to build an LDIF file that is then processed by ldapadd to add the records to the LDAP database. Once added to the database, the records can be viewed with ldapsearch:

sendmail only queries the LDAP server for routing information if the host
portion of the recipient address is listed in the $={LDAPRoute}
class. Values in this class can be individually defined using
LDAPROUTE_DOMAIN macros or they can be read from a file using the
LDAPROUTE_DOMAIN_FILE macro. The sendmail configuration in this
recipe uses the LDAPROUTE_DOMAIN_FILE macro and reads the list of
values for the $={LDAPRoute} class from the LDAP server. The
following example shows how the LDAP administrator might store the
$={LDAPRoute} data on the LDAP server so that sendmail can retrieve
it later:

The sample LDIF file contains a sendmailMTAClass record, which
is an object class defined in the sendmail.schema file.
The record sets the sendmailMTAClassName attribute to
LDAPRoute and defines the values for that class using
sendmailMTAClassValue attributes. After the LDIF file is converted
and added to the LDAP database using the ldapadd
command, an ldapsearch command shows the content of the
LDAP record:

sendmail configuration

Three commands in this recipe's sendmail configuration help sendmail use the LDAP records that we have just created: the confLDAP_DEFAULT_SPEC define, the LDAPROUTE_DOMAIN_FILE macro, and the ldap_routing feature.

The confLDAP_DEFAULT_SPEC define sets default values that
sendmail uses to access the LDAP database. For many sendmail configurations that
use LDAP, the confLDAP_DEFAULT_SPEC define is not required because
the default values are correct. The confLDAP_DEFAULT_SPEC define is
used in this recipe to prevent sendmail from displaying a warning message. When
sendmail is configured with the ldap_routing feature, it
complains every time it is run if the configuration does not also contain a
confLDAP_DEFAULT_SPEC define. For example:

Adding the confLDAP_DEFAULT_SPEC define to this recipe's
configuration eliminates this warning, but, of course, care must be taken to set
the correct values for the define. If the values in the
confLDAP_DEFAULT_SPEC define are incorrect, sendmail will not be
able to query the LDAP server successfully. The sample
confLDAP_DEFAULT_SPEC define in the Solution section contains two
values:

-h
The -h argument defines the hostname of the LDAP server. If you have multiple servers, use the hostname of the server that stores the sendmail data. If you have a single server, the HOST value you configured in the ldap.conf file is probably the hostname you should use here.

-b
The -b argument defines the LDAP default base distinguished name used for sendmail queries. The value should match the suffix of the database you wish to query, as defined in the slapd.conf by the suffix entry for that database. The suffix used in this recipe is the system default defined for all LDAP clients by the BASE command in the ldap.conf file. Because the recipe uses the system default, it is not strictly necessary to define it with the confLDAP_DEFAULT_SPEC command.

The values defined by the confLDAP_DEFAULT_SPEC define appear in the sendmail.cf file on the LDAPDefaultSpec option line, as this grep shows:

If the -h and -b arguments work with ldapsearch, they should work for sendmail.

The @LDAP string used in place of the file path for the LDAPROUTE_DOMAIN_FILE macro tells sendmail to load the $={LDAPRoute} class from the LDAP server. Either an LDAPROUTE_DOMAIN_FILE macro or some LDAPROUTE_DOMAIN macros must appear in the configuration file when the ldap_routing feature is used because sendmail only uses LDAP routing for hosts listed in the $={LDAPRoute} class.

Finally, the ldap_routingFEATURE macro
is added to the configuration. The sendmail configuration in the Solution
section uses the simplest form of this command, which contains no optional
arguments; however, in this form, the ldap_routing
feature defines two database maps using two sendmail.cfK commands:

ldapmh accepts a recipient address as the key and returns the
name of the mail host that should be used to reach that address. The mail host
returned by the ldapmh map is used as the $h value
for the mail delivery triple.

ldapmra accepts a recipient address as the key and returns the
mail routing address that should be used instead of the recipient address to
deliver the mail. The value returned by ldapmra becomes the
$u value for the mail delivery triple.

Testing the results

A few simple tests show the impact of this recipe. First, a
sendmail-bt command tests that sendmail is
successfully reading the LDAP data:

The $={LDAPRoute} command shows that sendmail successfully
loaded this class from the LDAP server. The /map commands show that
sendmail can read LDAP routing data from both the ldapmh and the
ldapmra maps.

Next, sendmail-bv is used to show how the mail
delivery triples are rewritten for recipient addresses that contain hostnames
listed in $={LDAPRoute}:

Refer back to the ldapsearch shown in an earlier example. You'll see that:

The record for kathy@rodent.wrotethebook.com returns only a mail routing address.

The record for alana@wrotethebook.com returns only a mail host.

The record for craig@horseshoe.wrotethebook.com returns both a mail host and a mail routing address.

The sendmail-bv tests show how these various return values impact the mail delivery triple:

In the first test, the mail routing address becomes the user value of the
triple. The mailer and host values are necessary to deliver to that user
address.

In the second test, the mail host returned by the LDAP query becomes the
host value in the delivery triple. The mailer used is the relay
mailer because the mail will be relayed to that host for delivery. Because no
mail routing address was returned by LDAP, the relay host is sent the original
address, and it becomes the remote host's responsibility to deliver the mail
to that address.

The third test returns both a mail host value and a mail routing address.
In that case, the relay mailer is again used to relay mail to the
remote host specified by the mail host value, and the delivery address passed
to that host is the mail routing address. It then becomes the responsibility
of that remote host to deliver the mail to the mail routing
address.

LDAP routing is only one step in the delivery process. The virtusertable and the mailertable
are both applied after LDAP routing, and if the mailer used to deliver to the host returned by LDAP has the F=A flag set, aliasing is also applied. [6] If the mail is relayed to a remote host, that host also processes the address. The sendmail-bv test results clearly show the impact of LDAP routing, but only in part, because this recipe does not perform any subsequent processing on these addresses. On a production system with a more complex configuration, sendmail-bv may not provide such clear results. However, it is possible to test the impact of LDAP rulesets more directly.

The ldap_routing feature adds the LDAPExpand ruleset to query LDAP and process the return value, and it adds two rules to the Parse1 ruleset to call the new ruleset. Use sendmail-bt to check whether or not LDAP routing is applied to a specific address and to see the delivery triple returned for that address. Here is an example:

Recipe 5.10 provides another example of testing the ldap_routing feature by passing an address to
Parse1. [7] See Recipe 5.10 for more details about this ruleset.

The ldap_routing feature

The ldap_routingFEATURE command used in this recipe is the simplest form of the command. The command accepts up to four optional arguments. Here is the complete syntax:

FEATURE(ldap_routing, mhmap, mramap, bounce, detail)

The four optional arguments are:

mhmap

This is a custom mail host map definition. It replaces the default ldapmh map created by the ldap_routing feature. At a minimum, the map definition must include -k and -v arguments.

mramap

This is a custom mail routing address map definition. It replaces the default ldapmra map created by the ldap_routing feature. At a minimum, the map definition must include -k and -v arguments.

bounce

This argument tells sendmail what to do when a recipient address is not found in the LDAP database. Normally, delivery continues and sendmail delivers the mail to the recipient address as directed by the rest of the configuration. Two keywords can be used for this argument:

passthru
This keyword tells sendmail to continue with the delivery, which is the default action.

bounce
This tells sendmail to return the mail as undeliverable. Actually, any value other than passthru causes sendmail to bounce the mail; however, the sendmail developers suggest that, for the sake of clarity, you use the keyword bounce to reject the mail.

detail

This tells sendmail how to handle recipient addresses that use the +detail syntax. By default, an address that contains +detail information is looked up as-is, with the +detail information intact. Two keywords can be used to change this:

strip
This keyword directs sendmail first to lookup the address with the +detail information, and then, if no match is found, to lookup the address without the +detail information.

preserve
Just like strip, this keyword directs sendmail first to lookup the address with the +detail information, and then, if no match is found, to lookup the address without the +detail information. In addition to modifying the lookup procedure, this keyword modifies the value returned from the LDAP database. If LDAP returns a mail routing address, sendmail appends the +detail information from the recipient address to the mail routing address when the preserve keyword is specified.

See Also

The sendmail book covers the LDAPROUTE_DOMAIN_FILE macro in Section 23.7.11.18, the ldap_routing feature in Section 23.7.11.17, and the arguments available for the confLDAP_DEFAULT_SPEC define in Section 21.7.11. The Using LDAP for Aliases, Maps, and Classes
section of the cf/README file also provides important information.

[6] By default, the F=A flag is set for the local mailer.

[7] The focus characters (<>) are used in the address because Parse1, which is normally called after canonify has inserted them, expects focus
characters.

Check back here next week for two more recipes from the book that cover configuring sendmail for STARTTLS and limiting the SMTP command set.

Craig Hunt
has worked with computer systems for the last thirty years.