#!/bin/bash

usage () {
	echo "Usage: $0 [OPTIONS]"
	echo "    reconfigure system into EAL4 evaluated configuration"
	echo "Options:"
	echo "    -h|--help         show help"
	echo "    -i|--interactive  run interactively (default)"
	echo "    -a|--automated    run noninteractively"
	echo "    -v|--verbose      print detailed information while running"
}

args="$*"

base=/usr/lib/eal4
lib=$base/lib
funcs=$base/functions

LOGFILE=/var/log/certification-sles-ibm-eal4

# get all functions that this script needs:
for f in $funcs/*.sh; do
  . $f
done

# run various sanity checks before proceeding

if [ "`/usr/bin/id -nu`" != "root" ]; then
  die "This script must be run as root."
fi

if [ ! -f /proc/1/cmdline ]; then
  die "/proc is not mounted. Running a test?"
fi

if mount | grep ' / ' | grep "type ext3.*,acl" >/dev/null; then :; else
  die "root filesystem must be ext3 with ACL support on. See ECG."
fi

if mount | grep "type nfs"; then
  die "please unmount all NFS shares first. See ECG."
fi

if cat /etc/fstab | sed 's/#.*//; s/\t/ /g' | grep ' subfs '; then
  die "please convert subfs entries to iso9660 in /etc/fstab first. See ECG."
fi

if cat /etc/fstab | sed 's/#.*//; s/\t/ /g' | grep ' debugfs '; then
  die "please remove debugfs entry from /etc/fstab first. See ECG."
fi

if grep 'trusted:.*:.*:.' /etc/group >/dev/null; then :; else
  die "No trusted users. You won't be able to use 'su'. See ECG."
fi

# special case to avoid a common cause of confusion - xinetd is not
# in the default install and people keep forgetting it.
#
if rpm -q xinetd >/dev/null 2>&1; then :; else
  die "You need to have xinetd installed to proceed. See ECG."
fi

if [ `arch` = "ia64" ]; then
  if mount | grep `df /boot/efi | sed 1d | awk '{print $1}'` | grep "umask=077" >/dev/null; then :; else
    die "Filesystem containing /boot/efi must be mounted with option 'umask=077'. See ECG."
  fi
fi

if mount | grep "type nfs"; then
  die "please unmount all NFS shares first. See ECG."
fi

if [ `rpm -q --queryformat='%{ARCH}\n' pwdutils` = 'ppc64' ]; then
    die "You MUST replace the 64bit version of pwdutils with the 32bit version to match the audit plugin. See ECG."
fi

if [ `rpm -q --queryformat='%{ARCH}\n' pwdutils` = 'ppc64' ]; then
    die "You MUST replace the 64bit version of pwdutils with the 32bit version to match the audit plugin. See ECG."
fi

FileExe () {
    file "$1" | sed 's/.*\(ELF[^,]*-bit\).*/\1/'
}

if [ "`FileExe /usr/sbin/sshd`" != "`FileExe /sbin/pam_tally`" ]; then
    file /usr/sbin/sshd /sbin/pam_tally
    die "sshd and pam_tally executables don't match, please reinstall pam and/or openssh. See ECG."
fi

# if the tests above were okay, let's proceed.

interactive="yes"
verbose=""

# commandline parsing
while [ ! -z "$1" ]; do
  case $1 in
    -h|--help)
	usage
	exit 0
	;;
    -i|--interactive)
	interactive=yes
	shift
	;;
    -a|--automated)
	interactive=""
	shift
	;;
    -v|--verbose)
	verbose="yes"
	shift
	;;
    *)
    	usage
	exit 1
	;;
  esac
done


# open logfile with first log:
logn "  ---  `date` script running: $0 args: $args"

if [ ! -z "$interactive" ]; then
	if [ ! -t 0 ]; then
		die "Interactive mode requested, but stdin is not a terminal"
	fi

	echo "You have chosen to run the reconfiguration in interactive mode."
	echo "The evaluated configuration requires that *all* the steps are"
	echo "done. If you want to do this automatically, stop and re-run the"
	echo "script in noninteractive mode ('-a' option)."
	echo 
	echo "The reconfiguration involves removing packages and modifying the"
	echo "system configuration, which may result in a system that is"
	echo "not useful to you. For example, the X11 desktop is removed."
	echo
	echo "Please read the documentation before proceeding."
	echo
	if ask "Continue?" "n"; then :; else
		die "Aborted. Your system was not modified."
	fi
fi

## bootloader configuration: grub needs no action, but if lilo is installed...
#. /etc/sysconfig/bootloader
#case "$LOADER_TYPE" in
#  elilo)
#	;;
#  *lilo*)
#        log "Your system uses lilo to load the kernel on system startup."
#        log "The evaluated configuration does not support lilo as bootloader."
#        log "It is necessary that you install the grub bootloader and configure"
#        log "it so that your system will boot safely. The lilo bootloader will"
#	log "either be removed automatically during the further processing of"
#	log "this script, or you remove the package yourself (rpm -e lilo)"
#        die "The script stops here."
#        ;;
#    *)
#        ;;
#esac


# rebuild the rpm database first before touching any packages:
if confirm "Running rpm --rebuilddb before package removal."; then
    rpm --rebuilddb
else
    log "rpm --rebuilddb was aborted."
fi


# package removal first.
ALLINSTALLEDPACKAGES=`rpm -qa --queryformat='%{NAME}\n'`
logn "installed packages on the system: $ALLINSTALLEDPACKAGES"

PREREQPACKAGES=`cat $lib/packagelist.required`
ALLCERTIFIEDPACKAGES=`cat $lib/packagelist.eal4`
ALLTOLERATEDPACKAGES=`cat $lib/packagelist.tolerated`
pack_to_be_removed=""   # reminder...

for packreq in $PREREQPACKAGES; do
  packisok=""
  for packinst in $ALLINSTALLEDPACKAGES; do
    if [ "$packinst" = "$packreq" ]; then
      packisok="$packinst"
    fi
  done
  if [ -z "$packisok" ]; then
    die "Required prerequisite package '$packreq' missing, aborting."
  fi
done

for packinst in $ALLINSTALLEDPACKAGES; do
  packisok=""
    for packeal in $ALLCERTIFIEDPACKAGES $ALLTOLERATEDPACKAGES; do
	if [ "$packeal" = "$packinst" ]; then
	    packisok="$packinst"
	fi
    done
  if [ -z "$packisok" ]; then
      pack_to_be_removed="$pack_to_be_removed $packinst"
  fi
done

pack_to_be_removed=`echo $pack_to_be_removed`
if [ ! -z "$pack_to_be_removed" ]; then
    log "I want to remove the following packages:"
    log "$pack_to_be_removed"

    if confirm "Removing these RPMs from the system."; then
      logn "running: rpm -e --allmatches $pack_to_be_removed"
      rpm -e $pack_to_be_removed || die "rpm package removal was unsuccessful.\
      Please do it manually. You can find the list of packages to be removed in the logfile $LOGFILE."
    else
      log "removal of packages has been aborted"
      failure=1
    fi
fi

# need the correct architecture - checking the 'glibc' arch via rpm doesn't
# work on ppc64, since it returns 'ppc'. Use plain 'arch' which gets the right
# result. Determining compatible RPMs is done below.
arch=`arch`

installed_kernel=`rpm -qf --queryformat='%{NAME}\n' /boot/vmlinuz 2>/dev/null | tail -1`
if [ -z "$installed_kernel" ]; then
  # that didn't work, plan B...
  # There's not /boot/vmlinuz on iSeries.
  installed_kernel=`rpm -qa --queryformat='%{NAME}\n' \
  | egrep '^(kernel-)' | egrep -v 'tools|source' | tail -1`
fi
[ -z "$installed_kernel" ] && die "Can't figure out the installed kernel version. Giving up."
logn "installed kernel is: $installed_kernel"

# package installation:
# Get all files compatible with the architecture from the rpm directory

UPDATE_RPM_FILE="$lib/packagelist.updates-needed"

case "$arch" in
i386)
	archpacks="$base/rpm/*.i386.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep '(i386|noarch).rpm' $UPDATE_RPM_FILE`
	;;
i486)
	archpacks="$base/rpm/*.i[34]86.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep '(i[34]86|noarch).rpm' $UPDATE_RPM_FILE`
	;;
i586)
	archpacks="$base/rpm/*.i[345]86.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep '(i[345]86|noarch).rpm' $UPDATE_RPM_FILE`
	;;
i686)
	archpacks="$base/rpm/*.i[3456]86.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep '(i[3456]86|noarch).rpm' $UPDATE_RPM_FILE`
	;;
ppc64)
	#if grep '^machine.*:.*CHRP' /proc/cpuinfo > /dev/null; then
	#	# pSeries
	#elif grep '^machine.*:.*iSeries' /proc/cpuinfo > /dev/null; then
	#	# iSeries
	#fi
	archpacks="$base/rpm/*.ppc.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep '(ppc|noarch).rpm' $UPDATE_RPM_FILE`
	;;
*)
	archpacks="$base/rpm/*.$arch.rpm $base/rpm/*.noarch.rpm"
	updates_needed=`egrep "($arch|noarch).rpm" $UPDATE_RPM_FILE`
	;;
esac

# Install the packages, and check for any that need special handling.

packstoinstall=""
new_kernel=""
for pack in $archpacks; do
  [ ! -f "$pack" ] && break

  name_of_package=`rpm -qp --queryformat='%{NAME}\n' $pack`
  logn "running: rpm --checksig $pack (package name: $name_of_package)"
  rpm --checksig $pack > /dev/null 2>&1
  if [ "$?" = 0 ]; then
    logn "  $pack sigcheck ok"
    case "$name_of_package" in
	kernel-*)
	    if [ "$name_of_package" = "$installed_kernel" ]; then
		packstoinstall="$packstoinstall $pack"
		new_kernel=`basename $pack`
	    fi
	    ;;
	#glibc*)
	#    die "glibc upgrade not supported. Use service pack to do that."
	#    ;;
	*)
	    packstoinstall="$packstoinstall $pack"
	    ;;
    esac
  else
    die "checksig: package signature check for package $pack failed."
  fi
done

# check that the list of packages to install contains all required ones

missing_req_pack=""
for req_pack in $updates_needed; do
  found=
  for pack in $packstoinstall; do
    if [ `basename $pack` = "$req_pack" ]; then
      found=y
      break
    fi
  done

  # Kernel is once again a special case, don't complain if we
  # have a valid kernel (ie _smp) we want to install and a different
  # kernel (ie _deflt) is missing.
  if expr "$req_pack" : "kernel-" >/dev/null; then
    if [ -z "$found" ]; then
      # Did we pick a kernel to install from $base/rpm/ ?
      if [ "$new_kernel" ]; then
	# Is it listed in the list of required updates ?
	if echo "$updates_needed" | grep "$new_kernel" >/dev/null; then
	  found=y
	fi
      fi
    fi
  fi

  if [ -z "$found" ]; then
    missing_req_pack="$missing_req_pack $req_pack"
  fi
done

if [ "$missing_req_pack" ]; then
  die "The following required packages are missing from $base/rpm (see ECG for maintenance web download instructions): $missing_req_pack"
fi

if [ "$packstoinstall" ]; then
  log "I want to install all packages from $base/rpm."
  log "The list of packages is $packstoinstall"
  if confirm "Installing these packages."; then
    logn "running: rpm --oldpackage --force --nodeps -Uhv $packstoinstall"
    rpm --oldpackage --force --nodeps -Uhv $packstoinstall || die "rpm package installation failed.\
       Please do it manually. The package list can be found in $LOGFILE"
  else
    log "Installation of packages aborted."
    failure=1
  fi
fi



# runlevel link removal:
if confirm "Removing all runlevel links from /etc/init.d/rc3.d."; then
  ( cd /etc/init.d/rc3.d; rm -f * nosuchfileordirectory_dummy )
  log "all runlevel symlinks removed."
else
  log "Removal of runlevel symlinks aborted."
  failure=1
fi


PERMITTED_SERVICES=`cat $base/lib/permitted_services`
logn "permitted services: $PERMITTED_SERVICES"
if confirm "Making links in /etc/init.d/rc3.d for all allowed services."; then
  for service in $PERMITTED_SERVICES; do
    logn "insserv /etc/init.d/$service"
    insserv /etc/init.d/$service
  done
  log "new runlevel symlinks created."
else
  log "new runlevel symlink creation in /etc/init.d/rc3.d aborted."
  failure=1
fi

for file in `cd $base; find etc -type f`; do
  if confirm "Replacing the file /$file."; then
    logn "running: replace $file"
    replace $file
  else
    log "replacement of file /$file aborted."
    failure=1
  fi
done

#if confirm "Installing new and updated manpages."; then
#  log "Replacing manpages from $base/man to system paths."
#  cd $base/man
#  find . -type f -print0 | xargs -0 tar cf - | (cd /usr/share/man ; \
#	tar xfvvp -)
#  logn "manpages replaced."
#else
#  log "Replacing of manpages from $base/man to system paths aborted."
#  failure=1
#fi


# permissions of files:

if confirm "Removing setuid/setgid bits from all files in the system."; then
    output=`find / \( ! -fstype ext3 -prune -false \) -o  \
        -type f \( -perm -4000 -o -perm +2000 \) -print0 | \
	xargs -0 chmod -v u-s,g-s 2>&1`
    log "$output"
else
    log "setuid/setgid bit removal aborted."
    failure=1
fi

if confirm "Setting permissions according to /etc/permissions.eal4."; then
    logn "running: chkstat -set /etc/permissions.eal4"
    chkstat -set /etc/permissions.eal4
    log "Permissions are set (/etc/permissions.eal4)"
else
    log "setuid and setgid bits setting aborted (/etc/permissions.eal4)"
    failure=1
fi

# default runlevel:
if confirm "Changing default runlevel to 3."; then
    logn "changing default runlevel to 3"
    awk '/^id:.:initdefault:$/ {
		print "id:3:initdefault:"; next; }
	    { print $0; }' < /etc/inittab > /etc/inittab.new
    logn "running: old /etc/inittab"
    old /etc/inittab
    logn "running: mv /etc/inittab.new /etc/inittab"
    mv /etc/inittab.new /etc/inittab
else
    log "initdefault change to 3 aborted."
    failure=1
    bootfail=1
fi

if [ -x /sbin/zipl ]; then
	if confirm "Run 'zipl' to update the boot loader configuration?"; then
		zipl
	fi
fi

# finally:
if [ -z "$bootfail" -a -z "$failure" ]; then
    log "Reconfiguring the system to the evaluated configuration is complete."
    log "It is now necessary to reboot the system."
    if confirm "Rebooting the system."; then
	log "rebooting the system now. Sleeping for 10 seconds..."
	exec 42>&-	# close logfile...
	sleep 10
	logn "running: /sbin/init 6"
	/sbin/init 6
	echo "Waiting to be killed..."
	sleep 600
    else
	log "reboot aborted. Please note that the system must be rebooted for"
	log "the configuration to be complete."
	failure=1
    fi
fi



