72f26835d09ef13971e915dcc7e5cce835f326cb
[clamav-cn.git] / clamav-sanesecurity
1 #!/bin/bash
2 #
3 # $Id: update_sanesecurity.sh 706 2008-09-15 20:27:11Z mendel $
4 #
5 # A Modified version of the update script originally written by
6 # Bill Landry
7 #
8 # Modified by Dragan Dosen <ddosen@ffzg.hr>
9 #
10 # Modified by Ivan Rako <irako@srce.hr>
11 #
12 # Modified by Rick Cooper: Contact sanescript@dwford.com
13 #
14 # Modified by Norbert Buchmuller <norbi@nix.hu>
15 #
16 # TODO:
17 #       * split off changelog to a separate file
18 #       * split off configuration to a separate file
19 #       * support optional gzipping (if the downloaded file ends in .gz|.bz2, unzip it)
20 #       * support for protocol (rsync://|http://|https://|ftp://) auto-detection (and call the appropriate download method)
21 #       * support for more flexible download URLs (a list of them eg.)
22 #       * support for external configuration file
23 #       * support for additional DBs (securiteinfo.com, www.malware.com.br)
24 #
25 # Last updated Sep 15, 2008
26 #       FIX 09/15/2008
27 #       Looks for 'main.cld' in $clam_db_dir as new versions of ClamAV use
28 #               '.cld' extension for virus definition files.
29 #
30 #       FIX 04/26/2008
31 #       Changed '--debug' to mean '--syslog-loglevel=none --stderr-loglevel=debug'.
32 #       Added diagnostic message when tty is detected (and so random sleep is disabled).
33 #
34 #       FIX 12/21/2007
35 #       Fixed a grave bug: previously it failed to detect if the newly downloaded
36 #               database was corrupt.
37 #       Detects if the user runs the script from a terminal, and disables
38 #               the random sleep if it is so.
39 #
40 #       FIX 09/26/2007
41 #       Fixed a bug: now it passes the log levels to the unprivileged child.
42 #       Fixed a bug: now the unprivileged child does not attempt to reload ClamAV db.
43 #       Prints usage message if asked for.
44 #
45 #       FIX 09/21/2007
46 #       Added SELinux support.
47 #               Thanks Andrew Colin Kissa <kissaa@sentech.co.za>.
48 #       Added support to run the script as root (the superuser privileges are only used
49 #               when changing the owner:group and security context of the signature files,
50 #               and when reloading clamd).
51 #
52 #       FIX 09/20/2007
53 #       Fixed a bug introduced by the 08/28/2007 change: the "SPAM.ndb" file was saved
54 #               (incorrectly) with the name "SPAM.hdb" and thus ClamAV refused to load it.
55 #               Thanks Keith Brazington <keith@quetz.co.uk>.
56 #
57 #       FIX 08/28/2007
58 #       Refactored logging to avoid code/text duplication.
59 #       Refactored downloading to avoid code duplication.
60 #       Split up the main function into smaller ones.
61 #       Should be started as the clamav user, not root to avoid
62 #               security implications.
63 #       Uses proper temporary dir creation method (mktemp(1)) to avoid
64 #               security implications.
65 #       It is assured that a non-zero exit status is used when exiting because of an error.
66 #       Uses 'mail' syslog facility.
67 #       Different syslog log priorities are used for messages with different severity.
68 #       Diagnostic messages (incl. debug messages) go to stderr instead of stdout.
69 #       More careful quoting to allow spaces or other unexpected chars in variables.
70 #       Checking carefully for the exit status of all the important commands.
71 #       Rsync downloads the new signature file to a temporary directory
72 #               and only installs it after it is verified that ClamAV accepts
73 #               the signature file. (Note: This requires a relatively new version of Rsync
74 #               because versions older than cca. 2.6.9 unconditionally download the file, even
75 #               if it did not change.)
76 #       Uses '-p' option of 'cp' to preserve modification times.
77 #       No separate log files for each command, instead their output is logged if
78 #               they return a non-zero exit status.
79 #       PATH is appended to, not overwritten.
80 #       Use 'type -P' instead of 'which'.
81 #       Fixed a few typos.
82 #
83 #       FIX 08/13/2007
84 #       Using new SaneSecurity URLs
85 #
86 #       FIX 06/30/2007
87 #       Removed the -h (human readable) option from the rsync command line
88 #               as this was for the help option in older versions of rsync, and
89 #               it's not worth maintaining two versions of the command based on
90 #               rsync version. Thanks for the bug report Chris
91 #       Changed the SCAM_SIGS_URL variable to point to the proper URL. The old one
92 #       worked but this one is the correct/desired URL Thanks Steve Basford.
93 #
94 #       FIX 06/27/2007
95 #       Fixed incorrect check on CURL return code in phish.db section
96 #               causing it to consider a success as a failure. Thanks
97 #               Leonardo Rodrigues Magalhães
98 #
99 #       FIX 05/16/2007
100 #       Fixed a bug where downloading phish.ndb.gz for the first time results
101 #               in an error and the downloaded file removed (Thanks Gary V)
102 #
103 #       NEW 05/15/2007
104 #       Now have option to log to system logger (notify)
105 #               If you do not wish to use this logging function look below for
106 #               SYSLOG_ON=1 and set to SYSLOG_ON=0. This also will not function
107 #               if logger is not installed on your system for some reason
108 #
109 #       Now logs each download stats regardless of debug status, but only outputs
110 #               to console if in debugging mode, or error and doesn't output
111 #               to system logger.
112 #                              (as suggested by Gary V)
113 #       Changed the clamdb grep to use extended pattern matching as suggested
114 #               by Gary V
115 #       Altered the rsync portions to use --stats instead of --progress and
116 #               look for number of files transfered for confirmation of an update
117 #
118 #       NEW 05/08/2007
119 #       Updated the MSRBL-* files to update vi rsync (tested version 2.6.9)
120 #               the current db(s) will be saved before the update and if there is a
121 #               problem with the download or clam db tests the old version is
122 #               moved back into place, an error message is produced and the
123 #               corrupt file is moved to filename.bad for the operator to look into
124 #       Changed the detection of the clam db directory it's now very fast
125 #               and will accommodate trailing slash (ie. /usr/local/share/clamav/) and
126 #               will also check for main.cvd as well as the *.inc directories since
127 #               apparently it's possible to have an install with only the main.cvd,
128 #               at least temporarily
129 #
130 #       NEW 05/05/2007
131 #       Fixed a potential problem with the downloaded file size being zero
132 #       Now test a small txt file using the downloaded sig file, if clam doesn't
133 #               like it we don't move it or use it. Throw an error to the operator
134 #       Fixed a possible issue with the log size being very large if the site is
135 #               busy, now only return the last line from the transmission progress
136 #       Changed the operation to find the clam database dir to checking for
137 #               the standard /usr/local/share/clamav location first, and if not there
138 #               ask clamscan. (save a few seconds)
139 #
140 #
141
142 # Make sure the path to your ClamAV binaries is in here, it should
143 # cover all normal installations
144 export PATH="$PATH":/bin:/usr/bin:/usr/local/bin
145
146 # The file names and URLs of the scam and phish signature files from SaneSecurity
147 SCAM_SIGS="scam.ndb"
148 SCAM_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/scam.ndb"
149 PHISH_SIGS="phish.ndb"
150 PHISH_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/phish.ndb"
151 JUNK_SIGS="junk.ndb"
152 JUNK_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/junk.ndb"
153 LOTT_SIGS="lott.ndb"
154 LOTT_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/lott.ndb"
155 ROGUE_SIGS="rogue.hdb"
156 ROGUE_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/rogue.hdb"
157 SPAMIMG_SIGS="spamimg.hdb"
158 SPAMIMG_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/spamimg.hdb"
159 SPAM_SIGS="spam.ldb"
160 SPAM_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/spam.ldb"
161 SPEAR_SIGS="spear.ndb"
162 SPEAR_SIGS_URL="rsync://rsync.sanesecurity.net/sanesecurity/spear.ndb"
163
164 # The URLs of the spam and image-spam signature files from MSRBL
165 MSRBL_SPAM_SIGS="MSRBL-SPAM.ndb"
166 MSRBL_SPAM_SIGS_URL="rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-SPAM.ndb"
167 MSRBL_IMAGE_SIGS="MSRBL-Images.hdb"
168 MSRBL_IMAGE_SIGS_URL="rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-Images.hdb"
169
170 # SecuriteInfo  -ddosen
171 SI_VX_SIGS="vx.hdb"
172 SI_VX_SIGS_URL="http://clamav.securiteinfo.com/vx.hdb.gz"
173 SI_HONEYNET_SIGS="honeynet.hdb"
174 SI_HONEYNET_SIGS_URL="http://clamav.securiteinfo.com/honeynet.hdb.gz"
175 SI_SECURITEINFO_SIGS="securiteinfo.hdb"
176 SI_SECURITEINFO_SIGS_URL="http://clamav.securiteinfo.com/securiteinfo.hdb.gz"
177 SI_ANTISPAM_SIGS="antispam.ndb"
178 SI_ANTISPAM_SIGS_URL="http://clamav.securiteinfo.com/antispam.ndb.gz"
179
180 # Malware Black List  -ddosen
181 MBL_SIGS="mbl.db"
182 MBL_SIGS_URL="http://www.malware.com.br/cgi/submit?action=list_clamav"
183
184 # Log messages with this or greater severity to syslog
185 syslog_loglevel=error
186
187 # Log messages with this or greater severity to standard error
188 stderr_loglevel=none
189
190 # Use this syslog facility
191 syslog_facility=mail
192
193 # The script will sleep for a random amount of time before starting the
194 # actual update. This evens out the load on the update servers when people
195 # have a tendency to set cron jobs on the hour, half hour or quarter hour.
196 # The extra padding keeps the servers from being hammered all at once.
197 # These are the minimum and maximum sleep times in seconds.
198 min_sleep_time=30
199 max_sleep_time=600
200
201 # Should the script reload the clamd service
202 # (Should not be necessary if you have "SelfCheck" enabled in clamd.conf
203 # and "NotifyClamd" enabled in freshclam.conf.)
204 reload_clamd=0
205
206 # If the script is run as root, change the owner and group of
207 # the signature files to this user and group. (The username
208 # and group name should be separated by a colon.)
209 sigfile_owner_and_group=clamav:clamav
210
211 # If the script is run as root, perform downloading, checking and
212 # installation of the signature files as this user. (The SELinux
213 # security context fixing and the clamd reload need superuser
214 # privileges, so these will be performed as root.)
215 # Note: This user must be able to read and write signature files
216 # in ClamAV db dir.
217 unprivileged_user=${sigfile_owner_and_group%:*}
218
219 # Whether to preserve the temporary directory (for debugging purposes)
220 # on exit instead of deleting it (the default)
221 keep_temp_dir=0
222
223 ####################################################################
224 # No user tunable variables below
225 ####################################################################
226
227 ####################################################################
228 # Logging functions
229 #
230
231 # Return the numeric constant associated with the given
232 # severity name.
233 #
234 # Usage: numeric_loglevel=`numeric_log_severity $loglevel_name`
235 #
236 numeric_log_severity()
237 {
238         local name="$1"
239
240         # The severity names (same as in syslog)
241         local -a severity_names=(
242                 debug,deb,dbg
243                 info,inf
244                 notice
245                 warning,warn,wrn
246                 err,error
247                 crit,critical
248                 alert
249                 emerg,emergency,panic
250                 none,off
251         )
252
253         local i=0
254         local numeric_level
255         while [ -n "${severity_names[i]}" ]; do
256                 for levelname in ${severity_names[i]//,/ }; do
257                         if [ "$name" == "$levelname" ]; then
258                                 numeric_level=$i
259                                 break 2
260                         fi
261                 done
262                 let i++
263         done
264         if [ -z "$numeric_level" ]; then
265                 numeric_level=`numeric_log_severity debug`
266         fi
267
268         echo $numeric_level
269 }
270
271 # Log the given message with the given priority.
272 # Logging includes printing to stderr and sending the message to
273 # syslog, depending on the debug level and syslog loglevel settings.
274 #
275 # Usage: log level message [message_continuation [...]]
276 #
277 log()
278 {
279         local level="$1"
280         local -a message_parts='("${@:2}")'
281
282         local message=`echo -e "${message_parts[@]}"`
283
284         if [ $(numeric_log_severity $level) -ge $(numeric_log_severity $stderr_loglevel) ]; then
285                 echo "$program_invocation_short_name: [$level] $message" >&2
286         fi
287         if [ $(numeric_log_severity $level) -ge $(numeric_log_severity $syslog_loglevel) ]; then
288                 if [ -n "$logger" ]; then
289                         "$logger" -p ${syslog_facility}.${level} -i -t "$program_invocation_short_name" -- "${message//$'\n'/\\n}"
290                 fi
291         fi
292 }
293
294 ####################################################################
295 # Utility functions
296 #
297
298 # Run the command silently. If the command exits with a
299 # non-zero exit status, log an error message including the command run,
300 # the exit status and and the collected output, otherwise swallow the output.
301 #
302 # Usage: run_cmd "$cmd" ["$arg1" [...]]
303 #
304 run_cmd()
305 {
306         local -a cmd_and_args='("${@}")'
307
308         local output    # must be a separate command, as 'local' always returns 0 exit status
309         output=`"${cmd_and_args[@]}" 2>&1`
310         local exit_status=$?
311         if [ $exit_status -ne 0 ]; then
312                 log err "Error executing command <<<${cmd_and_args[*]}>>>, exit status: $exit_status, output: <<<$output>>>"
313         fi
314
315         return $exit_status
316 }
317
318 # Push one or more elements to the end of the array.
319 #
320 # Usage: push array_name "$elem1" [...]
321 #
322 push()
323 {
324         local -a array="$1" elems='("${@:2}")'
325
326         local len=`eval echo \\\${#$array[@]}`
327         for elem in "${elems[@]}"; do
328                 eval "$array[$len]=$elem"
329                 let len++
330         done
331 }
332
333 ####################################################################
334 # Signature file download/test/installation functions
335 #
336
337 # Check if ClamAV accepts the signature file,
338 # and moves it to the database dir if so.
339 # Exit status:
340 #       0   - sigfile was updated successfully
341 #       1   - sigfile was up-to-date or an error occurred
342 #
343 # Usage: check_and_install_sigfile "$file_basename"
344 #
345 check_and_install_sigfile()
346 {
347         local filename="$1"
348
349         local sigfile="$clam_db_dir/$filename"
350         local new_sigfile="$tmp_dir/$filename"
351
352         local sigfile_updated=0
353
354         if [ -s "$new_sigfile" ]; then
355                 # First we do a quick test of the downloaded file. If ClamAV doesn't
356                 # like it we won't use it and issue an error to the operator
357                 # renaming the file so they can inspect it themselves.
358                 run_cmd "$clamscan" --quiet -d "$new_sigfile" "$test_file"
359                 local exit_status=$?
360                 if [ $exit_status -eq 0 ]; then
361                         if [ -s "$sigfile" ]; then
362                                 run_cmd cp -pf "$sigfile" "${sigfile}.bak"
363                         fi
364                         run_cmd mv -f "$new_sigfile" "$sigfile"
365                         exit_status=$?
366                         if [ $exit_status -eq 0 ]; then
367                                 sigfile_updated=1
368                         else
369                                 log err "Cannot move '$new_sigfile' to '$sigfile', 'mv' exit status: $exit_status"
370                         fi
371                 else
372                         log err "ClamAV had a problem using '$new_sigfile' (exit status: $exit_status)."
373                         log err "We will NOT install '$new_sigfile' into the database directory."
374                         run_cmd mv -f "$new_sigfile" "${sigfile}.bad"
375                         exit_status=$?
376                         if [ $exit_status -eq 0 ]; then
377                                 log err "Preserving the corrupt file as '${sigfile}.bad' for you to check."
378                         else
379                                 log err "Cannot move the corrupt file from '$new_sigfile' to '${sigfile}.bad', 'mv' exit status: $exit_status."
380                         fi
381                 fi
382         elif [ -e "$new_sigfile" ]; then
383                 log warning "'$new_sigfile' was zero bytes and will not be used!"
384         fi
385
386         local ret_val=1
387         if [ $sigfile_updated -ne 0 ]; then
388                 log info "'$sigfile' was updated"
389                 ret_val=0
390         else
391                 log info "'$sigfile' was NOT updated"
392                 ret_val=1
393         fi
394
395         return $ret_val
396 }
397
398 # Update/download a gzip-compressed signature file using CURL,
399 # uncompress it, check if ClamAV accepts it, and install it in
400 # the database directory.
401 # Exit status:
402 #       0   - sigfile was updated successfully
403 #       1   - sigfile was up-to-date or an error occurred
404 #
405 # Usage: update_sigfile_with_curl "$url" "$file_basename"
406 #
407 update_sigfile_with_curl()
408 {
409         local url="$1" filename="$2"
410
411         if [ -z "$url" ]; then
412                 log info "Skipping '$filename' because no URL is configured for it"
413                 return 1
414         fi
415
416         local sigfile_gz="$clam_db_dir/${filename}.gz"
417         local new_sigfile_gz="$tmp_dir/${filename}.gz"
418         local sigfile="$clam_db_dir/$filename"
419         local new_sigfile="$tmp_dir/$filename"
420
421         local sigfile_gz_updated=0
422
423         declare -a curl_additional_flags
424
425         # If something happend to the sig file, or this is the first time
426         # this script has been run then we can't do the date test on the
427         # current file so we just grab what ever is current on the site
428         if [ -s "$sigfile_gz" ]; then
429                 log debug "Checking for newer version of '$sigfile_gz'"
430                 push curl_additional_flags "-z" "$sigfile_gz"
431         else
432                 log debug "'$sigfile_gz' does not exist, so doing initial download"
433         fi
434
435         if [ $stderr_loglevel != debug ]; then
436                 push curl_additional_flags "-s"
437         fi
438
439         run_cmd "$curl" -R "${curl_additional_flags[@]}" -o "$new_sigfile_gz" \
440                 -f -v --referer ";auto" --location "$url"
441         local exit_status=$?
442         if [ $exit_status -eq 0 -o $exit_status -eq 22 ]; then
443                 # If we don't have the download or it's zero bytes
444                 # something went wrong and we did not get an update.
445                 if [ ! -s "$new_sigfile_gz" ]; then
446                         if [ -e "$new_sigfile_gz" ]; then
447                                 rm -f "$new_sigfile_gz"
448                                 log warning "'$new_sigfile_gz' was zero bytes and will not be used!"
449                         fi
450                         if [ $exit_status -eq 22 ]; then
451                                 log warning "CURL returned an error code 22 which results from a HTTP error 4xx"
452                                 log warning "This might be caused by the file not being updated (HTTP 412) but"
453                                 log warning "it could be something else."
454                         fi
455                 fi
456         else
457                 log err "CURL had a problem getting '$new_sigfile_gz' from '$url', exit status: $exit_status"
458                 rm -f "$new_sigfile_gz"
459         fi
460
461         if [ -s "$new_sigfile_gz" ]; then
462                 "$gunzip" -cdf "$new_sigfile_gz" > "$new_sigfile"
463                 exit_status=$?
464                 if [ $exit_status -eq 0 ]; then
465                         run_cmd mv -f "$new_sigfile_gz" "$sigfile_gz"
466                         exit_status=$?
467                         if [ $exit_status -eq 0 ]; then
468                                 sigfile_gz_updated=1
469                         else
470                                 log err "Cannot move '$new_sigfile_gz' to '$sigfile_gz', 'mv' exit status: $exit_status"
471                         fi
472                 else
473                         rm -f "$new_sigfile_gz"
474                         log err "Cannot uncompress '$new_sigfile_gz', 'gunzip' exit status: $exit_status"
475                 fi
476         fi
477
478         if [ $sigfile_gz_updated -ne 0 ]; then
479                 log info "'$sigfile_gz' was updated"
480         else
481                 log info "'$sigfile_gz' was NOT updated"
482         fi
483
484         check_and_install_sigfile "$filename"
485 }
486
487 # Update/download the signature file using Rsync,
488 # check if ClamAV accepts it, and and installs it in
489 # the database directory.
490 # Exit status:
491 #       0   - sigfile was updated successfully
492 #       1   - sigfile was up-to-date or an error occurred
493 #
494 # Usage: update_sigfile_with_curl "$url" "$file_basename"
495 #
496 update_sigfile_with_rsync()
497 {
498         local url="$1" filename="$2"
499
500         if [ -z "$url" ]; then
501                 log info "Skipping '$filename' because no URL is configured for it"
502                 return 1
503         fi
504
505         local sigfile="$clam_db_dir/$filename"
506         local new_sigfile="$tmp_dir/$filename"
507
508         if [ -s "$sigfile" ]; then
509                 log debug "Checking for newer version of '$sigfile'"
510         else
511                 log debug "'$sigfile' does not exist, so doing initial download"
512         fi
513
514         # Rsync will download the file only if it is different from the one in
515         # $clam_db_dir. The downloaded file will be stored in $tmp_dir.
516         run_cmd "$rsync" --stats -t --compare-dest="$clam_db_dir/" \
517                 "$url" "$new_sigfile"
518         local exit_status=$?
519         if [ $exit_status -ne 0 ]; then
520                 log err "Rsync had a problem getting '$new_sigfile' from '$url', exit status: $exit_status"
521                 rm -f "$new_sigfile"
522         fi
523
524         check_and_install_sigfile "$filename"
525 }
526
527 ####################################################################
528 # Startup functions
529 #
530
531 # Check for the external programs/tools and
532 # set the corresponding variables to the found paths
533 #
534 # Usage: check_for_external_programs
535 #
536 check_for_external_programs()
537 {
538         # Look for the paths to the programs we are going to need
539         logger=`type -P logger`
540         clamscan=`type -P clamscan`
541         curl=`type -P curl`
542         gunzip=`type -P gunzip`
543         rsync=`type -P rsync`
544
545         # Check if the 'logger' binary is missing
546         if [ -z "$logger" -o ! -x "$logger" ]; then
547                 unset logger
548                 log err "Could not find the 'logger' program, no syslog will be attempted"
549         fi
550
551         # If we did not find any of our external programs
552         # give an error message and exit
553         local prg
554         for prg in clamscan curl gunzip rsync; do
555                 if [ -z ${!prg} ]; then
556                         log err "Cannot find '$prg'"
557                         log err "Exiting."
558                         exit 1
559                 fi
560         done
561 }
562
563 # Print usage message.
564 #
565 # Usage: print_usage
566 #
567 print_usage()
568 {
569         echo -e "Downloads unofficial ClamAV signature files from sanesecurity.com, msrbl.com, securiteinfo.com and malware.com.br"
570         echo -e "Usage: $0 [options]"
571         echo -e "OPTIONS:"
572         echo -e "  --syslog-loglevel=level\tSets the log level for syslog to 'level'."
573         echo -e "  --stderr-loglevel=level\tSets the log level for stderr to 'level'."
574         echo -e "  --debug\t\t\tShorthand for '--syslog-loglevel=none"
575         echo -e "  \t\t\t\t--stderr-loglevel=debug'."
576         echo -e "  --sleep\t\t\tEnable the (random length) sleep before"
577         echo -e "  \t\t\t\tstarting the download. (this is the default)"
578         echo -e "  --no-sleep\t\t\tDisable the (random length) sleep before"
579         echo -e "  \t\t\t\tstarting the download. (default if stdin is"
580         echo -e "  \t\t\t\ta tty)"
581         echo -e ""
582         echo -e "Log level can be one of these:"
583         echo -e "  debug, info, notice, warning, err, crit, alert, emerg"
584 }
585
586 # Parse options/arguments
587 #
588 # Usage: parse_arguments
589 #
590 parse_arguments()
591 {
592         local arg
593
594         # Parse options/arguments
595         while [ $# -ge 1 ]; do
596                 case "$1" in
597                         --debug|-d|debug|Debug|DEBUG)
598                                 syslog_loglevel=none
599                                 stderr_loglevel=debug
600                                 log debug "Debug mode is ON"
601                                 ;;
602                         --syslog-loglevel)
603                                 syslog_loglevel=$2
604                                 shift
605                                 ;;
606                         --syslog-loglevel=*)
607                                 syslog_loglevel=${1#--*=}
608                                 ;;
609                         --stderr-loglevel)
610                                 stderr_loglevel=$2
611                                 shift
612                                 ;;
613                         --stderr-loglevel=*)
614                                 stderr_loglevel=${1#--*=}
615                                 ;;
616                         --sleep)
617                                 no_sleep=0
618                                 ;;
619                         --no-sleep)
620                                 no_sleep=1
621                                 ;;
622                         --unprivileged-child)
623                                 unprivileged_child=1
624                                 ;;
625                         --help|-h|-\?)
626                                 print_usage
627                                 exit 0
628                                 ;;
629                         *)
630                                 log err "Got command line argument of '$1' and I don't understand it!"
631                                 log err "Exiting."
632                                 exit 1
633                                 ;;
634                 esac
635                 shift
636         done
637 }
638
639 # Create a temporary directory and arrange to remove it on exit,
640 # plus create an empty file to use for testing ClamScan
641 #
642 # Usage: create_temp_dir
643 #
644 create_temp_dir()
645 {
646         tmp_dir=`mktemp -d -t ${program_invocation_short_name}.XXXXXXXX` || (
647                 log err "Cannot create temporary directory"
648                 log err "Exiting."
649                 exit 1
650         )
651         local exit_status=$?
652         if [ $exit_status -ne 0 ]; then
653                 log err "Error running mktemp(1), exit status: $exit_status"
654                 log err "Exiting."
655                 exit 1
656         fi
657         log debug "Created temporary directory: '$tmp_dir'"
658         if [ $keep_temp_dir -eq 0 ]; then
659                 trap 'rm -rf "$tmp_dir"' EXIT
660         fi
661
662         # We create a file for ClamScan to test in debug mode.
663         test_file="$tmp_dir/test.file"
664         touch "$test_file"
665 }
666
667 # Log a couple of debug messages with a summary on some parameters
668 #
669 # Usage: log_startup_summary
670 #
671 log_startup_summary()
672 {
673         log debug "PHISH_SIGS           : $PHISH_SIGS_URL"
674         log debug "SCAM_SIGS            : $SCAM_SIGS_URL"
675         log debug "JUNK_SIGS            : $JUNK_SIGS_URL"
676         log debug "LOTT_SIGS            : $LOTT_SIGS_URL"
677         log debug "ROGUE_SIGS           : $ROGUE_SIGS_URL"
678         log debug "SPAMIMG_SIGS         : $SPAMIMG_SIGS_URL"
679         log debug "SPAM_SIGS            : $SPAM_SIGS_URL"
680         log debug "SPEAR_SIGS           : $SPEAR_SIGS_URL"
681         log debug "MSRBL_SPAM_SIGS      : $MSRBL_SPAM_SIGS_URL"
682         log debug "MSRBL_IMAGE_SIGS     : $MSRBL_IMAGE_SIGS_URL"
683         log debug "SI_VX_SIGS           : $SI_VX_SIGS_URL"
684         log debug "SI_HONEYNET_SIGS     : $SI_HONEYNET_SIGS_URL"
685         log debug "SI_SECURITEINFO_SIGS : $SI_SECURITEINFO_SIGS_URL"
686         log debug "SI_ANTISPAM_SIGS     : $SI_ANTISPAM_SIGS_URL"
687         log debug "MBL_SIGS             : $MBL_SIGS_URL"
688         log debug "ClamScan             : $clamscan"
689         log debug "CURL                 : $curl"
690         log debug "GunZip               : $gunzip"
691         log debug "RSync                : $rsync"
692         log debug "ClamAV db dir        : $clam_db_dir"
693         log debug "temp dir             : $tmp_dir"
694 }
695
696 # Sleep for a random time (determined by $min_sleep_time and $max_sleep_time global variables)
697 #
698 # Usage: random_sleep
699 #
700 random_sleep()
701 {
702         local sleep_time
703
704         sleep_time=$(($RANDOM * $(($max_sleep_time-$min_sleep_time)) / 32767 + $min_sleep_time))
705         log debug "Sleeping for $sleep_time seconds..."
706         sleep $sleep_time
707 }
708
709 # Find the ClamAV db dir and set the $clam_db_dir global variable
710 #
711 # Usage: find_clam_db_dir
712 #
713 find_clam_db_dir()
714 {
715         # Scan an empty test file with debug enabled to determine where ClamAV expects
716         # to find it's signature database
717         log debug "Checking for ClamAV database directory..."
718         clam_db_dir=`"$clamscan" --debug "$test_file" 2>&1 | \
719                 sed -ne 's/\/$//; s/^.*loading databases from \(.*\)$/\1/ip' | head -1`
720         log debug "Found ClamAV database directory: $clam_db_dir"
721
722         # Check for either the daily.inc, the main.inc dirs or the main.cvd one of which
723         # must exist for a functional clamav installation
724         if [ \
725           ! -d "$clam_db_dir/daily.inc" -a \
726           ! -d "$clam_db_dir/main.inc" -a \
727           ! -f "$clam_db_dir/main.cvd" -a \
728           ! -f "$clam_db_dir/main.cld" \
729         ]; then
730                 log err "None of '$clam_db_dir/daily.inc', '$clam_db_dir/main.inc',"
731                 log err "'$clam_db_dir/main.cvd', '$clam_db_dir/main.cld' found"
732                 log err "in your database directory. Either '$clam_db_dir' is NOT"
733                 log err "the correct database path or there is something wrong with your"
734                 log err "ClamAV installation. This path came from your '$clamscan' so I would guess"
735                 log err "you need to check your clamd.conf file and/or '$clam_db_dir'"
736                 log err "Exiting."
737                 exit 1
738         fi
739 }
740
741 #
742 # Change owner, group and security context of the signature files.
743 #
744 # Usage: chown_chcon sigfiles ...
745 #
746 chown_chcon()
747 {
748         local -a sigfiles='("$@")'
749
750         for i in "${sigfiles[@]}"; do
751                 [ -f "$i" ] || continue
752
753                 run_cmd chown $sigfile_owner_and_group "$i"
754                 exit_status=$?
755                 if [ $exit_status -ne 0 ]; then
756                         log err "chown had a problem changing ownership of signature file '$i', exit status: $exit_status"
757                         log err "Exiting."
758                         exit 1
759                 fi
760         done
761
762         # SELinux fix: change security context
763         if [ -n "$(type -P sestatus 2>/dev/null)" ] && [ "$(sestatus | head -n 1 | awk '{ print $3 }')" == "enabled" ]; then
764                 for i in "${sigfiles[@]}"; do
765                         [ -f "$i" ] || continue
766
767                         run_cmd chcon user_u:object_r:var_t "$i"
768                         exit_status=$?
769                         if [ $exit_status -ne 0 ]; then
770                                 log err "chcon had a problem changing security context of signature file '$i', exit status: $exit_status"
771                                 log err "Exiting."
772                                 exit 1
773                         fi
774                 done
775         fi
776 }
777
778 # Reload the ClamAV daemon
779 #
780 # Usage: reload_clamav_daemon
781 #
782 reload_clamav_daemon()
783 {
784         local -a clamd_reload_cmd
785         if [ -n "`type -P service`" ]; then
786                 clamd_reload_cmd=(service clamd reload)
787         else
788                 for init_script in /etc/{init,rc}.d/{clamd,clamav-daemon}; do
789                         if [ -x "$init_script" ]; then
790                                 clamd_reload_cmd=("$init_script" reload-database)
791                                 break
792                         fi
793                 done
794         fi
795         if [ -n "${clamd_reload_cmd[*]}" ]; then
796                 log info "Reloading ClamAV daemon"
797                 run_cmd "${clamd_reload_cmd[@]}"
798         else
799                 log err "Cannot reload ClamAV daemon because no initscript found"
800                 return 1
801         fi
802 }
803
804 ####################################################################
805
806
807 declare logger clamscan curl gunzip rsync
808 declare tmp_dir test_file
809 declare clam_db_dir
810 declare unprivileged_child=0
811 declare no_sleep=0
812
813 # Skip sleeping if run interactively
814 if tty -s; then
815         log debug "Disabling random sleep feature because stdin is a terminal."
816         no_sleep=1
817 fi
818
819 # The short name of this script
820 readonly program_invocation_short_name=`basename "$0"`
821
822 # The absolute name of this script
823 readonly program_invocation_absolute_name=$(readlink -f "$0" || type -P "$0")
824
825 # Startup
826 parse_arguments "$@"
827 if [ "$unprivileged_child" -eq 0 ]; then
828         log debug "Starting."
829 fi
830 check_for_external_programs
831 create_temp_dir
832 find_clam_db_dir
833 if [ "$unprivileged_child" -eq 0 ]; then
834         log_startup_summary
835         if [ "$no_sleep" -eq 0 ]; then
836                 random_sleep
837         fi
838 fi
839
840 # Change current directory to ClamAV database directory
841 # but just for the sake of safety we will use
842 # absolute paths for copy/move operations
843 cd "$clam_db_dir"
844
845 declare sigfile_updated=0
846 if [ "$unprivileged_child" -ne 0 -o $(id -u) -ne 0 ]; then
847         # Update/download the signature files
848         update_sigfile_with_rsync "$SCAM_SIGS_URL" "$SCAM_SIGS" && sigfile_updated=1
849         update_sigfile_with_rsync "$PHISH_SIGS_URL" "$PHISH_SIGS" && sigfile_updated=1
850         update_sigfile_with_rsync "$JUNK_SIGS_URL" "$JUNK_SIGS" && sigfile_updated=1
851         update_sigfile_with_rsync "$LOTT_SIGS_URL" "$LOTT_SIGS" && sigfile_updated=1
852         update_sigfile_with_rsync "$ROGUE_SIGS_URL" "$ROGUE_SIGS" && sigfile_updated=1
853         update_sigfile_with_rsync "$SPAMIMG_SIGS_URL" "$SPAMIMG_SIGS" && sigfile_updated=1
854         update_sigfile_with_rsync "$SPAM_SIGS_URL" "$SPAM_SIGS" && sigfile_updated=1
855         update_sigfile_with_rsync "$SPEAR_SIGS_URL" "$SPEAR_SIGS" && sigfile_updated=1
856         update_sigfile_with_rsync "$MSRBL_SPAM_SIGS_URL" "$MSRBL_SPAM_SIGS" && sigfile_updated=1
857         update_sigfile_with_rsync "$MSRBL_IMAGE_SIGS_URL" "$MSRBL_IMAGE_SIGS" && sigfile_updated=1
858         #
859         update_sigfile_with_curl "$SI_VX_SIGS_URL" "$SI_VX_SIGS" && sigfile_updated=1
860         update_sigfile_with_curl "$SI_HONEYNET_SIGS_URL" "$SI_HONEYNET_SIGS" && sigfile_updated=1
861         update_sigfile_with_curl "$SI_SECURITEINFO_SIGS_URL" "$SI_SECURITEINFO_SIGS" && sigfile_updated=1
862         update_sigfile_with_curl "$SI_ANTISPAM_SIGS_URL" "$SI_ANTISPAM_SIGS" && sigfile_updated=1
863         update_sigfile_with_curl "$MBL_SIGS_URL" "$MBL_SIGS" && sigfile_updated=1
864 else
865         # Re-execute the script as the unprivileged user to do the download/check/install part.
866         # (It exits with 0 exit status only if at least on the signature file were updated.)
867         su -s $SHELL $unprivileged_user -c "'$program_invocation_absolute_name' --unprivileged-child --syslog-loglevel=$syslog_loglevel --stderr-loglevel=$stderr_loglevel" && sigfile_updated=1
868
869         # Change owner, group and security context.
870         chown_chcon "$SCAM_SIGS" "$PHISH_SIGS" "$JUNK_SIGS" "$LOTT_SIGS" "$ROGUE_SIGS" "$SPAMIMG_SIGS" "$SPAM_SIGS" "$SPEAR_SIGS" "$MSRBL_SPAM_SIGS" "$MSRBL_IMAGE_SIGS" "$SI_VX_SIGS" "$SI_HONEYNET_SIGS" "$SI_SECURITEINFO_SIGS" "$SI_ANTISPAM_SIGS" "$MBL_SIGS"
871 fi
872
873 # Reload database
874 if [ $reload_clamd -ne 0 -a $sigfile_updated -ne 0 -a "$unprivileged_child" -eq 0 ]; then
875         reload_clamav_daemon
876 fi
877
878 if [ "$unprivileged_child" -eq 0 ]; then
879         log debug "Exiting."
880 fi
881 # izbacio ico, 2008-10-09
882 #if [ $sigfile_updated -ne 0 ]; then
883 #       final_exit_status=0
884 #else
885 #       final_exit_status=100
886 #fi
887 exit $final_exit_status