Chapter 12. Maintain Aliases

Aliasing is the process of replacing one recipient address with one or more different recipient addresses. The replacement address can be that of a single user, a list of recipients, a program, a file, or any mixture of these. In this chapter, we cover the aliases(5) file, one of the three methods of aliasing available with the sendmail program. We will cover the other two forms, :include: (for including separate files from within the aliases file) and ~/.forward (the user’s personal :include: file), in the next chapter.

Aliasing can be used to handle several complex delivery problems:

  • Delivering mail to a single user under a variety of usernames

  • Distributing a mail message to many users by specifying only a single recipient name

  • Appending mail to files for archival and other purposes

  • Filtering mail through programs and shell scripts

All the information that is needed to perform these tasks is contained in the aliases(5) file (which is often also stored in database format to expedite the lookup process).

The aliases(5) File

The aliases(5) file is one of several sources that can supply system mail aliases. We describe it first because it is the most traditional and because it illustrates the syntax and limitations common to all techniques.

The aliases(5) file is composed of lines of text. Any line that begins with a # is a comment and is ignored. Empty lines (those that contain only a newline character) are also ignored. Any line that begins with a space or a tab is joined (appended) to the line above it. All other lines of text are viewed as alias lines. The format for an alias line is:

local: alias

The local must begin a line. It is an address in the form of a local recipient address (we will discuss this in more detail soon). The colon follows the local on the same line and can be preceded with spaces or tabs. If the colon is missing, sendmail prints and syslog(3)s the following error message, and skips that alias line:

missing colon

The alias (to the right of the colon) is one or more addresses on the same line. Indented continuation lines are permitted. Each address should be separated from the next by a comma and optional space characters. A typical alias looks like this:

root: jim, sysadmin@server,
      gunther
↑
  indenting whitespace

Here, root is the local address to be aliased. When mail is to be locally delivered to root, it is looked up in the aliases(5) file. If found, root is replaced with the three addresses shown earlier, and mail is instead delivered to those other three addresses.

This process of looking up and possibly aliasing local recipients is repeated for each recipient until no more aliases are found in the aliases(5) file. That is, for example, if one of the aliases for root is jim and if jim also exists to the left of a colon in the aliases file, he too is replaced with his alias:

jim: jim@otherhost

The list of addresses to the right of the colon can be mail addresses (such as gunther or jim@otherhost), the name of a program to run (such as /etc/relocated), the name of a file onto which to append (such as /usr/share/archive), or the name of a file to read for additional addresses (using :include:, which will be covered in the next chapter).

The aliases(5) File’s Location

The location of aliases(5) is specified with the ServiceSwitchFile option (ServiceSwitchFile on page 1088) and the AliasFile option (AliasFile on page 970) in the configuration file. Be aware that because these two options interact, it might not suffice to simply declare one or the other. Also be aware that some systems (such as Solaris) supply service-switch files that will override the ServiceSwitchFile option’s setting.

Note that the service-switch file merely specifies the order in which various methods should be used to look up aliases, not the specific files. If it lists files as a method:

aliases    files

all the files declared with the AliasFile option will be looked up in the order in which they were declared:

  • If the AliasFile option specifies a file and if a service-switch file omits the files specification, the AliasFile option is ignored.

  • If the AliasFile option specifies a file and if a service-switch file omits the aliases line, the AliasFile option is used.

  • If the AliasFile option specifies a file and if there is no service-switch file, the AliasFile option file is used, except on systems that implement their own service-switch files.

  • If the AliasFile option is omitted and if there is no service-switch file or if there is a service-switch file but it omits an aliases line, sendmail silently presumes that it should not do aliasing.

Note that service-switch files and the AliasFile option can list other techniques for obtaining aliases in addition to, or instead of, an aliases(5) file. But this can lead to a side effect. For example, if your configuration file declares:

O AliasFile=/etc/aliases,nis:

and if the service-switch file aliases line specifies:

aliases    nis files

sendmail looks up aliases first with nis, then in the /etc/aliases file, then with nis a second time.

Local Must Be Local

The local part of an alias must be in the form of a local recipient.[195] This restriction is enforced each time sendmail reads the aliases(5) file. For every name to the left of a colon that it finds, sendmail performs the following normalization and verification steps.

To begin, sendmail normalizes each address by removing everything but the address part. For example, consider the following two alias lines:

george (George Washington): gw
George Washington <george>: gw

When sendmail reads these lines, it normalizes each into its address part:

george (George Washington)    becomes → george
George Washington <george>    becomes → george

Afterward, the address part is extracted and rewritten by the canonify rule set 3 and the parse rule set 0, to see whether it causes any delivery agent with the F=A flag set (F=A on page 767) to be selected. Generally, local addresses select the local delivery agent, which normally has the F=A flag set. Nonlocal addresses (such as gw@another.host) generally select one of the smtp delivery agents, which normally do not have the F=A flag set.

Prior to V8.7 sendmail, an address had to select the local delivery to allow itself to be aliased.

If the selected delivery agent has the F=u flag set (F=u on page 780), the address will be converted to lowercase before being looked up in the aliases database.

In the earlier example, the address george (after processing) selects the local delivery agent, and so these alias lines are legal. Internally (or in its database), sendmail stores the earlier alias as:

george: gw

When mail arrives that is addressed for delivery to george, sendmail rewrites that address with the canonify rule set 3 and the parse rule set 0. The parse rule set 0 selects the local delivery agent (or, for V8.7 and above, any agent with F=A set). The address george is looked up and replaced with gw. Internally, sendmail marks the recipient george as defunct, having been replaced with an alias, and then adds gw to the list of recipients.

The new recipient, gw, is then processed for delivery. The canonify rule set 3 and the parse rule set 0 are called once more and again select a local delivery agent. As a consequence, gw is also looked up. If it is found to the left of a colon in the aliases file, it too is replaced with yet another address (or addresses). This process repeats until no new local addresses are found.

Note that the entry george is marked defunct rather than being deleted to detect alias loops. To illustrate, consider the following two mutually referencing aliases:

george: gw
gw: george

The sendmail program first replaces george with gw, marking george as defunct. It goes to mark gw as defunct but notices that a loop has been formed. If sendmail is running in verbose mode (Verbose on page 1117), it prints:

aliasing/forwarding loop broken

and bounces the message.

Note also that aliases can get pretty complex. As a consequence, when one address aliases to many new addresses, this autodetection of loops can fail (but the problem will be caught later with “hop counting;” see MaxHopCount on page 1046).

Alias Nonlocal Addresses

As distributed, a normal configuration file will disallow certain addresses on the left side of the aliases file. Consider the following two addresses:

Bob@our.host:           bob
Bob@another.host:       bob@home.isp

In both examples, the intention is for mail to bob at the local host (our.host) to be delivered to the local mailbox for the user bob. This will happen in the first example (assuming a normal configuration file) because the @our.host part of the address will be removed by rule sets:

canonify           input: Bob @ our.host
Canonify2          input: Bob < @ our.host >
Canonify2        returns: Bob < @ our.host . >
canonify         returns: Bob < @ our.host . >
parse              input: Bob < @ our.host . >
Parse0             input: Bob < @ our.host . >
Parse0           returns: Bob < @ our.host . >
ParseLocal         input: Bob < @ our.host . >
ParseLocal       returns: Bob < @ our.host . >
Parse1             input: Bob < @ our.host . >
Parse1           returns: $# local $: Bob
parse            returns: $# local $: Bob
2                  input: Bob
2                returns: Bob
EnvToL             input: Bob
EnvToL           returns: Bob
final              input: Bob
final            returns: Bob
mailer local, user Bob

Because the local delivery agent was selected, and because that delivery agent has the F=A flag set (F=A on page 767), mail to Bob@our.host will be aliased for local delivery to the user bob.

The second address, , however, selects an esmtp delivery agent:

canonify           input: Bob @ another . host
Canonify2          input: Bob < @ another . host >
Canonify2        returns: Bob < @ another . host >
canonify         returns: Bob < @ another . host >
parse              input: Bob < @ another . host >
Parse0             input: Bob < @ another . host >
Parse0           returns: Bob < @ another . host >
ParseLocal         input: Bob < @ another . host >
ParseLocal       returns: Bob < @ another . host >
Parse1             input: Bob < @ another . host >
MailerToTriple     input: < > Bob < @ another . host >
MailerToTriple   returns: Bob < @ another . host >
Parse1           returns: $# esmtp $@ another . host $: Bob < @ another . host >
parse            returns: $# esmtp $@ another . host $: Bob < @ another . host >
2                  input: Bob < @ another . host >
2                returns: Bob < @ another . host >
EnvToSMTP          input: Bob < @ another . host >
PseudoToReal       input: Bob < @ another . host >
PseudoToReal     returns: Bob < @ another . host >
MasqSMTP           input: Bob < @ another . host >
MasqSMTP         returns: Bob < @ another . host >
EnvToSMTP        returns: Bob < @ another . host >
final              input: Bob < @ another . host >
final            returns: Bob @ another . host
mailer esmtp, host another.host, user Bob@another.host

Because the esmtp delivery agent does not have the F=A flag set, the presence of the address will be disallowed on the lefthand side of the aliases file:

% newaliases
/etc/mail/aliases: line 2: Bob@another.host... cannot alias nonlocal names

In the rare circumstance that you need to be able to alias nonlocal addresses, you can do so by adding the F=A flag to the smtp class of delivery agents. You do this by editing your mc configuration file and adding the following line above the definition for that class of delivery agents:

APPENDDEF(`SMTP_MAILER_FLAGS', `A')        ← prior to V8.10
MODIFY_MAILER_FLAGS(`SMTP', `+A')          ← V8.10 and above
MAILER(smtp)                               ← this must follow flag modifications

After that, build a new configuration file from this new mc file and install it. Thereafter, you will be able to successfully alias nonlocal addresses without errors.

Before undertaking this step, however, see FEATURE(virtusertable) on page 645 for a description of the FEATURE(virtusertable) which also allows nonlocal addresses to be transformed into inside or outside addresses. Note, too, that the User Database (userdb on page 942) allows recipient addresses to be changed so that they can be delivered to new hosts, and that the FEATURE(genericstable) in FEATURE(genericstable) on page 622 allows sender addresses to be changed to appear to be coming from new hosts. Clearly, there are many ways to achieve the same result, and one of those might be more suitable to your needs than the F=A flag.

Forms of Alias Delivery

Addresses in the righthand side of an alias entry can take four forms:

LHS:   user
LHS:   /file
LHS:   |program
LHS:   :include: file

The user specifies final delivery to a user’s mail spool file (subject to change by the user’s ~/.forward file), or delivery to a new address (e.g., newuser or user@newsite). The /file specifies delivery by appending to a file. The |program specifies delivery by piping the message through a program. The :include: specifies processing of a mailing list. The first three are covered here. The last is covered in the next chapter.

These righthand sides can be combined on a single line, where one is separated from another by a comma. For example:

LHS:  user,/file

Delivery to Users

Any address in the list of addresses to the right of the colon that does not begin with a /, |, or : character is considered to be the address of a user. The address can be local or remote.

If that user address to the right of the colon is prefixed with (or contains) a backslash character (\) and the address is a local one, all further aliasing is suppressed (including reading the user’s ~/.forward file), and the message is delivered with the local delivery agent.

Delivery to Files

When any of the addresses to the right of a colon in the alias list begin with a / character, delivery is made by appending the mail message to a file. This is automatic with all modern configuration files, but there are exceptions.[196] Beginning with V8.7 sendmail, any delivery agent for which the F=/ flag (F=/ (forward slash) on page 766) is set can also append messages to files. If you want to disable this ability, delete the F=/ flag from all delivery agent declarations in your configuration file.

In the list of addresses to the right of the colon, sendmail considers any local address that begins with the / character to be the name of a file.[197] Whenever the recipient address is a file, sendmail attempts to deliver the mail message by appending it to the file. This ability to deliver mail to files is included in sendmail primarily so that failed mail can be saved to a user’s ~/dead.letter file. It can also be used (through use of aliases) to deliver mail to other files, but that use is less than optimal, as you will see.

To deliver to a file, sendmail first performs a fork(2) and gives the child the task of delivery. The fork is necessary so that sendmail can change its effective uid and gid, as we will show. The child then performs a stat(3) on the file. If the file exists, its file permissions are saved for later use. If it doesn’t exist, the saved permissions are defaulted to 0600. Under V8.7, the decision to use stat(2) versus lstat(2) to obtain the permissions is determined by the SafeFileEnvironment option (SafeFileEnvironment on page 1084). Beginning with V8.9, the decision to use stat(2) versus lstat(2) depends on the FileDeliveryToSymLink setting (DontBlameSendmail=FileDeliveryToSymLink on page 1012) for the DontBlameSendmail option.

If the saved permissions have any execute bit set, the child exits with EX_CANTCREAT as defined in <sysexits.h>. If the file has a controlling user associated with it, any set-user-id and set-group-id bits are stripped from the saved permissions. If the file was listed in a ~/.forward file, the controlling user is the owner of the ~/.forward file. If it was listed in an :include:'d file, the controlling user is the owner of the included file. If the message is being processed from the queue, the controlling user can be specified in the qf file (C line on page 447).

Then, the queue df file (D line on page 449) is opened for reading (if it is not already open). If that file cannot be opened, sendmail prints the following error message but continues to attempt delivery:

mailfile: Cannot open df for file from sender

Here, the df is the name of the queue datafile that cannot be opened. The file is the name of the file to which sendmail is attempting to deliver the message. The sender is the address of the sender of the mail message.

Next, if the SafeFileEnvironment option (SafeFileEnvironment on page 1084) was declared, sendmail performs a chroot(2) into the directory specified. If the chroot(2) fails, sendmail prints and logs the following error and the child exits with EX_CANTCREAT:

mailfile: Cannot chroot(directory)

Next, regardless of whether the df file is opened, sendmail changes its gid:

  • If there is a controlling user, sendmail sets its gid to that of the controlling user.

  • Otherwise, if the set-group-id bit is set in the file’s saved permissions, sendmail changes its gid to that of the group of the file.

  • Otherwise, sendmail checks to see whether the U= equate is set for this delivery agent (U= on page 755). If the U= equate is set, sendmail changes its gid to that specified.

  • Otherwise, sendmail changes its gid to that specified by the DefaultUser option (DefaultUser on page 1000).

After this, sendmail changes its uid, using the same rules that it used for the gid.

The file (and possibly the path to it) is then checked to see whether it is safe to write to. See the -d44 debugging switch (-d44.4 on page 569) for a description of this process.

If safe, the file is then opened for writing in append mode. If sendmail cannot open the file, it prints the following error message, and the child exits with EX_CANTCREAT:

cannot open: reason for error here

If an open fails for a retryable reason, it is attempted 10 more times (sleeping progressively longer between each try)[198] on the assumption that on busy systems there might be a temporary lack of resources (such as file descriptors). The open includes file locking with flock(2) or fcntl(2) to prevent simultaneous writes.

Once the file is opened, the header and body of the mail message are written to it. Note that translations are controlled by the F= flags of the prog delivery agent for all but V8 sendmail. V8 sendmail uses the F= flags of the *file* delivery agent. For example, F=l (F=l (lowercase L) on page 774) marks this as final delivery.

If any write error occurs, sendmail prints the following error message, truncates the file to its length before any writes started, and quits trying to write to that file:

I/O error

Finally, the file’s permissions are restored to those that were saved earlier, and the file is closed with fclose(3). If the set-user-id or set-group-id bits were stripped because there was a controlling user, they are restored here.[199] If the file didn’t originally exist, its permissions become 0600.

Delivery Via Programs

When any of the addresses to the right of a colon in the alias list begin with a | character, delivery is made by piping the mail message through a program. This is automatic with modern configuration files.[200] Beginning with V8.7 sendmail, any delivery agent for which the F=| flag (F=| (vertical bar) on page 765) is set can also pipe messages through programs. To disable this ability, simply remove the F=| flag from all delivery agent declarations in your configuration file.

The forms that a program address can legally take in the aliases(5) file (or ~/.forward file; see Piping Through Programs on page 504) are as follows:

|prg
"|prg args"
|"prg args"

Here, prg is the full path of the program to be run (the environment variable PATH is not available). If command-line arguments are needed for the program, they must follow prg, and the entire expression must be quoted. The leading full quotation mark can either precede or follow the |. If the address is quoted with full quotation marks, the leading quotation mark is ignored in determining the leading | character.

To execute the program, sendmail executes the command in the P= equate of the prog delivery agent. That command is usually one of the following:

/bin/sh -c
/usr/bin/smrsh -c

These tell sendmail to run /bin/sh (the Bourne shell) or /usr/bin/smrsh (the sendmail restricted shell) to execute the program specified by prg. The -c tells that shell to take any arguments that follow and execute them as though they were commands typed interactively to the shell. These arguments are constructed by removing the leading | from the program address and appending what remains, quotation marks and all, to the P= command. For example, if an alias looked like this:

jim: "|/etc/local/relo jim@otherhost"

the Bourne shell would be executed with the following command line:

/bin/sh -c "/etc/local/relo jim@otherhost"

The result of all this is that sendmail runs the Bourne shell and then the Bourne shell runs the /etc/local/relo program.

Mail is delivered under this scheme by attaching the output of sendmail to the standard input of the shell and attaching the standard output and standard error output of the shell to the input of sendmail. The sendmail program simply prints the mail message to the shell and reads any errors that the shell prints in return.

Although this process appears to be fairly straightforward, many things can go wrong. Failure usually results in the mail message being bounced.

Possible failures

To communicate with the P= program (the Bourne shell), sendmail creates two communications channels using pipe(2). This can fail because the system is out of file descriptors or because the system file table is full. Failure results in one of the following errors:

openmailer: pipe (to mailer)
openmailer: pipe (from mailer)

Next, sendmail executes a fork(2). The child later becomes the P= program. This can fail because the system limit on the maximum allowable number of processes has been exceeded or because virtual memory has been exhausted. Failure causes the following error message to be printed:

openmailer: cannot fork

In establishing a communications channel, the sendmail child process creates a copy of its standard input file descriptor. This can fail because the system limit on available file descriptors has been exceeded. When this happens, the following message is printed (note that not all dup(2) failures produce error messages):

Cannot dup to zero!

Finally, the child transforms itself into the A= program with execve(2). If that transformation fails, the following error message is produced, where program is argv[0] for the A= program (in this case, usually /bin/sh):

Cannot exec program

Failure can be caused by a wide range of problems. If a retryable error occurs, the message is queued for a later try.

Programs in the aliases file are run with the prog delivery agent. As a consequence, that delivery agent should have the F=s (strip quotes) flag set.

Write a Delivery Agent Script

The program that is driven by the prog delivery agent can be a compiled executable binary, a shell script, or even a perl(1) script. The limitation on the kind of program that can be run is made by the sh(1) shell (if sh -c is used in the A=) or by execve(2) (if it is launched directly from the P=). You need to read the manuals on your system to determine your limitations. For example, not all versions of sh(1) allow constructs such as the following in scripts:

#!/usr/local/bin/perl

When this appears as the first line of a script, the #! tells sh(1) or execve(2) to run the program whose pathname follows, to execute the commands in the script.[201]

In writing a program for mail delivery using the prog delivery agent, some unexpected problems can arise. We will illustrate, using fragments from a Bourne shell script.

Duplicates Discarded

When sendmail gathers its list of recipients, it views a program to run as just another recipient. Before performing any delivery, it sorts the list of recipients and discards any duplicates. Ordinarily, this is just the behavior that is desired, but discarding duplicate programs from the aliases(5) file[202] can cause some users to lose mail. To illustrate, consider a program that notifies the system administrator that mail has arrived for a retired user:

#!/bin/sh
/usr/ucb/mail -s gone postmaster

This script reads everything (the mail message) from its standard input and feeds what it reads to the /usr/ucb/mail program. The command-line arguments to mail are a subject line of gone and a recipient of postmaster. Now consider two aliases that use this program:

george: "|/usr/local/bin/gone"
ben:    "|/usr/local/bin/gone"

When mail is sent to both george and ben, sendmail aliases each to the program |/usr/local/bin/gone. But because both of the addresses are identical, sendmail discards one.

To avoid this problem, design all delivery programs to require at least one unique argument. For example, the previous program should be rewritten to require the user’s name as an argument:

#!/bin/sh
if [ ${#} -ne 2 ]; then
        echo $0 needs a username.
        exit
fi
/usr/ucb/mail -s "$1 gone" postmaster

By requiring a username as an argument, the once-faulty aliases are made unique:

george: "|/usr/local/bin/gone george"
ben:    "|/usr/local/bin/gone ben"

Although the program paths are still the same, the addresses (names and arguments together) are different, and neither is discarded.

Correct exit(2) Values

The sendmail program expects its A= programs to exit with reasonable exit(2) values. The values that it expects are listed in <sysexits.h>. Exiting with unexpected values causes sendmail to bounce mail and gives an unclear message:

554 5.0.0 Unknown status val

Here, val is the unexpected error value. To illustrate, consider the following rewrite of the previous script:

#!/bin/sh
EX_OK=0                   # From <sysexits.h>
EX_USAGE=64               # From <sysexits.h>
if [ ${#} -ne 2 ]; then
        echo $0 needs a username.
        exit $EX_USAGE
fi
/usr/ucb/mail -s "$1 gone" postmaster
exit $EX_OK

Here, if the argument count is wrong, we exit with the value EX_USAGE, thus producing a clearer (two-line) error message:

/usr/local/bin/gone needs a username.
/usr/local/bin/gone... Bad usage.

If all goes well, we then exit with EX_OK so that sendmail knows the mail was successfully delivered.

Is It Really EX_OK?

When sendmail sees that the A= program exited with EX_OK, it assumes that the mail message was successfully delivered. It is vital for programs that deliver mail to exit with EX_OK only if delivery was 100% successful. Failure to take precautions to detect every possible error can result in lost mail and angry users. To illustrate, consider the following common C-language statement:

(void)fclose(fp);

If the file that is being written to is remotely mounted, the written data can be cached locally. All the preceding write statements will have succeeded, but if the remote host crashes after the last write (but before the close), some of the data can be lost. The fclose(3) fails, but the (void) prevents detection of that failure.

Even in writing small shell scripts, it is important to include error checking. The following rewrite of our gone program includes error checking but does not handle signals. We leave that as an exercise for the reader.

#!/bin/sh
EX_OK=0                   # From <sysexits.h>
EX_USAGE=64               # From <sysexits.h>
EX_TEMPFAIL=75            # From <sysexits.h>
if [ ${#} -ne 2 ]; then
        echo $0 needs a username.
        exit $EX_USAGE
fi
if /usr/ucb/mail -s "$1 gone" postmaster >/dev/null 2>&1
then
        exit $EX_OK
fi
exit $EX_TEMPFAIL

Note that by using EX_TEMPFAIL, we cause the message to be requeued if this script fails. That way, a bug in the script can be fixed, and the next queue run will succeed.

Special Aliases

The behavior of the sendmail program requires that two specific aliases (postmaster and MAILER-DAEMON) be defined in every aliases file.[203] Beginning with V8.7 sendmail, aliases that contain a plus character can be used to route mail on the basis of special needs. Also, beginning with V8.7 sendmail, databases that allow duplicates can be declared to help automate the creation of those files.

The Postmaster Alias

RFC2822 requires every site to accept for delivery mail that is addressed to a user named postmaster. It also requires that mail accepted for postmaster always be delivered to a real human being—someone who is capable of handling mail problems. If postmaster is not an alias, or a real user, sendmail syslog(3)s the following error:

can't even parse postmaster!

Unless a site has a real user account named postmaster, an alias is required in every aliases file for that name. That alias must be a list of one or more real people, although it can also contain a specification for an archive file or filter program. One such alias might look like this:

postmaster: bill, /mail/archives/postmaster,
       "|/usr/local/bin/notify root@mailhost"

Here, postmaster is lowercase. Because all aliases are converted to lowercase for lookup, Postmaster or even POSTMASTER could have been used for equal effect.

Note that there are three aliases to the right of the colon: a local user named bill, the full path of a file onto which mail messages will be appended, and a program to notify the user root at the machine mailhost that postmaster mail has arrived on the local machine. Naturally, a user should not have to be root to read mail, so on mailhost there would be a further alias of root to the address of a normal user.

As a convention, the special name postmaster can also be that of the user who gets duplicate copies of some bounced mail. This is enabled by using the PostmasterCopy option (PostmasterCopy on page 1064) in the configuration file:

OPpostmasterpre-V8.7
O PostmasterCopy=postmasterV8.7 and above
define(`confCOPY_ERRORS_TO', user)  ← mc configuration (V8.7 and later)

To disable sending copies of bounced mail to a special user (perhaps to protect privacy), omit this option from the configuration file.

Note that V8 sendmail does not send a copy of error mail to the postmaster if the error mail includes a Precedence: header with a value less than zero, such as junk, bulk, or list used by mailing lists.

Also note that some sites define this user as one who is always aliased to a filter program in the aliases file. For example, if the PostmasterCopy option is declared as:

OPmail-errorspre-V8.7
O PostmasterCopy=mail-errorsV8.7 and above
define(`confCOPY_ERRORS_TO', mail-errors) ←mc configuration (V8.7 and later)

and the corresponding aliases file entry is declared as:

mail-errors: "|/etc/mail/filter postmaster"

a program filter can be designed that discards all common error messages, such as mistyped addresses, and forwards what remains to postmaster.

Many sites have developed just such filters. One is distributed with the V8 sendmail source in the file contrib/mmuegel. Written by Michael S. Muegel of Motorola’s Corporate Information Office, it is a shar(1) file of several useful perl(1) scripts. One (postclip.pl) is a tool that filters out the body of bounced mail messages to prevent postmasters from potentially violating the privacy of senders.[204] It tries to retain all headers, regardless of how deeply they are buried in what appears to be the message body.

RFC2142 Common Mailbox Names

The name postmaster is required by RFC2822 and all sites must accept mail to that address. Another RFC, RFC2142, takes the concept of having a generalized postmaster address one step further by recognizing other roles, such as abuse, info, and marketing. For example, most web sites that sell products also accept email to the address sales, which is now a frequently used, generalized email address.

Table 12-1 shows all the newly required addresses defined by RFC2142. Of these, only postmaster is treated in a case-insensitive manner by sendmail.[205] That is, mail to postmaster, Postmaster, POSTMASTER, and PoStMaStEr will all be delivered to the same person.

Table 12-1. RFC2142-defined email addresses and aliases

Address

RFCs

Description

abuse

RFC2142

Accepts reports of unacceptable behavior

ftp

RFC959

Accepts mail reporting FTP needs or problems

hostmaster

RFC1033 through RFC1035

Accepts mail reporting needs or problems with DNS

info

RFC2142

Replies to requests for information about the business, its products, and its services

marketing

RFC2142

Handles marketing communications

news

RFC977

A synonym for Usenet

noc

RFC2142

Accepts mail for the network operations center, which deals with network infrastructure problems and requests

postmaster

RFC2821 and RFC2822

Accepts mail describing email problems

sales

RFC2142

Replies with product or service information

security

RFC2142

Sends or receives security notices, answers security concerns

support

RFC2142

Accepts mail describing problems with products or services

usenet

RFC977

Accepts email notification of problems with the Usenet News system (abuse should be reported to abuse, however)

uucp

RFC976

For sites that support UUCP, accepts mail describing problems with that service

webmaster

RFC2068

Accepts mail describing problems with or requests for changes in web services

www

RFC2068

A synonym for webmaster

Note that each of these “required” addresses is actually required only if you offer the service indicated in the description (in Table 12-1). For example, if you do not run UUCP (as few do), you may safely ignore mail to uucp. If you later add UUCP services, you should add an alias for uucp.

RFC2142, then, suggests that a well-formed aliases file might contain the following entries:

info:        recipient
marketing:   recipient
sales:       recipient
support:     recipient
abuse:       recipient
noc:         recipient
security:    recipient
postmaster:  recipient
hostmaster:  recipient
usenet:      recipient
news:        recipient
webmaster:   recipient
www:         recipient
uucp:        recipient
ftp:         recipient

Note that recipient will be a person in some instances, and in others it will be a program or a file.

In addition to requiring specific recipient addresses, RFC2142 also requires that mailing lists always have a mailbox that can be reached using the literal suffix -request. That is, if a mailing list is named bobs, the administrative address must be bobs-request.

This behavior is easy to maintain using sendmail and could be implemented in an aliases file entry that looks like this:

bobs:                :include:/mail/lists/bobs.list
owner-bobs:    postmaster
bobs-request:  bob

Here, the first line defines the actual mailing list as a list of addresses read from the file /mail/lists/bobs.list. The second line defines the address that should process bounced email generated by this list. The third line defines the -request address that will receive administrative email concerning the list.

The MAILER-DAEMON Alias

When mail is bounced, the notification of failure is always shown as being from the sender whose name is defined by the $n sendmail macro ($n on page 836). Traditionally, that macro is given the value mailer-daemon. The following, for example, shows how to use the confMAILER_NAME mc macro to assign the value mailer-daemon to the $n sendmail macro:

define(`confMAILER_NAME', `mailer-daemon')

That tradition is enforced by the fact that if $n is not defined, it defaults to mailer-daemon.

There needs to be an alias for whatever name is defined for $n because users occasionally make the mistake of replying to bounced mail. Two typical choices (one or the other) are:

mailer-daemon: postmaster
mailer-daemon: /dev/null

Here, the name to the left of the colon should be whatever was defined for $n in the configuration file, traditionally (and recommended to be) mailer-daemon. The first alias forwards all mailer-daemon reply mail to the postmaster. Many site administrators prefer the second, which discards such mail by using /dev/null.

Plussed Detail Addressing

Plussed detail addressing is a simple way to achieve more versatile aliasing. It is available only with V8.7 sendmail and above, and it requires that you use a configuration file that comes with V8 sendmail. To illustrate its use, consider the need to have mail routed to different sets of administrators depending on how the address root is augmented:

root: hans, george
root+db:   root, dbadmin@server.db.here.edu
root+*:    root, root@here.edu

Here, the first line shows a normal sort of alias in which mail sent to root will instead be delivered to the local users hans and george. The second line is still not all that special because we could as easily have used an alias such as root_db to accomplish the same thing. It sends mail to root+db to the local root users and to the database administrators in another department, dbadmin@server.db.here.edu.

The third line is where things start to get interesting. The +* in it will match anything or nothing following the plus, so mail sent to root+ will be sent both to the local root users and to the central administrators at root@here.edu. But so will anything following the plus that is not db, such as root+foo.

If the +* form is omitted:

root: hans, george
root+db:   root, dbadmin@server.db.here.edu

the default for plussed addresses other than root+db becomes root. That is, when sendmail looks up a plussed address (for example, root+foo) it does so in the following order:

  • Look for an exact match. Does root+foo match root+db?

  • Look for a wildcard match. Does root+* exist? If so, use that alias for root+foo.

  • Look for a base match. Does the root of root+foo exist as an alias? If so, use that alias for root+foo.

Note that plussed users is a simple mechanism that is intended to solve simple needs. In distributing a common aliases file to many machines, for example, plussed users can furnish a hook that allows customization based on simple alias extensions. Because plussed users is simple, attempts to extend it to handle complex needs will likely fail. If your needs are complex, consider using the User Database (userdb on page 942) or writing custom hooks in checkcompat( ) (Appendix C on page 1248) instead.

Beginning with V8.12, a new mc feature allows you to preserve the plus sign and what follows it, and to pass that whole address to your delivery agent. FEATURE(preserve_local_plus_detail) (FEATURE(preserve_local_plus_detail) on page 637) is useful with cyrusbb, cyrus, and other delivery agents.

Duplicate Entries and Automation

Ordinarily, duplicate local names on the lefthand side of the colon in an aliases file will result in an error. For example, consider this abstract from an aliases file:

staff: bob
staff: george

Running newaliases on this file would produce the following error message and would cause the first entry to be ignored:

Warning: duplicate alias name george

Sometimes, however, it is advantageous to produce an aliases file with duplicates. Consider this abstract from a script that adds new users:

if [ $GROUP = "staff" ]
then
        echo "staff: $USER" >> /etc/aliasdir/groups
fi

Here, we seek to add the user whose login name is stored in $USER to the mailing list called staff. To prevent sendmail from complaining, we declare the /etc/aliasdir/groups file like this in the configuration file:

define(`ALIAS_FILE', `dbm:-A /etc/aliasdir/groups')

Here, the dbm tells sendmail this is a ndbm(3)-type file (it could also be btree or hash for db(3)-type files). The -A switch tells sendmail to append duplicates rather than rejecting them. To illustrate, revisit the earlier aliases file:

staff: bob
staff: george

The first alias line is read and stored normally with this key and value pair:

staff    bob
↑  ↑
 key      value

The second line is then appended to the first line, because of the -A switch, to form:

staff    bob,george
↑    ↑
 key      value

The comma is intelligently inserted by sendmail.

Although this technique can simplify the maintenance of some alias files, it should not be overused. Each append requires the prior entry to be read, the space for it and the new entry to be allocated, the old and new entries to be concatenated, and the result to be stored in such a way as to replace the original. This process slows down sendmail noticeably when it rebuilds large files with many duplicates.

As an alternative, consider using the :include: mechanism described in the next chapter (:include: Mailing Lists on page 486).

The aliases Database

Reading the aliases file every time sendmail begins to run can slow mail delivery and create a lot of unnecessary computational overhead. To improve efficiency, sendmail has the ability to store aliases in a separate database format on disk. In this format, sendmail rarely needs to read the aliases file. Instead, it merely opens the database and performs lookups as necessary.

The sendmail program builds its database files by reading the aliases(5) file and rewriting that file in database format. Usually, the aliases file is called aliases. With that name, ndbm(3) database files are called aliases.pag and aliases.dir, and the db(5) database file is called aliases.db.

The sendmail program offers several forms of database, one of which is chosen at compile time (confMAPDEF on page 88).

Rebuild the Alias Database

You tell sendmail to rebuild its database files by running it in -bi mode. This mode can be executed in two different ways:

% newaliases
% /usr/sbin/sendmail -bi

The first form is shorthand for the second. Either causes sendmail to rebuild those files. If the database is successfully built, sendmail prints a single line:

895 aliases, longest 565 bytes, 30444 bytes total

This shows that 895 entries appeared to the left of colons in the aliases file. The longest list of addresses to the right of a colon was 565 bytes (excluding the newline). And there were 30,444 total bytes of noncomment information in the file.

V8 sendmail supports multiple alias database files (see the AliasFile option, AliasFile on page 970). Consequently, each line of its output is prefixed with the name of the aliases file being rebuilt. For example:

/etc/aliasdir/users: 895 aliases, longest 565 bytes, 30444 bytes total
/etc/aliasdir/lists: 34 aliases, longest 89 bytes, 1296 bytes total

Beginning with V8.11, sendmail allows only root and the user listed with the TrustedUser option (TrustedUser on page 1112) to rebuild the aliases database.[206] If you are neither, you will see the following error message, and the database rebuild will fail:

Permission denied (real uid not trusted)

Check the Right Side of Aliases

When V8 sendmail rebuilds the alias database files, it can optionally be told to check the legality of all addresses to the right of the colons. The CheckAliases option (CheckAliases on page 982) turns on this check:

define(`confCHECK_ALIASES', true)   ← mc configuration (V8.7 and later)
-on                                 ← command-line shorthand (V8.7 and later)

Each address is validated by running it through the canonify rule set 3, and then the parse rule set 0. Rule set parse must select a delivery agent for the address. If it does, the address is silently validated and accepted. If not, the address is skipped, and the following warning is printed:

address... bad address

Other errors might be printed before this line that indicate more specific reasons for the failure. For example:

... Unbalanced '<'

The -d20.1 debugging switch (-d20.1 on page 553) can be used to gain a better idea of why the address failed. But be forewarned: the -d20.1 switch can produce many screens of output.

In general, we do not recommend setting the CheckAliases option to true in the configuration file because it can cause each right-side address to be resolved through DNS and thus slow down the rebuild considerably. For better efficiency, leave the CheckAliases option off in the configuration file and turn it on only when rebuilding by hand:

% newaliases -OCheckAliases
% newaliases -onold-style shorthand, still legal

Use Trailing Dots

It is often desirable to create aliases files that have nonlocal addresses to the right of the colon:

# sean took a job at the fire station
sean:        sean@firehouse.eli.nv.us

Normally, there is no harm in putting nonlocal addresses in your aliases file. But terrible things can go wrong when the Internet goes bad. Consider, for example, when the name server for firehouse.eli.nv.us begins to act up. Then it is possible for you to run:

% newaliases -on

and have the run seem to hang, when it is only stuck, waiting for a bad name server to give back information about firehouse.eli.nv.us. If the wait is long, you might be tempted to kill the rebuild with a kill(8) of −9.

When sendmail’s rebuild is killed while stuck, the aliases database can be left in an incomplete state or with a size of zero. In either instance, inbound mail will likely begin to bounce. When that happens, you can immediately rebuild with the -on omitted. This will restore the bad aliases database to a good state.

There might be times, however, when you want the aliases database rebuilt with the -on always included. In such an instance, we recommend that you reduce the risk of sendmail hanging by placing a dot at the end of any addresses that seem suspect. For example:

# sean took a job at the fire station
sean:        sean@firehouse.eli.nv.us.
                                     ↑
                                   add a dot

The presence of the dot short-circuits sendmail’s lookup of that address. The address is presumed good, and the rebuild of the aliases database can continue at a fast rate.

Prevent Simultaneous Rebuilds

The alias database files can be rebuilt in two ways: automatically, by the daemon or by users sending mail (and thereby indirectly running sendmail),[207] or explicitly, by users rebuilding the database with newaliases (or the -bi command-line switch). To prevent one rebuild from compromising and corrupting another, sendmail uses file locking.

The sendmail program uses flock(2) or fcntl(2) with F_SETLK to lock the aliases file (depending on how it was compiled). If the aliases file is already locked (because the database is currently being rebuilt), sendmail prints the following message:

Alias file name is already being rebuilt

If sendmail is attempting to rebuild because it was run as newaliases or with the -bi command-line switch, the previous message is printed, and the program exits. Otherwise, the previous message is printed, and sendmail waits for the aliases file to become unlocked.

Once the aliases file is locked, sendmail next looks to see whether the key @ appears in the database. If that key is missing, sendmail knows the database is still being rebuilt. If the AliasWait option (AliasWait on page 973) has a value, sendmail waits that amount of time for the other rebuild to finish. If the AliasWait option is missing or has a zero value, sendmail plows ahead, trusting the previous lock to prevent simultaneous rebuilds.

The sendmail program waits the number of seconds specified by the AliasWait option for an @ key to appear in the database. If that key doesn’t appear within that wait, sendmail continues with the rebuild, assuming that some other process died while attempting to rebuild.

Before entering the key (the name to the left of the colon) and contents (everything to the right of the colon) pairs into the database, sendmail truncates the database (reduces it to size zero), thereby removing the @ key.[208] After all the key and content pairs have been written to the database, sendmail adds a new @ key to show that it is done.

Finally, sendmail closes the database and the aliases file. Closing the aliases file releases all locks it has on that file.

No DBM Aliasing

Some versions of Unix do not provide the libraries that are needed to compile sendmail with database support. When neither the db(3) nor ndbm(3) library is available, and when no other method for getting aliases is declared (such as nis), sendmail keeps aliases in its internal symbol table.

When the symbol table is used, sendmail reads the aliases text file only once, when sendmail starts or is forked as a child. If the aliases text file changes, a running daemon will not automatically recognize that change. Instead, the daemon must be killed, and restarted, before it can use any new aliases text file entries.

In general, we discourage you from running sendmail in daemon mode without external aliases database files.

Prevent Aliasing with -n

At times, it is desirable to run sendmail so that it does not perform aliasing. When aliasing is disabled, sendmail uses the recipient address as is. No addresses are ever looked up in the aliases file, even if they are local.

The -n command-line switch tells sendmail not to perform aliasing of recipient addresses. This switch is rarely used but can be handy in a couple of situations.

Is an Alias Bad?

In tracking down local delivery problems, it can be difficult to determine where the problem lies. If you suspect a bad alias, you can force aliasing to be skipped and see whether that causes the problem to go away:

% /usr/sbin/sendmail -n user < /dev/null

This tells sendmail to send an empty mail message (one containing mandatory headers only) to the recipient named user. The -n prevents sendmail from looking up user either in the aliases database or in that user’s ~/.forward. If user resolves to the local delivery agent, the message will be delivered, and you should therefore suspect an aliasing problem.

Other switches, such as -v (verbose) and -d (debugging), can be combined with -n to view the delivery process in more detail.

Filtering Recipients with a Shell Script

The -n command-line switch can also be used to suppress aliasing when delivering to a list of recipients that has already been aliased. For example, consider the following script, which attempts to restrict delivery to users who have mail delivered locally and to skip users who have mail forwarded offsite:

#!/bin/sh
EX_OK=0                   # From <sysexits.h>
EX_NOUSER=67              # From <sysexits.h>
EX_SOFTWARE=70            # From <sysexits.h>
if [ ${#} -ne 2 ]; then
        echo Usage: $0 list-name
        exit $EX_USAGE
fi
trap "exit 70" 1 2 13 15
LIST= "`/usr/sbin/sendmail -bv $1 \
        | grep "mailer local" 2>&1'" \
        | sed 's/\.\.\..*$//'
if [ -z "$LIST" ]
        echo "$1 expanded to an empty list"
        exit $EX_NOUSER
fi
if /usr/sbin/sendmail -n $LIST >/dev/null 2>&1
then
        exit $EX_OK
fi
exit $EX_SOFTWARE

The sendmail program is called twice inside this script. First, it is given the -bv switch, which causes it to expand the list of recipients in $1. That expansion includes aliasing (and ~/.forward aliasing) for each name in the list. The output produced looks like this:

user1... deliverable: mailer local, user user1
user2@otherhost... deliverable: mailer smtp, host otherhost, user user2@otherhost

The grep(1) program selects only those lines that contain the expression "mailer local", thus indicating a local user. The sed(1) program then discards from the ... to the end of each selected line. The result, a list of local recipients only, is saved in the shell variable LIST.

The sendmail program is called with the -n switch, which prevents it from re-aliasing the list of names in $LIST (they have already been aliased once).

Note that this script should not be used as is because it checks only for the delivery agent named local, rather than for any delivery agent that can perform final delivery.

Pitfalls

  • The dbm and ndbm forms of the aliases(5) database files contain binary integers. As a consequence, those database files cannot be shared via network-mounted filesystems by machines of differing architectures. This is not a problem for Sleepycat db files.

  • The aliases file and database files can be used to circumvent system security if they are writable by the wrong users. Proper ownership and permissions are checked and enforced only by V8.9 and above sendmail. Restrictions on who can rebuild are enforced beginning with V8.11 sendmail.

  • Versions of sendmail that use the old-style dbm(3) libraries can cause overly long alias lines (greater than 1024 bytes) to be silently truncated. With the new databases, such as ndbm(3), a warning is printed. Note that V8 sendmail does not support old-style dbm(3) for this very reason.

  • Recursive (circular self-referencing) aliases are detected only when mail is being delivered. The sendmail program does not look for such alias loops when rebuilding its database.

  • Because of the way V8.8 sendmail and above lock the aliases file for rebuilding on some operating systems, that file must be writable by root. If it is not, sendmail prints the following and skips the rebuild:

    warning: cannot lock aliases: Permission denied

This can be a problem if the master aliases file is shared via NFS because root is normally mapped to nobody.



[195] * If you set the F=A flag for the various smtp delivery agents, the local part of an alias can be specified as a network or remote address, such as user@host.domain.

[196] * If yours is an old configuration file that does not automatically recognize a leading / character, you will need to add a new rule near the end of your rule set 0. For example:

R/$+ $@ $#local $: /$1

[197] Note that an @host prevents this interpretation. That is, /a is a file, but /a@host is not. This distinction is necessary for X.400 addresses to be handled correctly.

[198] * The progression is 0 seconds for the first sleep, then 10 seconds, then 20 seconds, then 30 seconds, and so on.

[199] * This is because some paranoid systems, such as BSD Unix, turn off the set-user-id and set-group-id bits when a file is written other than by root.

[200] If your older configuration file does not automatically recognize a leading | character, you might need to add a new rule near the end of your rule set 0. For example:

R|$+ $@ $#local $: |$1

[201] * Not all versions of Unix support this feature, and on some of those that do support it, only a few shells are supported.

[202] Under V8 sendmail, this is no longer a problem for duplicate programs listed in ~/.forward files (Piping Through Programs on page 504) but still is a problem for aliases. The solution that sendmail uses is to internally append the uid of the ~/.forward file’s owner to the program name, thus making the program name more unique.

[203] * RFC2142 adds others to this list (RFC2142 Common Mailbox Names on page 474), such as abuse, webmaster, and so on.

[204] * Note that this can also be done with the nobodyreturn keyword (PrivacyOptions=nobodyreturn on page 1066) with the PrivacyOptions option.

[205] Although RFC2142 requires that they all be treated in a case-insensitive manner.

[206] * V8.12 and above sendmail are no longer set-user-id root, which further limits who can rebuild aliases.

[207] * Under pre-V8.12, this occurred only if the AutoRebuildAliases option (AutoRebuildAliases on page 978) was set to true. This option has been removed beginning with V8.12 sendmail, and the aliases database can no longer be automatically rebuilt.

[208] * Even though we show how sendmail rebuilds its aliases file, you should not take this as advice to use makemap(1) to perform that task. You should use newaliases (or the -bi command-line switch) only to rebuild.