Friday, January 8, 2010

ClamAV as a sendmail milter

SkyHi @ Friday, January 08, 2010
Step 1 - Compiling sendmail
Step 2 - Compiling ClamAV
Step 3 - Configuring and starting ClamAV
Step 4 - Tying ClamAV into sendmail


ClamAV is a popular tool which scans e-mail for Windows-based viruses1. It can work in several manners, such as the traditional Unix way of accepting the data through stdin and sending reports out through stdout, stderr and/or an appropriate exit code, or it can be used as a sendmail milter.

The principle of a sendmail milter is simple. As the mail is coming down the pipe from the remote host, sendmail feeds it through the milter and then waits for the milter's reaction. If the reaction is "all is well" then processing carries on as usual. If, on the other hand, the result of the milter operation is "I didn't like this!" then the mail is rejected right there and then.

The aim of this becomes apparent in the current climate of 'Net abuse in which the number of junk e-mails and virus infections2 outweighs the amount of genuine e-mail by far, and in which viruses routinely forge the From: address from which they claim to be sent. If we were to accept the mail, discover that it was infected after having accepted it, and then strive to comply with the relevant RFC which states that the sender must be informed in the event of mail not being delivered to the final recipient, we'd be bouncing mail back to innocent bystanders whose only mistake (admittedly a big mistake given the abysmal security track record of the most widespread operating system for desktop PCs) was to have their e-mail address in a friend's address book or in the clear on a web page.

Note that bouncing the original mail back on the one hand, and sending a thoughtful message saying "Your message to XYZ was not delivered because it contained virus ABC" on the other are both equally abusive!

There is only one way we can reject an infected mail outright without generating an abusive bounce, and that is to reject the mail during the SMTP session in just the same way you'd reject spam from a blacklisted IP address or domain. This is precisely the purpose of using ClamAV as a sendmail milter.

I'll be assuming that you're not a total newbie in this document. It is assumed that you're familiar with compiling software and playing with configuration files, above all sendmail's configuration files.

Step 1 - Compiling sendmail

If you're using a binary distribution of sendmail prepared by a Linux distribution supplier such as Debian, Mandrake, SuSE or Red Hat/Fedora, the chances are that your binary has been compiled with milter support and you can skip this part and move straight on to step 2. As a general rule, if you have a file called libmilter.a in /usr/lib or /usr/local/lib and a directory called libmilter in /usr/include or /usr/local/include, then your version of sendmail has been compiled with libmilter support. If not, read on.

Grab a source tarball from a mirror (see for a list of mirrors) and untar the tarball. As of writing this (23/JUL/2004) the latest stable version of sendmail is 8.13.0.

Now set it up so that milter support will be added. Go into the devtools/Site directory under the source root and edit (or create if it doesn't already exist) site.config.m4 so that it contains these 2 lines:
APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
Now go back to the source root and build/install sendmail as usual.

Before running sendmail, we'll need to build libmilter. Go into the libmilter directory under the sendmail source tree root and run:
# sh Build
# make install
The libmilter library and include files are now installed.

Step 2 - Compiling ClamAV

Start by creating the "clamav" user and group as which the milter will run (it's not a good idea for it to run as root):
# groupadd clamav
# useradd -g clamav clamav
Create the directory /usr/local/share/clamav in which we'll be instructing ClamAV to put its virus signature database, then give the directory appropriate permissions:
# mkdir /usr/local/share/clamav
# chown clamav:clamav /usr/local/share/clamav
Now grab the ClamAV source from A list of available downloads is available at Latest is 0.75 as of today.

Untar the tarball, build and install ClamAV:
# tar -xzf clamav-0.75.tar.gz
# cd clamav-0.75
# ./configure --disable-clamuko --enable-milter --with-dbdir=/usr/local/share/clamav
# make
# make install
Step 3 - Configuring and starting ClamAV

No particular "--prefix=..." option was given while compiling ClamAV, meaning that its installation prefix is the default /usr/local. ClamAV will therefore expect to find its configuration file in /usr/local/etc/clamav.conf. The sample configuration in the etc directory under the ClamAV source tree root is well commented and should give you plenty of information on what needs changing and what it should be changed to.

Once your clamav.conf file is set up in /usr/local/etc you can start the ClamAV daemon:
# /usr/local/sbin/clamd
It is also recommended that you make sure clamd is started when the machine boots. You can do this by appending the required command to your /etc/rc.d/rc.local file:
# echo "/usr/local/sbin/clamd" >> /etc/rc.d/rc.local
Next, before ClamAV can recognise viruses it needs a signature database. Furthermore, this database must be kept up to date as new varieties of virus are being released every day. ClamAV provides a tool called freshclam for this. This tool also needs a configuration file, /usr/local/etc/freshclam.conf. The sample provided in the ClamAV distribution can also be used as a starting point for your own configuration.

Once that's set up, we need to grab an initial virus signature database. We'll be logging the database retrieval in /var/log/clam-update.log, so create the file and above all give it permissions such that freshclam (running as user clamav, group clamav) will have write access to it:
# touch /var/log/clam-update.log
# chown clamav:clamav /var/log/clam-update.log
# chmod 640 /var/log/clam-update.log
Now invoke freshclam and get it to download the latest definitions (this can take some time on a slower connection):
# /usr/local/bin/freshclam -l /var/log/clam-update.log
ClamAV update process started at Sat Jul 24 17:04:12 2004
Reading CVD header (main.cvd): OK
Downloading main.cvd [*]
main.cvd updated (version: 24, sigs: 21793, f-level: 2, builder: tomek)
Reading CVD header (daily.cvd): OK
Downloading daily.cvd [*]
daily.cvd updated (version: 420, sigs: 1062, f-level: 2, builder: tomek)
Database updated (22855 signatures) from (
If you get a warning about there being no support for digital signatures while downloading the virus definition files, it's because you don't have the GNU MP arbitrary precision mathematical libraries installed. These are available from While they're not absolutely necessary, they are strongly recommended because they help ClamAV provide greater security and data integrity.

/var/log/clam-update.log should look something like this now:
ClamAV update process started at Sat Jul 24 17:04:12 2004
main.cvd updated (version: 24, sigs: 21793, f-level: 2, builder: tomek)
daily.cvd updated (version: 420, sigs: 1062, f-level: 2, builder: tomek)
Database updated (22855 signatures) from (
It will also contain the warning about the lack of support for digital signatures if the GMP libraries are not installed.

This update process should be automated and should happen at least twice a day. As root, run crontab -e and create this cron job:
# Update ClamAV database twice a day
0 2,13 * * * /usr/local/bin/freshclam --quiet -l /var/log/clam-update.log
Now you can test the software to make sure it's installed correctly. 'cd' into the test directory under the ClamAV source tree root. You'll see several files in there which contain ClamAV test signatures that ClamAV should pick up. Try scanning the 'test' file:
# clamdscan test
test: ClamAV-Test-Signature FOUND

----------- SCAN SUMMARY -----------
Infected files: 1
Time: 0.006 sec (0 m 0 s)
If you get something like this:
# clamdscan test
connect(): No such file or directory
ERROR: Can't connect to clamd.

----------- SCAN SUMMARY -----------
Infected files: 0
Time: 0.003 sec (0 m 0 s)
then the chances are that clamd isn't running. Try launching it and checking that it is indeed running afterwards:
# /usr/local/sbin/clamd
# ps ax | grep clamd | grep -v grep
11752 ?        S      0:00 /usr/local/sbin/clamd
If clamd refuses to start then double-check your /usr/local/etc/clamav.conf file and the permissions on all the objects it references. Remember that clamd runs as user clamav, group clamav.

Testing it on controlled test data is one thing, testing it on a real, "out there in the wild" virus infection is another. Below is a link to an infected e-mail I received, which contains the Worm.SomeFool.Gen-1 worm.


Note to users of Microsoft Windows: The "Download virus" link below points to an e-mail which contains a virus. DO NOT CLICK ON IT! The file is provided to serve as a testbed for users of GNU/Linux who are not vulnerable to the malevolent code it contains.

Let me make this clear for those who have difficulties understanding strong recommendations: IF YOU USE MICROSOFT WINDOWS, DO NOT CLICK ON THE "DOWNLOAD VIRUS" LINK BELOW.

This text in large font and in red cannot be considered insufficient warning by any stretch of the imagination. The author of this page and the ISP hosting it decline any and all responsibility for any infection arising from the misuse of or the inability to use this online resource. In other words, if you still want to go and download the virus then on your own head be it. You have been given fair warning.


Grab a copy of the virus from here: Download virus.

Now run it through clamdscan:
# clamdscan somefool.dat
somefool.dat: Worm.SomeFool.Gen-1 FOUND

----------- SCAN SUMMARY -----------
Infected files: 1
Time: 0.048 sec (0 m 0 s)
Now that we've ascertained that the virus scanning is working, we need to integrate it into sendmail's processing queue so that infected mails get rejected during the SMTP session.

Step 4 - Tying ClamAV into sendmail

First we need to start the daemon, clamav-milter, which sets up the unix socket that sendmail will use, and which therefore acts as a go-between between sendmail and the clamd analyser:
# /usr/local/sbin/clamav-milter -l -o -q /var/milter/clmilter.sock
`man clamav-milter' will give full explanations on the options in the above command line. This particular combination scans all inbound and outbound mail (-o) as well as that sent from within the LAN (-l) and suppresses messages to postmaster (-q) each time a virus is detected - I'm getting about 10 a day (which is a lot less than some) so I don't want to receive notification each and every time.

Also, add the command to your /etc/rc.d/rc.local so that the daemon is started whenever the machine is:
# echo "/usr/local/sbin/clamav-milter -l -o -q /var/milter/clmilter.sock" >> /etc/rc.d/rc.local
The final step is to get sendmail to use the milter. To do so, add the following lines to your /etc/mail/ file:
INPUT_MAIL_FILTER(`clmilter',`S=local:/var/milter/clmilter.sock, F=, T=S:4m;R:4m')dnl
define(`confINPUT_MAIL_FILTERS', `clmilter')
Build a new and restart sendmail, you're ready to start blocking viruses.

  1. I'm using the generic (and somewhat incorrect) term "virus" here in its sense which englobes all forms of malevolent code: virus, trojan, worm... They each have a different modus operandi, but going into details here would be beyond the purpose of this howto.
  2. Junk e-mail, aka "spam", and viruses are now firmly linked instead of being two totally independent scourges as they were not that long ago. What happens as a general rule is that the target PC becomes infected with a virus which turns it into an open proxy for relaying spam, or into something else which ultimately helps spammers, such as a nameserver for their spam domains or a reverse proxy which directs web requests back to the spammers' webservers while preserving their anonymity. You might find these links informative: