#!/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_rhosts - 06/14/93
#
# Checks rhosts files in order to see if user's configuration leaves the
# system open to attack.
#
# 05/14/2005 jfs Patch from Nicolas François which prints a warning
#                for any host in .rhost
# 03/31/2005 jfs Printout rhosts accesses
# 10/19/2003 jfs Patch from Nicolas François which uses -f instead of -e
#    in order to avoid Solaris problems. And also adds TESTLINK HOSTNAME
# 08/14/2003 jfs Added OUTPUTMETHOD to dependancies
# 08/12/2003 jfs Added check for existance, permissions and owner of
#                rhosts files (permissions are redundant, however)
# 08/08/2003 jfs Improved temporary file creation.
# 07/25/2002 jfs Added a sanity check for password files
# 04/28/93 dls  Added '-L' option to 'ls' to get the permissions from the
#               file instead of the symbolic link.
#               Complain if the .rhosts is a symbolic link or directory.
#
#-----------------------------------------------------------------------------
#
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 AWK CAT EXPR GEN_PASSWD_SETS HEAD JOIN LS RM SED OUTPUTMETHOD || 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 /etc/hosts.equiv and .rhosts files..."

haveallcmds AWK CAT EXPR GEN_PASSWD_SETS HEAD JOIN LS RM SED OUTPUTMETHOD || exit 1
haveallfiles BASEDIR WORKDIR || exit 1
haveallvars TESTLINK HOSTNAME || exit 1

safe_temp "$WORKDIR/home.hosts.$$" "$WORKDIR/pass.list.$$"
trap 'delete $WORKDIR/home.hosts.$$ $WORKDIR/pass.list.$$ ; exit 1' 1 2 3 15

check_entry()
{
  user="$1"
  rhost="$2"
  ruser="$3"
  plus=0

  if [ "$rhost" = '+' ]; then
    if [ "$user" = '+' ]; then
      message FAIL rcmd001f "" "User $user's .rhosts file has a '+ +' entry."
    else
      message FAIL rcmd002f "" "User $user's .rhosts file has a '+' for host field."
    fi
  fi
  if [ -n "$RHOST_SITES" ]; then
    note=1
    eval case "$rhost" in $rhostcase esac
    [ $note = 1 ] && {
      message WARN rcmd003w "" "User $user's .rhosts file has host \`$rhost'."
    }
  else 
      message WARN rcmd003w "" "User $user's .rhosts file provides access for user \`$ruser' at host \`$rhost'."
  fi

  if [ "$rhost" != '+' -a "$ruser" = '+' ]; then
    message WARN rcmd004w "" "User $user's .rhosts file has a '+' for user (host $rhost)."
  elif [ ! -n "$ruser" ]; then
    message INFO rcmd005i "" "User $user's .rhosts file has empty user field for host \`$rhost'."
  fi
# Linux allows comments in rhosts files
  [ "$OS" != "Linux" ] &&
  case "$rhost" in
    '#'*) {
      message WARN rcmd013w "" "User $user's .rhosts file contains an attempted comment line"
    }
    ;;
  esac
}

scan_file()
{
  user="$1"
  file="$2"
     
  # added #+@_ which should be valid characters.
  $AWK '/^[#\+@_a-zA-Z0-9\.\-\\\t ]*$/ {print}' < "$file" | 
        {
          while read rhost ruser
          do
            check_entry "$user" "$rhost" "$ruser"
          done
        }
    
  # added #+@_ which should be valid characters.
  $AWK 'BEGIN {count = 0} !/^[#\+@_a-zA-Z0-9\.\-\\\t ]*$/ {count += 1} 
  END {print count}' < "$file" |
          {
            read count
            if [ "$count" -ne 0 ];then
              message WARN rcmd015w "" "$count lines of user $user's .rhosts file were not checked because of invalid characters."
            fi
          }

}

check_file()
{
  user=$1
  file=$2
  getpermit $file | {
    read _f owner group ur uw ux gr gw gx or ow ox suid sgid stk

    rtxt=
    wtxt=
    case "$gr$or" in
      00) rtxt='';;
      01) rtxt='world';;
      10) rtxt="group \`$group'";;
      11) rtxt="group \`$group' and world";;
      *) rtxt='???';;
    esac

    case "$gw$ow" in
      00) wtxt='';;
      01) wtxt='world';;
      10) wtxt="group \`$group'";;
      11) wtxt="group \`$group' and world";;
      *) wtxt='???';;
    esac

    [ -n "$rtxt$wtxt" ] && {
      [ -n "$rtxt" ] && rtxt="$rtxt read"
      [ -n "$wtxt" ] && wtxt="$wtxt write"
      sep=
      [ -n "$rtxt" -a -n "$wtxt" ] && sep='and'
      message WARN rcmd006w "" "User $user's .rhosts file has $rtxt$sep$wtxt access."
    }
  }
}

process_passwd()
{
  while read user homedir host
  do
# Root is no longer skipped since it's rhosts file is also sensible
#   [ "$homedir" = '/' -a "$user" != 'root' ] && continue

    [ "$host" = "$HOSTNAME" ] && {
      if [ $TESTLINK "$homedir/.rhosts" ]; then
	message WARN rcmd007w "" "User $user's .rhosts file is a symbolic link:"
	$LS -l "$homedir/.rhosts"
	$LS $LSGROUP $LSLINK "$homedir/.rhosts"
	[ -s "$homedir/.rhosts" ] && {
	  check_file $user "$homedir/.rhosts"
	  if [ -r "$homedir/.rhosts" ]; then
	    scan_file $user "$homedir/.rhosts"
	  else
	    message INFO read001i "" "Can not open $user's .rhosts file."
	  fi
	}
      elif [ -d "$homedir/.rhosts" ]; then
	message ALERT rcmd008a "" "User $user's .rhosts file is a directory:"
	$LS $LSGROUP -Rla "$homedir/.rhosts"
	echo " "
      elif [ -s "$homedir/.rhosts" ]; then
	check_file $user "$homedir/.rhosts"
	if [ -r "$homedir/.rhosts" ]; then
	  scan_file $user "$homedir/.rhosts"
	else
	  message INFO read001i "" "Can not open $user's .rhosts file."
	fi
      fi
    }
  done
}

file_rhosts ()
{
  while read user userid homedir
  do
    if [ -f "$homedir/.rhosts" ]
    then
            if [ "root" != "$user" ] ; then
               message WARN rcmd016w "" "User $user has a .rhosts file"
	    else
               message ALERT rcmd017a "" "Root has a .rhosts file"
	    fi
    	    fileid=`$LS -anl "$homedir/.rhosts"  | $AWK '{print $3}'`
# BUG: If no fileid is returned then there must be an error above
# (notice we use userids instead of names here just like in the spool checks)
	    [ -n "$fileid" ] && {
	    	fowner=`$AWK -F: '$3 ~ /^'$fileid'$/ { print $1 }' $passwd_set | $HEAD -1`
	    	[ $userid -ne $fileid ] && \
            	  message ALERT rcmd017a "" "User $user's .rhosts file does not belong to him, belongs to the user $fowner (user id $fileid)"
	    }
    fi
  done
}

saveifs="$IFS"

# By default, a warning will be issued for any host in any .rhosts file.
rhostcase='*) note=1;;'

[ -n "$RHOST_SITES" ] && {
  rhostcase=
  set -f
  for site in $RHOST_SITES
  do
    rhostcase="$rhostcase ${site}) note=0;;"
  done
  set +f
}

{
  [ -s /etc/hosts.equiv ] && {
    {
      hosts=
      netgroup=
      line=1
      while read entry
      do
	case $entry in
	  +@*) netgroup="${netgroup}`echo $entry | $SED -e 's/^+@//'` ";;
	  +|'+ '*) message FAIL rcmd009f "" "/etc/hosts.equiv contains '+'.";;
	  -*) [ $line -eq 1 -a "$HASHOSTEQUIVBUG" != 'N' ] && {
	    message FAIL rcmd011f "" "First line of hosts.equiv has leading '-'."
	    }
	  ;;
	  '#'*) {
	    # Linux allows comments in rhosts files
	    [ "$OS" != "Linux" ] && {
	    message WARN rcmd013w "" "/etc/hosts.equiv contains an attempted comment line"
	    hosts="$hosts
$entry"
	    }
	  }
	  ;;
	  *) hosts="$hosts
$entry";;
	esac
	line=`$EXPR $line + 1`
      done
      
      [ -n "$netgroup" ] && {
	hosts="$hosts `$BASEDIR/util/getnetgroup $netgroup`"
      }
      [ -n "$hosts" ] && {
	message WARN rcmd010w "" "/etc/hosts.equiv contains the following hosts:"
	wildcard=0
	saveifs=$IFS
	IFS="
"
	for host in $hosts
	do
	  IFS=$saveifs
	  if [ ! -n "$host" ]; then
	    :
	  elif [ "$host" = '*' ]; then
	    wildcard=1
	    echo " (netgroup wildcard)"
	  else
	    case "$host" in
	      ' '*) echo "$host";;
	      *) echo " $host";;
	    esac
	  fi
	  IFS="
"
        done
        IFS=$saveifs
	[ $wildcard -eq 1 ] &&
	  message FAIL rcmd012f "" "Wildcard netgroup member (,,)."
      }
    } < /etc/hosts.equiv
  }

  {
    if [ -n "$Tiger_PasswdFiles" ]; then
      [ -f $Tiger_PasswdFiles ] && $CAT "$Tiger_PasswdFiles" > $WORKDIR/pass.list.$$
    else
      $GEN_PASSWD_SETS $WORKDIR/pass.list.$$
    fi
  }

  [ -r $WORKDIR/pass.list.$$ ] && {
  while read passwd_set
  do
    echo
    echo "# Checking accounts from `$CAT $passwd_set.src`..."
  
    $AWK -F: '{print $1, $6}' $passwd_set |
    $BASEDIR/util/${GETFSHOST:=getfs-std} > $WORKDIR/home.hosts.$$
    $AWK -F: '{ printf("%s %s\n", $1, $6); }' $passwd_set |
    $JOIN -o 1.1 1.2 2.3 - $WORKDIR/home.hosts.$$ |
    process_passwd

    $AWK -F: '{print $1, $3, $6}' $passwd_set |
    file_rhosts

    [ ! -n "$Tiger_PasswdFiles" ] && delete $passwd_set $passwd_set.src
    delete $WORKDIR/home.hosts.$$
  done < $WORKDIR/pass.list.$$
  }

  delete $WORKDIR/pass.list.$$
} |
$OUTPUTMETHOD

#
exit 0
