home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Previous Section Next Section

7.6 The Milter Library

Beginning with V8.12, sendmail offers hooks to access external programs via sockets, and a library to build external programs to listen on sockets.[23] We first discuss the hooks inside the configuration file in support of external programs, and after that briefly discuss building your own program.

[23] V8.10 and V8.11 also included this ability, but you had to define _FFR_MILTER in your m4 Build file to enable it because it was not officially supported.

7.6.1 Enable with -DMILTER

To access external programs via sockets from sendmail, you need to compile sendmail with the MILTER compile-time macro defined. To build sendmail in this way, simply add a line such as this to your m4 Build file:

APPENDDEF(`confENVDEF', `-DMILTER')

Then, build sendmail in the usual manner.

If you are using precompiled sendmail, you can detect if it was built with the MILTER compile-time macro defined by running the following command:[24]

[24] The location of sendmail can vary based on the version of Unix you are running.

% /usr/sbin/sendmail -bt -d0.4 < /dev/null

If MILTER was defined, it will appear among a list of other defined macros in a line that will look something like this:

Compiled with: DNSMAP LOG MAP_REGEX MILTER  MIME7TO8 MIME8TO7
                                       
                                     note

If it doesn't appear, you will need to either download the sendmail source and build it yourself, or contact your operating system vendor and request a properly compiled version in binary form.

7.6.2 The X Configuration Command

With MILTER enabled, sendmail offers a way to submit messages to external programs that can be used to screen messages for spam indicators, viruses, or other content that you might want to reject. At the end of this chapter, we will show you the library routines to use when making this decision. Here, we discuss the hooks inside the configuration file that allow you to run external programs.

External programs are defined for use by sendmail using the X configuration file command. The form for that command looks like this:

Xname, equates ...                      cf file  
INPUT_MAIL_FILTER(`name', `equates ...')    mc file  
MAIL_FILTER(`name', `equates ...')      mc file  

The X in the first line, like all configuration commands, must begin a line. It is immediately followed by the name you will assign the external program, with no intervening spaces. That name is for sendmail's use only, and does not need to be the actual name of the program. The name is followed by a comma. If you accidentally prefix the name with a space (in the cf or mc form), or omit the name, the following error will print and the sendmail program will exit:

cf file: line number name required for mail filter 

The equates in this configuration command is a sequence of comma-separated expressions that are formed by a key-letter, an equal sign, and a value:

key-letter=value 

The recognized key-letters and their meanings are shown in Table 7-4.

Table 7-4. X configuration command key-letters

Key-letter

Description

F

Controlling flags

S

Description of the socket to use

T

The timeout to impose on the connection

For example, the following three mc file lines define three possible external program hooks:

INPUT_MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R')
INPUT_MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')
INPUT_MAIL_FILTER(`progC', `S=inet:3333@localhost')

The first example shows how to attach to a Unix-domain socket in the /var/run directory. The second example shows how to connect to an IPv6 socket on port 999 of the local host. The third example shows how to connect to an IPv4 socket on port 3333 of the local host. We will describe each equate in detail in the following three sections, but first, the following details should be noted.

If the = is missing from an equate, the following error is printed and sendmail exits:

cf file: line number Xname  `=' expected 

If the key-letter prefixing the = character is not one of the three shown in Table 7-4, the following error is printed and sendmail exits:

cf file: line number Xname  unknown filter equate badequate= 

The three external programs will be used in the order declared. First progA will be contacted on a Unix-domain socket. If it accepts the message, progB will be contacted on a network socket. If progB accepts the message, progC will be given the final crack at the message. When the nature of the sockets allows, some of these connections might be in parallel.

If you want to declare external programs, but don't want to set the order in which they are called, use the MAIL_FILTER mc macro instead:

MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R')
MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')
MAIL_FILTER(`progC', `S=inet:3333@localhost')

This is the same declaration as before, except that it omits the declaration of the order in which the programs sockets will be called. With this form, you will have to separately declare the order with the InputMailFilters option (Section 7.6.3):

define(`confINPUT_MAIL_FILTERS', `progB, progA, progC')

Note that if sendmail was not compiled with -DMILTER,[25] declaring a socket with these commands will cause the following error to be printed, and sendmail will exit:

[25] Use _FFR_MILTER with V8.10 or V8.11 sendmail.

Warning: Filter usage ('X') requires Milter support (-DMILTER)
7.6.2.1 The X configuration command F= equate

The F= equate, which stands for "Flags," can cause a message to be rejected or deferred if the connection to the socket fails or if the filter program gives a nonstandard response. If you want the message rejected on failure, you use the letter R, which stands for "reject." If you want the message to be deferred, you use the letter T, which stands for "temporary failure":

F=R       reject connection if the filter is unavailable or if it has an error
F=T       temporary fail and defer if the filter is unavailable or if it has an error

If any letter other than R or T is specified following the F=, or if the F= equate is missing, the message is passed through sendmail as if the entire X configuration-file command were omitted, or as if the socket could not be contacted.

7.6.2.2 The X configuration command S= equate

The S= equate, which stands for "Socket," is mandatory and specifies one of three different types of sockets as its value:

local     a Unix-domain socket
unix      synonym for local
inet      an IPv4 network socket
inet6     an IPv6 network socket

If you use a socket type other than one of those listed, the following error will print and sendmail will exit:

cf file: line number Xname  unknown socket type type: Protocol not supported 

The format for the S= equate looks like this:

S=type:specification  

The type is one of the three main types shown earlier. The colon is literal and must be present. The specification is particular to each type. For the local (or unix) type, the specification is the full pathname to a Unix-domain socket. For example:

S=local:/var/run/progA.soc

Note that the socket must not already exist. The MILTER library will automatically create a socket when one is needed.

The inet and inet6-type sockets use a specification that is a port number, immediately followed by an @ character, which is again immediately followed by a host or address specification. For example:

S=inet:1099@localhost                 port 1099 on the local machine, using IPv4
S=inet:1099@host.your.domain          port 1099 on another machine on your network,
									           using IPv4
S=inet6:1099@localhost                port 1099 on the local machine, using IPv6
S=inet:1099@123.45.67.89              port 1099 at IPv4 number 123.45.67.89
S=inet6:1099@2002:c0a8:51d2::23f4     port 1099 at IPv6 number 2002:c0a8:51d2::23f4

As we have seen in the previous section, the F= equate determines what will happen to a message should the connection to a socket fail.

7.6.2.3 The X configuration command T= equate

There are four timeouts that can affect the use of an external program connected via a socket.[26] They are tunable in your configuration file. Table 7-5 shows all four timeouts, the key-letter for each, and the default value for each.

[26] The T=C was added in V8.12 sendmail and was not available earlier.

Table 7-5. X configuration command T= letters

Key-letter

Default

Description

E

5 minutes

Overall timeout from sending EOM to filter to final EOM reply

R

10 seconds

Timeout for reading reply from the filter

S

10 seconds

Timeout for sending information from the MTA to a filter

C

5 minutes

Connection timeout

The form for each key-letter looks like this:

letter:value 

Space can surround the colon. If you specify more than one key-letter with a value, you must separate each from the other with a semicolon. Again, space can surround each semicolon:

letter:value;letter :value  

For example, the following code sets a timeout of 600 seconds for the connection to the socket, and 20 seconds for reads and writes:

T=C:600s; R:20s; S:20s

The letter s following each number stands for seconds. Instead, you can choose to use the letter m, which stands for minutes. The letters h for hours, d for days, and w for weeks are also available, but they don't make sense for use with this equate.

Note that for the C: key-letter, if you set the value to zero, the default timeout for the connect(2) system call will be used. See your system documentation to determine that default.

7.6.3 The InputMailFilters Option

Filters to connect to for processing messages through external programs are declared with the X configuration command (Section 7.6.2). One form of that command (for use in your mc file) not only declares the filter, but also defines the order in which the filters will be called:

INPUT_MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R')
INPUT_MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')

Here, the filters will be called in the order progA first and progB second, for each phase of the message. Table 7-6 shows which portion of the message is checked by each filter in time order. Note the change in order when the DATA phase begins (header/body).

Table 7-6. Filters called in time order

Filter

Screens what

progA

Connection information, such as hostname and IP address

progB

Connection information, such as hostname and IP address

progA

HELO/EHLO greeting information

progB

HELO/EHLO greeting information

progA

MAIL FROM: address and ESMTP arguments

progB

MAIL FROM: address and ESMTP arguments

progA

RCPT TO: address and ESMTP arguments

progB

RCPT TO: address and ESMTP arguments

progA

The message headers

progA

The message body

progA

The end of a message (a semaphore)

progB

The message headers

progB

The message body

progB

The end of a message (a semaphore)

Each filter is handed portions of a message envelope and body, in phases. For each phase, the filter can advise sendmail of one decision among several possible decisions. See libmilter/docs for a full description of this process.

Filters can also be declared with the MAIL_FILTER mc macro, but it does not set the order:

MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R')
MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')

When the order is not set, or when it is set but you wish to change it, you can use the InputMailFilters option. It defines the order for calling filters:

O InputMailFilters=progB, progA cf file 
define(`confINPUT_MAIL_FILTERS', `progB, progA')  mc file 

Here, the InputMailFilters option defines the order that the filters will be called to be the reverse of what was defined with the MAIL_FILTER mc command.

If you fail to define an order for the filters, no filters will be called, and no message screening will happen.

If your version of sendmail was not compiled with -DMILTER defined and you declare this option, you will get the following error, and sendmail will exit:

Warning: Option: InputMailFilters requires Milter support (-DMILTER)

If you list more than the number of filters permitted by MAXFILTERS (which defaults to 25), the following error will print and the extra filters will be ignored:

Too many filters defined, 25 max

If you misspell one of the filter names, the following error will print and that filter will be ignored:

InputFilter probB not defined

Note that all external programs and filters are connected to when the message is received via SMTP (either over the network or with the -bs command-line switch). None is called just before the message is transmitted. Such output filtering might appear in a future release of sendmail.

7.6.4 Build a Filter

A mail filter is a program that listens on a socket. It receives a message on that socket from sendmail interactively and in pieces. The sendmail program first offers the connection information, and the filter can take it for review, or decline it. If it accepts, it will screen that information and either reject the message based on its review, or allow the message. Then the next piece is offered and reviewed in the same manner. The order of the review is:

Connect

Review based on the IP address and hostname of the connecting site.

helo/ehlo

Review based on the hostname given as part of the SMTP HELO or EHLO command.

Sender

Review the envelope-sender as supplied as part of the SMTP MAIL FROM: command.

Recipient

Review the envelope-recipient as supplied as part of the SMTP RCPT TO: command.

Headers

Review the header portion of the email message.

EOH

Signals the end of the header portion of the message.

Body

Review the message body, which can include MIME-encoded portions.

EOM

Signals the end of the body portion of the message.

The program must quickly (within the timeouts defined by the X configuration command) parse the pieces and decide if the message should be accepted or rejected. The program then advises sendmail of its decision, using the libmilter API.

The sendmail source distribution includes a library and sample program that you should use to create your own filter program. Look in the directory libmilter. It contains the source for the library, a README file with the latest information, and a subdirectory libmilter/docs that contains all the documentation you will need in HTML format.

We recommend you build your filter program using the supplied library. Don't dig through the source to divine the current protocol because that protocol will evolve from version to version. Instead, use the API provided by the library.

If you don't wish to write your own filter program, consider the following, and search the Web for others:

http://www.milter.org/

A guide to and discussions about MILTERs in general.

http://mailbox.univie.ac.at/~at/vilter/

The vilter program scans incoming email and rejects or flags with a header line the infected messages.

http://www.amavis.org/

The amavis program is a mail virus scanner.

http://aeschi.ch.eu.org/milter/

The vbsfilter program will rename a variety of executable attachments to .txt, thus rendering them harmless.

http://sendmail.com/

Sendmail, Inc. offers several commercial filters.

    Previous Section Next Section