#!/bin/sh
#
#     tiger - A UN*X security checking system
#     Copyright (C) 1993 Douglas Lee Schales, David K. Hess, David R. Safford
#
#    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_inetd - 06/14/93
#
# Checks the configuration of the inetd configuration file and determines
# possible problems in them.
#
# check_inetd - 04/20/99
#     Portions corrected y Advanced Research Corporation (R)
# check_inetd - 12/13/2001 - jfs
#     Fixes to avoid false positives 
# check_inetd - 05/27/2002 - jfs
#     Changed TigerInstallDir to .
# check_inetd - 04/15/2003 - jfs
#     Separated the services check into a separate script.
# check_inetd - 04/23/2003 - jfs
#     Fixed requirements
# check_inetd - 05/26/2003 - jfs
#     Fixed calls to test (worked in Linux but not in other OS due
#     to -n not being implicit)
# check_inetd - 08/08/2003 - jfs
#     Safe temporary file creation.
# check_inetd - 08/09/2003 - jfs
#     Re added RM to dependancies since it's used by delete.
# check_inetd - 08/11/2003 - jfs
#     Added checks for the file permissions of the INETDFILE
# check_inetd - 08/13/2003 - jfs
#     Added one of the fixes done by ARSC which were lost in the merge
# check_inetd - 08/14/2003 - jfs Added OUTPUTMETHOD to dependancies.
#     Added checks from ARSC's tara 3.0.3 for more services 
#     (recommending the use of encryption) and implemented more checks
#     for other problematic services/servers. Also checked 019 from FAIL
#     to ALERT
# check_inetd - 10/07/2003 - jfs Removed temporary files
# check_inetd - 10/19/2003 - jfs Applied patch from Ryan Bradetich which
#     adds the exec service to the 'rexd' check.
# check_inetd - 11/18/2003 - jfs Do not report the service is not protected
#     if tcp wrappers are undefined (patch from Ryan Bradetich).
# check_inetd - 03/31/2005 - jfs Dont warn about tcp wrapper protection for
#     internal services
# check_inetd - 07/05/2006 - jfs - Fix deprecated syntax with sort.
#    Thanks to Cyril Chaboisseau and Adam James for providing a patch
#    (Debian bug: #369501)
#
# TODO
# - This script needs to be improved to warn for some of the 'chatty' issues
#   closed by Titan (currently only does this for some services)
#-----------------------------------------------------------------------------
#
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 SED AWK CAT GEN_INETD_SETS GREP JOIN LS SORT UNIQ RM OUTPUTMETHOD || exit 1
  haveallfiles BASEDIR WORKDIR INETDFILE || exit 1
  
  echo "--CONFIG-- [init003c] $0: Configuration ok..."
  exit 0
}

#------------------------------------------------------------------------
echo
echo "# Performing check of 'inetd'..."

haveallcmds SED AWK CAT GEN_INETD_SETS GREP JOIN LS SORT UNIQ RM OUTPUTMETHOD || exit 1
haveallfiles BASEDIR WORKDIR INETDFILE || exit 1

saveifs=$IFS

tmpinetd=$WORKDIR/sec.$$in
safe_temp "$tmpinetd" "$WORKDIR/cinetd.$$" "$WORKDIR/inet1.$$"
trap 'delete $tmpinetd $WORKDIR/cinetd.$$ $WORKDIR/inet1.$$; exit 1' 1 2 3 15

checkinetd ()
{
  infile=$1

  $JOIN -o 1.1 2.6 1.6 1.7 $infile $INETDFILE > $WORKDIR/cinetd.$$

  __xx="`$LS -id \`$AWK '{print $3}' $WORKDIR/cinetd.$$ |
           $GREP -v internal
          \` 2>/dev/null |
         $SORT |
         $UNIQ -c |
         $SORT -rn |
         $SED -e '1q'`"
  set x $__xx
	 
  count="$2"
  size="$3"
#  tcpdpath="$4"
  tcpdpath=$TCPD

#  [ "$count" -lt 2 ] && tcpdpath=
    
  $GREP -v '^#' $infile | grep -v '^$' |
  while read currservice s p t user currbinary parm1 parms
  do
    [ -z "$currservice" ] && continue
    if [ "$currservice" = 'rexd' -o "$currservice" = 'exec' ]; then
      message FAIL inet006f "" "'$currservice' service is enabled, consider disabling it."
    elif [ "$currservice" = 'sysstat' ]; then
      message WARN inet012w "" "'sysstat' service is enabled, consider disabling it."
    elif [ "$currservice" = 'netstat' ]; then
      message WARN inet013w "" "'netstat' service is enabled, consider disabling it."
    elif [ "$currservice" = 'telnet' ]; then
      message WARN inet098w "" "The 'telnet' server is enabled, consider using ssh instead."
    elif [ "$currservice" = 'ftp' ]; then
      message WARN inet098w "" "The 'ftp' server is enabled, consider using ssh/sftp instead."
    elif [ "$currservice" = 'login' ]; then
      message WARN inet098w "" "The 'login' server is enabled, consider using ssh/sftp instead."
    elif [ "$currservice" = 'shell' ]; then
      message WARN inet098w "" "The 'rsh' server is enabled, consider using ssh/sftp instead."
    elif [ "$currservice" = 'tftp' ]; then
      message WARN inet022w "" "The 'tftpd' server is enabled, consider disabling it"
    elif [ "$currservice" = 'finger' ]; then
      message WARN inet023w "" "The 'finger' server is enabled, consider disabling it"
    elif [ "$currservice" = 'rusers' ]; then
      message WARN inet024w "" "The 'rusers' server is enabled, consider disabling it"
    elif [ "$currservice" = 'echo' -a "$p" = 'udp' ]; then
      message WARN inet025w "" "The 'echo' udp server is enabled, consider disabling it"
    elif [ "$currservice" = 'chargen' -a "$p" = 'udp' ]; then
      message WARN inet025w "" "The 'chargen' udp server is enabled, consider disabling it"
    fi

    [ "$currbinary" = 'internal' ] && continue

    # Note: Does not make sense for the "small servers" services
    # (discard, chargen, echo....)
    if [ "$t" = "nowait" -a -n "$tcpdpath" -a "$currbinary" != "$tcpdpath" ]; then
      message WARN inet099w "" "'$currservice' is not protected by tcp wrappers."
    fi


    if [ -n "`echo $currbinary | $GREP '?'`" ] ; then
      currbinary=`echo $currbinary | $SED -e "s/\?//"`
    fi

    if [ -f "$currbinary" ]; then
      [ -n "$TigerCheckEmbedded" -a "$user" = 'root' ] && {
	echo "$currbinary $inetdsrc" >> $TigerCheckEmbedded
      }
 # SGI IRIX currbinary paths can be prefixed with '?' to indicate
 # optional existance of the binary
     elif [ "X$OS" = "XIRIX" -a "X`echo $currbinary | $SED 's:[?]::'`" != "X$currbinary" ]; then
	if [ -f "`echo $currbinary | $SED 's:[?]::'`" ]; then
		[ -n "$TigerCheckEmbedded" -a "$user" = 'root' ] && {
			echo "`echo $currbinary | $SED 's:[?]::'` $inetdsrc" >> $TigerCheckEmbedded
		 }
	else
		message INFO inet014i "" "`echo $currbinary | $SED 's:[?]::'` for $currservice does not exist (flagged as optional)."
	fi

    else
      message INFO inet010i "" "$currbinary for $currservice does not exist."
    fi
    
    if [ "X$OS" = "XIRIX" -a "X`echo $currbinary | $SED 's:[?]::'`" != "X$currbinary" ]; then
	lgetpermit "`echo $currbinary | $SED 's:[?]::'`" |
	pathmsg inet008 inet009 "`echo $currbinary | $SED 's:[?]::'`" root "inetd entry for $currservice service uses"
    else
	lgetpermit "$currbinary" |
	pathmsg inet008 inet009 "$currbinary" root "inetd entry for $currservice service uses"
    fi

    if [ "$currbinary" = "$tcpdpath" ]; then
      if [ -f "$parm1" ]; then
	[ -n "$TigerCheckEmbedded" -a "$user" = 'root' ] && {
	  echo "$parm1 $inetdsrc" >> $TigerCheckEmbedded
	}
      else
	message INFO inet010i "" "$parm1 for $currservice does not exist."
      fi
    
      lgetpermit "$parm1" |
      pathmsg inet008 inet009 "$parm1" root "inetd entry for $currservice service uses"
    fi
  done 

  while read service exppath currpath parm1 parms
  do
    if [ -n "`echo $currpath | $GREP '?'`" ]; then
     currpath=`echo $currpath | $SED -e "s/\?//"`
    fi
    if [ -n "`echo $exppath | $GREP '?'`" ]; then
     exppath=`echo $exppath | $SED -e "s/\?//"`
    fi
    if [ "$currpath" != "$exppath" -a "$currpath" != "$tcpdpath" ]; then
	message WARN inet005w "" "Service $service is using $currpath instead of $exppath."
    fi
  done < $WORKDIR/cinetd.$$
  
  delete $WORKDIR/cinetd.$$

  $AWK '$6 != "internal" {print}' $infile |
  $SORT > $WORKDIR/inet1.$$
# TODO: Consider this change done by ARSC:
#   $SORT -k 6,6 > $WORKDIR/inet1.$$
  $AWK '$6 != "internal" {print}' $INETDFILE |
  $SORT |
# TODO: Consider this change done by ARSC:
#   $SORT -k 6,6 |
  $JOIN -j 1 -o 1.1 2.1 1.6 - $WORKDIR/inet1.$$ |
  while read expservice currservice prog
  do
   if [ -n "$prog" ] ; then
    [ "$expservice" != "$currservice" ] && {
      message WARN inet007w "" "Binary $prog is being used for service $currservice.  Expected service is $expservice."
    }
   fi
  done 
  delete $WORKDIR/inet1.$$

# Print out any other entries that have been added
  $AWK '{ print $1 " " $3 }' $infile |
  $GREP -v '^#' | $GREP -v '^ $' | $SORT |
  while read service protocol
  do
	matchescount=""
	matches=`$AWK '{ print $1 " " $3 }' $INETDFILE | $GREP " $protocol$" | \
	$AWK '{ print $1 }'`
	for currservice in $matches
	do
		if [ "X$service" = "X$currservice" ]; then
			matchescount="$matchescount."
		fi
	done
	if [ "X$matchescount" = "X" ]; then
		x="`$EGREP \"^$service[     ]\" $infile`"
		message INFO inet011i "$x" "Locally added entry \`$service' in inetd.conf:"
	fi
  done
# TODO: ARSC's tara uses the following, consider using it since
# it looks (is?) more efficient
#  $AWK '{printf("%s %s\n",$1,$3)}' $infile | sort> $WORKDIR/f1.$$
#  $AWK '{printf("%s %s\n",$1,$3)}' $INETDFILE | $GREP -v "^#" | sort |
#  $COMM -23 $WORKDIR/f1.$$ - |
#  while read service protocol
#  do
#    if [ -n "$service" ]; then
#    message INFO inet011i "" "Locally added entry \`$service'in inetd.conf:"  
#    fi
#  done
#

}

realpath="$REALPATH -d"

[ ! -n "$REALPATH" -o ! $TESTEXEC "$REALPATH" ] && realpath="echo"

# Check the permissions in the /etc/inetd.conf
# TODO: Should the location of the inetd be hardcoded? 
# (it is in the gen_inetd programs)
getpermit /etc/inetd.conf |
while read _namefile _owner _group ur uw ux gr gw gx or ow ox suid sgid stk
do
     [ "$ur$uw$ux$gr$gw$gx$or$ow$ox" != '110000000' -a "$_group" != "root" ] && \
         message WARN inet017w "" "The $_namefile permissions are not 600."
     [ "$gw" != '0' ] && \
         message WARN inet018w "" "The $_namefile file can be written by any user of the group $_group."
     [ "$ow" != '0' ] && \
         message ALERT inet019a "" "The $_namefile file can be written by any user."
     [ "$_owner" != 'root' ] && \
         message FAIL inet020f "" "The $_namefile file is not owned by root (owned by $_owner)."
done


# Check the information in the INETDFILE
{

  haveallcmds GEN_INETD_SETS UNIQ &&
  haveallfiles INETDFILE && {
    $GEN_INETD_SETS |
    while read inetd_set
    do
      inetdsrc=`$CAT $inetd_set.src`
      echo "# Checking inetd entries from $inetdsrc"

      checkinetd $inetd_set

      delete $inetd_set $inetd_set.src
    done
  }
} |
$OUTPUTMETHOD

delete "$tmpinetd" "$WORKDIR/cinetd.$$" "$WORKDIR/inet1.$$"

exit 0
