In general use, SPAM® is a brand name of luncheon meat and is a registered trademark of Hormel Foods Corporation. After SPAM was lampooned by Monty Python in a famous sketch,[114] it was jokingly adopted by the Internet community to describe unsolicited mass postings across many USENET groups. Soon, a new word was coined, the lowercase “spam,” a word that now describes unsolicited, commercial email.
As you read this chapter, remember that spam is a moving target. On the one hand, sendmail offers constantly improving tools to filter and reject it. On the other hand, the spammer’s tools are also constantly being updated to bypass existing protections. Mix in the fact that laws are being written which might modify or limit spam, and you can think of this as an arms race. That is, you won’t set up sendmail just once and be done, but will find yourself continually modifying database files and rule sets in an effort to stay even with the spammer’s cleverness.
Over the years, spam email has evolved into a greater and greater threat. No longer is spam a mere nuisance, because it now seriously threatens all who receive email. Spam can contain viruses, spyware, and realistic-looking phishing attempts to steal identities and money. As spam has increased in volume, so too has the need to effectively fight it.
In fact, beginning with V8.14, sendmail now
recognizes that open HTTP proxies can be used to send spam. So now,
if the first command a sendmail server receives
from a client is GET, POST, CONNECT, or USER, V8.14 sendmail
immediately terminates the connection. This is the exact aggressive
antispam behavior you should always expect from
sendmail.
The rapid spread of the Internet has led to an increase of mail abuses. Prior to V8.8 sendmail, detecting and rejecting abusive email required that you write C-language code for use in the checkcompat/( ) routine. Beginning with V8.8 sendmail, important and useful checking and rejecting can be done from within pairs of complementary rule sets. They are presented here in the order that sendmail calls them:[115]
Local_check_relay
and
check_relayValidate the host initiating the SMTP connection.
Local_check_mail
and
check_mailValidate the envelope-sender address given
to the SMTP MAIL
From: command.
Local_check_rcpt
and
check_rcptValidate the envelope-recipient address
given to the SMTP RCPT
To: command.
check_eomValidate size of the message before calling any Milter.
check_compatCompare or contrast each pair of envelope-sender and envelope-recipient addresses before delivery, and validate them based on the result.
These routines are all handled in the same manner. If the rule
set returns anything other than a #error or a #discard delivery agent, the message is
accepted. Otherwise, the #error delivery agent causes the message
to be rejected or deferred (error on page
720) whereas the #discard
delivery agent causes the message to be accepted, then
discarded (discard on page
719).
V8.8 sendmail supports two
mechanisms for screening incoming SMTP connections.
One is the libwrap.a mechanism,
and the other is the check_relay rule set. V8.9
sendmail added a third
mechanism, the access database
(The access Database on page
277).
The Local_check_relay rule set provides a
hook into the check_relay rule set, which is used to
screen incoming network connections and accept or
reject them based on the hostname, domain, or IP
address. It is called just before the
libwrap.a code and can be
used even if that code was omitted from your release
of sendmail. Note that the
check_relay
rule set is not called if
sendmail was run with the
-bs
command-line switch (-bs on page
236).
The check_relay
rule set is called with a workspace that looks like
this:
host $| IPaddress
The hostname and IP address are separated by the
$| operator.
The host is the
fully qualified canonical name of the connecting
host. The IPaddress is the IP address of that
host in dotted-quad form without surrounding square
brackets, or the IPv6 address prefixed with a
literal IPv6:.
Note that if you also declare the FEATURE(use_client_ptr)
(FEATURE(use_client_ptr)—V8.13 and Later on page 297), the value from the ${client_ptr} macro
(${client_ptr} on page 813) will be used in place of the
IPaddress.
By default, the check_relay rule set allows all
connections. This behavior can be overridden or
enforced in the access database
by prefixing leftmost keys with a literal Connect: (Finer Control with V8.10 on page
282):
Connect:bad.host REJECT
Here, for example, any connection from the host bad.host is rejected.
The default behavior of the check_relay rule set can also be
overridden by the various DNS blacklist features
(see How DNSBL Works on page
260).
In the event you need to add checks to this check_relay rule set,
you can do so by adding a Local_check_relay rule set. Declaring
this latter rule set gives you a hook into the start
of check_relay,
which means your rules are applied before the
default rules.
One way to use Local_check_relay might be to list
offensive sites in a database and reject any
connections from those sites.[116] Consider a database that contains
hostnames or addresses as its keys and descriptions
of each host’s offense as its values:
hostA.edu Spamming site hostB.com Mail Bombing site 123.45.6 Offensive domain IPv6:2002:c0a8:51d2::23f4 Offending host
Notice that the keys can be hostnames, or IPv4 or IPv6 addresses. Such a database might be declared in the configuration file like this:
LOCAL_CONFIG Kbadhosts dbm -a<> /etc/mail/badhosts
Now, each time a site connects to your running daemon, the following rule set will be called:
SLocal_check_relay R $* $| $* $: $(badhosts $1 $) $| $2 look up hostname R $*<> $| $* $#error $@ 5.1.3 $: 550 Sorry, $1 denied R $* $| $* $: $2 select the IP address R $-.$-.$-.$- $: $(badhosts $1.$2.$3.$4 $) look up host address R IPv6 : $+ $: $(badhosts IPv6:$1 $) look up host or network address R $-.$-.$-.$- $: $(badhosts $1.$2.$3 $) look up network address R $*<> $#error $@ 5.1.3 $: 550 Sorry, $1 denied R $* $@ ok otherwise OK
The first rule looks up the host part in the database.
If it is found, the value (reason for rejection) is
returned and the two characters < > are appended.
The second rule looks for anything to the left of
the $| that ends
in < > and,
if anything is found, issues the error:[117]
550 5.1.3 Sorry, reason for rejectdeniedRejected connections are handled in the same way as connections rejected by the access database (The access Database on page 277).
The rest of the rules do the same thing, but also check for the IP address.
If the Local_check_relay rule set returns a
#error or
#discard
delivery agent, the connection is rejected. If it
returns a $#OK,[118] the connection is accepted and
subsequent check_relay rule set rules are
skipped:
SLocal_check_relay R $* $# OK skip check_relay rule set rules
But if it returns a $@OK, further check_relay rule set rules are allowed
which might themselves reject the connection:
SLocal_check_relay R $* $@ OK allow check_relay rule set rules
Note that the rules presented here are not nearly as complex or sophisticated as your site will likely need. They do not, for example, reject on the basis of the domain part of the hostname, nor do they reject on the basis of the individual host IP addresses.
Beginning with V8.14 sendmail,
any macro that is given a value as part of this
check_relay
rule set will have that value maintained by
sendmail for the duration of
the current SMTP session. To illustrate, consider a
policy that allows multiple recipients for local
delivery, but only one recipient per envelope when
mail is relayed. A rule inside this check_relay rule set
could, for example, define a flag:
Kstorage macro
R$* $: $(storage {WeAreRelaying} $@ TRUE $)This rule stores the constant value TRUE in the ${WeAreRelaying} macro.
Later, when the check_compat rule set (The check_compat Rule Set
on page 259) is called, the flag will cause
sendmail to limit the number
of allowed recipients.
Note that the rules in the Local_check_relay and check_relay rule sets
cannot be tested in rule-testing mode because that
mode wrongly interprets the expression $| (when you enter it at
the > prompt) as two separate text characters
instead of as a single operator. To test an address
that contains an embedded $| operator, we suggest that you create
a translation rule set something like this:
LOCAL_RULESETS STranslate R $* $$| $* $: $1 $| $2 fake for -bt mode
This rule set changes a literal $ and | into a $| operator so that you
can test rule sets such as Local_check_relay from rule-testing
mode:
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> Translate,Local_check_relay bogus.host.domain $| 123.45.67.89Here, the comma-separated list of rule sets begins
with Translate,
which changes the two-character text expression
$| into the
single operator $|. The result, an address expression
that is suitable for the Local_check_relay rule set, can then be
successfully tested.[119]
The Local_check_mail rule set provides a
hook into the check_mail rule set, which is used to
validate the envelope-sender address given in the
MAIL From:
command of the SMTP dialog:
MAIL From:<sender@host.domain>
The check_mail rule
set is called immediately after the MAIL From: command is
read. The workspace passed to check_mail is the
address following the colon in the MAIL From: command. That
envelope-sender address might or might not be
surrounded by angle braces.
If sendmail’s delivery mode is
anything other than deferred (-bd on
page 234), the check_mail rule set performs the
following default actions:
Calls the tls_client rule set (The access database with tls_server and
tls_client
on page 214) to perform TLS verification, if
needed
Accepts all envelope-sender addresses of the
form <
>
Makes certain that the host and domain part of the envelope-sender address exists
If the access database (The access Database on page 277) is used, looks up the envelope-sender in that database and rejects, accepts, or defers the message based on the returned lookup value
The Local_check_mail rule set provides a
hook into check_mail before the preceding checks
are made, and provides a place for you to insert
your own rules.
To illustrate one use for the Local_check_mail rule set, consider the
need to accept all mail from an internal domain,
even when many of the hosts in that domain cannot be
looked up with DNS.[120] One method might look like this:
LOCAL_RULESETS SLocal_check_mail R $* $: $>canonify $1 focus on the host R $* <@ $+. > $* $1 <@ $2> $3 strip trailing dots R $* <@ $+ > $* $: $2 isolate the host R $* . $+ . $+ $2 . $3 strip subdomains R internal.org $# OK
Here, we force the rule set named canonify to preprocess
the address so that any RFC2822 comments will be
thrown away and the host part of the address will be
focused.[121] We then strip any trailing dots from the
hostname to prevent a trailing dot from wrongly
affecting our validation. In the third line, we
throw away everything but the hostname. In the
fourth line, we throw away all but the rightmost two
components of the hostname to eliminate the host
part and any subdomain prefixes. What remains is the
domain name. We then compare that domain name to the
hostname internal.org. If they
match, we accept the sender. If they don’t match,
the default rules in the check_mail rule set continue to process
the address.
Note that if this Local_check_mail rule set returns
$#OK,[122] all subsequent check_mail rule set checks of the
envelope-sender will be suppressed:
SLocal_check_mail R $* $# OK skip check_mail rule set checks
But if it returns $@OK, further envelope-sender check_mail rule set
checks are processed (such as looking up the user
and host parts in the access
database, or trying to resolve the host
part):
SLocal_check_mail R $* $@ OK allow check_mail rule set checks
After this rule set is installed (and the sendmail daemon had been restarted), all mail from internal.org will be accepted during the SMTP dialog even if the hostname does not exist.
Other uses for the Local_check_mail rule set might include
limiting certain senders to only a few outbound
messages per day, by using an external database to
record attempts; rejecting the user part of sender
addresses for special reasons, such as being all
numeric; and rejecting mail from a specific list of
users at a given site.
If you need to base a decision to reject mail on both
the sender and the recipient, you might be able to
use the check_compat rule set described next,
or design your own rules for this rule set using
$&f ($f on page 824).
The Local_check_rcpt rule set provides a
hook into the check_rcpt rule set, which is used to
validate the recipient-sender address given in the
RCPT To:
command in the SMTP dialog:
RCPT To:<recipient@host.domain>
The check_rcpt rule
set is called immediately after the RCPT To: command is
read. The workspace that is passed to check_rcpt is the
address following the colon. The envelope-recipient
address might or might not be surrounded by angle
brackets and might or might not have other RFC2822
comments associated with it.
The check_rcpt rule
set has default rules that do the following:
Reject empty envelope-recipient addresses,
such as <
>, and those which have nothing
following the RCPT
To:.
Ensure that the envelope-recipient address is either local, or one that is allowed to be relayed.
If the access database
(The access Database on page
277) is used, look up the envelope-recipient’s
host in that database and reject, accept, or defer
the message based on the returned lookup value. If
the FEATURE(blacklist_recipients) (Reject per Recipient on page 284) is
declared, they also look up the envelope recipient
in that database.
The Local_check_rcpt gives you a hook into
the check_rcpt
rule set before any of the default rules are called.
To illustrate one use for the Local_check_rcpt rule
set, consider the need to reject all incoming mail
destined for the recipient named
fax. One method might look
like this:
LOCAL_RULESETS SLocal_check_rcpt R $* $: $>canonify $1 focus on host R fax <@ $=w . > $* $#error $@ 5.1.3 $: "cannot send mail to fax"
Here, the first rule calls the rule set named canonify to focus on the
host part of the address and normalize it. The
second rule rejects anything to fax in any of our local
domains (the $=w). A recipient address of fax at any other domain
will pass through these rules and be
accepted:
RCPT To: <fax@ourhost> 553 5.1.3 <fax@ourhost>... cannot send mail to fax
Other possible uses for this Local_check_rcpt rule set
include:
Creating a special bounce-handling machine that accepts all bounced mail, then logs and discards it
Creating a special performance-testing blackhole machine that accepts all outside mail and silently discards it
Note that if this Local_check_rcpt rule set returns
$#OK,[123] all subsequent checks with the check_rcpt rule set will
be suppressed:
SLocal_check_rcpt R $* $# OK skip check_rcpt rule set checks
But if it returns $@OK, further checks with the check_rcpt rule set are
processed (such as looking up the user and host
parts in the access database,
and such as validating that the host part is
local):
SLocal_check_rcpt R $* $@ OK allow check_rcpt rule set checks
If you need to base a decision to reject mail on both
the sender and the recipient, you can either use the
check_compat
rule set described next, or design your own rules
for this rule set using $&f ($f on
page 824).
Note that check_rcpt rule set rules apply only to
mail that arrives via SMTP. If your site submits
mail using SMTP, you might find locally originating
mail being wrongly rejected. If yours is such a
site, you can add the following rules to Local_check_rcpt, which
should fix the problem:
SLocal_check_rcpt
R $* $: $&{client_addr}
R 127.0.0.1 $# OKThe check_eom rule
set (V8.14 and later) is called after the
terminating dot is received from the sending client,
but before the xxfi_eom entry
(Milter xxfi_eom()
on page 1215) into any Milters is called. The
check_eom rule
set is called only if it exists in the configuration
file; otherwise, it is skipped. When it is called,
its workspace is passed an ASCII representation of
an unsigned integer which represents the size of the
message (header lines and body) in bytes
(characters). This size is the same as the value
stored in the ${msg_size} macro (${msg_size}
on page 835).
The check_eom rule
set can be used to validate the size of the message,
but it does not have to be used in that way.
Instead, you might, for example, have a policy that
requires only one recipient per message. One way to
use the check_eom
rule set to enable this policy might look like the
following:
Karith math
Scheck_eom
R$* $: $(math = $@ $&{nrcpts} $@ 1 $)
R TRUE $#OK
R FALSE $#error $@ 5.1.3 $: "Policy limits one recipient per envelope."Here, we first declare a database map of type arith called
math (arith on page 898). In the second
line, we declare the check_eom rule set, which, in this
case, contains three rules (the R lines).
The first rule compares the number of recipients in
the current value of the ${nrcpts} macro (${nrcpts}
on page 837) to the constant value 1 to see whether the two
are equal. The second rule matches if the two are
equal (if the number of recipients is one) and
returns OK so that the message will be accepted for
final review by Milters (if any). The last rule
rejects the message with a statement that the policy
“limits one recipient per message.” But note that
Milters can add recipients, so a better place to
enforce this policy is in the check_compat rule set
(The check_compat Rule Set
on page 259).
Not all situations can be resolved by simply checking
the RCPT To: or
MAIL From:
address. Sometimes you will need to make judgments
based on pairs of addresses, or non-SMTP addresses
or other information. To handle this situation, V8.8
introduced the check_compat rule set. Unlike check_mail and check_rcpt, check_compat is called
for all deliveries, not just SMTP transactions. It
is called after an address has undergone
aliases translation, just
after the check for too large a size (as defined by
M=; see M= on page 746) and just before
the checkcompat( ) routine
(Appendix C on page 1248).
Note that although with V8.12 and later you can still
write your own check_compat rule set, doing so has
been made unnecessary by the FEATURE(compat_check)
(FEATURE(compat_check)—V8.12 and Later on page 288). But also note that, as of V8.12,
you cannot both declare the FEATURE(compat_check) and use this
check_compat
rule set.
The check_compat
rule set is called with a workspace that looks like
this:
sender $| recipient
The sender and recipient addresses are separated by
the $| operator.
Each has undergone aliasing and
~/.forward file
processing.
One use for the check_compat rule set is to prevent a
certain user (here, operator/)
from sending mail offsite:
LOCAL_RULESETS SGet_domain R $* $: $>canonify $1 focus on host R $* <@ $+. > $* $1 <@ $2> $3 strip trailing dots R $* <@ $+ > $* $: $2 isolate the host R $* . $+ . $+ $2 . $3 strip host and subdomains SGet_user R $* $: $>3 $1 focus on host R $* <@ $+ > $* $@ $1 discard host Scheck_compat R $* $| $* $: $1 $| $>Get_domain $2 fetch recipient domain R $* $| $=w $@ ok local is OK R $* $| $=m $@ ok local is OK R $* $| $* $: $>Get_user $1 fetch sender user R operator $#error $@ 5.1.3 $: "operator might not mail off site"
First, we set up two subroutines patterned after the
code in the previous two sections. The first reduces
its workspace to just the domain part of an address.
The second reduces an address to just the user part.
These two subroutines are called by check_compat.
The first rule in check_compat uses the Get_domain subroutine to
convert the address on the right of the $| (the recipient) into
just a domain name. That right side is compared to
the local hostnames ($=w and $=m). If the domain is a local one,
delivery is allowed (we return anything but a
#error or a
$#discard).
But if the domain is an offsite one, we call Get_user to fetch the
user part of the address to the left of the $| (the sender). If that
user is operator, delivery is
denied and the message bounces.
Other uses for the check_compat rule set might include the
following:
Logging a record of when a DSN NOTIFY request of success is requested (${dsn_notify} on page 821)
Creating a class of users who, possibly for security reasons, might send mail inside the organization but not outside it
Screening a particular recipient to prevent that user from receiving objectionable mail from a specific source
Note that such rule sets cannot be tested in
rule-testing mode because that mode wrongly
interprets the expression $| (when you enter it at the >
prompt) as two separate text characters instead of a
single one. See Local_check_relay and check_relay on page 252 for one suggested solution to this
problem.
The acronym DNSBL stands for “Domain Name Services BlackList,” where the term BlackList refers to the desire to prohibit all spam.
When sendmail accepts a connection from another site, one of the first things it does is to get the IP address of that site. Once armed with that address, it can do a lookup of that address at a DNSBL site. To illustrate, we will use the mail-abuse.org site.[124] To see whether the connecting site is an open relay site, sendmail first reverses the IP address. For example, the address 123.45.67.89 becomes 89.67.45.123. Then sendmail prefixes the hostname relays.mail-abuse.org with that reversed IP address and looks up the result as though it is a hostname:
89.67.45.123.relays.mail-abuse.org
If that hostname is found, that means the site is listed with mail-abuse.org as an open relay site. If that hostname is not found, the site is a good one.
Prior to V8.12, the FEATURE(rbl) allowed you to use this
DNSBL process. Beginning with V8.10, a new FEATURE(dnsbl) was added.
As of V8.12, the FEATURE(rbl) was removed. The FEATURE(enhdnsbl) which is
an extended version of FEATURE(dnsbl) became available. These
features are summarized in Table 7-1
and explained in the following sections.
|
Feature |
Description |
|
|
Deprecated; see |
|
|
Reject mail from hosts in a DNS-based rejection list |
|
|
An enhanced version of |
The FEATURE(dnsbl)
is used to enable the blocking of email from open
relay sites, dial-up sites, or known spamming sites.
It does so by invoking the rbl technique discussed in the previous
section. The feature is included in your
mc configuration file like
this:
FEATURE(dnsbl) ← simple form FEATURE(dnsbl, `optional arguments') ← declared with arguments
In its simplest form, when mail arrives from a site, that site’s IP address is reversed and prefixed to the default host blackholes.mail-abuse.org.[125] If the lookup succeeds, the host is considered bad and the following error is sent in reply to the initial connection:
550 5.7.1 Rejected: IP listed at blackholes.mail-abuse.orgIf the address is not found, the connection is allowed and the mail is accepted depending on subsequent SMTP and header checks. By default, temporary failures are ignored and the connection is treated as good. If you wish temporary failures to cause the sending site to defer the message, you can supply a third argument such as this:
FEATURE(dnsbl, , ,`t')
If the third argument is a literal t character, instead of
ignoring temporary errors, the following will be
returned:
451 Temporary lookup failure of IP at blackholes.mail-abuse.orgAn argument can be supplied to this feature if you
wish to use a lookup host other than, or in addition
to, blackholes.mail-abuse.org.
The canonical name of the lookup host is simply
inserted following a comma after the literal
dnsbl:
FEATURE(dnsbl,`dialups.mail-abuse.org') FEATURE(dnsbl,`dialups.mail-abuse.org', ,`t')
Here, the same check and error returns are done as described earlier, but with the host you specify, dialups.mail-abuse.org, replacing the default host, blackholes.mail-abuse.org. The first of the two alternatives ignores temporary errors, and the second honors temporary errors.
Multiple dnsbl
features can be included in a single
mc file. Each will cause the
same host’s IP address to be looked up at a
different server. For example, the following will
cause the IP address to be looked up first with
blackholes.mail-abuse.org,
and then with
dialups.mail-abuse.org:
FEATURE(dnsbl)
FEATURE(dnsbl,`dialups.mail-abuse.org')In addition to the name of a lookup host, you can also specify your own error message as a second argument. For example, the following looks up the IP address on the host dialups.mail-abuse.org and issues a custom error message in the second argument to the feature (note that this is one line that is wrapped):
FEATURE(dnsbl,`dialups.mail-abuse.org', `"550 Mail from dial-up site " $&
{client_addr}
" refused"',`t')Here, the value of the {client_addr} macro will contain the IP
address of the offending host at the time the error
is reported.
Note that beginning with V8.14, the second argument
may be a literal discard or quarantine:
FEATURE(dnsbl,`dialups.mail-abuse.org', `discard') ← V8.14 and later FEATURE(dnsbl,`dialups.mail-abuse.org', `quarantine') ← V8.14 and later
Here, discard
causes the rejected message to be silently accepted
and discarded, whereas the quarantine causes the rejected message
to be accepted and quarantined.
Note that, beginning with V8.13, this FEATURE(dnsbl) no longer
uses the host
database-map type to look up addresses. Instead, it
now uses the dns
database-map type (dns on page
905). The DNSBL_MAP_OPT
mc macro has
been added to help tune the use of the dns database-map type
with the FEATURE(dnsbl).
The default declaration for the dns databasemap for this
feature looks like this:
Kdnsbl dns -R A -T<TMP>
If you wish to change the type of the lookup, you may
redefine the dns -R
A part of the expression:
define(`DNSBL_MAP', `dns -R TXT') FEATURE(dnsbl, ...)
Here, the DNSBL_MAP
redefines the lookup so that it performs TXT record lookups
instead of A
record lookups. Note that DNSBL_MAP must be defined before this
feature is declared for the feature to have any
effect.
You may also list additional arguments for the
dns databasemap
used with this feature. Those additional arguments
will follow the -T<TMP> part in the declaration
and are specified like this:
define(`DNSBL_MAP_OPT', `-d1s') FEATURE(dnsbl, ...)
Here, the -d1s
tells sendmail
to reduce the res_search(
) _res.retry
interval to one second from the default of five
seconds. Note that DNSBL_MAP_OPT must be defined before
this feature is declared for the feature to have any
effect.
The FEATURE(enhdnsbl) (for enhanced
dnsbl) is a
superset of the FEATURE(dnsbl) described earlier. It is
used like this:
FEATURE(enhdnsbl, optional args)The enhancement consists of additional arguments—that is, one or more literal addresses you expect returned when an address should be rejected. For example, the following rejects bad dial-up hosts and defers temporary lookup errors:
FEATURE(enhdnsbl,`dialups.mail-abuse.org',`"550 dial-up site refused"',`t',
`127.0.0.3.')
↑
additionalThe first three arguments are the same as those you
saw for the FEATURE(dnsbl) (FEATURE(dnsbl) on page 261): the lookup host, an error message,
and a t
character. But unlike the FEATURE(dnsbl), an error specified in
the second argument prevents temporary lookup errors
from being deferred. The third argument to FEATURE(enhdnsbl) (the
t) allows
temporary lookup errors to be recognized, which
causes delivery to be deferred:
451 Temporary lookup failure of address at dialups.mail-abuse.orgHere, the address is the IP
address of the sending host. The
dialups.mail-abuse.org
matches the lookup host specified in the second
argument to the FEATURE(enhdnsbl). If the t were omitted, as for
example:
FEATURE(enhdnsbl,`dialups.mail-abuse.org', `"550 dial-up site refused"', ,`127.0.0.3.')
temporary lookups will be ignored and the message will be accepted.
The fourth argument is the expected result of the
lookup. For the lookup host
dialups.mail-abuse.org, a
successful lookup (one that means the message should
be rejected) will return the address 127.0.0.3.
Different lookup hosts will return different
addresses on success, so you will need to visit the
appropriate web site to determine the address to
match. If the address is omitted from the FEATURE(enhdnsbl), any
successfully returned address will cause the message
to be rejected.
If more than one address can be returned, you can list up to five more following the first one. In the following, we list three possible returned addresses (the line is wrapped to fit the page):
FEATURE(enhdnsbl,`dialups.mail-abuse.org', `"550 dial-up site refused"', ,`127.0.0.1.', `127.0.0.2.', `127.0.0.3.')
Here, if any of the three addresses is returned, the message will be rejected. Note that if you don’t know specifically what will be returned, you can use rule LHS-operators (The R Configuration Command) in place of specific numbers. For example, instead of the three addresses shown earlier, you can specify one like this:
FEATURE(enhdnsbl,`dialups.mail-abuse.org', `"550 dial-up site refused"', ,`127.0.0.$- .')
Here, the $- will
match any number in that position. If you need to
restrict the range of acceptable values you can use
a class, perhaps like this:
LOCAL_CONFIG
C{OneTwoThree}1 2 3
FEATURE(enhdnsbl,`dialups.mail-abuse.org', `"550 dial-up site refused"',
,`127.0.0.$={OneTwoThree}.')Here, the $={OneTwoThree} class restricts a match
to any 127.0.0.
address that ends in a 1, 2, or 3. Other operators
you might find useful are $+ (match one or more), and $@ (match zero
tokens).
Note that beginning with V8.14, the second argument
may be a literal discard or quarantine:
FEATURE(enhdnsbl,`dialups.mail-abuse.org', `discard') ← V8.14 and later FEATURE(enhdnsbl,`dialups.mail-abuse.org', `quarantine') ← V8.14 and later
Here, discard
causes the rejected message to be silently accepted
and discarded, whereas the quarantine causes the rejected message
to be accepted and quarantined.
Beginning with V8.10, sendmail provides the ability to screen selected headers with rule sets. This is described in detail in Rules Check Header Contents on page 1130. In this section, we show two more techniques for using header checks to reject spam messages:
Reject messages that have subjects which indicate that the message contains a virus.
Reject messages that have an illegally formed
Received:
header.
Many messages that contain viruses, worms, or Trojan
horses have distinctive subject lines, the text of
which is usually reported in the news. When a new
virus is discovered, it is often quicker to reject
messages based on its reported subject line than it
is to await the latest update of your favorite virus
filter software. But this is only a temporary fix.
Because legitimate email will often share the same
subjects, it is best to only screen on the Subject: header between
the time the virus is detected and announced, and
the time your virus screening software is
updated.
One way to screen by subject is to create a database of subject lines to reject, and then use that database in a subject-checking rule set. Consider the following text file which contains one subject per line. The subject is to the left, the word REJECT is to the right, and the two are separated by one or more tab characters:
I Love You REJECT Visit Home Now! REJECT
If you were to call this file /etc/mail/spamsubjects, you could turn it into a database map with commands like this:
#cd /etc/mail#makemap -t\tab hash spamsubjects < spamsubjects
The -t command-line
switch tells makemap that the
key and value pairs are separated by a tab instead
of spaces or tabs. The backslash protects the tab
from interpretation by your shell. We use that
command-line switch because our keys can contain
internal spaces.[126]
Once this database is in place, it will be easy to update its contents whenever a new virus is announced. Because it is a database, you will be able to update it without having to restart sendmail. In fact, because the righthand side says REJECT, you simply have to change that word to OK to allow a header. This allows you to maintain a history of spam subjects for later review or reuse.
The rules for the use of this database can be added to your mc configuration file like this:
LOCAL_CONFIG
Kspamsubjdb hash /etc/mail/spamsubjects
HSubject: $>ScreenSubject
LOCAL_RULESETS
SScreenSubject
R $* $: $(spamsubjdb $&{currHeader} $: OK $) $1
R REJECT $* $#error $: "553 Subject:" $1 ": Indicates virus, rejected"Here, the LOCAL_CONFIG part defines a database map
called spamsubjdb
of type hash that
will use the database file you created earlier. The
second line under LOCAL_CONFIG defines the Subject: header, and
says that the value of that header should be passed
(the $ >
operator) through the ScreenSubject rule set.
In the LOCAL_RULESETS part of your
mc file, the S configuration line
defines the ScreenSubject rule set, which has just
two rules.
The first rule looks up the entire workspace (the
$* operator) in
the LHS (lefthand side, The R Configuration Command, in the
database map called spamsubjdb. If the literal text of the
Subject:
header’s value is found in the database, the token
from the right side of the database, the REJECT in
our example, is returned. If it is not found in the
database, the default (as indicated by the $: operator) is returned
(the OK is returned). Whichever token is returned,
the original subject value is also returned (the
trailing $1
operator).
The second rule looks for the literal text REJECT in
the workspace, followed by zero or more tokens (the
$* operator).
If the workspace begins with REJECT, the message is
rejected; otherwise, it is accepted.
The RHS (right-hand side, The R Configuration Command) of the
second rule performs the rejection. The $#error instructs
sendmail to reject the
message. The $:
part defines the text of the error message that will
be issued. For a subject value of I Love You, the
following error will be produced during the SMTP
exchange.
553 5.3.5 Subject: I Love You : Indicates virus, rejected
Note that when sendmail sees an SMTP code of 553 that is not followed by a DSN code, it will insert the appropriate DSN code, here the 5.3.5.
Finally, we say again that you should reject email based on the subject only as a temporary measure. The likelihood that legitimate email will have an identical subject is very high. When erring, it is better to allow the occasional spam than it is to reject any legitimate email.
The Received:
header traces the succession of hosts that an email
message passes through. One technique used by spam
messages is to create false Received: headers both to mask the real
identity of the original sending host, and to divert
blame to some innocent site. One form of bad
Received:
header that appears in spam messages looks like
this:
Received: from ............................................................... ........................................................................ ........................................................................ ........................................................................ ........................................................................!
This form of Received: header was popular with spam
software for a few months, then fell out of favor.
The following rule shows one way of dealing with
such headers:
LOCAL_RULESETS
H*: $>ScreenForDots
SScreenForDots
R $+ .......... $* $#error $: "553 Ten or more dots begin " $&{hdr_name} "header"Here, the LOCAL_RULESETS part of your
mc file begins with an
unusual-looking H
configuration command. The H* is special (H* a Default for All Headers
on page 1134) because it matches all headers. When
sendmail screens headers, it
first calls each rule set specified for a specific
named header (as with Subject: in the previous section). If
no rule set exists for a particular header name,
sendmail next looks for the
special definition H* and, if found, passes the header to
that rule set. You can think of H* as specifying a
default rule set.
The rule set named ScreenForDots has only a single rule.
That rule matches any value part of any header that
does not have its own rule set. The LHS checks for a
value that begins with 10 dots followed by zero or
more arbitrary tokens.
Any header that has such a bad value will be rejected
and the message bounced. The bounce will have the
following text as its error, where the offending
header was the Received: header shown earlier:
553 5.3.5 Ten or more dots begin Received header
Remember that the techniques used by spam email
senders change over time—the bad guys learn and
adapt too. We solved the dots in the Received: header with a
general rule set because it was transient (a spam
technique used for a brief period and then
abandoned). The problem will doubtless appear again,
perhaps in a different header, or when some poor sap
downloads an old version of spamming software. But
by defining with a general-purpose rule set (the
H* one), we
anticipate the return of a technique in the future,
possibly with a differently named header.
Promiscuous relaying is the process of accepting email from outside your site and then transmitting it to another host also outside your site. Hosts that relay are quickly discovered by spam programs and are used to mask the identity of the originating spam site. Prior to V8.9, promiscuous relaying was allowed by default. Beginning with V8.9, promiscuous relaying is turned off by default.
In place of default relaying, V8.9 and later
sendmail provide a variety of
features, macros, and databases that allow you to relay in a
variety of manners. The access database
(The access Database on page 277)
provides a way to relay on a host-by-host, or network basis.
Adding domains to the class $=R ($=R on
page 874) is another method. In this section, we describe
features that allow you to tune relaying to your taste.
Table 7-2 lists the features
available as of V8.12.
|
Feature |
§ |
Description |
|
|
The access Database on page 277 |
Screen addresses and set policy. |
|
|
FEATURE(loose_relay_check) on page 270 |
Allow %-hack relaying. |
|
|
FEATURE(promiscuous_relay) on page 271 |
Allow all relaying. |
|
|
FEATURE(relay_based_on_MX) on page 271 |
Relay for any site for which you are an MX server. |
|
|
FEATURE(relay_entire_domain) on page 272 |
Relay based on |
|
|
FEATURE(relay_hosts_only) on page 273 |
Interpret domains in relay domains, and access database, as hosts. |
|
|
FEATURE(relay_local_from) on page 273 |
Relay if SMTP |
|
|
FEATURE(relay_mail_from) on page 274 |
Relay if SMTP |
In addition to the features we discuss here, you should also see Chapter 4 on page 154 for a discussion of how relaying can be controlled with AUTH= and STARTTLS.
Before you turn on relaying of any sort, be sure you understand the potential risks of your decision. A mistake that loosens relaying restrictions too much can open your site to abuse as a spam relay from anywhere in the world.
The following features are presented in alphabetical order, not in order of recommendation or safety. In fact, the first is more fraught with risk than the others. Take care to read about all these relaying features so that you fully understand them before choosing any.
Hosts and domains to which mail can be relayed are listed either in a special sendmail class, or in the access database. You add hosts and domains to the special class with either the RELAY_DOMAIN mc macro, or the RELAY_DOMAIN_FILE mc macro.
A special class (currently $=R)[127] holds as its list of values host and
domain names to which
sendmail should allow mail to
be relayed. Relaying is discussed in general in
the sections that follow.
You add domain names to this class like this:
RELAY_DOMAIN(`list of hosts and domains')
Here, the list is one or more hosts or domains separated from each other by spaces:
RELAY_DOMAIN(`our.internal.domain our.company.domain')
If you find it more convenient to list them on separate lines, you can do so like this:
RELAY_DOMAIN(`our.internal.domain') RELAY_DOMAIN(`our.company.domain')
The list can be host or domain names, or IP
addresses, or network numbers. IPv6 addresses can
be specified by prefixing each with the literal
text IPv6:, as
for example:
host.another.domain ← a hostname your.domain ← a domain name 123.45 ← a network (leftmost numbers) 123.45.67.89 ← a host IP address IPv6:2002:c0a8:02c7 ← an IPv6 network IPv6:2002:c0a8:51d2::23f4 ← an IPv6 host address
You can also maintain a list of hosts, domains, and addresses that can be relayed to in an external file. That file is declared with the following macro:
define(confCR_FILE, `path') ← deprecated RELAY_DOMAIN_FILE(`path')
The recommended value for path is /etc/mail/relay-domains:
RELAY_DOMAIN_FILE(`/etc/mail/relay-domains')
This declaration causes a list of relay hosts,
domains, or addresses to be read from the file
/etc/mail/relay-domains.
Because RELAY_DOMAIN_FILE is implemented with an
F configuration
command (The F Class Command on
page 857), you can add whatever F command arguments you
desire. For example:
RELAY_DOMAIN_FILE(`-o /etc/mail/relay-domains')
Here, the -o switch makes the presence of the
/etc/mail/relay-domains file
optional.[128]
If you are currently reading relaying
information from a file declared directly with the
F configuration
command, you are encouraged to convert to this new
macro. Use of it will insulate you from change in
the future if a different class name is ever
used.
The percent-hack is a form of address that uses the
% character to
route mail through one host to another. For example,
the following address uses the percent-hack to relay
mail through hostA for delivery
to hostB:
user%hostB@hostA
The intention here is to cause sendmail to connect to the hostA host and send the message by specifying user@hostB as the envelope recipient, meaning that hostA will relay the message to hostB.
V8 sendmail no longer allows the
percent-hack form of relaying without first
performing two checks. First, the connected-to host,
the one following the @ (hostA), is
looked up in the class macro defined by the RELAY_DOMAIN
mc macro, and in the
access database. If the
connected-to host is neither in that class nor OK’d
by the access database, the
message is rejected with:[129]
550 5.7.1 Relaying denied.
If the connected-to host is OK,
sendmail looks up the
destination host hostB, also in
the class macro defined by the RELAY_DOMAIN
mc macro (The RELAY_DOMAIN mc macro
on page 269) and in the access
database. If the destination host is neither in that
class nor OK’d by the access
database, the message is rejected; otherwise, it is
accepted for delivery.
In brief, for the percent-hack to work, both hosts
must be listed in the class macro defined by the
RELAY_DOMAIN
mc macro, or OK’d by the
access database, or
both.
One way to list them might look like this in your mc configuration file:
RELAY_DOMAIN(`hostA.domain hostB.domain') ← V8.9 and laterIf it is not possible for you to know ahead of time which hosts should be listed in that class, you might want to loosen this check. But be forewarned: if you think you need to loosen this check, you probably do not need to.
To loosen these checks, you can use the FEATURE(loose_relay_check) which is
declared in your mc
configuration file like this:
FEATURE(loose_relay_check) ← CAUTION!Use this feature with caution! It risks allowing
spammers to relay through your server because it
skips the check for the destination host—that is,
the host following the % (hostA in our
example). Consider, for example, a spam site sending
a spam message with the following envelope
recipient:
user%site.to.spam@your.mail.server
If your mail server is listed with RELAY_DOMAIN (as it
would be if it relays mail inward to your internal
network), and if your mail server uses this FEATURE(loose_relay_check), there is
nothing to prevent a spam message from being relayed
to any arbitrary site on the Internet, or for your
site to be abused as a spam relay.
Note that this feature can be of benefit in an internal network protected by a well-configured mail gateway and a firewall because it allows testing of internal mail hubs as potential MX servers for internal-only email.
Sometimes it is beneficial to set up a mail server
that will relay mail from any host that connects to
it. Consider a main mail-sending machine that exists
behind a firewall. In this example, the mail-sending
machine is separate from the mail-receiving machine.
The mail-sending machine has inbound port 25 blocked
at the firewall so that it cannot receive mail from
anywhere but the internal network. In such an
arrangement, it is simpler to allow any internal
host to relay mail than to specify individual hosts
or domains in the access
database, or with the class $=R, or with authentication.
If this simpler approach is applicable to your site,
and if your network is totally secure around port
25, you can enable unfettered or “promiscuous”
relaying with this FEATURE(promiscuous_relay). You declare
it like this:
FEATURE(`promiscuous_relay')
To underscore the risk associated with this feature, the following warning will be printed each time you build with your mc configuration file:
*** WARNING: FEATURE(`promiscuous_relay') configures your system as open
relay. Do NOT use it on a server that is connected to the Internet!By declaring this feature, you tell sendmail to allow mail received by the local machine from anywhere in the world to be relayed outward to any machine in the world. This opens up the local machine to be used by spam engines worldwide, and almost guarantees that the local machine will eventually become listed by one or more DNSBL sites.
You should use this feature only if the affected
machine is secured by other means. If you don’t have
an effective firewall, or don’t have knowledgeable
network administrators, you should avoid using this
FEATURE(promiscuous_relay).
When sendmail receives a message bound for another host, it might be doing so because the local machine is listed as an MX record for that other host (How sendmail Uses DNS on page 325) and the other host is temporarily down. When that other host comes back up, sendmail will deliver all such queued messages to it. You allow relaying for hosts for which your site serves as an MX record, by listing the names of those sites in your relay-domains file ($=R on page 874).
There might be times, however, when your site must be
an MX server for an unknown number of sites, or an
unknown variety of domains. One such example might
occur when your machine is behind a firewall on a
private network. You might be the central MX site
for all internal domains that are created or renamed
often. For such sites, sendmail
offers the FEATURE(relay_based_on_MX) that looks
like this:
FEATURE(`relay_based_on_MX')
When you declare this feature, you allow sendmail to relay mail to any host for which your site is listed as an MX record. Fortunately, you don’t have to keep track of which hosts do list your site because this feature makes the process automatic.
This feature should be used only in an environment where you administer or trust the DNS records. You should not use it if your DNS lookups come from the Internet at large because, in that instance, anyone in the world would be able to use your machine as an MX server without your knowledge or permission.
Note that you should not use this FEATURE(relay_based_on_MX) if you also
use the -z switch
with the bestmx
database map (bestmx on page
902).
Also note that relaying for MX purposes is different
from relaying for the % hack (see FEATURE(loose_relay_check) on page 270).
By default, only hosts listed in the
access database (The access Database on page 277) with
the righthand side keyword RELAY, or hosts that are listed with
the RELAY_DOMAIN
macro (The RELAY_DOMAIN mc macro
on page 269), are allowed to relay mail through the
local host. You can allow all the hosts in your
domain to relay mail through the mail server by
listing them there, but it is much easier to use the
shorthand method provided by the FEATURE(relay_entire_domain):
FEATURE(`relay_entire_domain')
When you define this feature, you enable any host
listed in the class $=m (which contains your domain; $=m on page 872) to relay
mail through the local host. Note, however, that if
your host is named something such as
bob.gov, your host and domain
will be the same. Whatever you do, never put a
top-level domain such as gov,
or com, or
de into $=m, or you will find
your site relaying mail for any host in that
top-level domain.
Note that $=m
should not be used to have mail accepted as local
under a variety of domains. Instead, use the
FEATURE(domaintable) (FEATURE(domaintable) on page 621).
Normally, relaying is based on the domains listed with
the RELAY_DOMAIN
mc macro (The RELAY_DOMAIN mc macro
on page 269) or in the file specified by the
RELAY_DOMAIN_FILE
mc macro (The RELAY_DOMAIN_FILE mc macro
on page 269) or on the domains allowed to relay in
the access database. When
sendmail checks to see
whether a domain should be allowed to relay, it
interprets each domain as a top-level domain. For
example, if RELAY_DOMAIN listed the following
entry, or if the RELAY_DOMAIN_FILE file contained the
following entry:
your.domain
all the following domains would also match that single domain entry:
sub.your.domain a.very.deep.sub.your.domain
As an alternative, you can have
sendmail interpret each name
as the literal name of a host. If you prefer this
second method, you can enable it by declaring the
relay_hosts_only feature like
this:
FEATURE(relay_hosts_only)
With this feature declared,
sendmail will compare the
sending host to the list of hosts, and to hosts
looked up in the access
database, on a host-by-host basis. For example, if
the RELAY_DOMAIN
defined the following:
sub.domain
only a host named sub.domain would be allowed to relay. Another host—say, hostB.sub.domain—would not be allowed to relay unless it too was listed, or OK’d by the access database.
Clearly this feature gives you more control over who
can and cannot relay. It can be of value at a site
that is populated by some network printers and some
Unix machines. The file specified by RELAY_DOMAIN_FILE could
be set up to allow the Unix machines to relay, but
not the printers.
During an SMTP conversation, the sending host
specifies the address of the envelope sender by
issuing a MAIL
From: SMTP command. RFC2822 commentary
and DSN extensions are then discarded from that
specified address, and the result is stored in the
$f macro ($f on page 824).
If you wish, you can use the value in the $f macro to determine
whether a message should be relayed to any outside
or inside host. Although such a method is fraught
with risk, it is still made available with the
FEATURE(relay_local_from) which is
declared like this:
FEATURE(`relay_local_from')
Because this feature poses risk, the following warning will be printed each time you build with your mc configuration file:
*** WARNING: FEATURE(`relay_local_from') may cause your system to act as open
relay. Use SMTP AUTH or STARTTLS instead. If you cannot use those,
try FEATURE(`relay_mail_from').When you declare this feature you cause the domain
part of the address in $f (the portion of the address to the
right of the @
character) to be compared to the list of hosts in
the $=w class
macro ($=w on page
876). Recall that the class $=w contains all the names by which the
local host can be known. If the domain in $f is found in that
class, relaying is allowed.
The risk should be obvious. Because $f is given its value as
a part of the SMTP MAIL
From: command, that address can be
forged to appear local by anyone on the Internet.
That is, by declaring this feature, you are opening
up your host to abuse by the entire world.
So, why does sendmail offer this
FEATURE(relay_local_from)? If you
administer a site that is behind a firewall and an
Internet mail hub, and if your internal machines
cannot be contacted on any port from the outside
world, you might find this a simple way to allow
global relaying within that network.
We suggest, however, that SMTP AUTH (Support SMTP AUTH on page 183) or
STARTTLS (STARTTLS on page 202) will provide
a safer way to authenticate local origination
addresses upon which to base the permission to
relay. A safer way to relay based on connection
domains is the Connect: keyword in the
access database. If you
prefer a simpler solution, the FEATURE(relay_mail_from), described next,
might be just what you are looking for, although it,
too, is risky.
During an SMTP conversation, the sending host
specifies the address of the envelope sender by
issuing a MAIL
From: SMTP command. RFC2822 commentary
and DSN extensions are then discarded from that
address, and the result is stored in the $f
sendmail macro ($f on page 824).
If you wish, you can use the value in the $f
sendmail macro to determine
whether a message should be relayed to any outside
or inside host. Although such a method is fraught
with risk, it is still made available with the
FEATURE(relay_mail_from) which is
declared like this:
FEATURE(`relay_mail_from')
Because this feature poses risk, the following warning will be printed each time you build your cf file from your mc file:
*** WARNING: FEATURE(`relay_mail_from') may cause your system to act as open
relay. Use SMTP AUTH or STARTTLS instead.By declaring this feature, you cause the address in
the $f
sendmail macro to be prefixed
with a literal From: and looked up in the
access database (The access Database on page 277). If
it is found in that database, and the value returned
is a literal RELAY, that address is allowed to be
relayed:
From:bob@your.domain RELAY ← this sender can relayIf you want to base the decision to relay on a domain
instead of on an individual’s address, you can
declare this feature with an additional argument
that is a literal domain:
FEATURE(`relay_mail_from', `domain')
With this extra argument in place, the domain part of
the address in $f
(the portion of the address to the right of the
@ character)
will be prefixed with a literal From: and looked up in
the access database. If it is
found, and if the value returned is a literal RELAY,
that domain will be allowed to be relayed:
From:your.domain RELAY ← this domain can relayThis feature is fraught with risk. By defining it, you
allow anyone on the Internet to spoof allowed
addresses as part of any SMTP MAIL From: command. If
you want to allow local hosts to relay mail from the
local network to the world, you can either
authenticate with SMTP AUTH (Support SMTP AUTH on page 183) or STARTTLS (STARTTLS on page 202), or you can relay based on
connections using the Connect: keyword in the
access database.
UUCP addresses are those that use a ! character to separate
address components. For example, the following
address says to send the message first to the host
hostA, and then
hostA will relay that message
to the user at hostB:
hostB!user@hostA
If you have tuned your site to prevent unintended
relaying, misuse of the nouucp feature can open your site to an
unexpected form of relaying.
Consider a workstation on your network that forwards
all its mail to the central mail hub using LOCAL_RELAY (LOCAL_RELAY mc Macro on
page 604) or LUSER_RELAY ($L
on page 832). If that workstation also
defines:
FEATURE(`nouucp', `nospecial')
addresses containing the ! character will not be recognized as
special and will be forwarded to the mail hub as
is.
If, on the mail hub, you forget to declare the
FEATURE(nouucp), the as-is address
forwarded to it will be
recognized as special. Because the address was
received from an internal workstation, relaying is
allowed. The !
address will have the hostA
part stripped and the result will be relayed to
user@hostB.
Thus, it is a good idea to define nouucp on the mail hub
if you define it on any of your workstations.
Beginning with V8.9, sendmail
will refuse a mail message if the address specified
as part of the SMTP MAIL
From: has a domain part that cannot be
looked up. For example, if the domain
foo.bar does not exist, the
following error will be logged with
syslog and the message will
be rejected with the same error message:
553 5.1.8<user@your.domain>... Domain of sender address other@foo.bar does not exist
If the domain cannot be looked up, the result is a temporary error:
451 4.1.8<user@your.domain>... Domain of sender address other@foo.bar does not resolve
We recommend rejecting such addresses, but there might be circumstances in which you cannot. If, for example, you are behind a firewall and lack access to full DNS lookups, you might want to accept everything. But if that is the case, you will need a sending mail hub with good DNS access so that you can reply to such messages.
You can accept such addresses by defining the FEATURE(accept_unresolvable_domains):
FEATURE(`accept_unresolvable_domains')
This tells sendmail to accept all
envelope-sender addresses, even if the domain part
following the @
cannot be looked up with DNS.
The sendmail program refuses to
accept a message if the address specified as part of
an SMTP MAIL
From: command lacks a domain. That is,
if the address has a user part but lacks the
@ followed by a
domain, the message will be rejected:
MAIL From:<bob@foo.com> ← good, has a domain part MAIL From:<bob> ← bad, lacks a domain part
Some mail submission programs will submit mail without
including a domain part. Improperly configured PCs
are one example, as are poorly configured Unix
hosts. Generally, such problems will appear on your
local network. If you lack the authority to fix such
a problem, you can tweak
sendmail to accept such
addresses by including the FEATURE(accept_unqualified_senders)
like this:
FEATURE(`accept_unqualified_senders')
Note that this feature accepts unqualified addresses regardless of the port on which they are received. Such a broad solution might be acceptable on an internal network, but it is discouraged on machines that service the Internet. For those hosts, we recommend you tune acceptance or rejection of unqualified addresses on a port-by-port basis.
The DaemonPortOptions option u modifier (DaemonPortOptions=Modify=
on page 996), when set, has the same effect as
declaring this feature for the given single port.
That is, unqualified addresses are accepted on a
port-by-port basis, without the need to declare this
feature.
The DaemonPortOptions option f modifier (DaemonPortOptions=Modify=
on page 996), when set, tells
sendmail to reject
unqualified addresses received on this port, even if
this feature is declared. That is, you accept
unqualified addresses on all ports by declaring this
feature, and then reject them on a port-by-port
basis with this f
keyword.
The access database was introduced in
V8.9 sendmail, and improved upon in
V8.10. It provides a single, central database with rules to
accept, reject, and discard messages based on the sender
name, address, or IP address. It is enabled with the
FEATURE(access_db).[130]
For example, consider an access database with the following contents:
From:postmaster@spam.com OK From:spam.com REJECT
Here, mail from postmaster at the site spam.com is accepted, whereas mail from any other sender at that site is rejected.
Note that this example uses V8.10 syntax. Next, we will describe the access database using the old V8.9 syntax, and then describe the V8.10 and V8.12 updates.
To enable use of this access database, declare it in your mc configuration file like this:
FEATURE(`access_db')
This enables use of the access database, and enables the default database type and path as:
hash /etc/mail/access ← V8.11 and earlier hash -T<TMPF> /etc/mail/access ← V8.12 and later
Note that with V8.12 and later a -T<TMPF> has been
added to specify that temporary errors should return
a 4xy SMTP code.
If you wish to use a different database type or
pathname, you can do so by providing an appropriate
argument to the FEATURE(access_db):
FEATURE(`access_db', `hash -o /etc/mail/access') ← V8.11 and earlier FEATURE(`access_db', `hash -o -T<TMPF> /etc/mail/access') ← V8.12 and later
Here, we add the -o
switch (-o on page 889) to the
definition to make the existence of the
/etc/mail/access database
file optional.
Beginning with V8.12, this feature takes two more arguments:
FEATURE(`access_db', `db specification'`skip', `lookupdotdomain')
The skip (the third
argument), if present, enables SKIP as a possible
return value for the access
database (SKIP on page
280).
The lookupdotdomain
(the fourth argument), if present, enables the same
behavior as though you independently declared the
FEATURE(lookupdotdomain) (FEATURE(lookupdotdomain) on page 628).
Beginning with V8.14, the new keyword relaytofulladdress can
appear as either the second, third, or forth
argument. Here, for example, it is supplied as the
second argument:
FEATURE(`access_db', `db specification'`relaytofulladdress') ← V8.14 and later
If given as an argument to this feature, relaytofulladdress
allows entries like the following to appear in the
access
database:
To:user@host.domain RELAY ← V8.14 and laterThis allows relaying based on the recipient’s full address rather than just to the host, as was the case under previous versions.
To create the access database, you first create a text file that contains lines of hosts, addresses, and IP addresses paired with keywords and values. After that, you run makemap to create the actual database from the text file. If the text file is named /etc/mail/access, you would build the database like this:
#cd /etc/mail#makemap hash access < access
The text file itself looks like this:
key value
↑
whitespace: one or more tabs or spacesThe text file is composed of two columns of
information. The lefthand column is the key which is
composed of a prefix and an address expression. The
prefix depends on the rule set doing the lookup. For
some it is Connect: or From:, and for others it is TLS_Srv: or TLS_Clt:. These are
described in the sections of this book dealing with
the appropriate rule set.
The address expression can be any of the following depending on what the rule set is trying to do:
host.your.domain ← a hostname your.domain ← a domain name user@ ← a username user@host.another.domain ← a user address 123.45.67.89 ← an IPv4 host address 123.45 ← an IPv4 network (leftmost numbers) IPv6:2002:c0a8:51d2::23f4 ← an IPv6 host address IPv6:2002:c0a8:02c7 ← an IPv6 network (leftmost numbers)
Note that for usernames the @ is mandatory. More address
expressions can be used than we show here. These are
the most common. Others are described under the rule
sets that use them.
The righthand column contains the value, which can be keywords or values that determine what should be done with the item described on the left. They are shown in Table 7-3, and are described in the sections indicated.
|
Righthand value |
§ |
Description |
|
OK |
OK on page 279 |
Accept the lefthand-side entry. |
|
RELAY |
RELAY on page 279 |
Allow the lefthand side to relay mail through this machine. |
|
REJECT |
REJECT on page 280 |
Reject the lefthand side (bounce the message). |
|
DISCARD |
DISCARD on page 280 |
Reject the lefthand side (bounce the message). |
|
SKIP |
SKIP on page 280 |
Stop looking for the key, and don’t look for any future parts of the key. |
|
XYZ text |
XYZ text on page 280 |
Reject with custom SMTP code and message. |
|
ERROR:XYZ text |
ERROR:XYZ text on page 281 |
Reject with optional custom SMTP code and message. |
|
ERROR:D.S.N:XYZ text |
ERROR:D.S.N:XYZ text on page 281 |
Reject with a more precise DSN code. |
|
SUBJECT |
The access database and Local_Relay_Auth on page 213 |
Also look up the CERT subject. |
|
VERIFY |
The access database with tls_server and tls_client on page 214 |
Verify the certificate. |
|
VERIFY:bits |
The access database with tls_server and tls_client on page 214 |
Verify the certificate and require minimum number of encryption bits. |
|
ENCR:bits |
The access database with tls_server and tls_client on page 214 |
The minimum number of encryption bits. |
The OK righthand value for the access database tells sendmail to accept the user, host, domain, or address on the lefthand side. That is, even if other rules in the rule set that did the lookup reject it (because the domain cannot be looked up with DNS, for example), this return value will still cause it to be accepted by that rule set. Note, however, that other rule sets can subsequently reject it.
The RELAY access database
righthand value tells
sendmail to allow the user
(if the FEATURE(relay_mail_from) is defined),
host, domain, or address listed on the lefthand
side to relay mail through this machine. It also
allows mail to be relayed by anyone when routing
is to the host, domain, or IP addresses listed.
Note that RELAY also includes the behavior of
OK.
The REJECT access
database righthand value tells
sendmail to reject the user,
host, domain, or address listed on the lefthand
side. The rejection will use the default message
defined by the confREJECT_MSG
mc macro (Rejection Message for REJECT on page
283).
The DISCARD access
database righthand value tells
sendmail to accept any sender
that is listed on the lefthand side (either as a
user, host, or IP address), but to silently
discard the message using the discard delivery agent
(discard on page 719). The
DISCARD keyword can also be used for recipients if
the FEATURE(blacklist_recipients) (Reject per Recipient on page 284) is
declared. When a recipient is discarded and when
there are other recipients in the envelope, all
recipients are discarded. The one exception is if
DISCARD is returned to the check_compat rule set,
established by the FEATURE(compat_check) (FEATURE(compat_check)—V8.12 and Later on page 288), only the one recipient is
discarded.
This keyword provides a way to list hosts, domains, or addresses that can give a default behavior. Such defaults are defined by your selection of features to enable. Sometimes, for example, you might desire to have the lookup of a host, domain, or address return (if found), but have no further checks performed on it. One way to do that is by using this special SKIP keyword as the return value for the lookup:
From:bob.domain SKIP
If the lookup was done to see whether relaying is OK for the domain bob.domain, this SKIP instructs sendmail to act as though the lookup did not find bob.domain. Thus, if the default is to deny relaying, relaying for bob.domain will be denied. If the default is to allow relaying, relaying for bob.domain will be allowed.
The main use for SKIP is with the FEATURE(lookupdotdomain)
(FEATURE(lookupdotdomain) on page 628). With that feature defined, you
could set up the access
database like this:
From:server.bob.domain SKIP From:.bob.domain RELAY
Here, mail from the machine server.bob.domain will be handled by the default rules. All other hosts in the bob.domain domain will be allowed to relay.
The SMTP protocol, as documented in RFC2821,
defines a set of three-digit codes that have
special meaning to the sending site. When
sendmail rejects the envelope
sender, it does so by printing a 550 code in reply
to the MAIL
From: command. This special form of
keyword in your access
database allows you to cause
sendmail to print a code,
followed by words of your choice. Consider this
entry in the access
database:
From:sales@spam.com ERROR:554 Spam delivery is unavailable.
Here we chose 554, which stands for “service unavailable.” When mail is received at your site, this rule will cause the following interaction in the SMTP conversation:
MAIL From:<sales@spam.com> 554 5.0.0 <sales@spam.com>... Spam delivery is unavailable.
The text you give following the SMTP might or might not appear in the bounced mail that sales receives.
The XYZ code you specify does not have to be a 500 code (meaning failure). You are also free to use 400 codes (to defer the mail) too. Deferral might be appropriate as a means to handle a temporary resource limitation:
newsupdates@your.domain 421 Our database is down for two days for repair
This righthand value is the same as the
"XYX text"
expression discussed earlier, but the ERROR: signals that the
envelope sender should be rejected. The XYZ is optional, and
sendmail will supply a value
if it is missing. This provides a handy way to
reject mail without having to remember the correct
SMTP numbers:
sales@cybermarketing.com ERROR: Stop spamming us
When this address arrives from the outside, the SMTP will look like this:
MAIL From:<sales@cybermarketing.com> 553 5.3.0 <sales@cybermarketing.com>... Stop spamming us
This righthand value is the same as ERROR:XYZ text, but it
allows you finer control of the SMTP rejection
message.[131] The D.S.N can be any of the DSN codes
defined in RFC1893. You should use this form if
you change the SMTP code from the default used by
sendmail:
newsupdates@your.domain 450 Cache mailbox disk is full
Here, for example, you reject mail to newsupdates at your site because the database is down, so you cannot drain the cache file. By changing the SMTP error from its default, you will cause sendmail to wrongly report the DNS error:
RCPT To:<newsupdates@your.domain> 450 4.0.0 Cache mailbox disk is full
The 4.0.0
is not the correct DSN code for a full mailbox.
Instead, you should specify 4.2.2, like this:
newsupdates@your.domain ERROR:4.2.2:450 Cache mailbox disk is full
This form uses three colon-delimited fields on
the righthand side of the
access database. The first
field is the literal ERROR string. That is followed by your
specification for the correct DSN code. The third
field is the SMTP error text as we described
earlier.
Prior to V8.10, the lefthand side of the
access database could contain
only a user, host, domain, or address, and would
only look them up based on the client name or
address, the MAIL
From: address, or the RCPT To:
address.
Beginning with V8.10, sendmail offers much finer control of addresses and rejections in the access database. The lefthand side of the access database can begin with one of three possible prefixes:[132]
Connect:The address is either the IP address or the hostname of a connecting host.
From:The address is that of an envelope sender.
To:The address is that of an envelope recipient.
When an address is looked up in the access database, it is first looked up with the prefix. If it is not found, it is looked up again without a prefix, meaning that the old access databases will still work with newer versions of sendmail. To illustrate, consider this update to the access database shown in the previous section:
From:spamuser@hotmail.com REJECT From:cybermarkets.com REJECT Connect:example.org REJECT Connect:192.168.212 REJECT
This access database will cause mail from spamuser@hotmail.com to be rejected. Mail from any user at cybermarkets.com will be rejected, connections from the host example.org will be rejected, and any mail from any host with an IP address ranging from 192.168.212.0 through 192.168.212.255 will have the initial connection rejected. In that last example, any missing righthand part of an IP address is assumed to be a wildcard for matching purposes.
This behavior is exactly the same as the example without prefixes, with one exception. The example.org line, without a prefix, will reject mail from all users at example.org as well as connections from example.org.
A more complex example will better illustrate the properties of the three prefixes:
To:bob@host.domain RELAY ← V8.14 and later with relaytofulladdress (§ 7.5 on page 277) defined To:friend.domain RELAY From:friend.domain RELAY Connect:friend.domain OK Connect:bad.domain REJECT
Here, we reject or allow based only on the envelope-recipient or the connecting host.
The first two lines say that mail arriving at your site, regardless of its origin, will be allowed to be relayed to bob at the site friend.domain (the first line) or to any user at the site friend.domain (the second line). This provides a way to allow relaying on a host-by-host basis, even if you have all relaying turned off with the various antirelay features (Relaying on page 267). This is useful if you are a secondary MX site for friend.domain.
The second line says that we will also relay mail from
the site friend.domain. A line
such as this requires that you have declared the
FEATURE(relay_mail_from) (FEATURE(relay_mail_from) on page 274) with a literal domain second argument.
Here, if the envelope sender is any user at
friend.domain, the envelope
recipient can be a local or remote address.
The third line says that we will specifically accept
connections from the host
friend.domain. We do this
because that site might be rejected if it is listed
on some DNSBL site. An OK via a Connect prefix overrides
any rejection based on DNSBL lists.
The fourth line rejects connections from the host bad.domain no matter what. This is one way to reject connections on a site-by-site basis, if, for example, you want to block messages from a site that is pushy but not eligible for listing with a DNSBL server.
Even if you lack an immediate use for these prefixes you should consider using them, just to experience their power.
When an address is rejected because of the presence of REJECT in the access database, it is rejected with the default message:
550 5.7.1 Access denied
Beginning with V8.9 sendmail, you
can change that message (to augment it or to clarify
the reason for the rejection) using the confREJECT_MSG
mc file macro. For example, to
show why the message was rejected, you could place
the following in your mc
file:
define(`confREJECT_MSG', `550 Access denied. See http://www.your.domain/ access_denied')
Because the message you specify will be quoted in the configuration file, you cannot place any m4 macros or positional m4 macros in the message. They will be silently stripped from the message.
But note that, beginning with V8.13, quotation marks
are no longer automatically inserted. Instead, the
value in confREJECT_MSG is inserted into your
cf file as is
(with no added quotation marks). Note that if you
previously depended on this auto-quoting in your
mc file, you
will now have to add quotation marks of your
own.
The FEATURE(access_db) (The access Database on page 277)
provides a way to selectively reject envelope-sender
addresses. By declaring this FEATURE(blacklist_recipients), you
enable the access database to
also selectively reject envelope-recipient
addresses:
FEATURE(`blacklist_recipients')
Consider the need to prevent outsiders from posting to strictly inside mailing lists. In this example, the mailing lists are handled on a machine different from that on which outside mail is received. On the receiving machine, you would put lines such as these in your access database:
board@our.domain 550 Outside access to private mailing list banned accounting@our.domain 550 Outside access to private mailing list banned 401k-help@our.domain 550 Outside access to private mailing list banned
By declaring this FEATURE(blacklist_recipients), these
addresses will be prevented from receiving outside
mail.
All forms of addresses in the access database can be used for this recipient rejection. Consider:
To:badguy@ ERROR:550 Mailbox disabled for this user To:host.our.domain ERROR:550 This machine bans email To:123.45.67.89 ERROR:550 Printers cannot receive email
Be careful when rejecting recipients based on the
username alone, as in the first line in this
example, because the username is rejected for both
the envelope sender and the envelope recipient.
Thus, this line will reject mail to both badguy locally, and from
badguy at all
other sites in the world.
When a connection is made to your site by another, the
access database is checked to
reject unwanted connections.[133] It is checked again when the SMTP
MAIL From:
command is given to accept or reject the envelope
sender. It is checked a third time when the SMTP
RCPT To:
command is given to accept or reject the envelope
recipient, and prevent unwanted relaying.
This order is good for most sites, but might not be
the best for your particular needs. In case it
isn’t, the FEATURE(delay_checks) offers a way to
check the SMTP RCPT
To: address first, before the other two
checks, and then proceed with those other two
checks, if appropriate. Delayed checks are enabled
with the FEATURE(delay_checks) which you declare
in your mc file like
this:
FEATURE(access_db) FEATURE(delay_checks)
Note that the FEATURE(access_db) needs to be enabled
before you enable the FEATURE(delay_checks).
Once enabled, the order of checks is changed. If the righthand side in the access database is either REJECT or an SMTP error for the envelope recipient, the envelope recipient is rejected as usual. But if the envelope recipient is allowed, the envelope sender is then checked, and if it is rejected, the envelope recipient is rejected with the envelope sender’s error message. If the envelope sender is allowed, the connecting host is checked, and if it is rejected, the envelope recipient is rejected with the connecting host’s error message.
For example, consider the following abstract from an access database:
To:postmaster@ OK From:larry@ REJECT Connect:spammer.domain REJECT
With the FEATURE(delay_checks) enabled, the
first check will come as part of the SMTP RCPT To: command, and
that address will be looked up in the
access database. In this
example, if the user part of the recipient address
is postmaster, the message will
be accepted[134] by the current calling rule set.
Subsequent rule sets can still reject it.
If the user part of the envelope-sender address is not
postmaster, the address given
to the earlier SMTP MAIL
From: command will be looked up. If that
envelope-sender address has a user part that is
larry (in our example) the
message will be rejected, but because it is too late
to reject the SMTP MAIL
From:, the rejection will be given to
the SMTP RCPT To:
command.
If the envelope sender is OK, the name of the
connecting host will be looked up. If the host is
found in the access database,
and if the righthand side is REJECT, the message is
rejected and the error will be reported in reply to
the SMTP RCPT To:
command. If the host is found in the
access database, and if the
righthand side is RELAY, the message is allowed to
be relayed. If the host is not found, and if no
other relaying is allowed, the message will not be
allowed to be relayed and the denial-of-relay error,
if any, will be reported in reply to the SMTP
RCPT To:
command.
One reason to check the SMTP RCPT To: address first might be to
allow mail from a spam site to be delivered to a
specific local user, but still block mail from that
site for all other users. Another reason might be to
block mail from a spam site for a specific user, but
allow it to be delivered to all others. You can tune
the access database to do one
(but only one) of these two things by defining the
FEATURE(delay_checks) with an extra
argument that must be one, and only one, of two
possible lowercase words:
FEATURE(access_db) FEATURE(`delay_checks', `friend') ← this one or FEATURE(`delay_checks', `hater') ← this one, but not both
When the extra argument is friend, you can allow mail from a spam
site to a specific local user, while still blocking
mail from that site for all other users. When the
extra argument is hater, you can block mail from a spam
site for a specific user, while allowing it to be
delivered to all other users. If the extra argument
is neither (or was uppercase), the following error
will be printed when you build your configuration
file, and that file will be incomplete:
*** ERROR: illegal argument bad word here for FEATURE(delay_checks)The check_rcpt
(Local_check_rcpt and check_rcpt
on page 257) rule set performs the lookup, and the
relationship found (friend or hater) determines whether the check_mail (Local_check_mail and check_mail
on page 255) and check_relay (Local_check_relay and check_relay on page 252) rule sets should be called to
perform further checks.
If the extra argument is friend and if the database lookup
returns FRIEND, those further rule set checks will
be skipped and the message will be accepted. But if
the database lookup fails to find the address, or
returns something other than FRIEND, further
screening by the check_mail and check_relay rule sets is
performed.
If the extra argument is hater and the database lookup returns
HATER, further screening by the check_mail and check_relay rule sets is
performed. But if the database lookup fails to find
the address, or returns something other than HATER,
those additional rule sets will be skipped.
As a first step, decide which of the two forms you prefer (remember, you can do only one or the other), and then add the new definition to your mc file, generate a new configuration file, and install it. Once the new configuration is ready, you can use one of two new righthand-side keywords in your access database:
This address is allowed to receive messages
that would otherwise be rejected by the check_mail and check_relay rule sets.
You can use this keyword only if you defined
friend when you
declared the FEATURE(delay_checks).
This address will only receive messages that
are also accepted by the check_mail and check_relay rule sets. An address not
listed as SPAMHATER (V8.10 and V8.11) or HATER
(V8.12) will have processing by those additional
rule sets skipped. You can use this keyword only
if you defined hater when you declared the FEATURE(delay_checks).
To illustrate, consider the following abstract from
such an access database where
we declared the FEATURE(delay_checks) as friend:
Connect:spam-mail.com REJECT To:abuse@your.domain SPAMFRIEND ← V8.10 through V8.11 Spam:abuse@your.domain FRIEND ← V8.12
The site spam-mail.com is one of possibly many sites that will be rejected by our site because they are known spammers. Mail addressed to abuse@your.domain will be accepted even if it is from any of the rejected spam sites.
In the following example, we declared the FEATURE(delay_checks) as
hater:
Connect:spam-mail.com REJECT To:payroll@your.domain SPAMHATER ← V8.10 through V8.11 Spam:payroll@your.domain HATER ← V8.12 To:abuse@your.domain OK
Here, the site spam-mail.com is one of possibly many sites that will be rejected by our site because they are known spammers. Mail to payroll@your.domain will have spam sites rejected. Mail to abuse@your.domain will still be allowed to receive mail from otherwise rejected sites.
Whether you choose the spam haters or spam friends approach is entirely dependent on your site’s unique needs. Think through the logic of your choice before setting up your access database. If you make a mistake, you might inadvertently allow spam to a user who doesn’t want it.
Note that you cannot mix friends and haters. If you
do, sendmail will ignore the
mismatch. For example, if you declare the FEATURE(delay_checks) as
friend and
place the following entry into your
access database, that entry
will be ignored:
Spam:payroll@your.domain HATER ← ignored because delay_checks specified friendRemember, you must choose one, and only one: either
friend or
hater. You
cannot choose both, nor can you mix the two.
Note that the syntax of this FEATURE(delay_checks) has changed. It
differs between V8.10 through V8.11 and V8.12. If
you have already used this feature with V8.10 or
V8.11 sendmail, you will need
to change it for V8.12. You will need to add the
Spam: prefix
and new righthand-side values to your
access database.
As an aid to conversion, the old syntax will be
ignored. Once you have finished converting from the
earlier syntax to the new, you can redeclare this
V8.12 FEATURE(delay_checks) by adding a
literal n as a
third argument:
FEATURE(`delay_checks', `friend', `n') ← V8.12 and later FEATURE(`delay_checks', `hater', `n') ← V8.12 and later
This n turns off
backward compatibility (the ability to ignore the
old syntax) and causes the old syntax to produce an
error. This is a good way to check to be sure your
conversion was good.
Also note that, with V8.12, if an envelope recipient
is found to be trusted, using one of the mechanisms
listed with the AuthMechanisms option (AuthMechanisms on page 975), that
envelope-recipient is accepted and further
access database checks are
skipped.
Beginning with V8.12 sendmail,
you can create a rule set that makes decisions about
envelope-sender and envelope-recipient pairs with
entries in the access database.
To enable these checks, just add the FEATURE(compat_check) to
your mc configuration
file:
FEATURE(access_db) ← must be first
FEATURE(compat_check)Once this is enabled, you can then add entries such as
the following to your access
database (note that the <@ > is literal):
Compat:sender<@>recipient keywordHere, the Compat:
prefix is literal and must be present. It is
immediately followed (with no intervening spaces) by
the envelope-sender address, a literal <@>, and the
envelope-recipient address (where the
envelope-recipient address has already undergone
aliasing and processing by a user’s
~/.forward file). Neither
address should be surrounded with angle braces. The
address pair is followed by whitespace (spaces and
tabs) and then a keyword. There are three possible
keywords:
Mail from this sender to this recipient is accepted, and then discarded and logged. DISCARD can be followed by a colon. It can also be followed by optional text that will be logged as the reason for the discard.
Mail from this sender to this recipient is rejected with a temporary error (causing the message to be deferred for a later delivery attempt). This keyword must be followed by a valid 4xy SMTP code and text that describes the reason for the temporary failure.
Mail from this sender to this recipient is rejected with a permanent error (causing the message to bounce). This keyword must be followed by a valid 5xy SMTP code and text that describes the reason for the rejection.
To illustrate, consider the following example of such entries in an access database:
Compat:bin@your.site<@>admin@your.site DISCARD Compat:ads@spam.site<@>taka@your.site DISCARD Compat:db@your.site<@>lp@your.site TEMP:421 printer is down for repair Compat:bob@your.site<@>betty@your.site ERROR:553 Interoffice banter banned Compat:betty@your.site<@>bob@your.site ERROR:553 Interoffice banter banned
The first line might be used at a site where the pseudouser bin generates a great deal of automated email and that email is sent to admin, among others. This line causes that automated mail to be accepted and discarded.
The second line might be used to prevent mail from a known spam list from being sent to the user taka at your site. This line causes that spam mail to be accepted and discarded.
The third line might be used to defer mail to a printer from the database. The TEMP will cause the message to be deferred for a later try. You could then remove this line after the printer is repaired and back in service.
The last two lines show a way to prevent two users at your site from sending email to each other. The idea is that it is OK for them to send email to each other at other sites, but not this one. Each such prohibited message is rejected and bounced. For this scheme to work, however, you will need to place an empty root-owned ~/.forward file in each of the two users’ home directories to prevent them from bypassing this restriction by setting up their own ~/.forward files.
This last example underscores a weakness in this
FEATURE(compat_check). Because each
envelope recipient undergoes
aliases translation, and
~/.forward translation before
the lookup, the entry in the
access database must
correctly represent the translated address. For
example, consider a pseudouser named
nill who has an
aliases file entry such as
this:
# deep six mail to nill nill: /dev/null
Here, the intention is to have all mail to nill delivered to the /dev/null file. If you wanted selected mail to nill to be rejected, do not do this:
Compat:user@other.domain<@>nill@your.site ERROR:553 Don't mail to nill
↑
noteThe nill@your.site will never be found because by the time this lookup happens, nill has been transformed into /dev/null. The correct way to set up your access database to handle this situation would look like this:
Compat:user@other.domain<@>/dev/null ERROR:553 Don't mail to nill
↑
noteAlso note that when the recipient is an actual user (as, for example, bob):
Compat:user@other.domain<@>bob@your.site ERROR:553 Don't mail to bob
bob can alter his ~/.forward file at any time, thus rendering his recipient entry useless.
Normally, lookups of hosts in the
access database are literal.
That is, host.domain is looked
up first as host.domain and
then as domain. If you declare
the FEATURE(lookupdotdomain)[135] (FEATURE(lookupdotdomain) on page 628) or add a literal lookupdotdomain fourth
argument (Enabling the access Database Generally
on page 277) to the FEATURE(access_db)’s declaration, you
cause the sequence to become
host.domain, then
.domain, and lastly
domain. This feature allows
you to structure an access
database to handle the domain differently than it
handles hosts in the domain:
From:.domain REJECT From:domain OK
Here, envelope senders with a host part of @anything.domain will be rejected, but those with a host part of @domain will be accepted. To illustrate, consider the following attempt to accept mail only from cs.Berkeley.EDU and to reject mail from hosts in that subdomain:
From:.cs.Berkeley.EDU REJECT From:cs.Berkeley.EDU OK
Beginning with V8.12, it is possible to select queue
groups using the access
database by declaring the FEATURE(queuegroup). Queue groups and
the FEATURE(queuegroup) are discussed in
detail in Queue Groups (V8.12 and Later) on page 408.
Beginning with V8.12, it is possible to accept, reject, and allow relaying based on the STARTTLS and AUTH= SMTP extensions. These abilities, and the features that support them, are detailed in Chapter 5 on page 183.
As spam and phishing problems become more and more pervasive, sendmail has added more features specifically targeting those problems:
The FEATURE(badmx) (FEATURE(badmx)—V8.14 and Later on page 291) rejects any client hostname, the
domain part of which resolves to a bad MX record
(V8.14 and later).
The FEATURE(block_bad_helo) (FEATURE(block_bad_helo)—V8.14 and Later on page 292) rejects clients who provide a
HELO/EHLO argument which is
either unqualified (lacks a domain part) or one of
the server’s names (V8.14 and later).
The FEATURE(greet_pause) (FEATURE(greet_pause)—V8.13 and Later on page 293) suppresses slamming attempts
(V8.13 and later).
The experimental FEATURE(mtamark) (FEATURE(mtamark)—V8.13 and Later,
Experimental
on page 295) implements the MTA mark proposal
(V8.13 and later).
The FEATURE(require_rdns) (FEATURE(require_rdns)—V8.14 and Later on page 296) rejects clients whose IP address
cannot be properly resolved (V8.14 and
later).
The FEATURE(use_client_ptr) (FEATURE(use_client_ptr)—V8.13 and Later on page 297) causes the connecting client’s IP
address to be screened early even if the FEATURE(delay_checks) is
used (V8.13 and later).
Most Windows PCs that exist on the Internet lack a fixed IP address. Instead, each uses the DHCP protocol to fetch a fresh IP address each time the machine boots. Such a machine is unable to publish an MX record (Set Up MX Records on page 332) because it has no fixed IP address. Unfortunately, many Windows PCs are hijacked without knowledge of the owner and are made to send out spam email. From such a hijacked machine, it is unlikely that a valid MX record will exist.
To avoid getting spam from such machines, you may use
the FEATURE(badmx). It is declared like
this:
FEATURE(`badmx')
With this feature declared, each time a client machine
connects to your server, the hostname found (by
reverse lookup of the connecting client) is stripped
back to the domain part. For example, if the host
www.example.com were to
connect to your server, the connecting host’s IP
address would be 192.0.34.166. That address is
reverse looked up to find the hostname
www.example.com. This
FEATURE(badmx)
strips the host part from the hostname (the www) and
performs an MX lookup on the result (the
example.com part):
If the lookup returns a temporary error (a DNS retry), the following SMTP error is returned to the client and the connection is deferred:
450 4.1.2 MX lookup failure for domain part looked up is shown hereIf the lookup returns no MX record, the following SMTP error is returned to the client and the connection is refused:
550 5.1.2 Illegal MX record for recipient host domain part looked up is shown hereIf any of the IP addresses returned in the MX list begin with 127.0 (the loopback interface) or 10. (a nonrouting address) or 0. (a broadcast address), that address is considered bad and the following SMTP error is returned to the client and the connection is refused:
550 5.1.2 Invalid MX record for recipient host domain part looked up is shown hereThe FEATURE(badmx)
uses the bestmx
database map (bestmx on page 902);
the regex
database map (regex on page 932);
and the dns
database map (dns on page 905).
You may use this FEATURE(badmx) only if
sendmail was built with
support for all three database-map types.
The HELO and
EHLO client
SMTP commands are each formed by a command followed
by a hostname:
HELO client.host.domain EHLO client.host.domain
According to RFC2821, the hostname provided by the
client (following the HELO or EHLO SMTP) must be:
The canonical hostname of the sending client machine. That is, a host with a domain part. For example, the name “foo” is bad because it lacks a domain. The name “.com” is bad because it lacks a host part. But the name “foo.com” is canonical and valid.
The name of the sending client. That is, it must not be any of the names by which the server knows itself, such as “localhost” or “127.0.0.1.”
Although RFC2821 requires that these characteristics
be met by the client, it also prohibits rejection
based on bad values. If your site is besieged by
spam, however, the niceties of RFC2821 may not seem
worth following. If you desire to reject badly
formed HELO/EHLO hostnames, you may do so by using
this FEATURE(block_bad_helo):
FEATURE(`block_bad_helo')
Note that, with this feature defined, certain clients are not checked at all:
If the client has authenticated with
AUTH, the
HELO/EHLO host is not
checked.
If the client is listed with the RELAY_DOMAIN
mc macro (The RELAY_DOMAIN mc macro
on page 269) or in the file specified by the
RELAY_DOMAIN_FILE
mc macro (The RELAY_DOMAIN_FILE mc macro
on page 269), that client’s HELO/EHLO hostname is not
checked.
Otherwise, all other clients have the host part of the
HELO/EHLO greeting
checked:
If the hostname exists as part of the
server’s $=w
class ($=w on page
876), the HELO/EHLO command is rejected.
If the hostname exists as an IP address in
the server’s $=w class ($=w on page 876), the
HELO/EHLO command is
rejected.
If the hostname has only an initial dot, a
final dot, or no dot at all, the HELO/EHLO command is
rejected.
If the HELO/EHLO greeting is rejected, the client
will receive a permanent rejection like the
following:
550 5.7.1 bogus HELO name used: client's bogus hostname hereThis FEATURE(block_bad_helo) is implemented
as the last check in the check_rcpt rule set (Local_check_rcpt and check_rcpt
on page 257).
Slamming is a technique used by some senders of spam email. It allows spamming machines and hijacked proxies to send a great deal of spam email very rapidly, without the need to monitor for rejections.[136] This is a boon to spam-email companies, but a bane to those who resent that behavior.
To slam, a spammer first opens a connection to the SMTP server (in our case, a listening sendmail daemon). Normally, the sending client will not send anything to the server until the server issues its initial greeting:
220 mail.example.com ESMTP Sendmail 8.14.1/8.14.1; Thu, 13 Aug 2007 07:45:41 −0800 (PST)
With slamming, however, the client does not wait for the initial greeting. Instead, the offending client sends its entire SMTP message all at once, then disconnects, before the server (sendmail) has a chance to review the message’s contents.
The FEATURE(greet_pause) was added to V8.13
sendmail to combat slamming.
You use the FEATURE(greet_pause) like this:
FEATURE(`greet_pause', `ms_pause')The FEATURE(greet_pause) takes a single
argument, an integer representation of the number of
milliseconds to wait before sendmail may send its
initial greeting. The
ms_pause sets the
default wait (we cover this shortly). If
ms_pause is missing, no
default is set. If
ms_pause is greater
than five minutes, the wait is silently truncated to
five minutes.[137]
If sendmail detects input from the client during this wait, that input is interpreted as an indication of slamming. If slamming is detected, the following rejection (instead of the initial greeting shown earlier) will be issued to the client:
554 server_host_name not accepting messagesWhenever a slamming site is rejected like this, the
following is logged with syslog(3):
rejecting commands fromhost[ip_addr] due to pre-greeting traffic ← V8.13 and earlier rejecting commands fromhost[ip_addr] aftersecsseconds due to pre-greeting traffic ← V8.14
Beginning with V8.14, this FEATURE(greet_pause) will not log
anything if the connecting client disconnects on its
own because of the wait.
If the offending site continues to send SMTP commands, each command will be rejected with the following:
554 5.5.0 Command rejected
But note that beginning with V8.14, greet_pause will be
called only if the connection has not already been
rejected. Prior to V8.14, greet_pause was called whether the
connection was already rejected or not.
The FEATURE(greet_pause) may also take
advantage of the access database. To do so, the
FEATURE(greet_pause) must be declared
after the FEATURE(access_db) (The access Database on page 277) is
declared. If greet_pause is declared before access_db (or if
access_db is
not declared), the access database cannot be used with
this feature.
When the access
database is enabled, sendmail looks up the connecting host
in the access
database just before it begins to wait. First, the
hostname (as taken from the ${client_name} macro; ${client_name} on page 812) is looked up to see whether the
canonical hostname is in the database. Then, the
host part (to the left of the dot) is recursively
stripped to see whether the domain part is listed in
the database (host.sub.domain,
then sub.domain, then
domain). If nothing matches,
the same lookups are performed for the client’s IP
address (as taken from the ${client_addr} macro; ${client_addr} on page 810). First the full address is looked
up, and then the network portions on dot boundaries
are looked up (192.168.2.5,
then 192.168.2, then
192.168, then
192).
To put entries into the access database’s source file, you
prefix each line with a literal GreetPause and a colon.
You then specify the host, domain, or IP address
followed by a tab,[138] then an ASCII representation of the
number of milliseconds to wait. For example:
GreetPause:host.domain5000 GreetPause:domain0 GreetPause:127.0.0.10 GreetPause:192.186.25000
Here, the first entry tells sendmail to wait 5,000 milliseconds
(five seconds) before issuing its initial greeting
to host.domain (a hostname).
The second entry tells sendmail to not wait at all (zero
milliseconds) for the domain
listed. The third entry tells sendmail to not wait
when the connection is from the loopback interface (a
memory interface on the local machine). And the last
line tells sendmail to wait 5,000 milliseconds
before sending its initial greeting to any host on
the 192.168.2
network.
If a connecting client is not found in the access
database, the wait used is taken from the second
argument to the FEATURE(greet_pause):
FEATURE(`greet_pause', `ms_pause')Here, ms_pause sets the
default number of milliseconds to pause for any
host, domain, or IP address that is not found in the
access
database.
Note that any detection of slamming will result in no Milter being called, and will prevent checkcompat( ) from being called.
One way to reduce spam email is to set up a mechanism for marking each MTA as an MTA. To illustrate, consider a spam email received from a host with the IP address 192.168.123.45, that claims to be a legitimate MTA. Currently, sendmail can only look up that address using various open relay sites to see whether the IP address corresponds to an open relay, and to reject the message if it does. Under the MTA mark proposal,[139] sendmail can look up a special TXT record associated with that address to see whether that IP address is marked as that of an MTA. You may emulate this lookup using dig(1) like this:
% dig txt _perm._smtp._srv.45.123.168.192.in-addr.arpa
Here, the _perm._smtp._srv is a literal defined
by the MTA mark proposal. The 45.123.168.192 is the
original IP address reversed, and the in-addr.arpa is the
special domain used to treat IP addresses like
domain names.
This lookup can return one of two possible TXT records. A “1” means that this IP address is that of an MTA. A “0” (or any other character) means that this IP address is not that of an MTA. Mail from an unmarked MTA may, under this proposal, be rejected.
Once this proposal is in place, spam sites will no longer be able to send spam email via hijacked PCs, via hired PCs, or via worms implanted in PCs. When spam email does arrive, you will be certain that it is from a marked MTA and only from a marked MTA. Then, by blocking email from that IP address, you will be able to turn off that site’s spam at the source.
This experimental FEATURE(mtamark) enables use of this
proposal, but it should not be used unless you are
willing to experiment. It is declared like
this:
FEATURE(`mtamark', `reject', `tempfail')
Here reject is either a
rejection message of your own or, if it is omitted,
a default that looks like this:
550 Rejected: $&{client_addr} not listed as MTAHere, the ${client_addr} macro (${client_addr} on page 810) contains the IP address of the
connecting host that was looked up.
The second argument, the
tempfail, is either a
literal t or a
temporary failure message of your own. The t causes the following
default to be used:
451 Temporary lookup failure of _perm._smtp._srv.$&{client_addr}Thus, if the lookup fails to get a “1”, the
reject text is used and
the message is rejected. If the lookup fails for a
temporary (recoverable) reason the
tempfail text is used
and the message’s acceptance is deferred.
Note that, if the MTA mark proposal is revised at a
later date, the literal _perm._smtp._srv may need to be
changed. If so, you may replace it by adding a third
argument to the feature declaration, such as
_permit._srv:
FEATURE(`mtamark', `reject', `tempfail', `_permit._srv')
The default timeout for the lookup is five seconds. If
that turns out to be too short for your needs, you
may increase it by defining the MTAMARK_TO
mc
macro:
define(`MTAMARK_TO', `20') FEATURE(`mtamark', `reject', `tempfail')
Note that the timeout must be defined before you declare the feature.
When a client machine connects to your
sendmail server,
sendmail records the IP
address of the connecting client in the ${client_addr} macro
(${client_addr} on page 810). Next, sendmail
looks up that IP address (performs a reverse DNS
lookup of that address) to find the client’s
hostname. The status of that lookup is stored in the
${client_resolve} macro (${client_resolve}
on page 814). This FEATURE(require_rdns) rejects
connections from clients for whom the reverse lookup
fails.
You declare this FEATURE(require_rdns) like this:
FEATURE(`require_rdns')
If you declare this feature, the following logic will be performed as the last step under basic relay checks:
If the value in ${client_addr} is also in the RELAY_DOMAIN (The RELAY_DOMAIN mc macro
on page 269) or the RELAY_DOMAIN_FILE (The RELAY_DOMAIN_FILE mc macro
on page 269) list of domains and hosts for which
to relay, the connection is allowed to relay, and
no further checking is done by this
feature.
If the result of the lookup (the value in
the ${client_resolve} macro) is the literal
OK, the address
is accepted and any additional relay checks are
performed.
If the result of the lookup (the value in
the ${client_resolve} macro) is the literal
FAIL, the
following error is returned in the SMTP
transaction and the connection is
disallowed:
550 5.7.1 Fix reverse DNS for failed IP address hereIf the result of the lookup (the value in
the ${client_resolve} macro) is the literal
TEMP, the
following error is returned in the SMTP
transaction and the connection is
tempfailed:
451 4.1.8 Client IP address failed IP address here does not resolveIf the result of the lookup (the value in
the ${client_resolve} macro) is the literal
FORGED, the
following error is returned in the SMTP
transaction and the connection is
tempfailed:
451 4.1.8 Possibly forged hostname for failed IP address hereThis feature should probably not be set if you relay based on IP addresses in the access database, because the feature does not look in that database.
The check_relay
rule set (Local_check_relay and check_relay on page 252) is used to screen incoming network
connections and accept or reject them based on the
hostname, domain, or IP address. The check_relay rule set is
called with a workspace that looks like this:
host $| IPaddress
The host name and
IP address are separated by the $| operator. As of
V8.13, this FEATURE(use_client_ptr) causes a new
rule to be inserted as the first rule under the
check_relay
rule set, which substitutes the value of the
${client_ptr}
macro (${client_ptr} on page 813) for the prior host value
passed.
Essentially, this causes V8.13 sendmail to behave like
earlier versions of sendmail that did not use the delay_checks.
If your site supports dial-up clients or machines that are assigned an IP address on startup, you should prevent such machines from sending mail directly to the outside world. If you fail to take this precaution, you might find such machines sending spam email that you can neither detect nor control. The easiest way to limit mail access to the world is with a firewall or router. Make it your published policy to always configure your firewall or router to prevent access to port 25 for all but your main mail hub machines.[140] This prevents dial-up clients from sending mail directly to the world. Instead, they will be required to send all email by way of your mail hub machines—which PC mail-reading software can easily be configured to do.
On your mail hub machines, you will need to use any of the appropriate methods discussed in the relaying section (Relaying on page 267) to enable the hub to relay messages outward for your dial-up clients. By requiring that all outbound email from dial-up clients be relayed through your mail hub, you enable your hub to impose limits on sending rates, to limit the number of recipients per envelope, and to log all email transactions. In brief, this puts you in position to detect spam attempts by your customers.
A common technique used by spammers is to lie
about the true host that was used to send the
offensive email by manufacturing headers that
mislead the end recipient. Such headers can range
from falsely made-up Message-Id: headers, to misleading
Received:
headers. As an ISP, it is your responsibility to
ensure that all mail passing through your hubs is
truthfully labeled. One way to do this is to
ensure that all hostnames in headers are fully
canonical.
One sure way to know whether your site is
spamming is to receive and read email from people
who complain about receiving such spam. You should
always read mail addressed to
Postmaster. As an added
precaution, you should also create an alias for
the address abuse and read that mail too.
Complaints will also be sent to
webmaster about HTTP
problems, and to
hostmaster about DNS
problems. You should accept and read all mail that
might indicate a problem needing attention.
If you are running an old version of sendmail and have not yet upgraded, beware that you might be running a site that will relay email to anywhere in the world. Called “promiscuous relaying,” this could get your site listed with DNSBL sites. Try to upgrade soon.
[114] * See http://www.spam.com/ for the official story.
[115] * See the FEATURE(delay_checks)
(Accept and Reject per Recipient
on page 284) to see how that feature changes this
order.
[116] * We illustrate this scheme, despite the fact that it is available in the access database, because other meaningful uses for this rule set are rare.
[117] * Actually, the message is not printed; instead, the SMTP daemon goes into a “reject everything” mode. This prevents some SMTP implementations from retrying the connection.
[118] † Actually,
$#anything
will have the same effect, but you should use
$#OK only to
remain compatible with future releases of
sendmail.
[119] * Don’t be
tempted to put this rule directly into the
Local_check_relay rule set. You might
someday encounter an address that has the two
adjacent characters $ and | as a legal part of it. Also be aware
that such addresses might be intentionally sent to
circumvent your checks.
[120] * Normally, sendmail rejects mail from a site whose name cannot be found with DNS with the error “domain of sender must exist.”
[121] † The name
canonify
corresponds to rule set 3.
[122] ‡ Actually,
$#anything
will have the same effect, but you should use
$#OK only to
remain compatible with future releases of
sendmail. Other rules might
still reject it, possibly for other reasons, so
always test new rules carefully.
[123] * Actually,
$#anything
will have the same effect, but you should use
$#OK only to
remain compatible with future releases of
sendmail.
[124] * This is a commercial site to which your name server must subscribe to use. Visit http://www.mail-abuse.com/ to find your cost, which can range from free for hobbyists to several thousand dollars for ISPs. Other such sites have existed prior to this writing, but they are now defunct. Sites worth investigating are http://www.ordb.org/, http://spamcop.net/, and http://www.iki.fi/.
[125] † See http://www.mail-abuse.org/ for information about how to subscribe to this service.
[126] * Depending on your shell, you might have to prefix the tab with a control-V character to embed it into your command line.
[127] * Do not use this class directly. Instead, use the macros we present here. If you use it directly, you risk breaking your configuration file if sendmail changes in the future.
[128] † This is not
recommended, but serves as an example of one way
to modify the underlying F command.
[129] * Beginning with
V8.12, you can customize this rejection message
using the confRELAY_MSG
mc configuration macro. To
use it, include the leading 550, but exclude the
5.7.1, and replace the old text with the new (see
cf/README).
[130] * Another feature,
FEATURE(blacklist_recipients), allows
recipients to also be rejected. Yet another,
FEATURE(delay_checks), allows even
finer tuning based on the desire of individual
recipients.
[131] * In support of
the examples in this section, we are assuming the
FEATURE(blacklist_recipients) (Reject per Recipient on page 284) has
also been declared so that the
access database can reject
mail to specific local recipients.
[132] * Note that V8.11 and later have added prefixes by embodying them in new features. See the sections that follow for a description of these new prefixes.
[133] * If the host is listed with the RELAY_DOMAIN mc macro (The RELAY_DOMAIN mc macro on page 269) or in the file specified by the RELAY_DOMAIN_FILE mc macro (The RELAY_DOMAIN_FILE mc macro on page 269), it is relayed without checking the access database.
[134] * Accepting and
rejecting based on the user part of an address
require that you also declare the FEATURE(blacklist_recipients) (Reject per Recipient on page
284).
[135] * If you have not
already done so, you will also first need to
declare the FEATURE(access_db).
[136] * Hijacking worms, loaded into unsuspecting PCs, are often used as proxies to perform just this sort of rapid spam email attack.
[137] † RFC2821 defines five minutes as the maximum timeout for the 220 greeting.
[138] * Unless you set a different column delimiter with the -t command-line switch for makemap.
[139] * As of this writing, see http://mtamark.space.net/draft-stumpf-dns-mtamark-04.txt.
[140] * There are many legal issues surrounding ad hoc filtering of customer access. You are strongly advised to consult with an attorney before applying such filters.