Author: jv
License: The MIT License, Copyright (c) 2008 jv
Description: basic setup to scan files added to specified directories automatically for viruses using ClamAV; for instructions on how to run ClamAV from a system service agent account (non-root) see
here; use at your own risk
Platform: Mac OS X 10.4.11 Client
Requirements: sudo port install clamav (after installing MacPorts); to fix the command search path, insert the following statement at the end of your
~/.bash_login file: export PATH="/opt/local/bin:/opt/local/sbin:$PATH" and then: source ~/.bash_login
sudo port install clamav
man clamd
man clamd.conf
man clamdscan
man clamscan
man freshclam
sudo cp -p /opt/local/etc/example-freshclam.conf /opt/local/etc/freshclam.conf
sudo sed -i "" -e 's/^Example/#Example/' /opt/local/etc/freshclam.conf
sudo nano /opt/local/etc/freshclam.conf
id -G -n $(whoami) | grep -Eo 'wheel|admin'
dseditgroup -o checkmember -m $(whoami) wheel; echo $?
dseditgroup $(whoami)
dseditgroup wheel
dseditgroup admin
sudo dscl . -append /Groups/wheel GroupMembership $(whoami)
sudo chown -R root:wheel /opt/local/share/clamav
sudo chmod -R 0770 /opt/local/share/clamav
freshclam
clamscan /path/to/file
sudo clamscan -r /tmp
sudo clamscan -r /private/var/tmp
clamscan -r ~/Library/Caches
clamscan -r ~/Library/Caches/java
sudo clamscan -r ~/Library/Mail
clamscan -r ~/Library
sudo cp -p /opt/local/etc/clamd.conf /opt/local/etc/clamd.conf.orig
sudo sh -c '
cat << EOF > /opt/local/etc/clamd.conf
LogFile /private/var/log/clamd.log
LogFileMaxSize 10M
LogTime yes
TemporaryDirectory /private/var/tmp
DatabaseDirectory /opt/local/share/clamav
LocalSocket /tmp/clamd
FixStaleSocket yes
TCPAddr 127.0.0.1
MaxConnectionQueueLength 30
MaxThreads 20
ExitOnOOM yes
ScanOLE2 yes # Microsoft Office documents and .msi files
ScanPDF yes
ArchiveMaxFileSize 100M
ArchiveMaxCompressionRatio 0
#VirusEvent echo virus: %v >> /path/to/file.txt
EOF
'
sudo chown root:wheel /opt/local/etc/clamd.conf
sudo chmod 750 /opt/local/etc/clamd.conf
Create the launchd item /Library/LaunchDaemons/net.clamav.clamd.plist
sudo nano /Library/LaunchDaemons/net.clamav.clamd.plist
<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabledkey>
/>
<key>Labelkey>
net.clamav.clamd</string>
<key>ProgramArgumentskey>
/opt/local/sbin/clamdstring>
-c</string>
<string>/opt/local/etc/clamd.confstring>
</array>
<key>RunAtLoadkey>
/>
<key>UserNamekey>
root</string>
dict>
</plist>
sudo chown root:wheel /Library/LaunchDaemons/net.clamav.clamd.plist
sudo chmod 0644 /Library/LaunchDaemons/net.clamav.clamd.plist
sudo launchctl load -w /Library/LaunchDaemons/net.clamav.clamd.plist
Create watchdirs.sh
mkdir -p "$HOME/Documents/ClamAV/Quarantine"
mkdir -p "$HOME/Documents/ClamAV/Downloads"
mkdir -p "$HOME/Documents/ClamAV/EmailAttachments"
sudo chown -R $(whoami):wheel "$HOME/Documents/ClamAV"
sudo chmod -R 770 "$HOME/Documents/ClamAV"
touch ~/.clamav_ timestamp
sudo chown $(whoami):wheel "$HOME/.clamav_timestamp"
sudo chmod 400 "$HOME/.clamav_timestamp"
stat -x $HOME/.clamav_timestamp
exec >/dev/console 2>&1
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... STARTED ...\n"
/bin/sleep 60
SCANDIR1="$HOME/Documents/ClamAV/Downloads"
SCANDIR2="$HOME/Documents/ClamAV/EmailAttachments"
QUARANTINEDIR="$HOME/Documents/ClamAV/Quarantine"
TOUCHFILE="$HOME/.clamav_timestamp"
find=/usr/bin/find
clamdscan=/opt/local/bin/clamdscan
clamscan=/opt/local/bin/clamscan
if [[ ! -e "$TOUCHFILE" ]]; then
/usr/bin/touch -afm "$TOUCHFILE"
/usr/sbin/chown $(whoami):$(whoami) "$TOUCHFILE"
/bin/chmod 400 "$TOUCHFILE"
$clamdscan --copy="$QUARANTINEDIR" "$SCANDIR1" || $clamscan -i --copy="$QUARANTINEDIR" -r "$SCANDIR1"
$clamdscan --copy="$QUARANTINEDIR" "$SCANDIR2" || $clamscan -i --copy="$QUARANTINEDIR" -r "$SCANDIR2"
exit 0
fi
timestamp=$(/bin/date "+%Y%m%d%H%M.%S")
if /bin/ps -ax | /usr/bin/grep clamd | /usr/bin/grep -v grep > /dev/null; then
$find -x "$SCANDIR1" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamdscan --copy="$QUARANTINEDIR"
$find -x "$SCANDIR2" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamdscan --copy="$QUARANTINEDIR"
else
$find -x "$SCANDIR1" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamscan -i --copy="$QUARANTINEDIR"
$find -x "$SCANDIR2" -type f \( -newercm "$TOUCHFILE" -or -newermm "$TOUCHFILE" \) -print0 | xargs -0 $clamscan -i --copy="$QUARANTINEDIR"
fi
/usr/bin/touch -f -t $timestamp "$TOUCHFILE"
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... DONE ...\n"
exit 0
sudo chown root:wheel ~/Documents/ClamAV/watchdirs.sh
sudo chmod 0770 ~/Documents/ClamAV/watchdirs.sh
Create the ~/Library/LaunchAgents/net.clamav.dirwatcher.plist launch agent
<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabledkey>
/>
<key>Labelkey>
net.clamav.dirwatcher</string>
<key>LowPriorityIOkey>
/>
<key>OnDemandkey>
/>
<key>ProgramArgumentskey>
/full/path/to/Documents/ClamAV/watchdirs.shstring>
</array>
<key>StartIntervalkey>
1800</integer>
<key>WatchPathskey>
/full/path/to/Documents/ClamAV/Downloadsstring>
/full/path/to/Documents/ClamAV/EmailAttachmentsstring>
</array>
dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabledkey>
/>
<key>Labelkey>
net.clamav.dirwatcher</string>
<key>LowPriorityIOkey>
/>
<key>ProgramArgumentskey>
/full/path/to/Documents/ClamAV/watchdirs.shstring>
</array>
<key>StartIntervalkey>
7200</integer>
dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabledkey>
/>
<key>Labelkey>
net.clamav.dirwatcher</string>
<key>LowPriorityIOkey>
/>
<key>ProgramArgumentskey>
/full/path/to/Documents/ClamAV/watchdirs.shstring>
</array>
<key>StartCalendarIntervalkey>
Hour</key>
<integer>7integer>
Minute</key>
<integer>45integer>
</dict>
<key>StartCalendarIntervalkey>
Hour</key>
<integer>12integer>
Minute</key>
<integer>30integer>
</dict>
dict>
</plist>
sudo chown root:wheel ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
sudo chmod 0770 ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
ls -l ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
launchctl load -w ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
sudo reboot
open -a Console
freshclam; sleep 5; sudo clamd RELOAD
Alternative watchdirs.sh for a QueueDirectories virus scanner launch agent
exec >/dev/console 2>&1
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... STARTED ...\n"
/bin/sleep 5
QUARANTINEDIR="$HOME/Documents/ClamAV/Quarantine"
TOUCHFILE="$HOME/.clamav_timestamp"
QueueDirectory="$HOME/Documents/ClamAV/Downloads/QueueDirectory"
/bin/mkdir -p "$QueueDirectory"
MoveDir="$HOME/Documents/ClamAV/Downloads"
/bin/mkdir -p "$MoveDir"
find=/usr/bin/find
clamdscan=/opt/local/bin/clamdscan
clamscan=/opt/local/bin/clamscan
if [[ -f "$QueueDirectory"/.DS_Store ]]; then /bin/rm -f "$QueueDirectory"/.DS_Store; fi
DirTest="$($find -x "$QueueDirectory" -type d -maxdepth 1 -not -regex "^$QueueDirectory$")"
if [[ -n "$DirTest" ]]; then # download is directory
$find -x "$QueueDirectory" -type d -maxdepth 1 -not -regex "^$QueueDirectory$" -print0 | while read -d $'\0' dir; do
/usr/bin/touch -f -am "$TOUCHFILE"
/bin/sleep 3
unset -v file_modified
file_modified=0 # check if a file within $dir has been modified
while read -d $'\0' file; do
# set file_modified to 1 if at least one file has been modified
if [[ "$TOUCHFILE" -ot "$file" ]]; then file_modified=1; break; fi
done < <($find -x "$dir" -type f -print0 2>/dev/null)
if [[ -d "$dir" ]] && [[ "$TOUCHFILE" -nt "$dir" ]] && [[ $file_modified -eq 0 ]]; then
$clamdscan --no-summary --copy="$QUARANTINEDIR" "$dir" || $clamscan -i -r --no-summary --copy="$QUARANTINEDIR" "$dir"
bname="$(/usr/bin/basename "$dir")"
if [[ ! -d "$MoveDir/$bname" ]]; then
/bin/mv "$dir" "$MoveDir"
else
newMoveDir="$MoveDir/$(/bin/date "+%Y-%m-%d-%H%M.%S")-$bname"
/bin/mv "$dir" "$newMoveDir"
fi
fi
done
else
$find -x "$QueueDirectory" -type f -maxdepth 1 -print0 | while read -d $'\0' file; do # download is file
/usr/bin/touch -f -am "$TOUCHFILE"
/bin/sleep 3
if [[ -f "$file" ]] && [[ "$TOUCHFILE" -nt "$file" ]]; then
$clamdscan --no-summary --copy="$QUARANTINEDIR" "$file" || $clamscan -i --no-summary --copy="$QUARANTINEDIR" "$file"
# the mv command preserves metadata and resource forks of files on Extended HFS volumes (Mac OS X 10.4)
bname="$(/usr/bin/basename "$file")"
if [[ ! -f "$MoveDir/$bname" ]]; then
/bin/mv "$file" "$MoveDir"
else
newMoveDir="$MoveDir/$(/bin/date "+%Y-%m-%d-%H%M.%S")-$bname"
/bin/mv "$file" "$newMoveDir"
fi
fi
done
fi
if [[ -f "$QueueDirectory"/.DS_Store ]]; then /bin/rm -f "$QueueDirectory"/.DS_Store; fi
$find -x "$QueueDirectory" -not -type f -not -type d -print0 | while read -d $'\0' item; do /bin/rm -f "$item"; done
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): ... WATCHDIRS.SH for ClamAV ... DONE ...\n"
exit 0
#------------------------------------
# nano ~/Library/LaunchAgents/net.clamav.dirwatcher.plist
Disabled
Label
net.clamav.dirwatcher
ProgramArguments
/full/path/to/Documents/ClamAV/watchdirs.sh
QueueDirectories
/full/path/to/Documents/ClamAV/Downloads/QueueDirectory
Update clamd virus database automatically
/bin/sleep 120
exec >/dev/console 2>&1
/usr/bin/curl -I -L -s --max-time 15 database.clamav.net 1>/dev/null
if [[ $(echo $?) -eq 0 ]]; then
/opt/local/bin/freshclam -u root
/bin/sleep 3
(/bin/sleep 3; echo RELOAD; /bin/sleep 3; echo "exit") | /usr/bin/telnet -u /tmp/clamd >/dev/null 2>&1
/bin/sleep 3
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): clamd virus database successfully updated\n"
exit 0
else
echo -e "\n$(/bin/date "+%Y-%m-%d %H:%M:%S %Z"): updating the clamd virus database failed; no internet connection to database.clamav.net established\n"
exit 1
fi
sudo chown root:wheel /usr/local/sbin/update_clamd_db.sh
sudo chmod 0770 /usr/local/sbin/update_clamd_db.sh
<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabledkey>
/>
<key>Labelkey>
net.clamav.update.clamd.db</string>
<key>ProgramArgumentskey>
/usr/local/sbin/update_clamd_db.shstring>
</array>
<key>RunAtLoadkey>
/>
<key>StartIntervalkey>
10800</integer>
<key>UserNamekey>
root</string>
<key>GroupNamekey>
wheel</string>
dict>
</plist>
sudo chown root:wheel /Library/LaunchDaemons/net.clamav.update.clamd.db.plist
sudo chmod 0644 /Library/LaunchDaemons/net.clamav.update.clamd.db.plist
sudo launchctl load -w /Library/LaunchDaemons/net.clamav.update.clamd.db.plist
sudo reboot
bash -n $HOME/Documents/ClamAV/watchdirs.sh
bash -n /usr/local/sbin/update_clamd_db.sh
sudo plutil -convert binary1 -- /Library/LaunchDaemons/net.clamav.clamd.plist \
~/Library/LaunchAgents/net.clamav.dirwatcher.plist \
/Library/LaunchDaemons/net.clamav.update.clamd.db.plist
plutil -- /Library/LaunchDaemons/net.clamav.clamd.plist \
~/Library/LaunchAgents/net.clamav.dirwatcher.plist \
/Library/LaunchDaemons/net.clamav.update.clamd.db.plist
sigtool -l | grep -Ei 'adware|spy' | nl
sigtool -l | grep -i phish | nl
Further information:
-
Run ClamAV from a system service agent account
- ClamAV an open-source anti-virus toolkit
- Configuring Clamav's clamd for enhanced virus-scanning performance
- Updating ClamAV on OS X Server 10.4.7-10.4.11
- Using Open Source Tools to Filter Email on Mac OS X Server
- The Anti-Virus Or Anti-Malware Test File
- Mac OS X Unix Tutorial: Part 4 - Managing Permissions
- launchd