#!/bin/sh
#
#     tiger - A UN*X security checking system
#     Copyright (C) 2002 Javier Fernandez-Sanguino
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2, or (at your option)
#    any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#     Please see the file `COPYING' for the complete copyright notice.
#
# check_findeleted - 16 April 2003
#
# This script will check if deleted files are being used by any process
# in the current system. This is somewhat usual behaviour for some applications
# but it might also be an indication of:
#  a) an intruder launching processes and then deleting its files so that
#     the admin does not notice it.
#  b) servers which have been patched after an updated but have not been
#     restarted and are, thus, using removed library files and are still
#     vulnerable.
#
# DATE 	NAME 	DESCRIPTION_OF_CHANGES
# 03/01/2004 - jfs - Fix error in lsof temporary file creation
#              (Debian bug #235951)
# 02/25/2004 - jfs - Fix error in safe_temp call
# 02/24/2004 - jfs - Do not operate over 'CHR' file types to avoid false
#              positives (Closes Debian bug #232704). 
#              Reduce verbosity by joining multiple messages into a
#              single one (Closes Debian Bug #231148)
#              Also simplify calls to improve performance.
# 09/13/2003 - jfs - Applied patch from Nicholas François to fix an error
#              which might allow some rogue processes to slip by
# 08/08/2003 - jfs - Avoided race conditions.
# 04/16/2002 - jfs - First version based on Brian Hatch's articles
#              (http://www.hackinglinuxexposed.com/articles/20020507.html)
# 04/17/2002 - jfs - Use safe delete() instead of $RM
#
#-----------------------------------------------------------------------------
#
# TODO:
# - Consider generating one message per command instead of per file
#   (better for reporting but worst for the .ignore mechanism)
#
# NOTES:
#
# A generic way to remove these messages for a given directory 
# DIR of PROGRAM would be:
#
# Program PROGRAM .* is using a deleted file: .*DIR.*
#
# Add that line to your configuration's tiger.ignore file and the
# messages will not appear in the reports.
# 
# Just for reference, there are a number of services that run with deleted
# files frequently, this includes (in Linux) things like gconfd-2,
# MozillaFirebird (stuff under  the user's .phoenix dir) and
# some applications that make use of /dev/null or /dev/console
# (I don't know why lsof flags those as "deleted", but they have been removed
# from the script)
#
#-----------------------------------------------------------------------------
#
# This is the directory Tiger is installed on
TigerInstallDir="/usr/share/tiger"

#
# Set default base directory.
# Order or preference:
#      -B option
#      TIGERHOMEDIR environment variable
#      TigerInstallDir installed location
#
basedir=${TIGERHOMEDIR:=$TigerInstallDir}

for parm
do
   case $parm in
   -B) basedir=$2; break;;
   esac
done

#
# Verify that a config file exists there, and if it does
# source it.
#
[ ! -r $basedir/config ] && {
  echo "--ERROR-- [init002e] No 'config' file in \`$basedir'."
  exit 1
}

. $basedir/config

. $BASEDIR/initdefs
#
# If run in test mode (-t) this will verify that all required
# elements are set.
#
[ "$Tiger_TESTMODE" = 'Y' ] && {
  haveallcmds GREP CAT LSOF PS CUT SORT AWK || exit 1
  haveallfiles BASEDIR WORKDIR || exit 1
  haveallvars TESTLINK HOSTNAME || exit 1
  
  echo "--CONFIG-- [init003c] $0: Configuration ok..."
  exit 0
}

#------------------------------------------------------------------------
echo
echo "# Performing check of deleted files used by processes..."

haveallcmds GREP CAT LSOF PS CUT SORT AWK || exit 1
haveallfiles BASEDIR WORKDIR || exit 1


safe_temp "$WORKDIR/deleted.$$" "$WORKDIR/lsof.$$"
trap 'delete $WORKDIR/deleted.$$ $WORKDIR/lsof.$$ ; exit 1' 1 2 3 15

# TODO: Use Lsof's -f option to only recover the data
# we need and also to provide some more data useful for us
# (such as PPID)
$LSOF -n  >>$WORKDIR/lsof.$$
# It can be deleted if name includes (deleted) or
# TYPE = DEL
$CAT $WORKDIR/lsof.$$ | $GREP "(deleted)" >>$WORKDIR/deleted.$$
$CAT $WORKDIR/lsof.$$ | $GREP "DEL" >>$WORKDIR/deleted.$$
delete $WORKDIR/lsof.$$

$CAT $WORKDIR/deleted.$$ | $SORT -u |
while read command npid user fd type device node name
# Note: Remove CHR file types also since this leads to false positives
# (i.e. /dev/null)
do
# It can be deleted if name includes (deleted) or
# TYPE = DEL, but SYSV or CHR 'files' are not considered
	if [ -n "`echo $name | $GREP \"(deleted)\"`" -a "$type" != "CHR" ] || [ -z "`echo $name | $GREP SYSV`" -a "$type" = "DEL" ] ; then
		 ppid=`$PS -p $npid -o pid,ppid | $GREP -v PID |$AWK '{print $2}'`
		 if [ -n "$ppid" ] ; then
			 pname=`$PS -p $ppid -o pid,cmd | $GREP -v PID | $AWK '{print $2}'`
		 fi
		 mtype="WARN"
		 mval="kis012w"
		 pmessage="Program $command (pid $npid, parent $ppid) is using a deleted file: '$name'"
		# TODO: This lsof calls should probably be possible
		# using the lsof file created previously, doing so
		# might avoid false positives or errors
		# (if proceses disappear)
		listenport=`$LSOF -np $npid | $GREP \(LISTEN\) |  $SORT -u | $CUT -d : -f 2 | $AWK '{printf "%s ", $1}'`
		if [ -n "$ppid" ] ; then
			listenpport=`$LSOF -np $ppid | $GREP \(LISTEN\) |  $SORT -u | $CUT -d : -f 2 | $AWK '{printf "%s, ", $1}'`
		fi
		# TODO:
		# There can be servers either in the same process or
		# its children. This code has to be improved in
		# order to make it possible to better report that fact.
		# It currently does _not_ check children processes properly
		if [ -n "$listenport" ]; then
			mtype="FAIL"
			mval="kis011f"
			pmessage="$pmessage and is a a server listening on port(s) $listenport"
		fi 
		if [ -n "$listenpport" ]; then
			mtype="FAIL"
			mval="kis011f"
			pmessage="$pmessage and its parent process $pname is a server listening on port(s) $listenpport"
		fi
		 message $mtype $mval "" "$pmessage."
	fi
done

delete $WORKDIR/deleted.$$


exit 0
