Enhance Local Mail Handling on Your Unix Server

SAN FRANCISCO (05/03/2000) - Procmail is a drop-in replacement for /bin/mail that does much, much more than the latter program. Some other appealing features are its simplicity -- just one executable and one config file -- and its performance. Written in C, it has a low impact on system resources and claims to be 1.4 times faster than the average /bin/mail in user-CPU time.

Procmail's system requirements include any Unix or POSIX-compliant system and a compatible mail transport agent (MTA). The latter can be any mailer that can process RFC822-compliant mail, including Sendmail, Qmail, or Postfix.

The latest version of the procmail sources is available from the procmail FTP site. As of this writing, the latest version is 3.14, but owing to some minor bugs that won't be fixed until the release of 3.14.1, version 3.13.1 should be regarded as the latest stable version.

Configuration is a matter of reviewing and making necessary changes to the makefile and config.h files, then running make install. These files shouldn't require any changes, because the make process includes a sophisticated autoconfiguration routine. If not compiling as root, you'll have to separate the steps with make, followed by su, and then make install.

It is recommended that procmail be installed suid root. You should note that when in explicit delivery mode (e.g. when called from Sendmail), procmail will always change uid and gid to the recipient's defaults as soon as it starts reading his or her .procmailrc file.

When not in explicit delivery mode (e.g. when called from the recipient's .forward file), procmail will always change uid and gid to the real uid and gid of the invoker, thus losing any suid and sgid privileges. These two precautions should eliminate any security holes, because procmail will always have the uid of the user whose commands it is executing.

Using procmail with sendmail

Procmail can be used with sendmail as a local mail delivery agent, or as a general-purpose prog mailer. If you're using a recent version of sendmail, you can use the m4 macro files to greatly simplify sendmail configuration for whichever use of procmail you desire. In this case, there are four variables and two features that relate to procmail. The features are procmail and local_procmail; the variables (with default value in brackets) are:

PROCMAIL_MAILER_PATH -- [/usr/local/bin/procmail]: The path to the procmail program. This is also used by FEATURE(local_procmail).

PROCMAIL_MAILER_FLAGS -- [SPhnu9]: Flags added to procmail mailer. Flags DFM are always set. This is not used by FEATURE(local_procmail); tweak LOCAL_MAILER_FLAGS instead.

PROCMAIL_MAILER_ARGS -- [procmail -Y -m $h $f $u]: The arguments passed to the procmail mailer. This is not used by FEATURE(local_procmail); tweak LOCAL_MAILER_ARGS instead.

PROCMAIL_MAILER_MAX -- [undefined]: If set, establishes the maximum size message that will be accepted by the procmail mailer.

Local mailer

The easiest way to use procmail is as a drop-in replacement for the local mailer. This entails mail delivery as normal, plus extra processing if a .procmailrc file is present in the home directory of the recipient. If you're building a sendmail config file from scratch, or if you maintain your sendmail config using the m4 macros, define local_procmail in your .mc file as FEATURE(local_procmail) and to rebuild sendmail.cf. This doesn't use PROCMAIL_MAILER_FLAGS or PROCMAIL_MAILER_ARGS for the local mailer; you have to tweak LOCAL_MAILER_FLAGS and LOCAL_MAILER_ARGS instead.

If you maintain sendmail.cf directly, you must edit the local mailer definition. If you're using a sendmail 8.6.x or older, use this:

Mlocal, P=/usr/local/bin/procmail, F=lsSDFMhPfn, S=10, R=20, A=procmail -Y -a $h -d $uIf you're using sendmail 8.7 or newer, then use this:

Mlocal, P=/usr/bin/procmail, F=SAw5:|/@glDFMPhsfn, S=10/30, R=20/40, T=DNS/RFC822/X-Unix, A=procmail -Y -a $h -d $uProcmail as a local mailer needs root privileges on startup. On some systems, procmail may also need to be sgid daemon or mail. Check this by looking at the current mail delivery agent and copying its permissions. You can also use make recommend, which will suggest appropriate permissions for your system. The optional lockfile program may also need the same group permissions, to allow it to create and unlink lockfiles in the mail spool directory.

Changing the mail spool directory to the user's home directoryYou may wish to store mail in the recipient's home directory, rather than in the traditional /usr/spool/mail. This has some obvious advantages. Mail is subject to the user's quota limitations. There is often more room on the home partition(s) than on /usr/mail. The quota limitations also apply to /usr/spool/mail or /usr/mail if procmail does the delivery. These quota limitations often don't work with the regular /bin/mail because that typically writes the mailbox with root permissions (eluding the quota restrictions).

If you intend to install procmail as the local mailer and you want mail to be delivered to, say, $HOME/.mail by default, uncomment, and if necessary change, SYSTEM_MBOX in config.h, and rebuild the program. Procmail will now deliver to this location by default. All users must have their MAIL environment variable set to this value. If a mail client in use at your site doesn't use MAIL, either fix the source of that program or create symbolic links in /var/mail.

Prog mailer

Procmail can also be used as a prog mailer and then as a general mail filter.

Use the procmail feature when configuring sendmail with m4 macros. The entry in sendmail.cf reads as follows:

Mprocmail, P=/usr/local//bin/procmail, F=mSDFMhun, S=11, R=21, A=procmail -m $h $g $uMailertablesSendmail and procmail in combination provide a simple way to forward all mail for a given domain to a single person, using the sendmail mailertable feature.

This involves defining procmail as a prog mailer. mailertable:example.com procmail:/etc/procmailrcs/example.com/etc/procmailrcs/example.com:

:0 # forward mail for example.com

! -oi -f $1 person@other.host

This would send any mail for (anyone)@example.com to person@other.host. Within the procmail script, $1 is the name of the sender and $2 is the name of the recipient. Note that if you use this with FEATURE(local_procmail), you should list FEATURE first.

Basic recipes

The rc file, whether it's the global /etc/procmailrc or each user's $HOME/.procmailrc, can have two types of entry: environment variable assignments and recipes.

The key concept with procmail is the idea of a recipe, which is a rule in the procmail rc file (/etc/procmailrc or $HOME/.procmailrc). Each recipe has three parts: header, pattern, and action. The header identifies this as a recipe and declares options. The pattern, as the name implies, matches a regular expression against the message header, or body, or both. The action performs some task if the regular expression matches.

:0 [flags] [ : [locallockfile] ]

[zero or more conditions (one per line)][exactly one action line]A second colon, after the 0 on the first line, tells procmail to use locking.

Locking is important because you may have two procmail processes running at the same time, both trying to deliver mail to the same location. A second colon alone says to [?what!?]; followed by the filename to be given to the lock file.

The lockfile will be created and unlinked after processing is complete. Thus, if a second instance of procmail should happen along in the meantime, it will see that the file already exists and wait.

Flags can be any combination of the following (though of course some combinations will not make much sense):

H: Match the pattern against the header (default).

B: Match the pattern against the body.

D: Distinguish case in pattern matches (default is to ignore case).

A: On a match, only follow this recipe if the last recipe without the A flag matched as well. a: as A, but, additionally, the last recipe must have successfully completed.

E: The inverse of A -- that is, only follow this recipe if the immediately preceding recipe was not executed. e: Only follow this recipe if the immediately preceding recipe failed -- that is, the action line was executed and returned an error code. h: Feed the header to the action line (default). b: Feed the body to the action line (default). f: Treat the action line pipe as a filter. c: Generate a carbon copy of this mail. Processing of the mail continues. w: Wait for the action line filter or program to finish and check its exit code (which is usually ignored). If the filter is unsuccessful, the mail hasn't been processed.

W: As w, but suppressing any "program failure" message.

I: Ignore any write errors, as one might receive from an early closed pipe. r: Raw mode; just write the mail out as is. Without this, procmail will make sure the message ends with an empty line.

The condition mostly uses standard egrep regular expressions syntax, with a few additions. These are:

!: Invert the condition.

$: Evaluate what follows using the sh(1) substitution rules for matter inside double quotes. Skip leading whitespace and reparse it.

?: Use the exit code of the specified program.

<: bytes.="" check="" is="" length="" less="" message="" number="" of="" specified="" than="" the="" total="" whether="">

>: Check whether the total length of the message is greater than the specified number of bytes. variable ??: Match the remainder of the condition against the value of this environment variable.

\: Escape, to quote any of the special characters above at the start of a line.

Finally, the action line offers these possibilities:

!: Forwards the message to the specified email addresses.

|: Starts the specified program. If the command given contains shell metacharacters, a shell will be started in which to run it. Variable assignment before this symbol will assign the output of the program to that variable. A pipe symbol alone, with no program, will send the email to stdout.

{: Followed by whitespace, { marks the start of a nesting block. Everything until a closing } will depend on the conditions specified for this recipe.

For example:

:0

*

mail

This is a very simple recipe that stores mail in a file called mail. As you can see, the first line of a recipe begins with :, and the second line with *. A recipe can have more than one pattern line, but only one header line and only one action line. Don't confuse the star at the beginning of the pattern line with a wildcard. The default matching action is "match," and because we don't have a pattern this recipe will always succeed.

When writing procmail recipes, it's best to test them first. You can do this easily on the command line. Simply invoke procmail (giving the name of the rc file you wish to test as the first argument) and redirect a sample email message to procmail's standard input, like so:

$ procmail ./test.rc < ./sample.msg

Where sample.msg is of the form:

From: somebody@example.com

To: me@example.com

This is a test

Note the blank line between header and body. Replace the To: address with your own. Let's test our example recipe.

$ procmail test.rc < test.msg

procmail: [991] Mon Mar 1 17:05:45 1999

procmail: Match on ".*"

procmail: Assigning "LASTFOLDER=mail"

procmail: Opening "mail"

procmail: Acquiring kernel-lock

Folder: mail

$ cat mail

From: somebody@example.com

To: paul

This is a test

Procmail uses special environment variables that you can change in the rc file.

Here is a list of the more important ones.

LOGABSTRACT: If set, procmail writes a one-line summary to the logfile, consisting of the From and Subject fields, the location where the message went, and its size in bytes.

LOGFILE: Where to send error, diagnostic and LOGABSTRACT messages.

SHELL: What shell to use. Default is /bin/sh VERBOSE: Turns on extended diagnostics. Default is off.

Recipes in action

Here is a simple /etc/procmailrc that logs all arriving mail in what may be a more convenient and easily-customizable format than sendmail. It also does some simple MIME conversions (the recipe for this is taken from procmailex(5)), and implements a very simple autoresponder that replies to people who write to certain addresses. The autoresponder is easily changed so that, for example, people writing to abuse@yourdomain.com get a formula message about spam concerns, with their message then being sent on to the appropriate person at your site.

# set some environment variables

SHELL=/bin/sh

LOGFILE=/var/log/procmail

LOGABSTRACT=all

VERBOSE=off

# convert MIME types

:0

* ^Content-Type: *text/plain

{

:0 fbw

* ^Content-Transfer-Encoding: *quoted-printable | $MIMENCODE -u -q :0 Afhw | formail -I "Content-Transfer-Encoding: 8bit" :0 fbw * ^Content-Transfer-Encoding: *base64 | $MIMENCODE -u -b :0 Afhw | formail -I "Content-Transfer-Encoding: 8bit"}# auto-responder:0 H* $ ! ^$MYXLOOP* ! ^FROM_DAEMON* ^Subject: *(get|send|mail|gimme)\/( +(info|help))+{ GETTING=$MATCH # What got me here?

MYFROM="From: example.com's Magic Mail Responder " SUBJECT=`formail -xSubject` BOUNDARY=`formail -x'Message-Id:' | sed 's/[@ ]//g' | cut -c1-65` # log this in its own logfile LOGFILE= AUTOREPLYLOG=/var/log/autoreply-log :0 ch:

$AUTOREPLYLOG

# This is how I get real headers.

:0 fhW

| formail -rY -I"$MYFROM" \

-I"$MYXLOOP"

BOUNDARY=`echo '' ; echo '' ; echo --$BOUNDARY` :0 fbW | echo '' ; \ echo $BOUNDARY ;\ echo '' ;\ echo 'You asked for it, you got it.';\ echo ''; # help :0 fWb * GETTING ?? help | cat - ; echo $BOUNDARY ; \ echo '' ; cat /usr/local/etc/company-info; echo '' :0 | ( cat - ; echo "$BOUNDARY--" ) | $SENDMAIL -t -oi }LOGABSTRACT=$ABSTRACTVALThere is much more to procmail, including the use of its companion programs, formail(1) and lockfile(1). In this article, I simply wanted to explain how procmail can enhance local mail handling on a Unix server. The resources given below should be a help in exploring procmail in more detail.

Paul Dunne is a freelance writer and consultant.

Join the newsletter!

Error: Please check your email address.

More about SendMailSpool

Show Comments

Market Place