Simscan/Related Docs/Simscan ClamAV Chkuser Installation Guide

From Qmailwiki
Jump to: navigation, search



This is a quick guide to install Simscan, ClamAV and Chkuser setting up an antivirus/antispam[1] solution to your Qmail server.

By following this guide you’ll end up with a running installation of these products. The goal here is to not miss any detail needed to set them up. If you see that something is missing, contact me or add it by yourself.

It’s assumed you already know how to manage a Qmail server, and you know how to compile products from the source. By following this installation process we’re going to apply 2 patches over netqmail, so you have to understand what you’re doing.

This installation was tested only in a RedHat 9.0 but it should work for other systems too.

What are Simscan, ClamAV and Chkuser?

We use these three packages to implement an antivirus/antispam[1]solution to our Qmail/Vpopmail mail server.

Simscan is a simple program that enables qmail-smtpd to reject viruses, spam, and block attachments during the SMTP conversation so the email never makes it into your computers. It is completely open source and uses other open source components. Very efficient and written in C.

Simscan can apply 4 different types of scanners:

  1. virus scanner
  2. attachment extension scanner (searches for .pif, .scr, … attachments)
  3. regular expression scanner
  4. spam content scanner.

More info about it in:

Clam AntiVirus is a GPL anti-virus toolkit for UNIX. The main purpose of this software is the integration with mail servers (attachment scanning). The package provides a flexible and scalable multi-threaded daemon, a command line scanner, and a tool for automatic updating via Internet. The programs are based on a shared library distributed with the Clam AntiVirus package, which you can use with your own software. Most importantly, the virus database is kept up to date.

More info about it in:

Chkuser is a patch to qmail-smtpd executable adding to it a lot of new features, specially, the capability of refusing to receive messages for e-mail accounts that doesn’t exist. The original qmail-smtpd accepts by default all messages, checking later for the existence of recipients. So, if the message is delivered to not existing recipients, a lot of additional system work and network traffic are generated, with multiple expensive bouncing if the sender is a fake one. Chkuser also enables qmail-smtpd to respond to settings passed by a bunch of new environment variables, like CHKUSER_RCPTLIMIT and CHKUSER_WRONGRCPTLIMIT.

More info about it in:

How they work together

In summary, when a new SMTP connection begins, qmail-smtpd will first execute the Chkuser tests to know if it will accept or reject the message, if it accepts, qmail-smtpd will trigger Simscan that will trigger ClamAV for virus scanning, and after it, if the message was considered clean, Simscan triggers qmail-queue to deliver the message to its destination, otherwise it blocks the message by returning an error code to qmail-smtpd.

[1] Currently the antispam capabilities of the presented installation are limited to those added by the Chkuser package. For a more complete antispam solution we should add Spamassassin to this installation. Simscan was written with Spamassassin in mind, but we’re still not using it here. A future version of this guide will include the Spamassassin integration with Simscan. More info about it, look up in


This guide assumes that you already have a mail server running with these three products:

  • netqmail-1.05: Note: If you’re using the standard qmail-1.03 package, it should suffice, but you need to patch it with QMAILQUEUE patch to enable Simscan to scan the mail messages.

We won’t cover the installation of these packages in this document. Refer to and to get installation instructions.

Downloading the packages

This is the list of the packages necessary to make Simscan, ClamAV and Chkuser work. For each package listed, go to its website and download the latest stable version of it. There are required and recommended packages. You can choose not to install the recommended packages, but you will lose the features they add.

NOTE: We’re listing here the packages names in the versions that were used in our installation, but you should use the most recent versions.

TIP: Download all the packages to a directory called /root/packages/simscan so you can better follow the commands we’re going to use later.

ClamAV required packages

  • clamav-0.86.2.tar.gz:This is the package with the antivirus. Download its latest version from
  • zlib and zlib-devel packages: They provide the ability of working with .zip files. These already come installed in Redhat 9.0, so we won’t cover their installations.
  • gcc compiler suite: (both 2.9x and 3.x are supported) this already come installed in Redhat 9.0, so we won’t cover its installation.

ClamAV recommended packages

  • gmp-4.1.4.tar.gz: It's very important to install the GMP package because it allows freshclam (a ClamAV component) to verify the digital signatures of the virus databases. Download its latest version from
  • curl-7.14.0.tar.gz: ClamAV uses curl version >= 7.10.0 to follow the links inside a mail message and check if they are pointing to viruses. We’re not currently enabling this feature here, but we want to be able to use it eventually. Download the latest version from
  • bzip2 and bzip2-devel library: Used to unpack bzip2 compressed files. To install them in your Redhat, run ‘up2date bzip2 bzip2-devel’ and you’re done.

Simscan required packages

Simscan recommended packages

  • pcre-6.1.tar.gz: the Perl Compatible Regular Expressions libraries. Needed to enable simscan’s ‘regex’ scanner. Download its latest version from
  • qmail-queue-custom-error.patch: Enables Simscan to return the appropriate message for each e-mail it refuses to deliver. It is bundled with Simscan source, in the ~/contrib directory, so you don’t have to download it from elsewhere.
  • ClamAV: ClamAV is required for the antivirus function of Simscan. You should have already downloaded ClamAV at this point.
  • ripmime- ripmime is used to activate the 'attach' scanner (that does attachment blocking). Download its latest version from

Chkuser required packages

Applying the patches over Qmail

Before we install ClamAV and Simscan, we’ll install the Qmail patches. Let’s begin with Chkuser.

Applying Chkuser

Untar chkuser-2.0.8b-release.tar.gz to /usr/local/src directory:

cd /usr/local/src
tar -xvzf /root/packages/simscan/chkuser-2.0.8b-release.tar.gz

Now go to netqmail source directory and apply the netqmail-1.05_chkuser-2.0.8.patch:

cd /usr/local/src/netqmail-1.05/netqmail-1.05
patch < /usr/local/src/chkuser-2.0.8b-release/netqmail-1.05_chkuser-2.0.8.patch

The patch command will return the lines bellow:

patching file Makefile
patching file TARGETS
patching file chkuser.c
patching file chkuser.h
patching file chkuser_settings.h
patching file conf-cc
patching file qmail-smtpd.c

Fixing the path to vpopmail home in the source files

Chkuser believes the default home directory for the vpopmail user is /home/vpopmail, which is not true for most of vpopmail installations. Here vpopmail is installed under /var/vpopmail, so let’s edit the files Makefile and conf-cc to fix the path to vpopmail home directory.

Edit Makefile:

vi Makefile

Substitute the line with the wrong path by this one bellow:


Edit conf-cc

vi conf-cc

Substitute the line with the wrong path by this one bellow:

cc -O2 -I/var/vpopmail/include

Setting qmail-smtpd to run under vpopmail UID and GID

To verify the existence of the email accounts, qmail-smtpd will need to read vpopmail files and directories. The standard qmail installation sets qmail-smtpd to run under ‘qmaild’ UID and GID, we need to change it to vpopmail user UID and GID.

Bellow is an example of a /service/qmail-smtpd/run script to run qmail-smtpd as Vpopmail UID and GID:

VPOPMAILUID=`id -u vpopmail`
VPOPMAILGID=`id -g vpopmail`
VpopmailHome=`grep vpopmail /etc/passwd|cut -d':' -f6`
exec /usr/local/bin/softlimit -m 10000000 \
/usr/local/bin/tcpserver -v -p -x $VpopmailHome/etc/tcp.smtp.cdb \
-u $VPOPMAILUID -g $VPOPMAILGID 0 smtp rblsmtpd -b -r /var/qmail/bin/qmail-smtpd 2>&1

NOTE: the softlimit we’re using here is 10,000,000 bytes instead of the usual 2,000,000 bytes. This bigger softlimit exists to give room for simscan and clamav execution under the qmail-smtpd process.

Applying qmail-queue-custom-error.patch

Untar simscan to /usr/local/src:

cd /usr/local/src
tar -xvzf /root/packages/simscan/simscan-1.1.tar.gz

Patch netqmail:

cd /usr/local/src/netqmail-1.05/netqmail-1.05
patch < /usr/local/src/simscan-1.1/contrib/qmail-queue-custom-error.patch

The patch command will return the following lines:

patching file qmail.c
Hunk #1 FAILED at 14.
Hunk #2 succeeded at 56 (offset 11 lines).
Hunk #4 succeeded at 152 (offset 11 lines).
1 out of 4 hunks FAILED -- saving rejects to file qmail.c.rej
patching file qmail.h

As you can see, the patch to qmail.h was successful, but the patch for qmail.c was unsuccessful. This is because this patch is not prepared to be installed after the QMAILQUEUE patch that comes installed with netqmail.

To solve this or you edit qmail.c and add manually the rejected hunk saved in qmail.c.rej, or you can use the qmail.c file bellow that was previously edited by Darrel (don’t know his lastname) and posted in the simscan’s mailling in The thread subject was ‘qmail-queue patch isn't compatible withqmail-queue-custom-error.patch’.

For convenience, the contents of this file are also in APPENDIX A. qmail.c source for netqmail-1.05 with QMAILQUEUE patch plus qmail-queue-custom-error.patch in this document.

So, if you are using netqmail-1.05 and the same qmail-queue-custom-error.patch we’re using, you can cut and paste the contents posted in that link and save it in a file called qmail.c.

Recompiling Netqmail

Time to compile qmail with the patches we just applied:

cd /usr/local/src/netqmail-1.05/netqmail-1.05

For precaution, backup your actual qmail binaries, just in case something goes wrong:

tar -cvzf /root/qmail_bin.tar.gz /var/qmail/bin

Stop qmail execution

svc -d /service/qmail*

Certify that SMTP and POP are no longer LISTENING for network connections. The following netstat command CANNOT print any line:

netstat -ln | grep -G ':25\|:110'

Install the new binaries:

make setup check

Start qmail daemons again:

svc -u /service/qmail*

Check to see if everything is fine. Certify that your daemons were started without errors.

Testing Chkuser

To see chkuser in action, start an SMTP connection by hand and try to send an e-mail for some account that doesn't exist in your local server:

telnet localhost 25
mail from
250 ok
rcpt to: unexistet_user@localdomain.mynet.example
511 sorry, no mailbox here by that name (#5.1.1 - chkuser)

If chkuser is already working, you’re going to see a lot of CHKUSER messages for each new SMTP connection inside qmail-smtpd logfile, usually in /var/log/qmail-smtpd/current.

Try to send an e-mail for a user that doesn’t exist in your domain, and see what chkuser will answer to you. It’ll be something like:

<>: said: 511 sorry, no mailbox here by that name (#5.1.1 - chkuser)

It also logs when an existing user is found.

If everything was ok, just because of chkuser you now have a mail server much nicer than you had before ;-)

Activating chkuser antispam features

Among all the new features Chkuser introduces into qmail-smtpd, we picked up these two that we believe are essential:

  • CHKUSER_RCPTLIMIT: A qmail-smtpd environment variable that enables defining a limit for the maximum number of recipients permitted in 1 SMTP connection.
  • CHKUSER_WRONGRCPTLIMIT: A qmail-smtpd environment variable that permits defining a limit for the maximum number of wrong recipients (recipients that don’t exist) permitted in 1 SMTP connection

To enable these variables, edit the file /var/vpopmail/etc/tcp.smtp and add the following line:


NOTE: The limits where setting here make sense to our needs adjust them to fit yours.

Recompile the tcp.smtp.cdb file:

tcprules /var/vpopmail/etc/tcp.smtp.cdb \
/var/vpopmail/etc/tcp.smtp.tmp < /var/vpopmail/etc/tcp.smtp

Soon you’ll see in qmail-smtpd logfile chkuser blocking the spammers.

ClamAV installation

Installing ClamAV prerequisites

To install gmp-4.1.4.tar.gz, untar it to /usr/local/src and compile it with the commands bellow:

cd /usr/local/src
tar -xvzf /root/packages/simscan/gmp-4.1.4.tar.gz
cd gmp-4.1.4
make install

To install curl-7.14.0.tar.gz, first remove the existing curl and curl-devel rpm packages, if they are installed

rpm -e curl curl-devel

Untar curl-7.14.0.tar.gz to /usr/local/src and compile it with the following commands:

cd /usr/local/src
tar -xvzf /root/packages/simscan/curl-7.14.0.tar.gz
cd curl-7.14.0
make install

To install bzip2 and bzip2-devel, download them from redhat update network:

up2date bzip2 bzip2-devel

Installing ClamAV

Create clamav user and group:

groupadd clamav
useradd -g clamav -s /bin/false -c "Clam AntiVirus" clamav

Untar clamav:

cd /usr/local/src
tar -xvzf /root/packages/simscan/clamav-0.86.2.tar.gz

Compile it, setting /etc as its “sysconfdir” directory:

cd clamav-0.86.2
./configure --sysconfdir=/etc
make install-strip

Setting up ClamAV startup

Now it’s time to start clamd daemon and set it to start whenever the system boots.

If you try to to start it now by hand, you’ll see the following error messages

ERROR: Please edit the example config file /etc/clamd.conf.
ERROR: Can't open/parse the config file /etc/clamd.conf

It enforces you to read the /etc/clamd.conf and edit it. So edit it:

vi /etc/clamd.conf

And comment the 'Example' line, as bellow:

# Comment or remove the line below.

Change the path to the logfile to /var/log:

LogFile /var/log/clamd.log

Add clamd to the local initialization script, /etc/rc.d/rc.local:

echo '/usr/local/sbin/clamd' >> /etc/rc.d/rc.local

Start it manually now


Activating the virus definitions update

To activate the automatic download of the virus definitions database, you have to schedule a utility called /usr/local/bin/freshclam in your crontab.

Before you try to run freshclam, edit its configuration file /etc/freshclam.conf and comment the line “Example” just like you did in /etc/clamd.conf. Or freshclam won’t execute and will print an error message.

vi /etc/freshclam.conf
# Comment or remove the line below.
# Example

After editing this file, add the freshclam command line to an hourly execution in your crontab:

Open the root crontab:

crontab -e

Add the line bellow

N * * * *   /usr/local/bin/freshclam --quiet

Where “N” is a number between 0 and 59 defining a minute to start the job execution. The ClamAV website asks for people not to use the multiple of 10 time slots because a lot of freshclam clients are connecting at those time slots.

You can execute freshclam by hand right now and see it updating the virus database:


It will print something like:

ClamAV update process started at Sat May 28 16:26:44 2005
main.cvd is up to date (version: 31, sigs: 33079, f-level: 4, builder: tkojm)
Downloading daily.cvd [*]
daily.cvd updated (version: 898, sigs: 1782, f-level: 5, builder: ccordes)
Database updated (34861 signatures) from (IP:

Simscan installation

Installing Simscan prerequisites

To install pcre-6.1.tar.gz, untar it to /usr/local/src and compile it with the commands bellow:

cd /usr/local/src
tar -xvzf /root/packages/simscan/pcre-6.1.tar.gz
cd pcre-6.1
make install

To install ripmime, untar it to /usr/local/src and compile it with the commands bellow:

cd /usr/local/src
tar -xvzf /root/packages/simscan/ripmime-
cd ripmime-
make install

The other prerequisites, ClamAV and qmail-queue-custom-error.patch, where already installed at this point.

Installing Simscan

Adding simscan user and group:

useradd -g clamav -s /bin/false -c \
"Simscan - a qmail-queue substitute" simscan

Next, download this patch for simscan.c ( ) to /root/packages/simscan/. This patch will call ripimime with the --disable-qmail-bounce option, which you want.

Untar it to /usr/local/src:

cd /usr/local/src
tar -xvzf /root/packages/simscan/simscan-1.1.tar.gz
cd simscan-1.1
patch -p0 < /root/packages/simscan/simscan.c.diff

Configuring simscan installation:

./configure --enable-per-domain=y \
--enable-attach=y --enable-clamav=y \
--enable-regex --enable-received \
--enable-clamavdb-path=/usr/local/share/clamav \

The command above will print the following lines:

Current settings

user                  = simscan
qmail directory       = /var/qmail
work directory        = /var/qmail/simscan
control directory     = /var/qmail/control
qmail queue program   = /var/qmail/bin/qmail-queue
clamdscan program     = /usr/local/bin/clamdscan
clamav scan           = ON
trophie scanning      = OFF
attachement scan      = ON
ripmime program       = /usr/local/bin/ripmime
custom smtp reject    = ON
drop message          = OFF
regex scanner         = ON
quarantine processing = OFF
domain based checking = ON
add received header   = ON
spam scanning         = OFF

Compile and install it:

make install-strip

Creating Simscan control files

Since we compiled Simscan with the --enable-per-domain option, simscan will be able to do “per domain scanning”. Per domain scanning allows the administrator to explicitly state what scanning occurs for what domain. In addition, attachment scanning can be enabled or disabled for each domain.

Simscan will read its scanning rules from /var/qmail/control/simcontrol.cdb. This .cdb file is generated by running /var/qmail/bin/simscanmk. This command will create that .cdb file based in a text file called /var/qmail/control/simcontrol, this is the file where we’ll define the per domain scanning rules.

Edit /var/qmail/control/simcontrol:

vi /var/qmail/control/simcontrol

Add the following rule to disable the spam scanner, enable clam and attach scanners, setting the attach scanner to unconditionally block every e-mail containing .pif, .bat, .com and .exe attachments:


NOTE: The syntax above is for a ‘default rule’, a rule valid for all domains in the machine. Observe that there is no domain name before the initial colon “:” sign. To add a rule valid only for a specific domain, put the domain name before the colon, Example.:,attach=no. Read the README file from simscan source for additional help with the syntax.

Generate ~/simcontrol.cdb file:


There is another .cdb file simscan reads, /var/qmail/control/simversions.cdb, from where it gets the “scanner versions” information. This information is used by simscan to add a “Received: by simscan...” header to each scanned message containing the appropriate version of each of its scanners. The added header will look like this one bellow:

Received: by simscan 1.1.0 ppid: 8053, pid: 8054, t: 1.7363s
scanners: regex: 1.1.0 attach: 1.1.0 clamav: 0.85.1/m:31/d:858

To create the /var/qmail/control/simversions.cdb file simply run simscanmk with the ‘-g’ option:

/var/qmail/bin/simscanmk -g

This command will discover the proper scanner versions and add them to the .cdb file. Remember to rerun this command every time you update one of the scanners, say after you update clamav to a newer version.

Testing Simscan

Before definitely activating simscan, test if from the command line, enabling the DEBUG messages.

Create a draft text file used to be a mail body:

echo “hi, testing.” > mailtest.txt

Then, send this file as an e-mail message with the following command:

echo "hi testing" > mailtest.txt
env QMAILQUEUE=/var/qmail/bin/simscan SIMSCAN_DEBUG=2 /var/qmail/bin/qmail-inject \
somercpt@somedomain < mailtest.txt

NOTE: remember to put a valid e-mail in place of ‘somercpt@somedomain’.

This will print a lot of debugging info, and if everything was ok, the last few lines will look like these:

simscan: cdb looking up version regex
simscan: cdb looking up version attach
simscan: calling clamdscan
simscan: cdb looking up version clamav
simscan: normal clamdscan return code: 0
simscan: done, execing qmail-queue
simscan: qmail-queue exited 0

Activating Simscan

Now that (hopefully) simscan is running fine, let's inform its existence to qmail-smtpd process, by setting the QMAILQUEUE environment variable.

You can do this by adding setting this variables in the tcprules inside tcp.smtp file. This way simscan will be activated on IP basis.

(NOTE: There is also a manner to activate simscan globally, usefull if you're using vpopmail --enable-roaming-users)

So, edit /var/vpopmail/etc/tcp.smtp and add the following line:


Regenerate the tcp.smtp.cdb file:

tcprules /var/vpopmail/etc/tcp.smtp.cdb \ 
/var/vpopmail/etc/tcp.smtp.tmp < /var/vpopmail/etc/tcp.smtp

You should see simscan activity in /var/qmail/qmail-smtp/current logfile.

About this guide

Author: Bruno Negrao G Zica

qmail.c source for netqmail-1.05 with QMAILQUEUE patch plus qmail-queue-custom-error.patch

#include "substdio.h"
#include "readwrite.h"
#include "wait.h"
#include "exit.h"
#include "fork.h"
#include "fd.h"
#include "qmail.h"
#include "auto_qmail.h"
#include "env.h"

static char *binqqargs[2] = { 0, 0 } ;

static void setup_qqargs()
    binqqargs[0] = env_get("QMAILQUEUE");
    binqqargs[0] = "bin/qmail-queue";

int qmail_open(qq)
struct qmail *qq;
  int pim[2];
  int pie[2];
  int pierr[2];


  if (pipe(pim) == -1) return -1;
  if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
  if (pipe(pierr) == -1) { 
     close(pim[0]); close(pim[1]); 
     close(pie[0]); close(pie[1]); 
     close(pierr[0]); close(pierr[1]); 
     return -1; 
  switch(qq->pid = vfork()) {
    case -1:
      close(pierr[0]); close(pierr[1]);
      close(pim[0]); close(pim[1]);
      close(pie[0]); close(pie[1]);
      return -1;
    case 0:
      close(pierr[0]); /* we want to receive data */
      if (fd_move(0,pim[0]) == -1) _exit(120);
      if (fd_move(1,pie[0]) == -1) _exit(120);
      if (fd_move(4,pierr[1]) == -1) _exit(120);
      if (chdir(auto_qmail) == -1) _exit(61);

  qq->fdm = pim[1]; close(pim[0]);
  qq->fde = pie[1]; close(pie[0]);
  qq->fderr = pierr[0]; close(pierr[1]);
  qq->flagerr = 0;
  return 0;

unsigned long qmail_qp(qq) struct qmail *qq;
  return qq->pid;

void qmail_fail(qq) struct qmail *qq;
  qq->flagerr = 1;

void qmail_put(qq,s,len) struct qmail *qq; char *s; int len;
  if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;

void qmail_puts(qq,s) struct qmail *qq; char *s;
  if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;

void qmail_from(qq,s) struct qmail *qq; char *s;
  if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;

void qmail_to(qq,s) struct qmail *qq; char *s;

char *qmail_close(qq)
struct qmail *qq;
  int wstat;
  int exitcode;
  int match;
  char ch;
  static char errstr[256];
  int len = 0;

  if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
  while( substdio_bget(&qq->ss,&ch,1) && len < 255){
  if (len > 0) errstr[len]='\0'; /* add str-term */


  if (wait_pid(&wstat,qq->pid) != qq->pid)
    return "Zqq waitpid surprise (#4.3.0)";
  if (wait_crashed(wstat))
    return "Zqq crashed (#4.3.0)";
  exitcode = wait_exitcode(wstat);

  switch(exitcode) {
    case 115: /* compatibility */
    case 11: return "Denvelope address too long for qq (#5.1.3)";
    case 31: return "Dmail server permanently rejected message (#5.3.0)";
    case 51: return "Zqq out of memory (#4.3.0)";
    case 52: return "Zqq timeout (#4.3.0)";
    case 53: return "Zqq write error or disk full (#4.3.0)";
    case 0: if (!qq->flagerr) return ""; /* fall through */
    case 54: return "Zqq read error (#4.3.0)";
    case 55: return "Zqq unable to read configuration (#4.3.0)";
    case 56: return "Zqq trouble making network connection (#4.3.0)";
    case 61: return "Zqq trouble in home directory (#4.3.0)";
    case 63:
    case 64:
    case 65:
    case 66:
    case 62: return "Zqq trouble creating files in queue (#4.3.0)";
    case 71: return "Zmail server temporarily rejected message (#4.3.0)";
    case 72: return "Zconnection to mail server timed out (#4.4.1)";
    case 73: return "Zconnection to mail server rejected (#4.4.1)";
    case 74: return "Zcommunication with mail server failed (#4.4.2)";
    case 91: /* fall through */
    case 81: return "Zqq internal bug (#4.3.0)";
    case 120: return "Zunable to exec qq (#4.3.0)";
      if (exitcode == 82 && len > 2){
        return errstr;
      if ((exitcode >= 11) && (exitcode <= 40))
        return "Dqq permanent problem (#5.3.0)";
      return "Zqq temporary problem (#4.3.0)";
Personal tools