ClamAV

ClamAV is commonly used to scan for viruses in email, so that they can be given the proper handling by a mail delivery agent such as procmail or directly by the MTA as in the case of a sendmail message filter (milter). It is also used to scan for viruses in disk files so that infected files can be deleted or quarantined.

ClamAV is installed in the usual manner by downloading its distribution file from http://www.clamav.net/. Once you've downloaded the distribution file, unpack it like this:

     tar -xvzf clamav-0.94.2.tar.gz

The install instructions are missing a part about creating the ClamAV userid and group before you run configure. Do something like this:

     su
     /usr/sbin/groupadd clamav
     /usr/sbin/useradd -c "ClamAV Virus Scanner" -g clamav -M \
       -s /sbin/nologin clamav

Next, we'll create a place for the daemon to store the logfiles, and an install directory:

     su
     mkdir /var/log/clamav/
     chown root:clamav /var/log/clamav/
     chmod ug=rwx,o=rx /var/log/clamav/
     mkdir /usr/local/clamav
     chown clamav:clamav /usr/local/clamav
     chmod ug=rwx,o=rx /usr/local/clamav

Change to the source directory, and configure and build the source:

     cd clamav-0.97.4
     ./configure --disable-clamuko --disable-milter --with-dbdir=/usr/local/clamav
     make

Then, when the build succeeds, install ClamAV:

     su
     make install

Note that the ClamAV install appears to have omitted the step of informing the linker that it needs to rebuild the linker cache so that it can see the ClamAV dynamic libraries. You can check this by running:

     su
     /sbin/ldconfig -p | grep libclam

If you don't see any results, you won't be able to run anything that links to these libraries (e.g. clamd).

The fix is to first make sure that the installed library directory is in "/etc/ld.so.conf" or one of the files that it includes from "/etc/ld.so.conf.d". If you used the ".configure" command shown above, you should be all set because the default is to install libclam into "/usr/local/lib" and this directory is always named in the "usr-local.conf" file in "/etc/ld.so.conf.d". If you picked some off-label library directory, you need to add another file to "/etc/ld.so.conf.d" that names the off-label directory in it. For example, if you chose "/usr/mystuff/lib" for the libclam library, you might create a file named "/etc/ld.so.conf.d/usr-mystuff.conf" that names "/usr/mystuff/lib" in it.

Once you're ready, run the following command:

     su
     /sbin/ldconfig

This should add libclam* to the /etc/ld.so.cache file and you'll be in business. Or, you can just take the Microsoft approach and reboot your machine a half dozen times.

/etc/clam/clamav.conf

Presumably, to use ClamAV (e.g. for email scanning), one will require that the clamd daemon be run. All of the parameters that control the way clamd operates are contained in its configuration file (there are virtually no command line parameters). The standard location for the config file is /etc/clamav.conf or maybe even /usr/local/etc/clamd.conf.

We, on the other hand, prefer to create a directory for ClamAV under /etc and put the config file, along with any other clam-related files there. It makes the /etc directory neater and groups all of the logically-related files together. Not only that but we don't have to go looking in some screwy place where the product's creator thought it would be cute to put things. So, we do:

     su
     mkdir /etc/clam
     chmod u=rwx,go=rx /etc/clam
     touch /etc/clam/clamav.conf
     chgrp clamav /etc/clam/clamav.conf
     chmod ug=rw,o= /etc/clam/clamav.conf

The options that you'll likely want to consider are those for: turning on logging; choosing how to communicate (e.g. via a socket); indicating where to store the PID file; setting where the virus signatures database is kept; adjusting scanning performance and tuning; picking what type of files to scan; limiting how far scanning goes. Here are some suggestions:

     #
     # Turn on logging, direct where it should go, etc.
     #
     LogTime Yes
     LogFileMaxSize 0
     LogFile /var/log/clamav/clamd.log
     #
     # Set where the PID file is kept and how we tawk, dahling.
     #
     PidFile /var/run/clamd.pid
     TCPSocket 2528
     #
     # Where the virus signatures database is squirreled away.
     #
     DatabaseDirectory /usr/local/clamav
     #
     # Performance and Tooning.
     #
     MaxConnectionQueueLength 25
     MaxThreads 50
     ReadTimeout 30
     MaxDirectoryRecursion 15
     FollowFileSymlinks Yes
     #
     # Use an alter ego.
     #
     User clamav
     #
     # Wot to scan.
     #
     AlgorithmicDetection Yes
     HeuristicScanPrecedence No
     ScanPE Yes
     ScanELF Yes
     ScanOLE2 Yes
     ScanPDF Yes
     ScanHTML Yes
     ScanMail Yes
     ScanArchive Yes
     MailFollowURLs No
     ScanPartialMessages No
     PhishingSignatures Yes
     PhishingScanURLs Yes
     #
     # How far should we try to go?
     #
     MaxScanSize 100M
     MaxFileSize 25M
     StreamMaxLength 25M
     MaxRecursion 16
     MaxFiles 10000
     #
     # Debugging stuff.
     #
     # Debug Yes
     # LeaveTemporaryFiles Yes

/etc/rc.d/init.d/clamav:

This startup script will start ClamAV's clamd daemon so that requests to check for viruses may be passed to it without incurring the overhead of starting up the Perl interpreter each time a message is to be identified.

     #!/bin/sh
     #
     # clamav - This script starts and stops the clamd daemon
     #
     # Revision History:
     # ewilde      2009Jan18  Initial coding.
     #
     # chkconfig: 2345 79 31
     # description: Clamd is a daemon process which uses ClamAV to check email \
     #              messages for VIRUSES.
     #
     # processname: clamd
     # pidfile: pidfile, what pidfile?  Its a Perl program, for cripes sake.
     # config: /etc/clam/clamav.conf
     #
     #
     # Define the install path for the ClamAV binaries, etc.  Define the
     # program to run.
     #
     INSTALL_PATH="/usr/local/sbin"
     CONFIG_FILE="/etc/clam/clamav.conf"
     CLAM_DAEMON="clamd"
     #
     # Load the RedHat functions.
     #
     if [ -f /etc/redhat-release ]; then
         . /etc/rc.d/init.d/functions
     fi
     #
     # Source the networking configuration.
     #
     if [ -f /etc/sysconfig/network ]; then
         . /etc/sysconfig/network
     else
         NETWORKING=no
     fi
     #
     # Source the clamd configuration or take the defaults.
     #
     if [ -f /etc/sysconfig/clamd ] ; then
         . /etc/sysconfig/clamd
     else
         CLAMD_PORT=2528
     fi
     [ -z "$CLAMD_PORT" ] && CLAMD_PORT=2528
     #
     # Check that networking is up.  We talk to our customers using TCP/IP.
     #
     [ "x$NETWORKING" != xyes ] && exit 0
     #
     # Make sure we have something to run.
     #
     [ -f $INSTALL_PATH/$CLAM_DAEMON ] || exit 0
     #
     # Clamd is dain bramaged.  You can't pass it any command line options.  So,
     # the port number must be set in the config file.  But, we want to set it in
     # /etc/sysconfig/clamd.  What to do?  About all we can do is check that it
     # matches what's in the config file.
     #
     ConfSocket=`grep TCPSocket $CONFIG_FILE`
     if [ "x$ConfSocket" != "xTCPSocket $CLAMD_PORT" ]; then
         echo Clamd port $CLAMD_PORT does not match the port set in $CONFIG_FILE
         exit 0
     fi
     #
     # Upon startup, start the daemon.
     #
     start()
         {
         echo -n "Starting $CLAM_DAEMON: "
         daemon $INSTALL_PATH/$CLAM_DAEMON -c $CONFIG_FILE
         RETVAL=$?
         echo
         #
         # If startup succeeded, lock the lockfile.
         #
         [ $RETVAL = 0 ] && touch /var/lock/subsys/clamd
         }
     #
     # Upon shutdown, stop the daemon.
     #
     stop()
         {
         echo -n "Shutting down $CLAM_DAEMON: "
         killproc $CLAM_DAEMON
         RETVAL=$?
         echo
         #
         # If shutdown succeeded, unlock the lockfile.
         #
         [ $RETVAL = 0 ] && rm -f /var/lock/subsys/clamd
         }
     #
     # See how we were called.
     #
     case "$1" in
         #
         # Start.
         #
         start)
             start
             ;;
         #
         # Stop.
         #
         stop)
             stop
             ;;
         #
         # Restart or reload (whatever).
         #
         restart|reload)
             stop
             start
             ;;
         #
         # Conditional restart.
         #
         condrestart)
             if [ -f /var/lock/subsys/clamd ]; then
                 stop
                 start
             fi
             ;;
         #
         # Give the status of clamd.
         #
         status)
             status $CLAM_DAEMON
             ;;
         #
         # Help text.
         #
         *)
             echo $"Usage: $0 {start|stop|restart|condrestart|status}"
             exit 1
     esac
     exit 0

/etc/sysconfig/clamd:

If you are planning to use clamd with the sendmailfilter, you should create this file, since doing so will cause the sendmail startup script to invoke clamd for email filtering. The options for the clamav startup script (above) are also included in this file.

The permissions on this file should be:

     -rw-r--r--    1 root     root

Here is a sample of the complete file:

     #
     # Configuration for the ClamAV daemon.
     #
     CLAMD_PORT=2528

It is very important that the virus signatures database be kept up to date, since it is used to describe all of the viruses that ClamAV detects. Not to put too fine a point on it but, if the virus signature isn't in the database, the virus isn't going to be found.

ClamAV has a program, called freshclam, that can be invoked on a regular basis to contact the mother ship and download a new virus database, if there is one. It should be run at regular intervals out of crontab (or as a daemon). It is easy to set up.

However, before we proceed, consider that virus database freshness is the most important thing in an anti-virus system. Consequently, it is critical that freshclam be able to alert you when something goes wrong.

/etc/clam/notify:

Create the following script to send event notifications to root, via email, whenever ClamAV has something important to say. Note that the indentation in front of "ENDMSG" can be tabs only. If your lame-butt text editor sticks spaces in there, the shell script will get a syntax error. Here is the script:

     #!/bin/sh
     # A shell script that can be used by freshclam to send messages to root
     # when a fresh copy of the virus signatures database cannot be downloaded.
     # Email the message to root so that they can see everything that's going on.
     # After all, if you're omnipotent, you need to know everything.
     HostName=`hostname`
     /bin/mail -s "Urgent message from ClamAV" root <<-ENDMSG
         ClamAV on $HostName failed to obtain a fresh copy of the virus
         signatures database.  Please investigate immediately.
         ENDMSG

Note that, in the above script, the two sets of lines between "/bin/mail -s ... <<-ENDMSG" and up to and including the line with "ENDMSG" must either not be indented at all or only indented with actual tab characters (not blanks). If they are not, the script will fail. Also, don't forget to add execute permissions to the script after you create it:

     su
     chmod ugo+x /etc/clam/notify

Then, it wouldn't hurt try it out after you've got it all set up. For example:

     /etc/clam/notify

Check that root receives the message.

/etc/clam/freshclam.conf:

Freshclam, being almost as brain dead in the command line parameters department as clamd (actually, it has them, the docs claim they override the config file, yet it still whines about the config file being missing and gives up, so maybe it is worse), a config file for it is required. The standard location for the config file is /usr/local/etc/freshclam.conf.

However, since we earlier started the trend of creating a directory for ClamAV under /etc and putting the ClamAV config file and other clam-related files there, we'll stick with a winner:

     su
     touch /etc/clam/freshclam.conf
     chgrp clamav /etc/clam/freshclam.conf
     chmod ug=rw,o= /etc/clam/freshclam.conf

Here is an example of a freshclam configuration file containing the options that you should consider:

     #
     # Turn on logging and direct where it should go, etc.
     #
     LogTime Yes
     LogFileMaxSize 0
     UpdateLogFile /var/log/clamav/freshclam.log
     #
     # Where the virus signatures database is squirreled away.
     #
     DatabaseDirectory /usr/local/clamav
     #
     # Use DNS to verify virus database version.
     #
     DNSDatabaseInfo current.cvd.clamav.net
     #
     # Where to find the database mirrors.  The last one is the fallback mirror.
     #
     DatabaseMirror db.us.clamav.net
     DatabaseMirror db.ca.clamav.net
     DatabaseMirror database.clamav.net
     #
     # Performance and Tooning.
     #
     ConnectTimeout 60
     ReceiveTimeout 120
     MaxAttempts 3
     ScriptedUpdates Yes
     CompressLocalDatabase No
     #
     # Use an alter ego.
     #
     DatabaseOwner clamav
     #
     # Notify root-ski if the database update process fails.
     #
     OnErrorExecute /etc/clam/notify
     #
     # Debugging stuff.
     #
     # Debug Yes

/etc/crontab:

Now we can add a line in /etc/crontab to schedule regular updates to the ClamAV virus signatures database:

     # Check for an updated ClamAV virus signatures database a couple of times a
     # day.
     40 6,18 * * * root /usr/local/bin/freshclam \
                        --config-file=/etc/clam/freshclam.conf >/dev/null 2>&1

/etc/logrotate.d/clamav:

Before the logfiles fill up the all the available disk space, you should add a config file to rotate them, to the logrotate config directory /etc/logrotate.d:

     /var/log/clamav/*.log {
         missingok
         notifempty
     }

Now, before the ClamAV daemon is started, you should run freshclam once to populate the databases:

     su
     /usr/local/bin/freshclam --config-file=/etc/clam/freshclam.conf

Also, if you're running logwatch, you may experience a problem whereby it doesn't process the clam-update logfiles properly and always generates a message that says:

     The ClamAV update process (freshclam daemon) was not running!
     If you no longer wish to run freshclam, deleting the freshclam.log
     file will suppress this error message.

This problem is caused by a timestamp and arrow that were added to the beginning of all clam-update log lines under more recent versions of ClamAV. Apparently, the logwatch scanner for ClamAV hasn't kept up and it does not know about the timestamps. If you get the latest version of logwatch and the problem still isn't fixed, you can try applying this patch:

/usr/share/logwatch/scripts/services/clam-update:

     --- clam-update-7.3.6 2010-09-09 08:37:51.000000000 -0400
     +++ clam-update 2010-09-09 08:16:02.000000000 -0400
     @@ -87,6 +87,10 @@
         # Freshclam ends log messages with a newline.  If using the LogSyslog option, this is
         # turned into a space.  So we remove a space from every line, if it exists.
         $ThisLine =~ s/ $//;
     +   # Later versions of Freshclam prepend each line with a timestamp (a very
     +   # good idea) and an arrow.  This messes up scanning (below).
     +   if ($ThisLine =~ /^\s*\w{3} \w{3} [\d ]\d ..:..:.. \d{4} -> /)
     +      { $ThisLine = substr($ThisLine, length($&)); }
         if (
             # separator of 38 dashes
             ($ThisLine =~ /^\-{38}$/) or