#!/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_passwdformat - jfs - October 2002
#
# 10/13/2002 - check_passwdformat
#       This script checks the format of the /etc/passwd file in order to
#       determine inconsistencies which indicate an intrusion 
#       or misconfiguration.
#       This script is based on SuSE's and OpenBSD's daily security check.
# 08/08/2003 - jfs - Improved temporary file creation.
# 10/07/2003 - jfs - Delete temporary files created.
# 10/10/2003 - jfs - Improved message and allowed daemon in uid 1 (for
#                    debian bug #211328). Also added password content 
#                    check and do not warn on lengths for locked users.
#                    (fixes debian bug 211327). Finally, added missing
#                    temporary files to removal and safe_temp calls
#
#-----------------------------------------------------------------------------
#
# This is the directory Tiger is installed on
TigerInstallDir='.'

#
# 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 UNIQ SORT TEE COLUMN GREP CAT JOIN LS RM || exit 1
  haveallfiles BASEDIR WORKDIR || exit 1
  
  echo "--CONFIG-- [init003c] $0: Configuration ok..."
  exit 0
}

#------------------------------------------------------------------------
echo
echo "# Checking the format of passwd and group files."

# Note /etc/passwd is not checked for since its not on the
# config files.
haveallcmds AWK UNIQ SORT TEE COLUMN GREP CAT JOIN LS RM || exit 1
haveallfiles BASEDIR WORKDIR || exit 1

# message FAIL XXXX0??f "" "MESSAGE TO WRITE IN REPORT"


OUT=$WORKDIR/passwdcheck.$$
TMPFILE=$WORKDIR/passwdcheck.tmp.$$
safe_temp $OUT $TMPFILE ${TMPFILE}-1 ${TMPFILE}-2
trap 'delete  $OUT $TMPFILE ${TMPFILE}-1 ${TMPFILE}-2 ; exit 1' 1 2 3 15
 
#
# /etc/passwd check
#
PW="/etc/passwd"
[ -r $PW ] &&  {
$AWK -F: '{
        if ($0 ~ /^[ 	]*$/) {
                printf("Line %d is a blank line.\n", NR);
                next;
        }
        if (NF != 7)
                printf("Line %d has the wrong number of fields.\n", NR);
        if ($1 ~ /^[+-]$/)
                next;
        if ($1 == "")
                printf("Line %d has an empty login field.\n", NR);
        else if ($1 !~ /^[A-Za-z0-9][A-Za-z0-9_\.-]*$/)
                printf("Login %s has non-alphanumeric characters.\n", $1);
        if (length($1) > 8)
                printf("Login %s has more than 8 characters.\n", $1);
        if ($2 == "")
                printf("Login %s has no password.\n", $1);
        else if ($2 !~ /^[x*!]+$/)
		printf("Login %s has a real password (it is not shadowed).\n", $1);
#if ($2 != "" && length($2) != 13 && ($10 ~ /.*sh$/ || $10 == "") &&
#   ($2 !~ /^\$[0-9a-f]+\$/)) {
#       if (system("test -d "$9" -a ! -r "$9"") == 0)
#          printf("Login %s if off but still has valid shell and home directory is unreadable\n\t by root; cannot check for existance of alternate access files.\n", $1);
#       else if (system("for file in .ssh .rhosts .shosts .klogin;
#               do if test -e "$9"/$file; then if ((ls -ld "$9"/$file | cut -b 2-10 | grep -q r) && (test ! -O "$9"/$file)) ; then exit 1; fi; fi; done"))
#                  printf("Login %s is off but still has a valid shell and alternate access files in\n\t home directory are still readable.\n",$1);
#}
        if ($3 == 0 && $1 != "root")
                printf("Login %s has a user id of 0 which should be reserved for root\n", $1);
        if ($3 == 1 && $1 != "bin" && $1 != "daemon" )
		printf("Login %s has a user id of 1 which should be reserved for bin or daemon\n", $1);
        if ($3 < 0)
                printf("Login %s has a negative user id.\n", $1);
        if ($4 < 0)
                printf("Login %s has a negative group id.\n", $1);
	if ($4 == 0 && $1 != "root")
		printf("Login %s has a group id of 0 which should be reserved for root\n", $1);
	if ($4 == 1 && $1 != "bin" && $1 != "daemon" )
		printf("Login %s has a group id of 1 which should be reserved for bin or daemon.\n", $1);
}' < $PW > $OUT

if [ -s "$OUT" ] ; then
        $CAT "$OUT" |
	while read message; do
		message FAIL pass009e "" "$message"
	done
fi

$AWK -F: '{ print $1 }' $PW | $SORT | $UNIQ -d > $OUT
if [ -s "$OUT" ] ; then
	message FAIL pass001w "" "File $PW has duplicate user names"
        $COLUMN "$OUT"
fi
$AWK -F: '{ print $1 " " $3 }' $PW | $SORT -n +1 | $TEE ${TMPFILE}-1 |
$UNIQ -d -f 1 | $AWK '{ print $2 }' > ${TMPFILE}-2
if [ -s "${TMPFILE}-2" ] ; then
	message FAIL pass001w "" "File $PW has duplicate user ids:"
        while read uid; do
                $GREP -w $uid ${TMPFILE}-1
        done < ${TMPFILE}-2 | $COLUMN
fi
}
#
# /etc/shadow check
#
PW="/etc/shadow"
[ -r $PW ] &&  {
$AWK -F: '{
        if ($0 ~ /^[ 	]*$/) {
                printf("Line %d is a blank line.\n", NR);
                next;
        }
        if (NF != 9)
                printf("Line %d has the wrong number of fields.\n", NR);
        if ($1 ~ /^[+-]$/)
                next;
        if ($1 == "")
                printf("Line %d has an empty login field.\n", NR);
        else if ($1 !~ /^[A-Za-z0-9][A-Za-z0-9_-]*$/)
                printf("Login %s has non-alphanumeric characters.\n", $1);
        if (length($1) > 8)
                printf("Login %s has more than 8 characters.\n", $1);
        if ($2 == "")
                printf("Login %s has no password.\n", $1);
	if ($2 != "" && length($2) != 13 && length($2) != 34 &&
	    length($2) != 1 && $2 !~ /^\$[0-9a-f]+\$/ && $2 !~ /^!/ )
		printf("Login %s has an unsual password field length.\n", $1);
	if ($2 != "" && $2 !~ /^\$[0-9a-f]+\$/ && $2 !~ /^[!*]/ )
		printf("Login %s has an unsual password content.\n", $1);
}' < $PW > "$OUT"

if [ -s "$OUT" ] ; then
        $CAT "$OUT" |
	while read message; do
		message FAIL pass009e "" "$message"
	done
fi
$AWK -F: '{ print $1 }' $PW | $SORT | $UNIQ -d > $OUT
if [ -s "$OUT" ] ; then
	message FAIL pass001w "" "File $PW has duplicate user names:"
        $COLUMN "$OUT"
fi
}
#
# /etc/group checking
#
GRP=/etc/group
[ -r $GRP ] &&  {
$AWK -F: '{
        if ($0 ~ /^[	 ]*$/) {
                printf("Line %d is a blank line.\n", NR);
                next;
        }
        if ($1 ~ /^[+-]$/)
                next;
        if (NF != 4)
                printf("Line %d has the wrong number of fields.\n", NR);
        if ($1 !~ /^[A-za-z0-9][A-za-z0-9_-]*$/)
                printf("Group %s has non-alphanumeric characters.\n", $1);
        if (length($1) > 8)
                printf("Group %s has more than 8 characters.\n", $1);
        if ($3 !~ /[0-9]*/)
                printf("Login %s has a negative group id.\n", $1);
        if (length($4) > 0 && $3 < 3)
		printf("Group %s(%s) has got the following members: %s\n", $1, $3, $4);
}' < $GRP > $OUT

if [ -s "$OUT" ] ; then
        $CAT "$OUT" |
	while read message; do
		message FAIL pass009e "" "$message"
	done
fi

$AWK -F: '{ print $1 }' $GRP | $SORT | $UNIQ -d > $OUT

if [ -s "$OUT" ] ; then
	message FAIL pass010w "" "File $GRP has duplicate group ids:"
        $COLUMN "$OUT"
fi
}

delete $OUT $TMPFILE ${TMPFILE}-1 ${TMPFILE}-2
exit 0
