#!/bin/bash # # fetch-sanesecurity-sigs # by Malcolm Scott, Retrosnub Internet Services # # # http://www.retrosnub.co.uk/sanesecurity # svn://svn.sanesecurity.retrosnub.co.uk/ # # $Revision$ # $Date$ # # ----------------------------------------------------------------------------- # Copyright (C) 2009-2011 Malcolm Scott # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License version 2 as published by the # Free Software Foundation. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA # ----------------------------------------------------------------------------- # # INSTALLATION AND USAGE: # # * Ensure that you have wget, gpg and rsync installed (in addition to a # standard set of UNIX tools). # * Configure this script by editing the configuration variables below. # * Set up cron to run this script periodically, at a randomly-selected time # (i.e. not just on the hour unless unavoidable), not more often than once # per hour. The script should be run either as root or as the user clamd # runs as. For example, add the following line to /etc/crontab or a file # in /etc/cron.d: # 42 * * * * root /path/to/fetch-sanesecurity-sigs >/dev/null # (Redirection of the output to /dev/null is recommended when running from # cron as the script is verbose in normal operation. This will not hide # errors, however.) # # Note that this script is tailored to the Sanesecurity setup (as of January # 2009). It should replace scripts using the pre-2009 setup (HTTP). It is # not intended to download non-Sanesecurity signatures; use another script # for that purpose. ### Configuration ### # ClamAV database location clamd_dbdir="/var/lib/clamav" # ClamAV daemon process ID file # (If this is commented out, the daemon will not be reloaded automatically) clamd_pidfile="/var/run/clamav/clamd.pid" # Directory in which this script will keep its persistent data data_dir="/var/lib/sanesecurity" # Directory in which this script will keep its cache # (This should not be the same as data_dir. Any unexpected files in this # directory will be deleted!) cache_dir="/var/cache/sanesecurity" # Mirror to use, in a form accepted by rsync # (Leave set to the round robin address unless you know what you are doing: # forcing the use of one particular mirror will cause excess load for that # mirror) mirror="rsync://rsync.sanesecurity.net/sanesecurity" # Signatures we don't want, as a space-separated list # (If this is empty we will load all signatures available on the mirror. # See http://www.sanesecurity.co.uk/databases.htm for guidance on which # signatures to choose.) exclude="INetMsg-SpamDomains-2m.ndb winnow_phish_complete.ndb" # Extra options for rsync # The default, --contimeout=30, makes for faster recovery if a mirror is # blocking your connection (e.g. if you connect too frequently). However # this is not available in older versions of rsync so you may need to # remove it. # Additionally, if you have a slow link you may want to add -z here to # enable compression. rsync_extra_opts="--contimeout=30" # Minimum interval between updates, in minutes # (Note that the download servers may be configured to drop connections made # at too great a rate) min_interval="30" # Randomly sleep before downloading if running noninteractively? # (Please leave this enabled, as it reduces peak load on the mirrors) random_sleep=1 # URL of the Sanesecurity GnuPG public key gpg_key_url="http://www.sanesecurity.net/publickey.gpg" # Location of GnuPG home directory # (If you change this, be sure that you understand the security implications: # signatures by *any* key in your public keyring will be accepted) gpg_homedir="$data_dir/gnupg" # Extra options for GnuPG, if required gpg_extra_opts="" # Exclude logical signatures (*.ldb)? # These are not supported by versions of ClamAV prior to 0.94. # If you use an old version of ClamAV, you should enable this option. #exclude_ldb=1 ### End of configuration ### umask 0022 # Check the configuration looks sane if [ ! -d "$clamd_dbdir" ] then echo "clamd_dbdir ($clamd_dbdir) does not exist; aborting" >&2 echo "(check your configuration)" >&2 exit 1 fi mkdir -p "$data_dir" "$cache_dir" # Set up GnuPG, if necessary if [ ! -d "$gpg_homedir" ] then echo "GnuPG homedir is nonexistant; initialising" >&2 echo "(This should only occur once)" >&2 mkdir -p "$gpg_homedir" chmod 0700 "$gpg_homedir" gpg_tmp="$(mktemp -t fetch-sanesecurity-sigs.XXXXXXXXXX)" if ! wget -O "$gpg_tmp" "$gpg_key_url" then echo "ERROR: could not fetch GnuPG public key; aborting" >&2 rm -f "$gpg_tmp" exit 4 fi if ! gpg --homedir "$gpg_homedir" $gpg_extra_opts --import "$gpg_tmp" then echo "ERROR: could not import GnuPG public key; aborting" >&2 rm -f "$gpg_tmp" exit 4 fi rm -f "$gpg_tmp" fi # This appears to be the most portable way to find the current timestamp # (suggestions on how to avoid calling perl are very welcome) time_now="$(perl -le print+time)" if [ ! "$time_now" -gt 1 ] then echo "Could not find current timestamp -- is perl installed?" >&2 exit 5 fi # Check that min_interval seconds have elapsed since last run if [ -s "$data_dir/update-stamp" ] then time_then="$(cat "$data_dir/update-stamp")" minutes_elapsed=$(( (time_now - time_then) / 60 )) if [ "$minutes_elapsed" -lt "$min_interval" ] then echo "Must wait at least $min_interval minutes between updates; aborting" >&2 exit 2 fi fi # Random sleep (see comment in configuration section) if [ "$random_sleep" = "1" ] then # Are we running noninteractively, i.e. without a tty on stdin? if ! tty -s then # $RANDOM is between 0 and 32767. # Use this to generate a time between 30 and 32767/200+30 = 193 seconds. sleep_time=$((RANDOM/200+30)) echo "Sleeping for $sleep_time seconds." sleep $sleep_time fi fi # Update the cache if [ "$exclude_ldb" = "1" ] then exclude="$exclude *.ldb" fi if [ "$exclude" ] then for i in $exclude do rsync_extra_opts="$rsync_extra_opts --exclude=$i --exclude=$i.*" rm -vf "$cache_dir"/$i "$cache_dir"/$i.* done fi if ! rsync --progress --delete -rt $rsync_extra_opts "$mirror/." "$cache_dir/" then echo "rsync failed; aborting." >&2 exit 3 fi echo "$time_now" > "$data_dir/update-stamp" chmod 0644 "$cache_dir"/* echo # Iterate through the databases in the cache installed=0 for db in "$cache_dir"/*.?db "$cache_dir"/*.ftm do db_name=$(basename "$db") db_live="$clamd_dbdir/sanesecurity-$db_name" # Only pay any attention to this database if it's newer than the installed version if [ -e "$db_live" -a ! "$db" -nt "$db_live" ] then echo "$db_name is already up-to-date; skipping" continue fi # Zero-length databases have no value and confuse the test below if [ ! -s "$db" ] then echo "$db_name is zero-length; discarding" continue fi # Check that the GnuPG signature is present and correct if ! gpg_out=$(gpg --homedir "$gpg_homedir" $gpg_extra_opts --verify "$db.sig" "$db" 2>&1) then echo "SECURITY ERROR: $db_name has a bad GnuPG signature; discarding:" >&2 echo "$gpg_out" >&2 continue fi # Test the database by asking ClamAV to check something with it if ! clamscan --quiet --tempdir="${TMPDIR:-/tmp}" --database="$db" - < /dev/null then echo "ERROR: $db_name fails a simple test; discarding" >&2 continue fi # Now we can actually install this database echo "Installing $db_name into $db_live" if rsync -p "$db" "$db_live" then installed=$((installed+1)) # Clean up stuff left behind by old scripts rm -vf "$clamd_dbdir/$db_name"* fi done # Clean up any databases which are now excluded but weren't previously if [ "$exclude" ] then for i in $exclude do db_live="$clamd_dbdir/sanesecurity-$i" if [ -f "$db_live" ] then rm -vf "$db_live"* installed=$((installed+1)) fi done fi # Finished; display summary and perhaps reload clamd echo if [ "$installed" -gt 0 ] then if [ "$installed" -eq 1 ] then s="" else s="s" fi echo "Installed $installed database$s" # Is clamd running? if [ "$clamd_pidfile" -a -f "$clamd_pidfile" ] then echo "Reloading ClamAV daemon" clamdscan --reload fi else echo "No databases installed." fi exit 0