#!/bin/sh
#
#     tiger - A UN*X security checking system
#     Copyright (C) 2000, 2001 Javier Fernandez-Sanguino Peña
#
#    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_listeningprocs - 08/23/2001
#
# 07/05/2006 jfs  Try not to botch when udp6 services are being read
#                 (Debian Bug #375165)
# 05/15/2005 jfs  Proper check for processes in loopback (Debian Bug #307695)
# 05/02/2004 jfs  Try to avoid eval problems if handling variables with
#                 special characters (such as space)
# 09/19/2003 - jfs - Allow empty okprocess and okprocessuser with the patch
#   provided by Nicolas François. Also added patch improving the 
#   use of NETSTAT and LSOF. Information gathered by both is piped to
#   the same block to generated the report.
#   Reordered changelog.
# 04/23/2003 - jfs - Changed TigerInstallDir to '.'
# 10/16/2002 - jfs - Changed the case statement into an eval in order
#   to properly work for more than two processes or users
#   (i.e. with "process1|process2")
#         
#
# TODO:
# - this script seems to have problems if a program opens an UDP socket
#   expecting a reply from a remote host. This might lead to sockets reported
#   as open spuriously (see Debian Bug#136991). Should this be fixed?
#   (the socket is in fact open and could be accessed remotely)
#
#-----------------------------------------------------------------------------
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 CUT GREP || exit 1
# Just netstat is needed to run this program, lsof is an addon
  haveallcmds LSOF || haveallcmds NETSTAT || exit 1
  haveallfiles BASEDIR WORKDIR || exit 1
  haveallvars TESTLINK HOSTNAME
  
  message CONFIG init003c "" "$0: Configuration ok..."
  exit 0
}

#------------------------------------------------------------------------
echo
echo "# Checking listening processes "

haveallcmds GREP CUT AWK SORT UNIQ || exit 1
haveallcmds LSOF || haveallcmds NETSTAT || exit 1

okprocessusers=${Tiger_Listening_ValidUsers-"root"}
okprocess=${Tiger_Listening_ValidProcs-""}
export okprocess okprocessusers

[ ! -x "$LSOF" ] && message INFO lin004i "" "Using $NETSTAT instead of lsof, information regarding processes will be more limited"

if [ -x "$LSOF" ] 
then
	$AWK '
	BEGIN {
		cmd = "$LSOF -FpPcnLt -nPi"

		while ((cmd | getline field) > 0){
			fieldtype = substr(field,1,1)
			sub("^.","",field)
			if      ("p" == fieldtype) {
			}
			else if ("c" == fieldtype) {
				command = field
			}
			else if ("L" == fieldtype) {
				user = field
			}
			else if ("t" == fieldtype) {
				type = field
			}
			else if ("P" == fieldtype) {
				proto = field
			}
			else if ("n" == fieldtype) {
				addr = field
				if (( type == "IPv4" ) && ( addr !~ "->" )) {
					printf ("%s %s %s %s\n", user, proto, addr, command)
				}
			}
		}
		close(cmd)
	}'
else
	$NETSTAT -lpeutw --numeric-hosts --numeric-ports |
	# remove header
	$SED -e '1d;2d' |
	while read type x y local remote listen user inode proc
	do
		[ "$type" = "udp" ] || [ "$type" = "upd6" ] && {
			# there is no listen filed, every field are shifted left
			user=$listen
			proc="$inode $proc"
			type="UDP"
		}

		[ "$type" = "tcp" ] && type="TCP"
		[ "$type" = "tcp6" ] && type="TCP"

		echo $user $type $local "`echo $proc | $CUT -f 2 -d /`"
	done
fi |
# sort on process and remove duplicates
$SORT -k 4 | $UNIQ |
while read user type asocket proc
do
	socket=`echo $asocket |  $CUT -f 2 -d :`
	address=`echo $asocket | $CUT -f 1 -d :`
# Should address = 127.0.0.1 be considered harmful?
# TODO: This could be an option
	[ "$address" = "127.0.0.1" ] && address="loopback"
	[ "$address" = "0.0.0.0" -o "$address" = "*" ] && address="every"
	isokprocess="no"
	isokuser="no"
	# TODO This should change into a function since it's used twice in the
	# code
	[ -n "$okprocess" ] && \
	isokprocess=`eval "case \"$proc\" in ${okprocess})  echo \"yes\" ;; \
	*) echo \"no\" ;; esac"`
	# Probably redundant since this is done already at the top
	[ -n "$okprocessusers" ] && \
	isokuser=`eval "case \"$user\" in ${okprocessusers})  echo \"yes\" ;; \
	*) echo \"no\" ;; esac"`
	if [ "$isokprocess" = "no" ] ; then
		if [ "$isokuser" = "yes" ] ; then
			[ "$address" = "every" -a "$Tiger_Listening_Every" != "N" ] && \
				message WARN lin002i "" "The process \`$proc' is listening on socket $socket ($type) on $address interface." || \
				message INFO lin002i "" "The process \`$proc' is listening on socket $socket ($type) on $address interface." 
		else
			[ "$address" != "loopback" ]  && {
				message WARN lin003w "" "The process \`$proc' is listening on socket $socket ($type on $address interface) is run by $user."
			} 
		fi # of if okuser
	fi # of if okprocess

done 

exit 0 
