image map linking to parts of siteSearch Me?What I DidWhat I DoWriting: archived articlesBlog: Glenn's Daily Thoughts
Putting the Bam-Bam on Spam

Please NOTE: This page hasn't been updated since about 2000. The information contained in it was generally applicable to older versions of sendmail, like 8.7, 8.8, and 8.9. Newer versions since 8.10 have made the whole process much simpler, requiring less nonsense.

how to keep non-local agents from abusing your mailer
and keep spammers from sending email to you and your users

Note: many kinds of spam are now illegal in several states and countries! Violators can be prosecuted by the State Attorney General's Office or by individuals suing under civil statute. For more on non-technical spam issues, see the CAUCE site or the Reporting Spammers by Location page on this site.

By Glenn Fleishman

Look, spam is just plain annoying to users. That's clear: nobody wants to know the latest Fen-Phen diet craze $19.95 offer. Unless it's those people who like getting phone calls during dinner. Protecting yourself and, if you're an ISP or LAN Manager, your users from unwanted trash isn't as difficult as you've probably been told.

There's a related problem that's less apparent: spammers using your relays to send mail to others. A short review: an SMTP mailer is always acting as a relay between a remote mail program, whether it's an end-user product like Eudora or another mailer daemon.

An SMTP mailer takes a from address (MAIL FROM) and a recipient address (RCPT TO) and then uses rulesets to determine how to deliver the mail (local, program, pager, IP, etc.). Anyone can use any SMTP mailer to send mail from themselves to anywhere with impunity - unless you have rulesets to prevent it.

By configuring your system to block unwanted mail, you'll only capture some percentage of spam. However, as more and more systems install these filters, spammers will certainly become more ingenious, but it will also become less and less desirable to spam, as you will have more obstacles to overcome. People spam now because it's simple and quick. As that changes -- and hundreds of thousands of system administrators are working to change the situation -- it'll be harder and people will move on to the next scam. We'll start with how to think about what you're trying to block and then proceed to implementation in sendmail V8 (v 8.8.x and 8.9).

If you're not using sendmail V8, here are links to the versions of various mail transfer agent (MTA) software that allow you to secure the server against relay hijacking:

Conceptual Underpinnings

Anyone can use your SMTP server to deliver mail anywhere, but you typically know who your users are who need to use it. Here's how to think about who and how to block.

Sendmail V8 Anti-Spam Implementation

Generous souls have always donated their best work to the Net. Version 8.9 of sendmail, a free update as always, comes with built-in features to control relay hijacking and spamming. If you're using an earlier version of sendmail, Claus Aßmann's rulesets will help you out.

Setting Up

Use m4: If you haven't used m4 before to make your sendmail configuration files, think about switching. There's great documentation online at, and I provide a sample file below that's complete for most configurations. On some older systems, like SunOS 4.x, you have to type at the command line
/usr/5bin/m4 ../m4/cf.m4 >
inside ./cf/cf/ to get m4 to work as expected. If you're not going to use m4, you're going to suffer a lot of pain in configuring rulesets and features.

Make /etc/spam directory: This directory will store file(s) that contain restricted addresses (email addresses, IPs, IP ranges, and domain names) and permitted relays.

Compile makemap: This comes with the sendmail distribution and you'll need it in order to make the database(s) for use with the rulesets.

8.9 and FEATURES

Sendmail 8.9 makes it a snap to add spam and relay controls. See the sample configuration file below for placement, but all you need do is add four FEATURES. How these work is well documented at the Sendmail site. The one thing that's confusing is setting up the access database.

I create a file called access.source, and in it I put all the different things I want to do: allow relaying from specific domains and email addresses, and restrict incoming mail from certain spamming IPs, IP ranges, domains, and addresses.

The access.source file is located in /etc/spam and it's a constructed just like the junk.source file below with a LHS (left-hand side) and RHS (right-hand side).

My file looks something like:

206.129.191 relay
208.215.241 relay relay ok "503 another thanks" "503 no" "503 no" "503 no"

After creating the file, use the makemap command (which you can compile and install as part of the sendmail package) to turn it into the database format needed by sendmail:
makemap btree /etc/spam/access.db < /etc/spam/access.source

More information about specific values for this database are at the Anti-Spam Configuration Page at the Sendmail site.

8.8.x and Claus's Rules
With sendmail 8.8.x and earlier, try spam and relay controlling rulesets created by Claus Aßmann in Germany. The documentation isn't on the Web site; it comes in the check.tar package you can download by clicking here or off Claus's site. Download the rulesets package and put them in ./cf/hacks/ for use with m4. There are several versions of each ruleset, each of which allows different options; I've chosen to use the ones that allow most flexibility in sending bounce messages back and allow for easy updating.

Next, edit your .mc file for your mail configuration to include the HACK() statements in the sample configuration file below. I edited the code in check_relay3 to provide information for bounced relays to contact me. The message tells them to send mail to the administration address at the site if they're getting the relay error in error - they can also send mail to a local user unless they're really a spammer.

The LHS (left-hand side) should have the email-address, domain name, or IP address. The IP address can be one to four bytes separated by periods, so you can block entire Class A, B, or C networks, or just a single machine number. The RHS (right-hand side) should be in quotes and contain the appropriate mail error code (such as 503) followed by a space and then the message you want returned. For example: (these are fictional - please don't use them!)
205.199.266 "503 This domain eats crabs for lunch" "503 You relay spam and won't stop when asked"
33 "503 Stanford University? We eat trees for lunch"

Even if the file is blank, you should create it and run makemap:
makemap btree /etc/spam/junk.db < /etc/spam/junk.source
You have to re-run makemap each time you update junk.source.

Also in /etc/spam, you'll create two plain text files. These files have to exist even with no entries before you restart sendmail, so touch LocalIP and LocalNames. These files contain the names of all trusted networks and domains; they can be as general as the first byte of an IP number or just a second-level domain name, or as specific as a machine's number.

What you're doing here is identifying locations from which users and servers may send mail to your SMTP server that doesn't get delivered locally. The confusing thing here can be that it doesn't matter what email return address they're using - that only matters with filtering incoming mail. Relay filtering is only concerned with where they are, not who they claim to be.

You can specify fragments just as with junk.source. So if your network is, put "208.33.75" in LocalIP. If you have a user dialing in over PSINet from Los Angeles, try to get as specific as possible in LocalNames. Don't enter ""; instead, enter "". Limit access, but keep it broad enough that you don't have to constantly reconfigure. Make sure and inclue all local networks and domains.

Run m4 to create your configuration file and install it, and then restart your mail server daemon. When you update any of the files, it's a good idea to kill -HUP the daemon, even though the documentation says you don't. It doesn't always seem to read the new values withough a HUP.

More sendmail info
Every sendmail V8 user should have bookmarked at the top of their list. The source for new releases, it also has m4 documentation and lots of nice extras to navigate sendmail. They have an anti-spam resource page.

Sample m4 configuration file

This is a sample m4 configuration file used on a SunOS 4.1.x system running sendmail 8.9.0. Notes are in regular type (and blue on some browsers); the configuration file is in fixed-width type. In one place, I've marked what code should be used for pre-8.9 and 8.9 and later.

VERSIONID(`@(#) 8.3 (Berkeley) 3/23/96')
FEATURE(smrsh, /usr/etc/smrsh)
smrsh is the CERT-approved sendmail shell that allows only execution of specially designated scripts and program put in a specific directory. smrsh ships with sendmail and you should use it to prevent any attempt to use sendmail to execute other programs on your systems.

FEATURE(genericstable, btree /etc/genericstable)
FEATURE(virtusertable, btree /etc/virtusertable)
Two of the most useful additions as regular features in sendmail 8.7 were the ability to create a table in which you can alias delivery to specific email addresses at specific domains or entire domains to other mailboxes. This allows and to be delivered to different mailboxes as well as have the right return address stamped and X-Authenticated on the way out.

define(`LOCAL_MAILER_PATH', /usr/bin/mail.local)
This one bit me. My local delivery agent is in a different location than the default; if you fail to specify this, local mail stops working.


These two define's turn off receiving mail from domain names that aren't real. I'm no longer using them as of 9/13/97 because they appear to block mail from domains that have no A record but do have a legitimate MX record. Problemetic.

For pre-8.9 sendmail (Claus's rulesets):
HACK(check_mail3, btree -a@JUNK /etc/spam/junk)
HACK(check_relay3, btree -a@JUNK /etc/spam/junk)
HACK(use_ip, /etc/spam/LocalIP)
HACK(use_names, /etc/spam/LocalNames)
These five hacks contain all the code necessary to ban unauthorized relays and local delivery from spam addresses. You can just ban relaying by including the three use_ hacks and check_rcpt4; check_mail3 and check_relay3 ban local delivery. Claus recommend using just use_ip or use_names, but I like the flexibility of defining local users either way.

For 8.9 sendmail (built-in features):
FEATURE(access_db, btree -o /etc/spam/access)
These four features set it up so that you can
a) relay an entire domain by specifying just the right-most part (like to relay all hosts at
b) control spammers via a single source file containing restricted addresses (IP, domain, and email) and allowed relaying
c) use the blacklist feature for locally defined spammers
d) tap into the Realtime Blackhole List
See the Anti-Spam Configuration page at for more details.

define(`confCHECKPOINT_INTERVAL', 5)
define(`confAUTO_REBUILD', True)
define(`confLOG_LEVEL', 8)
define(`confPRIVACY_FLAGS', ``authwarnings noexpn novrfy needmailhelo'')
define(`confTO_QUEUERETURN', 3d)
define(`confTO_QUEUEWARN', 12h)
define(`confQUEUE_LA', 6)
define(`confREFUSE_LA', 3)
define(`confMAX_DAEMON_CHILDREN', 12)
define(`confQUEUE_SORT_ORDER', priority)
define(`confMIN_QUEUE_AGE', 30m)
define(`confHOSTS_FILE', /etc/hosts)
define(`confSMTP_LOGIN_MSG', ``$j Sendmail 1.0/1.0; $b'')

Cw stout mailhost

Replace stout and with your local mailhost name and domain, of course.

Some typical log messages

Jun 2 20:52:44 stout sendmail[20316]: UAA20316: ruleset=check_mail, arg1=<2001@>, [], reject=451
<>... Sender domain must resolve
Jun 2 20:56:22 stout sendmail[20389]: UAA20389: ruleset=check_mail, arg1=<lbraz>, [], reject=451 <lbrazil@>... Sender domain must resolve
Jun 2 21:21:12 stout sendmail[20768]: VAA20768: ruleset=check_mail, arg1=<2001@>, [], reject=451
<>... Sender domain must resolve
Jun 2 21:55:47 stout sendmail[21308]: VAA21308: ruleset=check_mail, arg1=<lbraz>, [], reject=451 <lbrazil@>... Sender domain must resolve
Jun 2 22:52:07 stout sendmail[22230]: WAA22230: ruleset=check_mail, arg1=<adser>, []
, reject=501 <>... Sender domain must exist
Jun 2 14:12:00 stout sendmail[13241]: NOQUEUE: Null connection from client-151- [] (may be forged)
Jun 2 15:17:53 stout sendmail[14646]: NOQUEUE: []: VRFY [rejected]
Jun 1 11:13:21 stout sendmail[11606]: NOQUEUE: ruleset=check_relay, arg1=mail.c, arg2=, [], re
ject=553 Mail from refused; see
Jun 1 11:20:04 stout sendmail[11713]: NOQUEUE: ruleset=check_relay, arg1=mail.c, arg2=, [], re
ject=553 Mail from refused; see