#!/usr/bin/env bash script_name="$(basename $(realpath $0))" working_dir="$(dirname $(realpath $0))" conf_file="${working_dir}/conf/${script_name%%.*}.conf" log_file="$(mktemp)" random_prefix="$(head -c 300 /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 8)" backup_date=$(date +%Y-%m-%d-%H%M) # ============= # --- Some functions # ============= clean_up() { if [[ -f "$_backup_crontab_file" ]]; then blank_line echononl " (Re)Install Crontab from previously saved crontab file '$_backup_crontab_file'.." crontab $_backup_crontab_file >> $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi fi # Perform program exit housekeeping rm -f $log_file rm -rf /tmp/*.${random_prefix} blank_line exit $1 } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n " $*\\c" 1>&2 else echo -e -n " $*" 1>&2 fi rm /tmp/shprompt$$ fi } echo_done() { if $terminal ; then echo -e "\033[80G[ \033[32mdone\033[m ]" fi } echo_ok() { if $terminal ; then echo -e "\033[80G[ \033[32mok\033[m ]" fi } echo_ignore() { if $terminal ; then echo -e "\033[80G[ \033[33mignore\033[m ]" fi } echo_warning() { if $terminal ; then echo -e "\033[80G[ \033[33m\033[1mwarn\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[80G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[80G[ \033[37mskipped\033[m ]" fi } fatal (){ blank_line if $terminal ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: \033[37m\033[1m$*\033[m" echo "" echo -e " \033[31m\033[1m Script will be interrupted..\033[m\033[m" else echo "fatal: $*" echo "Script will be interrupted.." fi clean_up 1 } error(){ blank_line if $terminal ; then echo -e " [ \033[31m\033[1mFehler\033[m ]: $*" else echo "Error: $*" fi blank_line } warn (){ if $terminal ; then echo "" echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" echo "" fi } info (){ if $terminal ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" echo "" fi } ## - Check if a given array (parameter 2) contains a given string (parameter 1) ## - containsElement () { local e for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done return 1 } trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters echo -n "$var" } blank_line() { if $terminal ; then echo "" fi } # ---------- # - Jobhandling # ---------- # - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM # - trap clean_up SIGHUP SIGINT SIGTERM # ============= # --- Some # ============= # - Support systemd ? # - if [[ "X$(which systemd)" = "X" ]]; then SYSTEMD_EXISTS=false else SYSTEMD_EXISTS=true fi # - Running in a terminal? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi #terminal=false # ---------- # Read Configurations from $conf_file # ---------- # - Give your default values here # - DEFAULT_QUARANTINE_BASE_DIR="/var/QUARANTINE" DEFAULT_CLAMAV_VIRUS_WHITE_LIST="/var/lib/clamav/my_whitelist.ign2" DEFAULT_WHITE_LIST_STRINGS="google.com" blank_line echononl "Include Configuration file.." if [[ ! -f $conf_file ]]; then echo_skipped warn "No configuration file '$conf_file' found!\n Loading default values.." else source $conf_file echo_done fi blank_line [[ -z "$QUARANTINE_BASE_DIR" ]] && QUARANTINE_BASE_DIR="$DEFAULT_QUARANTINE_BASE_DIR" Q_VIRUS_DIR="${QUARANTINE_BASE_DIR}/virus" [[ -z "$CLAMAV_VIRUS_WHITE_LIST" ]] && CLAMAV_VIRUS_WHITE_LIST="$DEFAULT_CLAMAV_VIRUS_WHITE_LIST" [[ -z "$WHITE_LIST_STRINGS" ]] && WHITE_LIST_STRINGS="$DEFAULT_WHITE_LIST_STRINGS" # List of strings to be whitelisted # declare -a whitelist_string_arr=() for _str in $WHITE_LIST_STRINGS ; do whitelist_string_arr+=("$_str") done # ----- # - Doing some pre-script tasks # ----- if $terminal ; then echo "" echo "" echo -e "\033[37m\033[1mDoing some pre-script tasks..\033[m" echo "" fi if $terminal ; then echo -e " \033[32mFind/Manage signatures that have already whitelistet\033[m..\n" fi # - List of already whitelistet signatures # - _found=false declare -a white_list=() if [[ ! -f "$CLAMAV_VIRUS_WHITE_LIST" ]] ; then if $terminal ; then echo -e " \033[33mNo whitelist file present\033[m..\n" fi else for _sig in $(grep -E "^MBL_" $CLAMAV_VIRUS_WHITE_LIST | sort -u) ; do _found=true _failed=false # Decode Signatures, save output in file # echononl "Decode Signature \033[1m$_sig\033[m .." sigtool --find-sigs=$_sig | sigtool --decode-sigs > /tmp/${_sig}.${random_prefix} 2> $log_file [[ $? -ne 0 ]] && _failed=true # Remove blank lines and lines that only contain whitespaces # sed -i '/^[[:space:]]*$/d' /tmp/${_sig}.${random_prefix} >> $log_file 2>&1 [[ $? -ne 0 ]] && _failed=true if $_failed ; then echo_failed error "$(cat $log_file)" continue else echo_ok fi if [[ -s "/tmp/${_sig}.${random_prefix}" ]]; then echononl "Add signature '$_sig' to whitelist array.." white_list+=("$_sig") if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi else echononl "Signature '$_sig' is NOT decodable.." echo_ignore rm -f /tmp/${_sig}.${random_prefix} > /dev/null 2>&1 #echononl "Delete '$_sig' from whitelist '$CLAMAV_VIRUS_WHITE_LIST'.." #sed -i "/$_sig/d" "$CLAMAV_VIRUS_WHITE_LIST" > $log_file 2>&1 #if [[ $? -eq 0 ]]; then # echo_ok #else # echo_failed # error "$(cat $log_file)" #fi fi blank_line done if ! $_found ; then if $terminal ; then echo -e " \033[33mNo already whitelistet signature present\033[m..\n" fi fi fi blank_line if $terminal ; then echo -e " \033[32mGathering/Manage signatures starting with 'MBL_' from quarantined e-mails\033[m..\n" fi # - Get signarures starting with "MBL_" found # - echononl "Gather signatures from quarantined e-mails .." found_sigs="$(grep -o -E " MBL_[[:digit:]]*" ${Q_VIRUS_DIR}/* 2> $log_file | cut -d ' ' -f2 | sort -u)" if [[ $? -eq 0 ]]; then echo_done else echo_failed error "$(cat $log_file)" fi blank_line declare -a whitelist_sig_arr=() declare -A virus_emails=() if [[ -z "$found_sigs" ]]; then if $terminal ; then echononl "\033[33mNo quarantined e-mails with \033[1mMBL_*\033[ signatures found.\033[m\n" fi else _failed=false for _sig in $found_sigs ; do for _str in ${whitelist_string_arr[@]} ; do if $(sigtool --find-sigs=$_sig | sigtool --decode-sigs | grep -q "$_str" 2> $log_file) ; then if containsElement "$_sig" "${white_list[@]}" ; then echononl "Signature \033[1m$_sig\033[m already whitelisted.." echo_ignore else blank_line echononl "Mark signatur \033[1m$_sig\033[m containing \033[1m$_str\033[m for whitelisting.." whitelist_sig_arr+=("$_sig") if [[ $? -eq 0 ]]; then echo_ok echononl "Decode Signature \033[1m$_sig\033[m .." sigtool --find-sigs=$_sig | sigtool --decode-sigs > /tmp/${_sig}.${random_prefix} 2> $log_file [[ $? -ne 0 ]] && _failed=true # Remove blank lines and lines that only contain whitespaces # sed -i '/^[[:space:]]*$/d' /tmp/${_sig}.${random_prefix} >> $log_file 2>&1 [[ $? -ne 0 ]] && _failed=true if $_failed ; then echo_failed error "$(cat $log_file)" _failed=false continue else echo_ok fi else echo_failed error "Adding signatur \033[1m$_sig\033[m to array 'whitelist_sig_arr' failed!" fi blank_line fi else echononl "Signature \033[1m$_sig\033[m does not affect \033[1m$_str\033[m.." echo_ignore fi done done fi blank_line if [[ ${#whitelist_sig_arr[@]} -lt 1 ]]; then if $terminal ; then info "No quarantined e-mails with whitelist signature(s) found. Nothing do to, exit now.." fi clean_up 0 fi if $terminal ; then echo -e " \033[32mGather/Manage quarantined e-mails with detected whitelist signature(s)\033[m..\n" fi for _sig in ${whitelist_sig_arr[@]}; do #_emails="$(grep INFECTED ${Q_VIRUS_DIR}/* | grep "$_sig" | cut -d ':' -f1 | sed -e "s#^${QUARANTINE_BASE_DIR}/##")" #_emails="$(grep INFECTED ${Q_VIRUS_DIR}/* | grep "$_sig" | cut -d ':' -f1)" _emails="$(grep "$_sig" ${Q_VIRUS_DIR}/* | cut -d ':' -f1)" for _email in $_emails ; do _email="${_email#"${QUARANTINE_BASE_DIR}/virus/"}" echononl "Add \033[1m$_email\033[m with signatur \033[1m$_sig\033[m to list.." virus_emails+=(["$_email"]="$_sig") if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi echononl "Add signature \033[1m$_sig\033[m to whitelist array.." if containsElement "$_sig" "${white_list[@]}" ; then echo_skipped else white_list+=("$_sig") if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi fi done done # ----- # - Main Part of Script # ----- if $terminal ; then echo "" echo "" echo -e "\033[37m\033[1mMain Part of Script.\033[m" echo "" fi if $terminal ; then echo -e " \033[32mWrite new whitelist file\033[m..\n" fi tmp_whitelist_file="/tmp/$(basename "${CLAMAV_VIRUS_WHITE_LIST}").${random_prefix}" :> "$tmp_whitelist_file" for _sig in ${white_list[@]} ; do _failed=false echononl "Add signatur \033[1m$_sig\033[m to clamav's whitelist file" while IFS='' read -r line || [[ -n "$line" ]] ; do echo "# $line" >> "$tmp_whitelist_file" 2>> "$log_file" [[ $? -ne 0 ]] && _failed=true done < "/tmp/${_sig}.${random_prefix}" echo "$_sig" >> "$tmp_whitelist_file" 2>> "$log_file" [[ $? -ne 0 ]] && _failed=true if $_failed ; then echo_failed error "$(cat $log_file)" _failed=false else echo_ok if ! $terminal ; then echo "" echo " Add Signatur "$_sig" to clamav's whitelist \"${CLAMAV_VIRUS_WHITE_LIST}\"" fi fi done blank_line echononl "Copy the temporary whitelist file to the designated location.." cp "$tmp_whitelist_file" "${CLAMAV_VIRUS_WHITE_LIST}" > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi # Remove blank lines and lines that only contain whitespaces # echononl "Remove empty lines from whitelist file.." sed -i '/^[[:space:]]*$/d' "${CLAMAV_VIRUS_WHITE_LIST}" > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi blank_line if $terminal ; then echo -e " \033[32mResend quarantined e-mails with detected whitelist signature\033[m..\n" fi if [[ ${#virus_emails[@]} -lt 1 ]]; then error "No quarantined e-mails with detected whitelist signature found." else for _virus_email in "${!virus_emails[@]}"; do _recipient="$( grep "X-Envelope-To-Blocked:" ${Q_VIRUS_DIR}/${_virus_email} 2> "$log_file" \ | grep -o -E "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" )" echononl "Resend blocked e-mail with signatur \033[1m${virus_emails[$_virus_email]}\033[m to \033[1m$_recipient\033[m" amavisd-release virus/${_virus_email} > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok if ! $terminal ; then echo "" echo " Resend blocked e-mail with signatur ${virus_emails[$_virus_email]} to $_recipient" fi echononl "Remove \033[1mvirus/${_virus_email}\033[m from QUARANTINE directory.." rm "${Q_VIRUS_DIR}/${_virus_email}" > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi else echo_failed error "$(cat $log_file)" fi blank_line done fi if $terminal ; then echo "" echo "" echo -e "\033[37m\033[1mDoing some post-script tasks..\033[m" echo "" fi echononl "Restart ClamAV Daemon" if $SYSTEMD_EXISTS ; then systemctl restart clamav-daemon > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi else /etc/init.d/clamav-daemon restart > "$log_file" 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed error "$(cat $log_file)" fi fi blank_line clean_up 1 # ----- # - Doing some post-script tasks # -----