ClamAV as a sendmail milter

Posted: September 21, 2008 in SENDMAIL, SMTP
Tags: , ,


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

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.

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.


Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s