Current File : //usr/local/maldetect/maldet
#!/bin/bash
#
##
# Linux Malware Detect v1.4.2
#             (C) 2002-2013, R-fx Networks <proj@r-fx.org>
#             (C) 2013, Ryan MacDonald <ryan@r-fx.org>
# inotifywait (C) 2007, Rohan McGovern <rohan@mcgovern.id.au>
# This program may be freely redistributed under the terms of the GNU GPL v2
##
#
ver=1.4.2
inspath=/usr/local/maldetect
cnf=$inspath/conf.maldet
intcnf=$inspath/internals.conf
datestamp=`date +"%m%d%y-%H%M"`
rver=$ver

header() {
	echo "Linux Malware Detect v$ver"
	echo "            (C) 2002-2013, R-fx Networks <proj@r-fx.org>"
	echo "            (C) 2013, Ryan MacDonald <ryan@r-fx.org>"
        echo "inotifywait (C) 2007, Rohan McGovern <rohan@mcgovern.id.au>"
	echo "This program may be freely redistributed under the terms of the GNU GPL v2"
	echo ""
}


if [ -f "$cnf" ] && [ ! "$cnf" == "" ]; then
   source $cnf
else
   header
   echo "maldet($$): {glob} $cnf not found, aborting."
   exit 1
fi

if [ -f "$intcnf" ] && [ ! "$intcnf" == "" ]; then
   source $intcnf
else
   header
   echo "maldet($$): {glob} $intcnf not found, aborting."
   exit 1
fi

if [ ! "$(whoami)" == "root" ]; then
	if [ -z "$public_scan" ] || [ "$public_scan" == "0" ]; then
		args="$@"
		if [[ "$args" =~ "modsec" ]]; then
			echo "1 maldet: OK"
			exit
		fi
		header
		echo "public scanning is currently disabled, please contact your system administrator to enable public_scan in conf.maldet."
		exit
	fi
	pub=1
	user=`whoami`
        quardir=$inspath/pub/$user/quar
        sessdir=$inspath/pub/$user/sess
        tmpdir=$inspath/pub/$user/tmp
        logf=$inspath/pub/$user/event_log
	mkdir -p $quardir >> /dev/null 2>&1
	mkdir -p $sessdir >> /dev/null 2>&1
	mkdir -p $tmpdir >> /dev/null 2>&1
	touch $logf
else
	echo $ver > $lmd_verf
fi

if [ -z "$wget" ]; then
	header
	echo "could not find required binary wget, aborting."
	exit 1	
fi

if [ -z "$md5sum" ]; then
	header
	echo "could not find required binary md5sum, aborting."
	exit 1	
fi

if [ -z "$od" ]; then
	header
	echo "could not find required binary od, aborting."
	exit 1	
fi

if [ -z "$find" ]; then
	header
	echo "could not find required binary find, aborting."
	exit 1	
fi

if [ -z "$perl" ]; then
	header
	echo "could not find required binary perl, aborting."
	exit 1	
fi

if [ -z "$EDITOR" ]; then
	defedit=`which nano 2> /dev/null`
	if [ -z "$defedit" ]; then
		EDITOR=vi
	else
		EDITOR=nano
	fi
fi

if [ "$hex_fifo_scan" == "1" ]; then
 mkfifo=`which mkfifo 2> /dev/null`
 if [ -f "$mkfifo" ] && [ ! -p "$hex_fifo" ]; then
	$mkfifo -m 666 $hex_fifo
 fi
fi

usage_short() {
cat <<EOF
signature set: $def_ver
usage maldet [-h|--help] [-l|--log] [-e|--report] [-p|--purge] [-c|--checkout]
[-b|--background] [-m|--monitor] [-k|--kill-monitor] [-a|--scan-all] [-r|--scan-recent]
[-q|--quarantine] [-s|--restore] [-n|--clean] [-u|--update] [-d|--update-ver]
EOF
}

usage_long() {
cat<<EOF
signature set: $def_ver
usage $0 [ OPTION ]
    -b, --background
      Execute operations in the background, ideal for large scans
      e.g: maldet -b -r /home/?/public_html 7

    -u, --update
       Update malware detection signatures from rfxn.com

    -d, --update-ver
       Update the installed version from rfxn.com

    -m, --monitor USERS|PATHS|FILE
       Run maldet with inotify kernel level file create/modify monitoring
       If USERS is specified, monitor user homedirs for UID's > 500
       If FILE is specified, paths will be extracted from file, line spaced
       If PATHS are specified, must be comma spaced list, NO WILDCARDS!
       e.g: maldet --monitor users
       e.g: maldet --monitor /root/monitor_paths
       e.g: maldet --monitor /home/mike,/home/ashton

    -k, --kill
       Terminate inotify monitoring service

    -r, --scan-recent PATH DAYS
       Scan files created/modified in the last X days (default: 7d, wildcard: ?)
       e.g: maldet -r /home/?/public_html 2

    -a, --scan-all PATH
       Scan all files in path (default: /home, wildcard: ?)
       e.g: maldet -a /home/?/public_html

    -c, --checkout FILE
       Upload suspected malware to rfxn.com for review & hashing into signatures

    -l, --log
       View maldet log file events

    -e, --report SCANID email
       View scan report of most recent scan or of a specific SCANID and optionally
       e-mail the report to a supplied e-mail address
       e.g: maldet --report
       e.g: maldet --report list
       e.g: maldet --report 050910-1534.21135
       e.g: maldet --report SCANID user@domain.com

    -s, --restore FILE|SCANID
       Restore file from quarantine queue to orginal path or restore all items from
       a specific SCANID
       e.g: maldet --restore /usr/local/maldetect/quarantine/config.php.23754
       e.g: maldet --restore 050910-1534.21135

    -q, --quarantine SCANID
       Quarantine all malware from report SCANID
       e.g: maldet --quarantine 050910-1534.21135

    -n, --clean SCANID
       Try to clean & restore malware hits from report SCANID
       e.g: maldet --clean 050910-1534.21135

    -U, --user USER
       Set execution under specified user, ideal for restoring from user quarantine or
       to view user reports.
       e.g: maldet --user nobody --report
       e.g: maldet --user nobody --restore 050910-1534.21135

    -co, --config-option VAR1=VALUE,VAR2=VALUE,VAR3=VALUE
       Set or redefine the value of conf.maldet config options
       e.g: maldet --config-option email_addr=you@domain.com,quar_hits=1

    -p, --purge
       Clear logs, quarantine queue, session and temporary data.
EOF
}

eout() {
arg=$1
stdout=$2
appn=maldet
	if [ ! -f "$logf" ]; then
		touch $logf
	fi
	logf_size=`$wc -l $logf | awk '{print$1}'`
	if [ "$logf_size" -ge "20000" ]; then
                trim=1000
                printf "%s\n" "$trim,${logf_size}d" w | ed -s $logf
	fi
        if [ ! "$arg" == "" ]; then
                echo "$(date +"%b %d %H:%M:%S") $(hostname -s) $appn($$): $arg" >> $logf
		if [ ! -z "$stdout" ]; then
			echo "$appn($$): $arg"
		fi
        fi
}

trap_exit() {
	if [ "$svc" == "m" ]; then
		echo
		eout "{glob} monitor interrupt by user, sending kill." 1
		monitor_kill
		exit
	elif [ "$svc" == "a" ] || [ "$sv" == "r" ]; then
		echo
		gen_report
		if [ ! "$tot_hit" == "0" ]; then
                    if [ "$suppress_cleanhit" == "1" ] && [ ! "$tot_hit" == "$cl_hit" ]; then
                       alert file $nsess
                    elif [ "$suppress_cleanhit" == "0" ]; then
                       alert file $nsess
                    fi
		fi
		mv $scan_session $nsess_hits
		rm -f $find_results $scan_session
		eout "{glob} scan interrupt by user, aborting scan..." 1
		eout "{scan} scan report saved, to view run: maldet --report $datestamp.$$" 1
		if [ "$quar_hits" == "0" ] && [ ! "$tot_hit" == "0" ]; then
		  eout "{glob} quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q $datestamp.$$" 1
		fi
		exit
	fi
}

check_opensslheartbleed() {
rpm=`which rpm 2> /dev/null`
if [ "$rpm" ]; then
	check_openssl=`$rpm -q openssl | grep 1.0.1 | head -n1`
	goodrev="5.7"
	if [ "$check_openssl" ]; then
	        rev=`echo $check_openssl| grep 1.0.1 | sed -e 's/el6_/ /' -e 's/.x86_64/ /' -e 's/.i686/ /' | awk '{print$2}'`
	        if [ "$(echo $rev | tr -d '.')" -lt "$(echo $goodrev | tr -d '.')" ]; then
	                alert_heartbleed=1
	        fi
	fi
fi

if [ "$alert_heartbleed" ]; then
	eout "{heartbleed} !! ATTENTION !! OpenSSL heartbleed vulnerability detected in $check_openssl package, run 'yum update -y openssl' and restart server immediately!" 1
	rver="$ver.heartbleed"
	if [ "$email_addr" ]; then
	emlf="$tmpdir/.hb.eml.$$"
	cat >> $emlf <<EOF
Important security notice follows;
	The Linux Malware Detect installation running on $HOSTNAME has detected a critical vulnerability with the currently installed version of OpenSSL, $check_openssl. This version is known to contain the Heartbleed (http://heartbleed.com/) vulnerability which can disclose sensitive information from SSL communications this host may be conducting, including but not limited to username/password credentials, transaction information along with other customer identifiable information. You are advised to take immediate corrective action to ensure the security of SSL communication with this host.

The OpenSSL packages can be updated using the 'yum' package manager as follows:

# yum update -y openssl

It is vitally important to restart the server to ensure all services that may be using the OpenSSL libraries are properly reloaded. Optionally, you can selectively restart services that conduct SSL communication, these services include but are not limited to:
Apache
cPanel/WHM
Dovecot
Courier
Exim
Pure-FTPd
ProFTPd

If you are currently running an automation platform such as cPanel/WHM, DirectAdmin or Plesk, you may need to manually update the software as it may build OpenSSL libraries statically into the software distribution making it vulnerable outside of system updates. This
is the case with cPanel/WHM which requires an 'upcp' operation in the event daily updates are not enabled. You can manually force an update of cPanel/WHM as follows:

# /scripts/upcp --force

The failure to restart the server or all associated OpenSSL services can leave the vulnerability open to exploitation by remote attackers.

Vulnerability Test:
http://filippo.io/Heartbleed/#$HOSTNAME
http://filippo.io/Heartbleed/#$HOSTNAME:993
http://filippo.io/Heartbleed/#$HOSTNAME:995
http://filippo.io/Heartbleed/#$HOSTNAME:2078
http://filippo.io/Heartbleed/#$HOSTNAME:2083
http://filippo.io/Heartbleed/#$HOSTNAME:2087
http://filippo.io/Heartbleed/#$HOSTNAME:2096

Further Reading:
http://heartbleed.com/
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160
https://www.openssl.org/news/secadv_20140407.txt
https://access.redhat.com/security/cve/CVE-2014-0160

--
Linux Malware Detect v$ver < proj@rfxn.com >
EOF

cat $emlf | mail -s "[!!] OpenSSL Heartbleed vulnerability on $HOSTNAME [!!]" $email_addr
rm -f $emlf
	fi
fi
}


check_clibkeyutils() {
files="/lib64/libkeyutils.so.1.9 /lib64/libkeyutils-1.2.so.2 /lib/libkeyutils.so.1.9 /lib/libkeyutils-1.2.so.2"
for lib in $files; do
        if [ -f "$lib" ]; then
		alert_clib=1
		if [ -z "$clib" ]; then
			clib="$lib"
		else
		        clib="$lib $clib"
		fi
        fi
done


if [ "$alert_clib" ]; then
	eout "{clib} !! ATTENTION !! root compromised libkeyutils libraries found at $clib" 1
	rver="$ver.clib"
fi

if [ "$alert_clib" ] && [ "$email_addr" ]; then
emlf=$tmpdir/.clib.eml.$$
cat >> $emlf <<EOF
Important message follows;
	One or more compromised libraries for libkeyutils have been found on $HOSTNAME.  This is an indication that this system has been root compromised and requires immediate investigation by a qualified system administrator. Please do not disregard this notice, IMMEDIATE ACTION IS REQUIRED, your server contains a trojaned system library that is known to be used for password harvesting and key logging of SSH authentication passwords.

EOF
ls -al $clib >> $emlf

cat >> $emlf <<EOF

Recommendation:
The best practice is to clean format and restore from known good backups. This compromise is potentially propagating through the use of login credentials supplied to 3rd party vendors. As such, it is imperative that all passwords used by this system, including root and administrative accounts, are not reused. Further, the SSH service should ideally be restricted to key-based authentication and firewall restricted to known trusted sources. For assistance with such tasks, please consult a qualified system administrator or your services provider.

Further Reading:
https://isc.sans.edu/diary/SSHD+rootkit+in+the+wild/15229
https://forums.cpanel.net/f185/sshd-rootkit-323962.html

--
Linux Malware Detect v$ver < proj@rfxn.com >
EOF

cat $emlf | mail -s "[!!] ROOT COMPROMISE maldet alert from $(hostname) [!!]" $email_addr
rm -f $emlf

fi
}

clean() {
file="$1"
hitname="$2"
v="$3"
sh_hitname=`echo $hitname | sed -e 's/{HEX}//' -e 's/{MD5}//' | tr '.' ' ' | awk '{print$1"."$2"."$3}'`
if [ -d "$cldir" ] && [ "$quar_clean" == "1" ] && [ -f "$file" ]; then
	if [ -f "$cldir/$sh_hitname" ] && [ -f "$file.info" ]; then
	        file_path=`cat $file.info | awk '{print$4}'`
		eout "{clean} restoring $file for cleaning attempt" $v
		restore "$file" >> /dev/null 2>&1
		eout "{clean} trying to clean $file_path with $sh_hitname rule" $v
		$(cat $cldir/$sh_hitname) $file_path
		eout "{clean} rescanning $file_path for malware hits" $v
		cleanst="1"
		scan_stage1 "$file_path" >> /dev/null 2>&1
		unset cleanst
		if [ -f "$file_path" ]; then
			echo "$file_path" >> $sessdir/clean.$$
			echo "$file_path" >> $tmpdir/.clean.alltime
			eout "{clean} clean successful on $file_path" $v
		else
			eout "{clean} clean failed on $file_path and returned to quarantine" $v
		fi
	elif [ -f "$cldir/$sh_hitname" ] && [ -f "$file" ]; then
	        file_path="$file"
		eout "{clean} trying to clean $file with $sh_hitname rule" $v
		$(cat $cldir/$sh_hitname) $file_path
		eout "{clean} scanning $file for malware hits" $v
		cleanst="1"
		unset clean_failed
		scan_stage1 "$file_path" 1 >> /dev/null 2>&1
		unset cleanst
		if [ "$clean_failed" == "1" ]; then
			eout "{clean} clean failed on $file" $v
		else
			echo "$file" >> $sessdir/clean.$$
			echo "$file_path" >> $tmpdir/.clean.alltime
			eout "{clean} clean successful on $file" $v
		fi
	fi
else
	eout "file path error on $file, aborting." $v
	exit
fi
}

restore() {
 file="$1"
 fname=`basename $file`
 if [ -f "$quardir/$file" ] && [ -f "$quardir/$file.info" ]; then
	file_owner=`cat $quardir/$file.info | awk '{print$1}'`
	file_group=`cat $quardir/$file.info | awk '{print$2}'`
	file_mode=`cat $quardir/$file.info | awk '{print$3}'`
	file_path=`cat $quardir/$file.info | awk '{print$4}'`
	chown $file_owner.$file_group "$quardir/$file" >> /dev/null 2>&1
	chmod $file_mode "$quardir/$file" >> /dev/null 2>&1
	mv -f "$quardir/$file" "$file_path"
	eout "{restore} quarantined file $file restored to $file_path" 1
 elif [ -f "$file" ] && [ -f "$file.info" ]; then
        file_owner=`cat $file.info | awk '{print$1}'`
        file_group=`cat $file.info | awk '{print$2}'`
        file_mode=`cat $file.info | awk '{print$3}'`
        file_path=`cat $file.info | awk '{print$4}'`
        chown $file_owner.$file_group "$file" >> /dev/null 2>&1
        chmod $file_mode "$file" >> /dev/null 2>&1
        mv -f "$file" "$file_path"
        eout "{restore} quarantined file $file restored to $file_path" 1
 else
	eout "{restore} invalid file or could not be found" 1
 fi
}

restore_hitlist() {
hitlist="$sessdir/session.hits.$1"
if [ -f "$hitlist" ]; then
 val_aquar=`tail -n1 $hitlist | awk '{print$5}'`
 val_mquar=`tail -n1 $hitlist | awk '{print$3}'`
 if [ "$val_aquar" ]; then
  for file in `cat $hitlist | awk '{print$5}'`; do
        if [ -f "$file" ]; then
                restore $file
        fi
  done
 elif [ "$val_mquar" ]; then
  for file in `cat $hitlist | awk '{print$3}'`; do
        quar_file=`cat $logf | grep -w "$file" | tail -n1 | awk '{print$12}' | tr -d "'"`
        restore $quar_file
  done
 else
	eout "{restore} could not find a valid hit list to restore." 1
 fi
fi
}

clean_hitlist() {
hitlist="$sessdir/session.hits.$1"
if [ -f "$hitlist" ]; then
 for file in `cat $hitlist | awk '{print$3}'`; do
	if [ -f "$file" ]; then
          hitname=`cat $hitlist | grep $file | awk '{print$1}'`
	  clean "$file" "$hitname" "$1"
	else
          hitname=`cat $hitlist | grep $file | awk '{print$1}'`
	  quarfile=`cat $hitlist | grep $file | awk '{print$5}'`
	  clean "$quarfile" "$hitname" "$1"
	fi
 done
else
	eout "{clean} invalid SCANID, aborting." 1
	exit
fi
}

quar_hitlist() {
hitlist="$sessdir/session.hits.$1"
if [ -f "$hitlist" ]; then
 for file in `cat $hitlist | awk '{print$3}'`; do
  if [ -f "$file" ]; then
	file_owner=`stat -c "%U" "$file"`
	file_group=`stat -c "%G" "$file"`
	file_mode=`stat -c "%a" "$file"`
	if [ "$pub" == "1" ]; then
		chattr -ia "$file" >> /dev/null 2>&1
		chmod 400 "$file"
	else
		chattr -ia "$file"
		chmod 000 "$file"
	fi
	file_name=`basename "$file"`
	file_namewc=`echo $file_name | $wc -m`
	rnd="$RANDOM"
	mv "$file" "$quardir/$file_name.$rnd"
	echo "$file_owner $file_group $file_mode $file" > $quardir/$file_name.$rnd.info
	eout "{quar} malware quarantined from '$file' to '$quardir/$file_name.$rnd'" 1
	echo "$file => $quardir/$file_name.$rnd" >> $tmpdir/.quar.alltime    
	if [ "$quar_susp" == "1" ]; then
		quar_susp "$file"
	fi
	if [ "$quar_clean" == "1" ] && [ ! "$cleanst" == "1" ]; then
		unset cleanst
		hitname=`cat $hitlist | grep $file | awk '{print$1}'`
		clean "$quardir/$file_name.$rnd" "$hitname" 1
	fi

  fi
 done
else
	echo "{quar} invalid quarantine hit list, aborting."
	exit
fi
}

view_report() {
 rid="$1"
 if [ "$rid" == "list" ]; then
	eout "Available Reports:"
	for file in `ls /usr/local/maldetect/sess/session.[0-9]* 2> /dev/null`; do
		SCANID=`cat $file | grep "SCAN ID"`
		TIME=`cat $file | grep "TIME"`
		if [ ! -z "$SCANID" ] && [ ! -z "$TIME" ]; then
			echo "$TIME | $SCANID"
		fi
	done
	exit
 fi
 if [ -f "$sessdir/session.$rid" ] && [ ! -z "$(echo $2 | grep '\@')" ]; then
	cat $sessdir/session.$rid | mail -s "$email_subj" $2
	eout "{report} report ID $rid sent to $2" 1
	exit
 fi
 if [ "$rid" == "" ] && [ -f "$sessdir/session.last" ]; then
	rid=`cat $sessdir/session.last`
	$EDITOR $sessdir/session.$rid
 elif [ -f "$sessdir/session.$rid" ]; then
	$EDITOR $sessdir/session.$rid
 else
	echo "{report} no report found, aborting."
	exit
 fi
}

view() {
	echo "Viewing last 50 lines from $logf:"
	tail -n 50 $logf
}

purge() {
	:> $logf
	rm -f $tmpdir/* $quardir/* $sessdir/*
	eout "{glob} logs and quarantine data cleared by user request (-p)" 1
}


quar_susp() {
file="$1"
user=`stat -c "%U" "$file"`
user_id=`id -u $user`
if [ ! "$user" == "" ] && [ "$user_id" -ge "$quar_susp_minuid" ]; then
	if [ -f "/scripts/suspendacct" ]; then
		if [ ! -f "/var/cpanel/suspended/$user" ]; then
		 /scripts/suspendacct $user "maldet --report $datestamp.$$" >> /dev/null 2>&1
		 eout "{quar} account $user cpanel suspended" 1
		 echo "$user" >> $sessdir/suspend.users.$$
                 echo "$user" >> $tmpdir/.susp.alltime
		fi
	else
		if [ "$(grep $user /etc/passwd | cut -d':' -f7 | grep /bin/false)" == "" ]; then
		 /usr/sbin/usermod -s /bin/false $user >> /dev/null 2>&1
		 eout "{quar} account $user suspended; set 'usermod -s /bin/false'"
                 echo "$user" >> $sessdir/suspend.users.$$
                 echo "$user" >> $tmpdir/.susp.alltime
		fi
	fi	
fi
}

quar() {
file="$1"
hitname="$2"
if [ -f "$file" ] && [ -d "$quardir" ]; then
 if [ "$quar_hits" == "1" ]; then
  file_owner=`stat -c "%U" "$file"`
  file_group=`stat -c "%G" "$file"`
  file_mode=`stat -c "%a" "$file"`
  file_name=`basename "$file"`
  file_namewc=`echo $file_name | $wc -m`
  rnd="$RANDOM"
  if [ "$quar_susp" == "1" ]; then
   quar_susp "$file"
  fi
  chattr -ia "$file"
  mv "$file" "$quardir/$file_name.$rnd"
  if [ "$pub" == "1" ]; then
   chmod 400 "$quardir/$file_name.$rnd"
  else
   chmod 000 "$quardir/$file_name.$rnd"
   chown root.root "$quardir/$file_name.$rnd"
  fi
  echo "$file_owner $file_group $file_mode $file" > $quardir/$file_name.$rnd.info
  eout "{quar} malware quarantined from '$file' to '$quardir/$file_name.$rnd'"
  echo "$file => $quardir/$file_name.$rnd" >> $tmpdir/.quar.alltime

  if [ ! -z "$scan_session" ]; then
   echo "$hitname : $file => $quardir/$file_name.$rnd" >> $scan_session
  fi

  if [ "$quar_clean" == "1" ] && [ ! "$cleanst" == "1" ]; then
   unset cleanst
   clean "$quardir/$file_name.$rnd" "$hitname"
  fi

 else
  if [ ! -z "$scan_session" ]; then
   echo "$hitname : $file" >> $scan_session
  fi
 fi
else
 eout "{quar} fatal error handling '$file'"
fi
}

scan() {
 spath="$1"
 spath=`echo $spath | tr '?' '*'`
 days=$2


 if [ ! -f "$find" ]; then
	eout "{scan} could not locate find command" 1
	exit
 fi
 if [ ! -f "$ignore_paths" ]; then
	touch $ignore_paths
	chmod 640 $ignore_paths
 elif [ ! -f "$ignore_sigs" ]; then
	touch $ignore_sigs
	chmod 640 $ignore_sigs
 fi

 if [ ! "$days" == "all" ]; then 
 val=`echo $days | grep "[[:alpha:]]"`
  if [ ! -z "$val" ]; then
	eout "{scan} days value must be numeric value in the range of 1 - 60, reverting to default (7)." 1
	days=7
  elif [ "$days" -gt "60" ]; then
	eout "{scan} days value must be numeric value in the range of 1 - 60, reverting to default (7)." 1
	days=7
  fi
 fi

 if [ ! -d $(echo $spath | tr '*' ' ' | awk '{print$1}') ] && [ ! -f $(echo $spath | tr '*' ' ' | awk '{print$1}') ]; then
	eout "{scan} invalid path $spath" 1
	exit
 fi

 scan_session=$tmpdir/.sess.$$
 find_results=$tmpdir/.find.$$
 touch $find_results
 touch $scan_session

 sigignore
 hex_sigs=`$wc -l $dat_hexstring | awk '{print$1}'`
 md5_sigs=`$wc -l $dat_md5hash | awk '{print$1}'`
 tot_sigs=$[md5_sigs+hex_sigs]
 if [ -z "$setmodsec" ]; then
	 eout "{scan} signatures loaded: $tot_sigs ($md5_sigs MD5 / $hex_sigs HEX)" 1
 fi

 if [ -f "$ignore_file_ext" ]; then
	if [ ! "$(cat $ignore_file_ext)" == "" ]; then
		for i in `cat $ignore_file_ext`; do
			if [ "$ignore_fext" == "" ]; then
				ignore_fext="! -iname *$i"
			else
				ignore_fext="$ignore_fext ! -iname *$i"
			fi
		done
	fi
 fi
 tmpdir_paths="/dev/shm /tmp /var/tmp"
 if [ "$days" == "all" ]; then
  if [ -z "$setmodsec" ]; then
	  eout "{scan} building file list for $spath, this might take awhile..." 1
  fi
  $find $spath $tmpdir_paths -maxdepth $maxdepth -type f -size +${minfilesize}c -size -$maxfilesize $ignore_fext | grep -vf $ignore_paths > $find_results
 else
  if [ -z "$setmodsec" ]; then
	  eout "{scan} building file list for $spath of new/modified files from last $days days, this might take awhile..." 1
  fi
  $find $spath $tmpdir_paths -maxdepth $maxdepth -type f -mtime -$days -size +${minfilesize}c -size -$maxfilesize $ignore_fext | grep -vf $ignore_paths > $find_results
 fi
 if [ ! -f "$find_results" ] || [ -z "$(cat $find_results)" ]; then
  if [ -z "$setmodsec" ]; then
	if [ "$days" == "all" ]; then
	 eout "{scan} scan returned zero results, please provide a new path." 1
	 exit
	else
	 eout "{scan} scan returned zero results, please increase days range or provide a new path." 1
	 exit
	fi
  fi
 fi

 res_col="1"
 move_to_col="echo -en \\033[${res_col}G"
 tot_files=`$wc -l $find_results | awk '{print$1}'`
 if [ -z "$setmodsec" ]; then
	 eout "{scan} file list completed, found $tot_files files..." 1
 fi
 touch $sessdir/clean.$$
 if [ ! -f "$scan_session" ]; then
      touch $scan_session
 fi

 if [ ! -z "$setmodsec" ]; then
	eout "{scan.modsec} scan of $spath in progress (id: $datestamp.$$)"
 fi
 cnt=0
 clamscan=`which clamscan 2> /dev/null`
 if [ -f "$clamscan" ] && [ "$clamav_scan" == "1" ]; then
	if [ -z "$setmodsec" ]; then
		eout "{scan} found ClamAV clamscan binary, using as scanner engine..." 1
	fi
	if [ "$string_length_scan" == "1" ]; then
		if [ -z "$setmodsec" ]; then
			eout "{scan} preprocessing file list for string length hits..." 1
			scan_strlen list "$find_results" >> /dev/null 2>&1
		fi
	fi
	if [ -d "/var/lib/clamav" ]; then
		clamav_db="-d /var/lib/clamav"
	elif [ -d "/var/clamav" ]; then
		clamav_db="-d /var/clamav"
	fi
        if [ -z "$setmodsec" ]; then
		eout "{scan} scan of $spath ($tot_files files) in progress..." 1
	fi
	for hit in `$clamscan -d $inspath/sigs/rfxn.ndb -d $inspath/sigs/rfxn.hdb $clamav_db -r --infected --no-summary -f $find_results 2> /dev/null | tr -d ':' | sed 's/.UNOFFICIAL//' | awk '{print$2":"$1}'`; do
		file=`echo $hit | tr ':' ' ' | awk '{print$2}'`
		signame=`echo $hit | tr ':' ' ' | awk '{print$1}'`
		clamsig=`echo $signame | grep -iE "HEX|MD5"`
		if [ -z "$clamsig" ]; then
			signame="{CAV}$signame"
		fi
		quar "$file" "$signame"
		if [ ! "$set_background" == "1" ]; then
        	  tot_hit=`$wc -l $scan_session | awk '{print$1}'`
	          cl_hit=`$wc -l $sessdir/clean.$$ | awk '{print$1}'`
		  if [ -z "$setmodsec" ]; then
	        	  $move_to_col && echo -n "maldet($$): {scan} processing scan results for hits: $tot_hit hits $cl_hit cleaned"
		  fi
		  cnt=$tot_files
		fi
	done
 else
  for i in `cat $find_results | tr ' ' '%'`; do
   i=`echo $i | tr '%' ' '`
   cnt=$[cnt+1]
   if [ ! -f "$scan_session" ]; then
	touch $scan_session
   fi

  if [ -z "$setmodsec" ]; then
   if [ ! "$set_background" == "1" ]; then
	  tot_hit=`$wc -l $scan_session | awk '{print$1}'`
	  cl_hit=`$wc -l $sessdir/clean.$$ | awk '{print$1}'`
	  $move_to_col && echo -n "maldet($$): {scan} $cnt/$tot_files files scanned: $tot_hit hits $cl_hit cleaned"
   fi
  fi
   if [ -f "$i" ]; then
    scan_stage1 "$i" >> /dev/null 2>&1
   fi
  done
 fi

  if [ -z "$setmodsec" ]; then
   echo
  fi
  tot_hit=`$wc -l $scan_session | awk '{print$1}'`
  cl_hit=`$wc -l $sessdir/clean.$$ | awk '{print$1}'`
  gen_report

  if [ ! -z "$setmodsec" ]; then
   if [ ! "$tot_hit" == "0" ]; then
	echo "0 maldet: $hitname $spath"
        eout "{scan.modsec} results returned FAIL hit found on $spath (id: $datestamp.$$)"
   else
	echo "1 maldet: OK"
        eout "{scan.modsec} results returned OK on $spath (id: $datestamp.$$)"
   fi
  else
   eout "{scan} scan completed on $spath: files $tot_files, malware hits $tot_hit, cleaned hits $cl_hit" 1
   eout "{scan} scan report saved, to view run: maldet --report $datestamp.$$" 1
   if [ "$quar_hits" == "0" ] && [ ! "$tot_hit" == "0" ]; then
    eout "{scan} quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q $datestamp.$$" 1
   fi
  fi

  if [ ! "$tot_hit" == "0" ]; then
        if [ "$suppress_cleanhit" == "1" ] && [ ! "$tot_hit" == "$cl_hit" ]; then
         alert file $nsess
        elif [ "$suppress_cleanhit" == "0" ]; then
         alert file $nsess
        fi
  fi
  mv $scan_session $nsess_hits
  rm -f $find_results $scan_session
}

scan_strlen() {
type=$1
file=$2
if [ "$string_length_scan" == "1" ] && [ "$type" == "file" ]; then
	flen=`$wc -L $file 2> /dev/null | awk '{print$1}'`
	if [ "$flen" -ge "$string_length" ]; then
		eout "{strlen} malware string length hit on $file"
		quar "$file" "{SA}stat.strlength"
	fi
elif [ "$string_length_scan" == "1" ] && [ "$type" == "list" ]; then
        list=$tmpdir/.strlen.flist.$$
        cp $file $list
        sed -i "s/'/\\\\'/g" $list
        cat $list | xargs wc -L 2> /dev/null | grep -vw total >> $list.strlen
        awk "{if (\$1>=$string_length) print\$2}" $list.strlen >> $list.hits
        for i in `cat $list.hits`; do
                if [ -f "$i" ]; then
                        eout "{strlen} malware string length hit on $i"
                        quar "$i" "{SA}stat.strlength"
                fi
        done
        rm -f $list.strlen $list.hits $list
fi
}

scan_stage1() {
 file="$1"
 clchk="$2"
 hash=`$md5sum "$file" | awk '{print$1}'`
 if  [ ! -z "$hash" ]; then
  val_hash=`grep -m1 $hash $dat_md5hash`
  if [ ! -z "$val_hash" ]; then
	md5_hit=$hash
	md5_hitname=`echo $val_hash | cut -d':' -f2`
	eout "{md5hash} malware hit $md5_hitname on $file"
	if [ "$clchk" == "1" ]; then
		clean_failed=1
	else
		quar "$file" "$md5_hitname"
	fi
	unset val_hash md5_hit md5_hitname
  else
	if [ -f "$file" ]; then
		scan_stage2 "$file" $clchk >> /dev/null 2>&1
	fi
	if [ -f "$file" ]; then
		scan_strlen file "$file" >> /dev/null 2>&1
	fi
  fi
 else
	eout "{scan} error could not read or hash $file, do we have permission?"
 fi
}

scan_stage2() {
 file="$1"
 clchk="$2"
 ftype=`file -b "$file" | egrep "core file|compressed data|archive data"`
 if [ -z "$ftype" ]; then
	if [ -p "$hex_fifo" ] && [ "$hex_fifo_scan" == "1" ]; then
                if [ "$OSTYPE" == "FreeBSD" ]; then
                        $od -v -N$hex_fifo_depth -tx1 "$file" | cut -c12-256 | tr -d ' \n' > $hex_fifo 2>&1 &
                else
                        $od -v -w64 -N$hex_fifo_depth -tx1 "$file" | cut -c9-256 | tr -d '\n ' > $hex_fifo 2>&1 &
                fi
	        val_hex=`$perl $hexmfifo_pl`
	else
                if [ "$OSTYPE" == "FreeBSD" ]; then
                        val_hex=`$perl $hexm_pl $($od -v -N$hexdepth -tx1 "$file" | cut -c12-256 | tr -d ' \n')`
                else
                        val_hex=`$perl $hexm_pl $($od -v -w$hexdepth -N$hexdepth -tx1 "$file" | tr -d '\n ')`
                fi
	fi
  if [ ! -z "$val_hex" ]; then
	hex_hit=`echo $val_hex | awk '{print$1}'`
	hex_hitname=`echo $val_hex | awk '{print$2}'`
	eout "{hexstring} malware hit $hex_hitname on $file"
        if [ "$clchk" == "1" ]; then
                clean_failed=1
	else
		quar "$file" "$hex_hitname"
	fi
	unset val_hex hex_hit hex_hitname
  fi
 fi
}

gen_report() {
if [ -f "$scan_session" ]; then
 tot_hit=`$wc -l $scan_session | awk '{print$1}'`
 nsess_hits=$sessdir/session.hits.$datestamp.$$
 echo "$datestamp.$$" > $sessdir/session.last
 nsess=$sessdir/session.$datestamp.$$
 cat >> $nsess <<EOF
malware detect scan report for $(hostname):
SCAN ID: $datestamp.$$
TIME: $(date +"%b %e %H:%M:%S %z")
PATH: $spath
EOF
if [ ! "$days" == "all" ]; then
cat >> $nsess <<EOF
RANGE: $days days
EOF
fi
cat >> $nsess <<EOF
TOTAL FILES: $tot_files
TOTAL HITS: $tot_hit
TOTAL CLEANED: $cl_hit

EOF
if [ "$quar_hits" == "0" ] && [ ! "$tot_hit" == "0" ]; then
cat >> $nsess <<EOF
NOTE: quarantine is disabled! set quar_hits=1 in conf.maldet or to quarantine results run: maldet -q $datestamp.$$
EOF
fi
 if [ "$quar_clean" -ne "0" ]; then
  if [ -f "$sessdir/clean.$$" ] && [ ! -z "$(cat $sessdir/clean.$$)" ]; then
cat >> $nsess <<EOF
CLEANED & RESTORED FILES:
$(cat $sessdir/clean.$$)

EOF
  fi
 fi
 if [ "$quar_susp" -ne "0" ]; then
   if [ -f "$sessdir/suspend.users.$$" ] && [ ! -z "$(cat $sessdir/suspend.users.$$)" ]; then
cat >> $nsess <<EOF
SUSPENDED ACCOUNTS:
$(cat $sessdir/suspend.users.$$)

EOF
   fi
 fi 
 if [ "$tot_hit" -ne "0" ]; then
  echo "FILE HIT LIST:" >> $nsess
  cat $scan_session >> $nsess
 fi
cat >> $nsess <<EOF
===============================================
Linux Malware Detect v$ver < proj@rfxn.com >
EOF
fi
}

trim_log() {
log=$1
logtrim=$2
if [ -f "$log" ]; then
  log_size=`$wc -l $log | awk '{print$1}'`
  if [ "$log_size" -gt "$logtrim" ]; then
    trim=$[logtrim/10]
    printf "%s\n" "$trim,${log_size}d" w | ed -s $log
  fi
fi
}

alert() {
type=$1
file="$2"

if [ "$email_alert" == "1" ]; then
 if [ "$type" == "file" ] && [ -f "$file" ]; then

  cat $file | $mail -s "$email_subj" $email_addr
  if [ ! "$(whoami)" == "root" ] && [ -z "$(echo $2 | grep '\@')" ]; then
	if [ -z "$setmodsec" ]; then
	  eout "{alert} sent scan report to config default $email_addr" 1
	  eout "{alert} send scan report to an alternate address with: maldet --report $datestamp.$$ you@domain.com" 1
	else
	  eout "{alert} sent scan report to config default $email_addr"
	fi
  else
	  eout "{alert} sent scan report to $email_addr" 1
  fi
 elif [ "$type" == "daily" ]; then
  rm -f $tmpdir/.daily.alert.hits $tmpdir/.daily.clean.hits $tmpdir/.daily.monitor.alert $tmpdir/.daily.susp.hits

  scan_session=`cat $sessdir/session.monitor.current`
  $tlog $scan_session daily.alert > $tmpdir/.daily.alert.hits
  $tlog $tmpdir/.clean.alltime daily.clean.alert > $tmpdir/.daily.clean.hits
  $tlog $tmpdir/.monitor.scanned.alltime daily.monitor.alert > $tmpdir/.daily.monitor.alert
  $tlog $tmpdir/.susp.alltime daily.susp.alert > $tmpdir/.daily.susp.hits

  tot_hits=`$wc -l $tmpdir/.daily.alert.hits | awk '{print$1}'`
  tot_cl=`$wc -l $tmpdir/.daily.clean.hits | awk '{print$1}'`
  tot_files=`$wc -l $tmpdir/.daily.monitor.alert | awk '{print$1}'`
  tot_susp=`$wc -l $tmpdir/.daily.susp.hits | awk '{print$1}'`

  trim_log $tmpdir/.monitor.scanned.alltime 5000
  trim_log $tmpdir/.clean.alltime 5000
  trim_log $tmpdir/.quar.alltime 5000
  trim_log $tmpdir/.susp.alltime 5000

  $tlog $sessdir/session.hits.$datestamp.$$ daily.alert >> /dev/null 2>&1
  $tlog $tmpdir/.clean.alltime daily.clean.alert >> /dev/null 2>&1
  $tlog $tmpdir/.monitor.scanned.alltime daily.monitor.alert >> /dev/null 2>&1
  $tlog $tmpdir/.susp.alltime daily.susp.alert >> /dev/null 2>&1

  if [ ! -z "$(cat $tmpdir/.daily.alert.hits)" ]; then
   tmpf=$tmpdir/.alert
   rm -f $tmpf
if [ "$tot_hits" -gt "$tot_files" ]; then
	tot_files=$tot_hits
fi
cat >> $tmpf <<EOF
malware detect $type monitor report for $(hostname):
SCAN ID: $datestamp.$$
TIME: $(date +"%b %e %H:%M:%S %z")
TOTAL FILES: $tot_files
TOTAL HITS: $tot_hits
TOTAL CLEANED: $tot_cl

EOF
if [ -f "$tmpdir/.daily.clean.hits" ] && [ ! "$tot_cl" == "0" ]; then
cat >> $tmpf <<EOF
CLEANED & RESTORED FILES:
$(cat $tmpdir/.daily.clean.hits)

EOF
fi
if [ -f "$tmpdir/.daily.susp.hits" ] && [ ! "$tot_susp" == "0" ]; then
cat >> $tmpf <<EOF
SUSPENDED ACCOUNTS:
$(cat $tmpdir/.daily.susp.hits)

EOF
fi

cat >> $tmpf <<EOF
FILE HIT LIST:
$(cat $tmpdir/.daily.alert.hits)
===============================================
Linux Malware Detect v$ver < proj@rfxn.com >
EOF
   cp $tmpf $sessdir/session.$datestamp.$$
   echo "$datestamp.$$" > $sessdir/session.last
   email_subj="$email_subj :: $type"
   cat $tmpf | $mail -s "$email_subj" $email_addr
   eout "{alert} sent $type alert to $email_addr"
   rm -f $tmpf $tmpdir/.daily.alert.hits $tmpdir/.daily.clean.hits $tmpdir/.daily.monitor.alert $tmpdir/.daily.susp.hits
  fi
 elif [ "$type" == "weekly" ]; then
  rm -f $tmpdir/.weekly.alert.hits $tmpdir/.weekly.clean.hits $tmpdir/.weekly.monitor.alert $tmpdir/.daily.susp.hits


  scan_session=`cat $sessdir/session.monitor.current`
  $tlog $scan_session weekly.alert > $tmpdir/.weekly.alert.hits
  $tlog $tmpdir/.clean.alltime weekly.clean.alert > $tmpdir/.weekly.clean.hits
  $tlog $tmpdir/.monitor.scanned.alltime weekly.monitor.alert > $tmpdir/.weekly.monitor.alert
  $tlog $tmpdir/.susp.alltime weekly.susp.alert > $tmpdir/.weekly.susp.hits

  tot_hits=`$wc -l $tmpdir/.weekly.alert.hits | awk '{print$1}'`
  tot_cl=`$wc -l $tmpdir/.weekly.clean.hits | awk '{print$1}'`
  tot_files=`$wc -l $tmpdir/.weekly.monitor.alert | awk '{print$1}'`
  tot_susp=`$wc -l $tmpdir/.weekly.susp.hits | awk '{print$1}'`

  trim_log $tmpdir/.monitor.scanned.alltime 5000
  trim_log $tmpdir/.clean.alltime 5000
  trim_log $tmpdir/.quar.alltime 5000
  trim_log $tmpdir/.susp.alltime 5000

  $tlog $sessdir/session.hits.$datestamp.$$ weekly.alert >> /dev/null 2>&1
  $tlog $tmpdir/.clean.alltime weekly.clean.alert >> /dev/null 2>&1
  $tlog $tmpdir/.monitor.scanned.alltime weekly.monitor.alert >> /dev/null 2>&1
  $tlog $tmpdir/.susp.alltime weekly.susp.alert >> /dev/null 2>&1

  if [ ! -z "$(cat $tmpdir/.weekly.alert.hits)" ]; then
   tmpf=$tmpdir/.alert
   rm -f $tmpf
if [ "$tot_hits" -gt "$tot_files" ]; then
	tot_files=$tot_hits
fi
cat >> $tmpf <<EOF
malware detect $type monitor report for $(hostname):
SCAN ID: $datestamp.$$
TIME: $(date +"%b %e %H:%M:%S %z")
TOTAL FILES: $tot_files
TOTAL HITS: $tot_hits
TOTAL CLEANED: $tot_cl

EOF
if [ -f "$tmpdir/.weekly.clean.hits" ] && [ ! "$tot_cl" == "0" ]; then
cat >> $tmpf <<EOF
CLEANED & RESTORED FILES:
$(cat $tmpdir/.weekly.clean.hits)

EOF
fi
if [ -f "$tmpdir/.weekly.susp.hits" ] && [ ! "$tot_susp" == "0" ]; then
cat >> $tmpf <<EOF
SUSPENDED ACCOUNTS:
$(cat $tmpdir/.weekly.susp.hits)

EOF
fi

cat >> $tmpf <<EOF
FILE HIT LIST:
$(cat $tmpdir/.weekly.alert.hits)
===============================================
Linux Malware Detect v$ver < proj@rfxn.com >
EOF
   echo "$datestamp.$$" > $sessdir/session.last
   cp $tmpf $sessdir/session.$datestamp.$$
   email_subj="$email_subj :: $type"
   cat $tmpf | $mail -s "$email_subj" $email_addr
   eout "{alert} sent $type alert to $email_addr"
   rm -f $tmpf $tmpdir/.weekly.alert.hits $tmpdir/.weekly.clean.hits $tmpdir/.weekly.monitor.alert $tmpdir/.weekly.susp.hits
  fi
 else
        eout "{alert} file input error, alert discarded."
 fi

fi
}

monitor_kill() {
	touch $tmpdir/stop_monitor
        inotify_pid=`pidof inotifywait`
	if [ -f "$tmpdir/monitor.pid" ]; then
	  monitor_pid=`cat $tmpdir/monitor.pid`
	fi
        kill -9 $inotify_pid $monitor_pid >> /dev/null 2>&1
        killall -9 inotifywait >> /dev/null 2>&1
        exit
}

monitor_cycle() {
 echo $$ > $tmpdir/monitor.pid
 if [ ! -f "$tmpdir/stop_monitor" ]; then
	 inotify_pid=`pidof inotifywait`
	 if [ -z "$inotify_pid" ]; then
	        eout "{mon} no inotify process found, exiting (are we a zombie process?)" 1
	        exit
	 fi
         log_size=`$wc -l $inotify_log | awk '{print$1}'`
         if [ "$log_size" -ge "$inotify_trim" ]; then
                trim=1000
                printf "%s\n" "$trim,${log_size}d" w | ed -s $inotify_log
                eout "{mon} inotify log file trimmed"
         fi
	 sleep $inotify_stime
	 monitor_check
 else
	rm -f $tmpdir/stop_monitor
	eout "{mon} monitoring terminated by user, inotify killed."
	exit
 fi
}

monitor_check() {
for file in `$tlog $inotify_log inotify | grep -E "CREATE|MODIFY|MOVED_FROM|MOVED_TO" | awk '{print$1}' | sort | uniq | tr ' ' '%'`; do
 file=`echo $file | tr '%' ' '`
 if [ -f "$file" ]; then
  sigignore 1
  echo "$file" >> $tmpdir/.monitor.scanned.alltime
  eout "{mon} inotify file scan $file"
  scan_stage1 "$file" >> /dev/null 2>&1
## CREATE,ISDIR: directory check for running inotifywait without -r
# elif [ -d "$file" ]; then
#  sigignore 1
#  eout "{mon} inotify directory scan $file"
#  find $file -maxdepth $maxdepth -type f -mmin -5 -size +${minfilesize}c -size -$maxfilesize > $tmpdir/.mmin.find.$$
#  for dirfile in `cat $tmpdir/.mmin.find.$$ | tr ' ' '%'`; do
#    dirfile=`echo $dirfile | tr '%' ' '`
#    echo "$dirfile" >> $tmpdir/.monitor.scanned.alltime
#    eout "{mon} inotify file scan $dirfile"
#    scan_stage1 "$dirfile" >> /dev/null 2>&1
#  done
#  rm -f $tmpdir/.mmin.find.$$
##
 fi
done
monitor_cycle
}

monitor_init() {
inopt="$1"
scan_session=$sessdir/session.hits.$datestamp.$$
touch $scan_session
echo "$scan_session" > $sessdir/session.monitor.current

if [ "$inopt" == "" ]; then
	eout "invalid usage of -m|--monitor, aborting." 1
	exit
fi

if [ ! -f "$inotify" ]; then
 eout "{mon} could not find inotify command" 1
 exit
fi

if [ -f "/boot/System.map-$(uname -r)" ]; then
	ksup=`grep -i inotify_ /boot/System.map-$(uname -r)`
	if [ -z "$ksup" ]; then
		eout "{mon} kernel does not support inotify(), aborting." 1
		exit
	fi
elif [ -f "/boot/config-$(uname -r)" ]; then
	ksup=`grep -m1 CONFIG_INOTIFY /boot/config-$(uname -r)`
	if [ -z "$ksup" ]; then
		eout "{mon} kernel does not support inotify(), aborting." 1
		exit
	fi
fi

inotify_pid=`pidof inotifywait`
if [ ! -z "$inotify_pid" ]; then
	eout "{mon} existing inotify process detected (try -k): $inotify_pid" 1
	exit
fi

rm -f $tmpdir/stop_monitor $tmpdir/inotifywait.pid

procs=`cat /proc/cpuinfo  | grep -c processor`
users_tot=`cat /etc/passwd | grep -ic home`
inotify_user_watches=$[inotify_base_watches*users_tot]
eout "{mon} set inotify max_user_instances to 128" 1
echo 128 > /proc/sys/fs/inotify/max_user_instances
eout "{mon} set inotify max_user_watches to $inotify_user_watches" 1
echo $inotify_user_watches > /proc/sys/fs/inotify/max_user_watches 

icnt=0
inotify_fpaths=$sessdir/inotify.paths.$$
rm -f $inotify_fpaths
touch $inotify_log
chmod 640 $inotify_log

if [ "$(echo $inopt | grep -E "users|user|USERS|USER")" ]; then
 for i in `cat /etc/passwd | cut -d':' -f1,3,6`; do
  user=`echo $i | cut -d':' -f1`
  user_id=`echo $i | cut -d':' -f2`
  user_home=`echo $i | cut -d':' -f3`
  icnt=$[icnt+1]

  if [ "$user_id" -ge "$inotify_minuid" ]; then
  if [ ! -z "$inotify_webdir" ] && [ -d "$user_home/$inotify_webdir" ]; then
	echo "$user_home/$inotify_webdir" >> $inotify_fpaths
	eout "{mon} added $user_home/$inotify_webdir to inotify monitoring array" 1
   elif [ -d "$user_home" ]; then
	echo "$user_home" >> $inotify_fpaths
	eout "{mon} added $user_home to inotify monitoring array" 1
   else
 	eout "{mon} could not find any suitable user home paths"
   fi
  fi
 done

 if [ -d "/dev/shm" ]; then
	echo "/dev/shm" >> $inotify_fpaths
	eout "{mon} added /dev/shm to inotify monitoring array" 1
 fi
 if [ -d "/var/tmp" ]; then
	echo "/var/tmp" >> $inotify_fpaths
	eout "{mon} added /var/tmp to inotify monitoring array" 1
 fi
 if [ -d "/tmp" ]; then
	echo "/tmp" >> $inotify_fpaths
	eout "{mon} added /tmp to inotify monitoring array" 1
 fi
elif [ -f "$inopt" ]; then
	tot_paths=`$wc -l $inotify_fpaths | awk '{print$1}'`
	if [ "$tot_paths" == "0" ]; then
		eout "{mon} no paths specified in $inopt, aborting." 1
		exit
	fi
        for i in `cat $inopt`; do
                if [ -d "$i" ]; then
                        eout "{mon} added $i to inotify monitoring array" 1
                        echo "$i" >> $inotify_fpaths
                else
                        eout "{mon} ignored invalid path $i" 1
                fi
        done

elif [ -d "$inopt" ] || [ "$(echo $inopt | grep -E ".*,.*")" ]; then
	for i in `echo $inopt | tr ',' '\n'`; do
		if [ -d "$i" ]; then
			eout "{mon} added $i to inotify monitoring array" 1
			echo "$i" >> $inotify_fpaths
		else
			eout "{mon} invalid path $i specified, ignoring." 1
		fi
	done
else
	eout "{mon} no valid option or invalid file/path provided, aborting." 1
	exit
fi

if [ -f "$ignore_inotify" ]; then
 cnt=`$wc -l $ignore_inotify | awk '{print$1}'`
 if [ "$cnt" > "0" ]; then
  for igfile in `cat $ignore_inotify`; do
    if [ "$igregexp" ]; then
	igregexp="$igregexp|$igfile"
    else
	igregexp="($igfile"
    fi
  done
  igregexp="$igregexp)"
  exclude="--exclude $igregexp"
 fi
fi

tot_paths=`$wc -l $inotify_fpaths | awk '{print$1}'`
eout "{mon} starting inotify process on $tot_paths paths, this might take awhile..." 1
$nice -n $inotify_nice $inotify -d -r -o $inotify_log --fromfile $inotify_fpaths $exclude --timefmt "%d %b %H:%M:%S" --format "%w%f %e %T" -m -e create,move,modify >> /dev/null 2>&1 &
sleep 2
inotify_pid=`pidof inotifywait`
if [ -z "$inotify_pid" ]; then
        eout "{mon} no inotify process found, check $inotify_log for errors." 1
	exit
else
        eout "{mon} inotify startup successful (pid: $inotify_pid)" 1
	eout "{mon} inotify monitoring log: $inotify_log" 1
        echo "$inotify_pid" > $tmpdir/inotifywait.pid
fi
monitor_cycle >> /dev/null 2>&1 &
}

checkout() { 
file="$1"
host=ftp.rfxn.com
user=anonymous
passwd=anonymous
upath=incoming

cfile="$(pwd)/$file"
if [ -f "$cfile" ]; then
	file=$cfile
fi

if [ -f "$file" ]; then

eout "{checkout} uploading $file to $host" 1

ftp -v -n -i $host << EOT
user $user@rfxn.com $passwd
prompt
cd $upath
lcd $lcd
binary
put "$file" "$RANDOM.$$.bin"
ascii
put "$file" "$RANDOM.$$.ascii"
bye
EOT

elif [ -d "$file" ]; then
	for i in `find $file -type f`; do
ftp -v -n -i $host << EOT
user $user $passwd
prompt
cd $upath
lcd $lcd
binary
put "$i" "$RANDOM.$$.bin"
ascii
put "$i" "$RANDOM.$$.ascii"
bye
EOT
	done
fi


}

sigignore() {
sil=$1
	chk=`$wc -l $ignore_sigs | awk '{print$1}'`
	if [ ! "$chk" == "0" ]; then
	   cat $dat_hexstring | grep -vf $ignore_sigs > $dat_hexstring.new
	   mv $dat_hexstring.new $dat_hexstring
	   cat $dat_md5hash | grep -vf $ignore_sigs > $dat_md5hash.new
	   mv $dat_md5hash.new $dat_md5hash
	   chmod 640 $dat_md5hash $dat_hexstring
	   if [ "$sil" == "1" ]; then
		   eout "{glob} processed $chk signature ignore entries"
	   else
		   eout "{glob} processed $chk signature ignore entries" 1
	   fi
        fi
}


lmdup() {
ofile=$tmpdir/.lmdup_vercheck.$$
tmp_inspath=/usr/local/lmd_update
rm -rf $tmp_inspath
rm -f $ofile

mkdir -p $tmp_inspath
chmod 750 $tmp_inspath

eout "{update} checking for available updates..." 1

$wget --referer="http://www.rfxn.com/LMD-$rver" -q -t5 -T5 "$lmdurl_ver" -O $ofile >> /dev/null 2>&1
if [ -s "$ofile" ]; then
	installed_ver=`echo $ver | tr -d '.'`
	current_ver=`cat $ofile | tr -d '.'`
	current_hver=`cat $ofile`
	if [ "$current_ver" -gt "$installed_ver" ]; then
		eout "{update} new version $current_hver found, updating..." 1
		$wget --referer="http://www.rfxn.com/LMD-$rver" -q -t5 -T5 "http://www.rfxn.com/downloads/maldetect-current.tar.gz" -O "$tmp_inspath/maldetect-current.tar.gz"
		if [ -f "$tmp_inspath/maldetect-current.tar.gz" ]; then
		 cd $tmp_inspath/
		 tar xfz maldetect-current.tar.gz
		 cd maldetect-*
		 chmod 750 install.sh
		 sh -c './install.sh' >> /dev/null 2>&1
		 eout "{update} completed update v$ver => v$current_hver, running signature updates..." 1
		 $inspath/maldet --update 1
		 eout "{update} update and config import completed." 1
		else
			eout "{update} could not download maldetect-current.tar.gz, please try again later." 1
			exit
		fi
	else
		eout "{update} hashing install files and checking against server..." 1
		md5sum $inspath/maldet $inspath/internals.conf $inspath/inotify/tlog $inspath/inotify/inotifywait $inspath/clean/* | awk '{print$1}' | tr '\n' ' ' | tr -d ' ' > $lmd_hashf
		ofile_hash=$tmpdir/.lmdup_hashcheck
		$wget --referer="http://www.rfxn.com/LMD-$rver" -q -t5 -T5 "$lmdurl_hash" -O $ofile_hash >> /dev/null 2>&1
		if [ -f "$ofile_hash" ]; then
			installed_hash=`cat $lmd_hashf`
			current_hash=`cat $ofile_hash`
			if [ ! "$installed_hash" == "$current_hash" ]; then
				eout "{update} version check shows latest but hash check failed, forcing update..." 1
		                $wget --referer="http://www.rfxn.com/LMD-$rver" -q -t5 -T5 "http://www.rfxn.com/downloads/maldetect-current.tar.gz" -O "$tmp_inspath/maldetect-current.tar.gz"
	        	        if [ -f "$tmp_inspath/maldetect-current.tar.gz" ]; then
	                	 cd $tmp_inspath/
	        	         tar xfz maldetect-current.tar.gz
		                 cd maldetect-*
        		         chmod 750 install.sh
		                 sh -c './install.sh' >> /dev/null 2>&1
        	        	 eout "{update} completed update v$ver => v$current_hver, running signature updates..." 1
        		         $inspath/maldet --update 1
	                	 eout "{update} update and config import completed." 1
                		else
        	                 eout "{update} could not download maldetect-current.tar.gz, please try again later." 1
	                         exit
				fi
			else
				eout "{update} latest version already installed." 1
			fi
		else
			eout "{update} latest version already installed." 1
		fi
	fi
else
	eout "{update} could not download version file from server, please try again later." 1
	exit
fi

rm -rf $tmp_inspath $ofile $ofile_hash
}

sigup() {
#check_clibkeyutils
#check_opensslheartbleed
eout "{sigup} performing signature update check..." 1
if [ -z "$def_ver" ]; then
	eout "{sigup} could not determine signature version" 1
	def_ver=0
else
	eout "{sigup} local signature set is version $def_ver" 1
fi

if [ ! -f "$wget" ]; then
	eout "{sigup} could not locate wget command" 1
	exit
fi

tmpf="$tmpdir/.hver$$"
$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_ver -O $tmpf
if [ ! -f "$tmpf" ]; then
	eout "{sigup} could not download signature data from server, please try again later." 1
	exit
fi
if [ -z $(cat $tmpf) ]; then
	eout "{sigup} could not download signature data from server, please try again later." 1
	exit
fi

nver=`cat $tmpf`

if [ -f "$dat_md5hash" ]; then
	lines_md5=`cat $dat_md5hash | $wc -l | awk '{print$1}'`
else
	lines_md5=0
fi
if [ -f "$dat_hexstring" ]; then
	lines_hex=`cat $dat_hexstring | $wc -l | awk '{print$1}'`
else
	lines_hex="0"
fi

if [ ! -f "$dat_md5hash" ] || [ ! -f "$dat_hexstring" ]; then
	def_ver=2013041200000
	eout "{sigup} signature files missing or corrupted, forcing update..." 1
elif [ "$lines_md5" -lt "1000" ] || [ "$lines_hex" -lt "1000" ]; then
	def_ver=2013041200000
	eout "{sigup} signature files corrupted, forcing update..." 1
fi

if [ "$nver" != "$def_ver" ]; then
	eout "{sigup} new signature set ($nver) available" 1

	$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_md5hash -O $dat_md5hash
	eout "{sigup} downloaded $defurl_md5hash" 1

	$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_hex -O $dat_hexstring
	eout "{sigup} downloaded $defurl_hex" 1

	$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_hex_cav -O $dat_hex_cav
	eout "{sigup} downloaded $defurl_hex_cav" 1

	$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_md5_cav -O $dat_md5_cav
	eout "{sigup} downloaded $defurl_md5_cav" 1

	$wget --referer="http://www.rfxn.com/LMD-$rver" -t5 -T5 -q $defurl_cl -O $tmpdir/maldet-clean.tgz
	cd $tmpdir/
	tar xfz $tmpdir/maldet-clean.tgz
	cp -f $tmpdir/clean/* $cldir
	rm -rf $tmpdir/maldet-clean.tgz $tmpdir/clean
        eout "{sigup} downloaded $defurl_cl" 1

	cat $tmpf > $def_verf
	eout "{sigup} signature set update completed" 1
	sigignore
	hex_sigs=`$wc -l $dat_hexstring | awk '{print$1}'`
	md5_sigs=`$wc -l $dat_md5hash | awk '{print$1}'`
	tot_sigs=$[md5_sigs+hex_sigs]
	eout "{sigup} $tot_sigs signatures ($md5_sigs MD5 / $hex_sigs HEX)" 1

else
	eout "{sigup} latest signature set already installed" 1
fi

rm -f $tmpf
}

if [ -z "$1" ]; then
        header
        usage_short
else
 while [ -n "$1" ]; do
 case "$1" in
	--mkpubpaths)
		if [ "$public_scan" == "1" ]; then
		chmod 711 $inspath/pub
		for user in `cat /etc/passwd | cut -d ':' -f1`; do
			uid=`id --user $user`
			if [ -z "$uid" ]; then 
				uid=9
			fi
			if [ -z "$pubuser_minuid" ]; then
				pubuser_minuid=10
			fi
			if [ "$uid" -ge "$pubuser_minuid" ] && [ ! -d "$inspath/pub/$user" ]; then
				mkdir -p $inspath/pub/$user/quar $inspath/pub/$user/sess $inspath/pub/$user/tmp >> /dev/null 2>&1
				touch $inspath/pub/$user/event_log >> /dev/null 2>&1
				chown -R $user.$user $inspath/pub/$user >> /dev/null 2>&1
				chmod 750 $inspath/pub/$user $inspath/pub/$user/quar $inspath/pub/$user/sess $inspath/pub/$user/tmp >> /dev/null 2>&1
				chmod 640 $inspath/pub/$user/event_log >> /dev/null 2>&1
				eout "{glob} created public scanning paths for user $user"
			fi
			unset uid user
		done
		exit
		else
			header
			echo "public scanning support not enabled in conf.maldet, aborting."
			exit
		fi
	;;
	--modsec)
		setmodsec=1
	;;
	-U|--user)
		shift
		user="$1"
        	quardir=$inspath/pub/$user/quar
	        sessdir=$inspath/pub/$user/sess
	        tmpdir=$inspath/pub/$user/tmp
	        logf=$inspath/pub/$user/event_log
	;;
	-co|--config-option)
		shift
		user=`whoami`
		if [ ! "$user" == "root" ]; then
		        tmpdir=$inspath/pub/$user/tmp
		fi
		tmpco=$tmpdir/config.cli
		rm -f $tmpco
		touch $tmpco
		echo $1 | sed -e 's/--config-option //' -e 's/-co //' | tr -d ' ' | tr ',' '\n' > $tmpco
		. $tmpco
		rm -f $tmpco
	;;
	-qd)
		shift
		if [ -d "$1" ];  then
			eout "{scan} set quarantine path: $1" 1
			quardir="$1"
		fi
	;;
	-b|--background)
		set_background=1
	;;
	-c|--checkout)
		shift
		header
		checkout "$1"
	;;
	--alert-daily)
		alert daily
	;;
	--alert-weekly)
		alert weekly
	;;
	-m|--monitor)
		header
		shift
                if [ "$OSTYPE" == "FreeBSD" ]; then
                        eout "{mon} not currently supported under FreeBSD" 1
                else
			svc=m
			trap trap_exit 2
			monitor_init "$1"
		fi
	;;
	-k|--kill-monitor)
		header
		if [ "$OSTYPE" == "FreeBSD" ]; then
			eout "{mon} not currently supported under FreeBSD" 1
		else
			eout "{mon} sent kill to monitor service" 1
			monitor_kill
		fi
	;;
	-a|--scan-all)
		shift
		if [ -z "$setmodsec" ]; then
			header
			#check_clibkeyutils
			#check_opensslheartbleed
		fi
		svc=a
		trap trap_exit 2
		spath="$1"
		if [ "$spath" == "" ]; then
			spath=/home
		fi
		if [ "$set_background" == "1" ]; then
			eout "{scan} launching scan of $spath to background, see $inspath/event_log for progress" 1
                        scan "$spath" all >> /dev/null 2>&1 &
		else
		        scan "$spath" all
		fi
	;;
	-r|--scan-recent)
		header
		svc=r
		trap trap_exit 2
		shift
		spath="$1"
		shift
		days="$1"
		if [ -z "$spath" ]; then
			eout "{scan} no path defined" 1
			exit
		fi
		if [ -z "$days" ]; then
			days=7
		fi
                if [ "$set_background" == "1" ]; then
                        eout "{scan} launching scan of $spath changes in last ${days}d to background, see $inspath/event_log for progress" 1
			scan "$spath" "$days" >> /dev/null 2>&1 &
		else
			scan "$spath" "$days"
		fi
	;;
	-l|--log)
		header
		view
	;;
	-e|--report)
		header
		shift
		view_report "$1" "$2"
	;;
	-p|--purge)
		header
		purge
	;;
	-d|--update-ver|--update-version)
		header
		lmdup
	;;
	-u|--update)
		shift
		if [ ! "$1" == "1" ]; then
			header
		fi
		sigup
	;;
	-s|--restore)
		header
		shift
		if [ -f "$sessdir/session.hits.$1" ]; then
			restore_hitlist "$1"
		else
			restore "$1"
		fi
	;;
	-q|--quarantine)
		header
		shift
		quar_hitlist "$1"
	;;
	-n|--clean)
		header
		shift
		clean_hitlist "$1"
	;;
	-h|--help)
		header
		usage_long
	;;
	*)
		header
		usage_short
	esac
	shift
	done
fi