#!/usr/bin/perl
#
# $Revision: 1.35 $
# Copyright 2009 Teleflora
# 
# Script used to harden a daisy server towards PCI/PABP compliance.
#

use strict;
use warnings;
use Getopt::Long;
use POSIX;
use English;

my $CVS_REVISION = '$Revision: 1.35 $';

my $OS = "";
my $ETCDIR = "";
my $HELP = 0;
my $INSTALL = 0;
my $VERSION = 0;
my $IPTABLES = 0;
my $SERVICES = 0;
my $BASTILLE = 0;
my $IPV6 = 0;
my $TIME = 0;
my $LOGGING = 0;
my $HOSTSALLOW = 0;
my $IDS = 0;
my $SSH = 0;
my $PAM = 0;
my $SUDO = 0;
my $pid = -1;

GetOptions (
	"install" => \$INSTALL,
	"iptables" => \$IPTABLES,
	"ipv6" => \$IPV6,
	"hostsallow" => \$HOSTSALLOW,
	"services" => \$SERVICES,
	"bastille" => \$BASTILLE,
	"help" => \$HELP,
	"sudo" => \$SUDO,
	"pam" => \$PAM,
	"time" => \$TIME,
	"logging" => \$LOGGING,
	"ids" => \$IDS,
	"ssh" => \$SSH,
	"etcdir=s" => \$ETCDIR,
	"version" => \$VERSION
);


# --version
if ($VERSION != 0) {
	print("$0 $CVS_REVISION\n");
	exit(0);
}


# --help
if ($HELP != 0) {
	usage();
	exit(0);
}


#
# The user is allowed to specify location of sudoers file; if they don't,
# default to standard location.  Why do we allow this?  It's very helpful
# in testing this program
#
# assert: if $ETCDIR is non-null, it was set on the command line
# print("ETCDIR = $ETCDIR\n");
#
if ($ETCDIR eq "") {
	$ETCDIR = "/etc";
}
if (! -d $ETCDIR) {
	print("Error: System config directory (/etc) not found... exiting\n");
	exit(1);
}

#
# assert: $POSDIR and $ETCDIR defined and exist
# print("POSDIR = <$POSDIR> ETCDIR = <$ETCDIR>\n");
#


determine_os();

showinfo("Starting $0 $CVS_REVISION");
showinfo("This is the \"$OS\" Operating System");


#--install
if($INSTALL != 0) {
	showinfo("Running Full System Setup...");
	$IPTABLES = 1;
	$IPV6 = 1;
	$HOSTSALLOW = 1;
	$SERVICES = 1;
	$BASTILLE = 1;
	$SUDO = 1;
	$PAM = 1;
	$TIME = 1;
	$LOGGING = 1;
	$SSH = 1;
}
	


# --iptables
if($IPTABLES != 0) {
	unless ($OS eq "RH72") {
		harden_iptables();
	}
}

# --ipv6
if($IPV6 != 0) {
	disable_ipv6();
}

# --hostsallow
if($HOSTSALLOW != 0) {
	modify_host_access();
}

# --pam
if($PAM != 0) {
	modify_securetty($ETCDIR);
	setup_password_policies();
	modify_pamsu($ETCDIR);
}

#--sudo
if($SUDO != 0) {
	modify_sudoers($ETCDIR);
}

#--services
if($SERVICES != 0) {
	setup_system_services();
}

#--bastille
if($BASTILLE != 0) {
	modify_bastille();
}

#--logging
if($LOGGING != 0) {
	modify_logrotate($ETCDIR);
}

#--ssh
if($SSH != 0) {
	modify_sshd();
}

#--time
if($TIME != 0) {
	modify_timeservers();
}

#--ids
#Do this last.
if($IDS != 0) {
	update_aide(); 
}

showinfo("Finished $0 $CVS_REVISION");


exit (0);

######################################################################
######################################################################
######################################################################




sub usage
{
	print ("\n");
	print("$0 $CVS_REVISION Usage:\n");
	print(<< "EOF");
$0 --help
$0 --version
$0 --install

-- Advanced Options --
$0 --iptables
$0 --ipv6
$0 --hostsallow
$0 --services
$0 --bastille
$0 --sudo
$0 --pam
$0 --time
$0 --logging
$0 --ids
$0 --ssh

EOF
}


sub determine_os
{
	$OS = "";

	# We could also use /usr/bin/lsb-release
	open(FILE, "< /etc/redhat-release");
	while(<FILE>) {

		# Fedora Core 3
		if(/(Fedora)(\s+)(Core)(\s+)(release)(\s+)(3)/) {
			$OS = "FC3";
			last;
		}

		# Fedora Core 4
		if(/(Fedora)(\s+)(Core)(\s+)(release)(\s+)(4)/) {
			$OS = "FC4";
			last;
		}

		# Fedora Core 5
		if(/(Fedora)(\s+)(Core)(\s+)(release)(\s+)(5)/) {
			$OS = "FC5";
			last;
		}

		# ES 6
		# Redhat Enterprise Linux Server 6
		if( /(release)(\s+)(6)/ ) {
			$OS = "RHEL6";
			last;
		}

		# ES 5
		if( (/(release)(\s+)(5)/)
		||  (/(CentOS)([[:print:]]+)(\s)(5)/) ) {
			$OS = "RHEL5";
			last;
		}

		# EL 4
		if( (/(release)(\s+)(4)/) 
		||  (/(CentOS)([[:print:]]+)(\s)(4)/) ) {
			$OS = "RHEL4";
			last;
		}

		# Redhat 7.2
		if(/(release)(\s+)(7\.2)/) {
			$OS = "RH72";
			last;
		}
	}
	close(FILE);
}



#
# Setup System services
# PCI-DSS 2.2.1
# PCI-DSS 2.2.2
#
sub setup_system_services
{
	my @whitelist = ();
	my @array = ();
	my $thisfile = "";
	my $servicename = "";

	showinfo("Enable System Services...");

	# Services we *do* want running.
	foreach $servicename (
	"acpid",
	"apcupsd",
	"altiris",
	"anacron",
	"atd",
	"blm",
	"bbj",
	"auditd",
	"cpuspeed",
	"crond",
	"cups",
	"cups-config-daemon",
	"daisy",
	"dgap",
	"dgrp_daemon",
	"dgrp_ditty",
	"dsm_sa_ipmi",
	"firstboot",
	"httpd",
	"instsvcdrv",
	"ipmi",
	"iptables",
	"irqbalance",
	"lm_sensors",
	"lpd",
	"lvm2-monitor",
	"mdmonitor",
	"mdmpd",
	"messagebus",
	"microcode_ctl",
	"multipathd",
	"network",
	"ntpd",
	"readahead_early",
	"readahead_later",
	"restorecond",
	"rhnsd",
	"sendmail",
	"smartd",
	"smb",
	"sshd",
	"syslog",
	"sysstat",
	"tfremote",
	"yum", #Cent 4.4
	"yum-updatesd",
	"PBEAgent",
	) {
		next unless (-f "/etc/init.d/$servicename");
		next if ( ($servicename eq 'cups-config-daemon') && ($OS eq 'RHEL5') );
		next if ( ($servicename eq 'ipmi') && (! -e '/dev/ipmi0') );
		push(@whitelist, $servicename);
	}

	showinfo("Hardening System Services...");

	# As a rule of thumb, if we don't know what it is, then, turn it off.
	open(PIPE, "/sbin/chkconfig --list | grep 3:on |");
	while (<PIPE>) {
		@array = split(/\s+/);
		$servicename = $array[0];
		next if (grep(/^($servicename)$/, @whitelist));

		if (-f "/etc/init.d/$servicename") {

			loginfo("Disabling System Service $servicename");
			system("/sbin/chkconfig $servicename off");
			# Stopping netfs will unmount loopback filesystems (ie, our install media)
			next if ($servicename eq "netfs");
			system("/sbin/service $servicename stop");
		}
	}
	close(PIPE);


	# Start whitelisted services if not already started.
	foreach $servicename (@whitelist) {
		next if ($servicename eq "iptables");

		loginfo("Enabling System Service $servicename");
		system("/sbin/chkconfig $servicename on");

		next if ($servicename eq "daisy");
		my $service_status = sys_service_status($servicename);
		next if ($service_status eq "running");

		loginfo("Starting System Service $servicename");
		unless ( ("$OS" eq "RH72") && $servicename eq 'sshd') {
			system("/sbin/service $servicename start");
		}
	}
}


sub sys_service_status
{
	my $servicename = $_[0];
	my $retval = "";
	my $running_re = ' is running\.\.\.';

	open(PIPE, "/sbin/service $servicename status |");
	while (<PIPE>) {
		chomp;
		if (/$running_re/) {
			$retval = "running";
			last;
		}
	}
	close(PIPE);

	return($retval);
}


sub modify_bastille
{

	showinfo("Mods per the Bastille Linux Report...");

	#
	# As per bastille linux report...
	# Remove suid for mount/unmount
	# Ie, one needs 'sudo' to run these.
	#
	foreach my $thisfile("/bin/mount",
	"/bin/umount",
	"/bin/traceroute",
	"/sbin/dump",
	"/sbin/restore",
	"/usr/bin/at",
	"/usr/sbin/usernetctl"
	) {
		system("chmod a-s $thisfile");
	}
	system("/sbin/service atd restart");


	# (Bastille Linux report)
	# Remove rlogin, rsh, etc.
	system("rpm --erase krb5-workstation > /dev/null 2> /dev/null");
	system("rpm --erase rsh > /dev/null 2> /dev/null");
}


#
# Modifications to ensure PCI 8.5.x
#
# PCI 8.5
#	Ensure proper user authenticaion and password management for
#	non-consumer users and administrators on all system components
#	as follows:
#	8.5.1
#		Control addition, deletion, and modification of user IDs,
#		credentials, and other identifier objects.
#	8.5.10
#		Require a minimum password length of at least 7 chars.
#	8.5.11
#		Use passwords containing both numeric and alpha chars.
#	8.5.12
#		Do not allow an individual to submit a new password that is
#		the same as any of the last four passwords used.
#
sub setup_password_policies
{
	my $pam_tally = "";
	my $pam_tally_rule = "";
	my $script_rev = $CVS_REVISION;

	showinfo("Hardening Password Policies...");

	# Enforce that users in the "dsyadmins" group may only have 1
	# simultaneious login at a time.
	#
	# Yes, this also applies to the 'tfsupport' user as well.
	# PABP 3.1 /  PCI 8.5.8
	loginfo("Setting up limits.conf");
	system("cp /etc/security/limits.conf /etc/security/limits.conf.$$");
	open(OLDFILE, "< /etc/security/limits.conf.$$");
	open(NEWFILE, "> /etc/security/limits.conf");
	while(<OLDFILE>) {
		next if(/^(\s*)(\%rtiadmins)/);
		next if(/^(\s*)(\%dsyadmins)/);
		next if(/^(\s*)(root)/);
		next if(/^(\s*)(tfsupport)/);
		print(NEWFILE);
	}
	#print(NEWFILE "\%dsyadmins\t-\tmaxlogins\t1\n");
	print(NEWFILE "root\t-\tmaxlogins\t10\n");
	print(NEWFILE "tfsupport\t-\tmaxlogins\t10\n");
	close(OLDFILE);
	close(NEWFILE);
	if(-z "/etc/security/limits.conf") {
		system("cp /etc/security/limits.conf.$$ /etc/security/limits.conf");
	}


	#
	# PCI 8.5.13
	#	Limit repeated access attempts by locking out the user ID
	#	after not more than 6 attempts.
	#
	# PCI 8.5.14
	#	Set the lockout duration to a minimum of 30 minutes or until
	#	administrator enables the user ID.
	#
	# http://searchenterrpiselinux.techtarget.com/tip/0,289483,sid39_gci1213570,00.html
	# Setup s.t. faillog is being used on a per-user basis. (Faillog settings are made in dsyuser.pl)
	#
	loginfo("Setting up pam.d rules.");

	# Older RHEL 4, Cent 4.4
	$pam_tally = "pam_tally.so";
	$pam_tally_rule = "pam_tally.so deny=6 no_magic_root per_user";


	# Updated RHEL4, RHWS5
	# Note the added "unlock_time" functionality in pam_tally2.
	if(-f "/sbin/pam_tally2") {
		$pam_tally = "pam_tally2.so";
		$pam_tally_rule = "pam_tally2.so onerr=fail deny=6 unlock_time=1800";
	}


	open(NEWFILE, "> /etc/pam.d/system-auth");

	if("$OS" eq "RH72") {
	print(NEWFILE << "EOF");
#\%PAM-1.0
# This file installed by harden_linux.pl $script_rev for \"$OS\"
#
auth        required      /lib/security/pam_env.so
auth	    required      /lib/security/pam_tally.so no_magic_root
auth        sufficient    /lib/security/pam_unix.so likeauth nullok remember=6
auth        required      /lib/security/pam_warn.so
auth        required      /lib/security/pam_deny.so

account     required      /lib/security/pam_tally.so onerr=fail deny=6 no_magic_root
account     required      /lib/security/pam_unix.so
account     required      /lib/security/pam_permit.so

password    required      /lib/security/pam_cracklib.so retry=2 minlen=7 lcredit=-1 ucredit=-1 dcredit=-1
password    sufficient    /lib/security/pam_unix.so nullok use_authtok md5 shadow remember=4
password    required      /lib/security/pam_deny.so

session     required      /lib/security/pam_limits.so
session     required      /lib/security/pam_unix.so
session     required      /lib/security/pam_warn.so
EOF


	} elsif("$OS" eq "FC3") {
	print(NEWFILE << "EOF");
#\%PAM-1.0
# This file installed by harden_linux.pl $script_rev for \"$OS\"
#
auth        required      /lib/security/\$ISA/pam_env.so
auth	    required      /lib/security/\$ISA/pam_tally.so no_magic_root
auth        sufficient    /lib/security/\$ISA/pam_unix.so likeauth nullok remember=6
auth        required      /lib/security/\$ISA/pam_warn.so
auth        required      /lib/security/\$ISA/pam_deny.so

account     required      /lib/security/\$ISA/pam_tally.so onerr=fail deny=6 no_magic_root
account     required      /lib/security/\$ISA/pam_unix.so
account     sufficient    /lib/security/\$ISA/pam_succeed_if.so uid < 100 quiet
account     required      /lib/security/\$ISA/pam_permit.so

password    requisite     /lib/security/\$ISA/pam_cracklib.so retry=2 minlen=7 lcredit=-1 ucredit=-1 dcredit=-1
password    sufficient    /lib/security/\$ISA/pam_unix.so nullok use_authtok md5 shadow remember=4
password    required      /lib/security/\$ISA/pam_deny.so

session     required      /lib/security/\$ISA/pam_limits.so
session     required      /lib/security/\$ISA/pam_unix.so
session     required      /lib/security/\$ISA/pam_warn.so
EOF


	} elsif("$OS" eq "FC5") {
	print(NEWFILE << "EOF");
#%PAM-1.0
# This file installed by harden_linux.pl $script_rev for \"$OS\"
#
auth        required      pam_env.so
auth        required      pam_tally2.so onerr=fail deny=6 unlock_time=1800
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_warn.so
auth        required      pam_deny.so

account     required      pam_tally2.so
account     required      pam_unix.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     required      pam_permit.so

password    required      pam_warn.so
password    requisite     pam_cracklib.so retry=2 minlen=7 lcredit=-1 ucredit=-1 dcredit=-1
password    sufficient    pam_unix.so md5 shadow nullok use_authtok remember=4
password    required      pam_deny.so

session     required      pam_limits.so
session     required      pam_unix.so
session     required      pam_warn.so
EOF


	} elsif("$OS" eq "RHEL4") {

	# RHEL4 
	print(NEWFILE << "EOF");
#\%PAM-1.0
# This file installed by harden_linux.pl $script_rev for \"$OS\"
# 
auth        required      /lib/security/\$ISA/pam_env.so
auth	    required      /lib/security/\$ISA/pam_tally.so no_magic_root
auth        sufficient    /lib/security/\$ISA/pam_unix.so likeauth nullok
auth        required      /lib/security/\$ISA/pam_warn.so
auth        required      /lib/security/\$ISA/pam_deny.so

account     required      /lib/security/\$ISA/$pam_tally_rule
account     required      /lib/security/\$ISA/pam_unix.so
account     sufficient    /lib/security/\$ISA/pam_succeed_if.so uid < 100 quiet
account     required      /lib/security/\$ISA/pam_permit.so

password    requisite     /lib/security/\$ISA/pam_cracklib.so retry=2 minlen=7 lcredit=-1 ucredit=-1 dcredit=-1
password    sufficient    /lib/security/\$ISA/pam_unix.so nullok use_authtok md5 shadow remember=4
password    required      /lib/security/\$ISA/pam_deny.so

session     required      /lib/security/\$ISA/pam_limits.so
session     required      /lib/security/\$ISA/pam_unix.so
EOF


	} else {

	# RHWS 5
	print(NEWFILE << "EOF");
#\%PAM-1.0
# This file installed by harden_linux.pl $script_rev for \"$OS\"
#
auth        required      pam_env.so
auth        required      $pam_tally_rule
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_warn.so
auth        required      pam_deny.so

account     required      $pam_tally
account     required      pam_unix.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     required      pam_permit.so

password    required      pam_warn.so
password    requisite     pam_cracklib.so retry=2 minlen=7 lcredit=-1 ucredit=-1 dcredit=-1
password    sufficient    pam_unix.so md5 shadow nullok use_authtok remember=4
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     [success=2 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     required      pam_warn.so
EOF
	}

	close(NEWFILE);

	#
	# Now that the system is configured to refer to password history,
	# make sure there is a password history file.
	#
	my $passwd_history_file = "/etc/security/opasswd";
	unless (-e $passwd_history_file) {
		system("touch $passwd_history_file");
		system("chown root:root $passwd_history_file");
		system("chmod 600 $passwd_history_file");
	}
}

#
# Modify pam file for 'su' so that an "su -" will only work if entered
# on one of the white listed tty lines or virtual consoles.
# Basically, disallow access to the "root" account unless you are physically at the box.
# PCI-DSS 8.3
# 
sub modify_pamsu
{
	my $etcdir = $_[0];
	my $timestamp = strftime("%Y-%m-%d_%H%M%S", localtime());
	my $conf_file = "$etcdir/pam.d/su";
	my $new_conf_file = "$conf_file-$timestamp";

	showinfo("Modify PAM Password Rules ...");

	#
	# Each platform has a different prefix for location of pam modules.
	#
	my $pam_dir = "";
	if ($OS eq "RH72") {
		$pam_dir = '/lib/security/';
	} elsif ($OS eq "FC3") {
		$pam_dir = '/lib/security/$ISA/';
	} elsif ($OS eq "FC5") {
		$pam_dir = "";
	} elsif ($OS eq "RHEL5") {
		$pam_dir = "";
	} else {
		print "Unsupported OS \"$OS\".  Will skip $conf_file edits\n";
		return(0);
	}

	unless (-s "$conf_file") {
		showinfo("$conf_file does not exist.  Will skip $conf_file edits.");
		return(-2);
	}

	system("grep pam_securetty.so $conf_file");
	if ($? == 0) {
		showinfo("$conf_file previously changed. Will skip $conf_file edits.");
		return(-5);
	}

	unless (open(OLD, "< $conf_file")) {
		showinfo("Could not open $conf_file for read. Will skip $conf_file edits.");
		return(-5);
	}

	unless (open(NEW, "> $new_conf_file")) {
		showinfo("Could not open $new_conf_file for write. Will skip $conf_file edits.");
		close(OLD);
		return(-6);
	}

	#
	# Add a line that prevents the 'su' program from working when run
	# on any tty other than what appears in the secure tty white list.
	#
	while (<OLD>) {
		if (/^auth.*sufficient.*pam_rootok.so.*/) {
			print (NEW $_);
			print (NEW "auth       required     ${pam_dir}pam_securetty.so\n");
			next;
		}

		# By default, just copy what we read.
		print(NEW $_);
	}

	close(OLD);
	close(NEW);

	# If we created a new conf file that is zero sized, that is bad.
	if (-z $new_conf_file) {
		showinfo("Copy of $conf_file is a zero size file. Will skip edits.");
		system("rm $new_conf_file");
		return(-6);
	}

	# Assume conf file was successfully transformed... so replace the old one with the new.
	system("chmod --reference=$conf_file $new_conf_file");
	system("chown --reference=$conf_file $new_conf_file");
	system("mv $new_conf_file $conf_file");

	return(0);
}

#
# Modify /etc/securetty so that tty12 will allow root logins
# PCI-DSS 8.3
#
sub modify_securetty
{
	my $etcdir = $_[0];
	my $timestamp = strftime("%Y-%m-%d_%H%M%S", localtime());
	my $conf_file = "$etcdir/securetty";
	my $new_conf_file = "$conf_file-$timestamp";

	showinfo("Modifying Secure TTY Settings...");

	unless (-s "$conf_file") {
		showinfo("$conf_file does not exist.  Will skip $conf_file edits.");
		return(-2);
	}

	unless (open(OLD, "< $conf_file")) {
		showinfo("Could not open $conf_file for read. Will skip $conf_file edits.");
		return(-5);
	}

	unless (open(NEW, "> $new_conf_file")) {
		showinfo("Could not open $new_conf_file for write. Will skip $conf_file edits.");
		close(OLD);
		return(-6);
	}

	#
	# Only add tty12 if it's not there.
	#
	my $tty12_seen = 0;
	while (<OLD>) {
		if (/^tty12/) {
			$tty12_seen = 1;
		}

		# By default, just copy what we read.
		print(NEW $_);
	}

	close(OLD);

	unless ($tty12_seen) {
		print(NEW "tty12\n");
	}

	close(NEW);

	# If we created a new conf file that is zero sized, that is bad.
	if (-z $new_conf_file) {
		showinfo("Copy of $conf_file is a zero size file. Will skip edits.");
		system("rm $new_conf_file");
		return(-6);
	}

	# Assume conf file was successfully transformed... so replace the old one with the new.
	system("chmod --reference=$conf_file $new_conf_file");
	system("chown --reference=$conf_file $new_conf_file");
	system("mv $new_conf_file $conf_file");

	return(0);
}


#
# Modify /etc/logrotate.d/syslog so that 1 year of syslog files are kept.
# PCI-DSS 10.7
#
sub modify_logrotate
{
	my $etcdir = $_[0];
	my $timestamp = strftime("%Y-%m-%d_%H%M%S", localtime());
	my $conf_file = "$etcdir/logrotate.d/syslog";
	my $new_conf_file = "$conf_file-$timestamp";
	my $returnval = -5;

	showinfo("Modifying $conf_file...");

	unless (-s "$conf_file") {
		showinfo("$conf_file does not exist...  Will skip edits.");
		return(-2);
	}

	unless (open(OLD, "< $conf_file")) {
		showinfo("Could not open $conf_file for read. Will skip edits.");
		return(-5);
	}

	unless (open(NEW, "> $new_conf_file")) {
		showinfo("Could not open $new_conf_file for write. Will skip edits.");
		close(OLD);
		return(-6);
	}

	#
	# Add 2 new lines at beginning of stanza for monthly rotate, and
	# saving 12 months of files.  If we have already made a pass through or
	# by coincidence it was already configured this way, don't change it.
	#
	while (<OLD>) {
		#
		# looking for start of stanza:
		# ".../var/log/messages ... {"
		if (/^.*\/var\/log\/messages .* {/) {
			print(NEW $_);
			print(NEW "    monthly\n");
			print(NEW "    rotate 12\n");
			next;
		}

		next if (/^(\s+)monthly$/);
		next if (/^(\s+)rotate 12$/);

		# By default, just copy what we read.
		print(NEW $_);
	}

	close(OLD);
	close(NEW);

	# If we created a new conf file that is zero sized, that is bad.
	if (-z $new_conf_file) {
		showinfo("Copy of $conf_file is a zero size file. Will skip edits.");
		system("rm $new_conf_file");
		return(-6);
	}

	# Assume conf file was successfully transformed... so replace the old one with the new.
	system("mv $new_conf_file $conf_file");
	system("chmod 0644 $conf_file");

	return(0);
}


#
# Note here that order of the sudoers rules matters.
#
# General strategy: copy the current sudoers file to a new copy, but
# delete any lines concerning daisy.  Then write the new daisy specific
# lines to the new file.  If all went well, swap file names of the old
# sudoers with the new.
#
# PCI-DSS 7.x
# PCI-DSS 10.x
#
sub modify_sudoers
{
	my $etcdir = $_[0];
	my $returnval = -5;
	my $admingroup = "";
	my $timestamp = strftime("%Y-%m-%d_%H%M%S", localtime());
	my $sudoers = "$etcdir/sudoers";


	showinfo("Setup Sudoers...");

	if (-z "$sudoers") {
		showinfo("$sudoers does not exist error...  Will skip sudoers edits.");
		return(-2);
	}

	# "Re-write" $sudoers without our "Daisy specific" rules.
	# Basically, ignore previously existing rules for "rti", "dsyadmins" and "tfsupport".
	$returnval = open(OLD, "< $sudoers");
	if (! $returnval) {
		showinfo("Could not open $sudoers for read. Will skip sudoers edits.");
		return(-5);
	}
	$returnval = open(NEW, "> $sudoers.$$");
	if (! $returnval) {
		showinfo("Could not open $sudoers.$$ for write. Will skip sudoers edits.");
		close(OLD);
		return(-6);
	}

	print(NEW "# sudoers file.\n");
	print(NEW "# Created by harden_linux.pl $CVS_REVISION\n");
	print(NEW << 'xxxEOFxxx');

# This file is automatically (re) created, and should not be hand-edited.




# Super users.
root	ALL=(ALL) ALL
tfsupport ALL=(ALL) ALL
%dsyadmins ALL=(ALL) ALL
%rtiadmins ALL=(ALL) ALL
User_Alias ADMINS = %dsyadmins, %rtiadmins, tfsupport


# Logging and password attempts.
Defaults:tfsupport passwd_timeout=5, passwd_tries=3
Defaults logfile=/var/log/sudo.log

# POS Administrative Users
ADMINS ALL=NOPASSWD: /bin/hostname
ADMINS ALL=NOPASSWD: /bin/ls
ADMINS ALL=NOPASSWD: /bin/netstat
ADMINS ALL=NOPASSWD: /bin/stty
ADMINS ALL=NOPASSWD: /bin/true
ADMINS ALL=NOPASSWD: /bin/zcat
ADMINS ALL=NOPASSWD: /usr/bin/cancel
ADMINS ALL=NOPASSWD: /usr/bin/enable
ADMINS ALL=NOPASSWD: /usr/bin/find
ADMINS ALL=NOPASSWD: /usr/bin/kill
ADMINS ALL=NOPASSWD: /usr/bin/pkill
ADMINS ALL=NOPASSWD: /usr/bin/lpstat
ADMINS ALL=NOPASSWD: /usr/bin/lpr
ADMINS ALL=NOPASSWD: /usr/bin/lpc
ADMINS ALL=NOPASSWD: /usr/bin/lpq
ADMINS ALL=NOPASSWD: /usr/bin/lprm
ADMINS ALL=NOPASSWD: /usr/bin/mpi
ADMINS ALL=NOPASSWD: /usr/bin/mdir
ADMINS ALL=NOPASSWD: /usr/bin/nmap
ADMINS ALL=NOPASSWD: /usr/bin/tac
ADMINS ALL=NOPASSWD: /usr/bin/tail
ADMINS ALL=NOPASSWD: /usr/bin/top
ADMINS ALL=NOPASSWD: /usr/bin/write
ADMINS ALL=NOPASSWD: /usr/bin/yum
ADMINS ALL=NOPASSWD: /etc/dgap/dpa
ADMINS ALL=NOPASSWD: /etc/dgap/dgapview
ADMINS ALL=NOPASSWD: /usr/lone-tar/ltmenu
ADMINS ALL=NOPASSWD: /sbin/apcaccess
ADMINS ALL=NOPASSWD: /sbin/ethtool
ADMINS ALL=NOPASSWD: /sbin/fuser
ADMINS ALL=NOPASSWD: /sbin/lsusb
ADMINS ALL=NOPASSWD: /sbin/reboot
ADMINS ALL=NOPASSWD: /sbin/service
ADMINS ALL=NOPASSWD: /sbin/shutdown
ADMINS ALL=NOPASSWD: /usr/sbin/aide
ADMINS ALL=NOPASSWD: /usr/sbin/accept
ADMINS ALL=NOPASSWD: /usr/sbin/dmidecode
ADMINS ALL=NOPASSWD: /usr/sbin/cupsenable
ADMINS ALL=NOPASSWD: /usr/sbin/cupsdisable
ADMINS ALL=NOPASSWD: /usr/sbin/eject
ADMINS ALL=NOPASSWD: /usr/sbin/lpmove
ADMINS ALL=NOPASSWD: /usr/sbin/sosreport
ADMINS ALL=NOPASSWD: /usr/sbin/system-config-network
ADMINS ALL=NOPASSWD: /usr/sbin/system-config-printer
ADMINS ALL=NOPASSWD: /usr/sbin/up2date


# OSTools
ADMINS ALL=NOPASSWD: /d/ostools/bin/rtibackup.pl
ADMINS ALL=NOPASSWD: /d/daisy/bin/rtibackup.pl
ADMINS ALL=NOPASSWD: /usr2/ostools/bin/rtibackup.pl
ADMINS ALL=NOPASSWD: /usr2/bbx/bin/rtibackup.pl

ADMINS ALL=NOPASSWD: /d/ostools/bin/updateos.pl
ADMINS ALL=NOPASSWD: /d/daisy/bin/updateos.pl
ADMINS ALL=NOPASSWD: /usr2/ostools/bin/updateos.pl
ADMINS ALL=NOPASSWD: /usr2/bbx/bin/updateos.pl

ADMINS ALL=NOPASSWD: /d/ostools/bin/tfprinter.pl
ADMINS ALL=NOPASSWD: /d/daisy/bin/tfprinter.pl
ADMINS ALL=NOPASSWD: /usr2/ostools/bin/tfprinter.pl
ADMINS ALL=NOPASSWD: /usr2/bbx/bin/tfprinter.pl

ADMINS ALL=NOPASSWD: /d/ostools/bin/altiriscontrol.pl
ADMINS ALL=NOPASSWD: /d/daisy/bin/altiriscontrol.pl
ADMINS ALL=NOPASSWD: /usr2/ostools/bin/altiriscontrol.pl
ADMINS ALL=NOPASSWD: /usr2/bbx/bin/altiriscontrol.pl


# RTI Specific.
%rti ALL=NOPASSWD: /usr2/bbx/bin/key_lock
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/applypatch.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/watchrti.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/killterms.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/rtiuser.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/doveserver.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/altiriscontrol.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/rti
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/killem
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/startbbx
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/sshbbx
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/checkit
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/dosdir
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/rti_mtools_wrapper
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/rtidevice.pl
%rtiadmins ALL=NOPASSWD: /usr2/bbx/bin/updateos.pl
%rtiadmins ALL=NOPASSWD: /usr2/basis/pro5/mkrecover/mrebuild


# Daisy Specific
%dsyadmins ALL=NOPASSWD: /d/daisy/utils/docd_update
%dsyadmins ALL=NOPASSWD: /d/daisy/utils/killemall
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/killemall.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/utils/mount.sh
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/tfupdate.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/utils/umount.sh
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/doaccessadm.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/purgecc.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/doccpurge.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/dokeyrotate.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/doprinterconfig.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/doverifybu.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/dsyuser.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/bin/dsyperms.pl
%dsyadmins ALL=NOPASSWD: /d/daisy/qrest


xxxEOFxxx

	# Deny admins the capability of obtaining a "raw shell."
	# As per RTI 12.7 PA-DSS audit.
	print(NEW "# Deny (easy) access to a raw shell.\n");
	open(SHELLS, "< /etc/shells");
	while (<SHELLS>) {
		chomp;
		print(NEW "ADMINS ALL=PASSWD: !$_\n");
	}
	close(SHELLS);
	print(NEW "ADMINS ALL=PASSWD: !/bin/su\n");
	print(NEW "ADMINS ALL=PASSWD: !/sbin/sulogin\n");
	print(NEW "ADMINS ALL=PASSWD: !/usr/bin/screen\n");
	

	#
	# Close our 'sudoers' file here.
	#
	close(NEW);


	# We created a zero size file. That is bad.
	if (-z "$sudoers.$$") {
		showinfo("Copy of '$sudoers.$$' is a zero size file. Will skip sudoers edits.");
		system("rm $sudoers.$$");
		return(-6);
	}

	# Are there any syntax errors?
	# Note, the RH7.2 distro does not offer the "-c" option on visudo
	if ("$OS" ne "RH72") {
		system("sudo /usr/sbin/visudo -c -f $sudoers.$$ > /dev/null 2> /dev/null");
		if ($? != 0) {
			showinfo("Newly created $sudoers has syntax errors. Will skip sudoers edits.");
			system("rm $sudoers.$$");
			return(-7);
		}
	}

	# File was successfully transformed... so replace the old one with the new.
	system("mv $sudoers.$$ $sudoers");
	system("chown root:root $sudoers");
	system("chmod 0440 $sudoers");

	return(0);
}



# 
# Setup iptables and tcpwrappers rules.
# PCI-DSS 1.1.5
# PCI-DSS 1.2.x
# 
sub harden_iptables
{
	my $fwcmd = "/sbin/iptables";
	my $port = "";
	my @ports = ();


	showinfo("Setup IPTables ...");

	#
	# If the iptables package is not installed, then nothing to do.
	#
	if (! -x $fwcmd) {
		showinfo("The iptables package is not installed ($fwcmd not found).");
		showinfo("Host firewall will not be configured.");
		return;
	}



	#
	# Allow network traffic, but log it.
	# http://www.sans.org/reading_room/whitepapers/linux/2059.php
	#
	# PCI 10.2
	#
	# Implement automated audit trails for all system components
	# to reconstruct the following events:
	# 10.2.1: All individual accesses to cardholder data
	# 10.2.2: All actions taken by any individual with root priv
	# 10.2.3: Access to all audit trails
	# 10.2.4: Invalid logical access attempts
	# 10.2.5: Use of identification and authentication mechanisms
	# 10.2.6: Initialization of audit logs
	# 10.2.7: Creation and deletion of system level objects
	#
	my $iptables_flags = "URG,ACK,PSH,RST,SYN,FIN";

	#
	# First, clean out the "filter" table:
	#	-F -> delete all rules in all chains
	#	-X -> delete every non-builtin chain
	#	-Z -> zero counters
	#
	# Check the return status on this first use of iptables cmd... it it works here,
	# we will just assume it works for the rest of the function.
	#
	loginfo("Deleting Old IPTables Rule Sets from Filter Table.");
	if (system("$fwcmd -t filter -F") != 0) {
		my $exitstatus = $? >> 8;
		showinfo("Exec of ($fwcmd) failed: exit status: $exitstatus.");
		showinfo("Host firewall will not be configured.");
		return;
	}
	system("$fwcmd -t filter -X");
	system("$fwcmd -t filter -Z");

	#
	# PCI DSS 1.2.1
	#	Restrict inbound and outbound traffic to that which is necessary
	#	for the cardholder data environment.  Verify that inbound and
	#	outbound traffice is limited to that which is necessary for
	#	for the cardholder data environment.  Verify that all other
	#	inbound and outbound traffic is specifically denied, eg by
	#	using an explicit "deny all" or an implicit deny after allow.
	#
	# Set the default policy in table "filter":
	#	DROP for the "INPUT" chain
	#	DROP for the "FORWARD" chain
	#	ACCEPT for the "OUTPUT" chain
	#
	loginfo("Setting Default Policies for Filter Table.");
	system("$fwcmd -t filter -P INPUT DROP");
	system("$fwcmd -t filter -P OUTPUT ACCEPT");
	system("$fwcmd -t filter -P FORWARD DROP");

	# Log accept rule
	# Make a new chain named "LOG_ACCEPT" and add rules to it.
	loginfo("Setting IPTables Logging Rules.");
	system("$fwcmd -N LOG_ACCEPT");
	system("$fwcmd -A LOG_ACCEPT -j LOG --log-level debug --log-prefix 'ACCEPT '");
	system("$fwcmd -A LOG_ACCEPT -j ACCEPT");

	# Loopback device
	system("$fwcmd -A INPUT -i lo -j ACCEPT");
	system("$fwcmd -A OUTPUT -o lo -j ACCEPT");
	system("$fwcmd -A INPUT -s 127.0.0.0/255.0.0.0 -i ! lo -j DROP");
	# If a rule is needed: Allow localhost access to TCP 127.0.0.1/2551 for APCUPSD daemon.

	#
	# Allow return traffic initiated from the host
	#
	system("$fwcmd -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT");

	#
	# Accept important ping traffic
	#
	## Allow inbound ICMP from 192.168.x.x (ping, traceroute)
	## Allow inbound ICMP from 10.x.x.x (ping, traceroute)
	## Allow inbound ICMP from teleflora IPs (ping,traceroute)
	#
	system("$fwcmd -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT");
	system("$fwcmd -A INPUT -p icmp --icmp-type echo-request -j ACCEPT");
	system("$fwcmd -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT");
	system("$fwcmd -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT");

	# Create a "deny/log" rule.

	#
	# INPUT rules.
	#
	## Allow new inbound ssh
	## Allow new inbound sftp
	system("$fwcmd -A INPUT -p tcp --dport ssh -m state --state NEW -j LOG_ACCEPT");




	## Allow inbound Cups
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 631 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 631 -j ACCEPT");


	## Allow inbound Samba
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p udp -m udp --dport 137:138 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p udp -m udp --dport 137:138 -j ACCEPT");

	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m state --state NEW -m tcp --dport 137:139 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m state --state NEW -m tcp --dport 137:139 -j ACCEPT");

	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m state --state NEW -m tcp --dport 445 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m state --state NEW -m tcp --dport 445 -j ACCEPT");



	## Allow inbound Basis 
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 32778 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 32778 -j ACCEPT");
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 42672 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 42672 -j ACCEPT");
	
	## Allow inbound Basis Basis License Manager
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 27000 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 27000 -j ACCEPT");


	## Allow inbound Pro5
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 2525 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 2525 -j ACCEPT");

	## Allow inbound rtibackup "slave server sync"
	system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport 15020 -j ACCEPT");
	system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport 15020 -j ACCEPT");


	#
	# Accept and log end of connection.
	#
	system("$fwcmd -A INPUT -p tcp --tcp-flags $iptables_flags SYN,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");
	system("$fwcmd -A INPUT -p tcp --tcp-flags $iptables_flags FIN,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");
	system("$fwcmd -A INPUT -p tcp --tcp-flags $iptables_flags RST,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");

	#
	# Slow down incoming ssh brute force attacks.
	# http://linuxquestions.org/questions/linux-security-4/breakin-attempt-normal-425588/
	#
	system("$fwcmd -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --name sshprobe --set -j ACCEPT");
	system("$fwcmd -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --name sshprobe --update --seconds 60 --hitcount 5 --rttl -j DROP");
	system("$fwcmd -I INPUT -p tcp --dport 15022 -m state --state NEW -m recent --name sshprobe --set -j LOG_ACCEPT");
	system("$fwcmd -I INPUT -p tcp --dport 15022 -m state --state NEW -m recent --name sshprobe --update --seconds 60 --hitcount 5 --rttl -j DROP");

	## Allow inbound BBj Connections.
	@ports = ();
	push(@ports, 80);
	push(@ports, 443);
	push(@ports, 1100);
	push(@ports, 2000);
	push(@ports, 2001);
	push(@ports, 2002);
	push(@ports, 2004);
	push(@ports, 2103);
	push(@ports, 8443);
	push(@ports, 11057);
	foreach $port (@ports) {
		system("$fwcmd -A INPUT -s 192.168.0.0/16 -p tcp -m tcp --dport $port -j ACCEPT");
		system("$fwcmd -A INPUT -s 10.0.0.0/8    -p tcp -m tcp --dport $port -j ACCEPT");
	}




	## TODO: Allow inbound RTI "odbc" only from local IP Addresses.



	#
	# Policy for INPUT is DROP so if it didn't match any of the above rules,
	# the packet is dropped.
	#

	#
	# OUTPUT rules
	#
	# PCI 1.2.1
	#	Restrict outbound traffic to that which is necessary for
	#	the cardholder data environment.
	#

	## Allow all outbound
	system("$fwcmd -A OUTPUT -p tcp --syn -j ACCEPT");
	system("$fwcmd -A OUTPUT -m state --state NEW -j ACCEPT");
	system("$fwcmd -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT");

	## Allow outbound ssh and sftp to Teleflora
	system("$fwcmd -A OUTPUT -p tcp --dport ssh -m state --state NEW -j ACCEPT");

	## Allow outbound DNS
	system("$fwcmd -A OUTPUT -p udp --dport domain -m state --state NEW -j ACCEPT");
	system("$fwcmd -A OUTPUT -p tcp --dport domain -m state --state NEW -j ACCEPT");

	## Allow outbound NTP traffic
	system("$fwcmd -A OUTPUT -p udp --sport ntp -m state --state NEW -j ACCEPT");

	## Allow outbound CUPS traffic
	system("$fwcmd -A OUTPUT -p tcp --dport 631 -m state --state NEW -j ACCEPT");

	## TODO: Allow outbound ICMP to all (ping, traceroute)

	## TODO: Allow outbound SMTP for RTI v12.x email.

	## TODO: Allow outbound POP for RTI v12.x "fetchmail"

	## Allow outbound http/https to tws.teleflora.com
	## Allow outbound http/https to TF Altiris Server
	## Allow outbound https to globalpayments
	## Allow outbound http/https to redhat
	## Allow outbound http/https to "whatismyip.com"
	system("$fwcmd -A OUTPUT -p udp --dport http -m state --state NEW -j ACCEPT");
	system("$fwcmd -A OUTPUT -p udp --dport https -m state --state NEW -j ACCEPT");

	system("$fwcmd -t filter -A OUTPUT -p tcp --tcp-flags $iptables_flags SYN,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");
	system("$fwcmd -t filter -A OUTPUT -p tcp --tcp-flags $iptables_flags FIN,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");
	system("$fwcmd -t filter -A OUTPUT -p tcp --tcp-flags $iptables_flags RST,ACK -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT");
	system("$fwcmd -t filter -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT");


	## Deny/log all remaining outbound ipv4
	#system("$fwcmd -A OUTPUT -j DROP");


	#
	# PCI 1.2.2
	#	Secure and syncrhonize router config files.  Verify that router
	#	config files are secure and synchronized, eg, running config
	#	files and start-up config files have the same config.
	#
	system("/sbin/service iptables save");
	if (-f "/etc/sysconfig/iptables") {
		system("/bin/chown root:root /etc/sysconfig/iptables");
		system("/bin/chmod 0600 /etc/sysconfig/iptables");
		if (-s "/etc/sysconfig/iptables") {
			system("/sbin/service iptables restart");
		} else {
			showinfo("$0: iptables data file empty... firewall will not start on next boot.\n");
		}
	} else {
		showinfo("$0: iptables data file missing... firewall will not start on next boot.\n");
	}


	#
	# Modify syslog to now log "kernel debug" messages, which is where these iptables messages will show up.
	#
	my $configfile = "/etc/syslog.conf";
	system("cp $configfile $configfile.$$");
	if (-s "$configfile.$$" > 0) {
		open(NEWFILE, "> $configfile");
		open(OLDFILE, "< $configfile.$$");
		while(<OLDFILE>) {
			next if(/kern\.debug/);
			print(NEWFILE);
		}
		print(NEWFILE "kern.debug\t\t\t/var/log/secure\n");
		close(OLDFILE);
		close(NEWFILE);
	}

	if (-s "$configfile" > 0) {
		unlink "$configfile.$$";
	} else {
		system("cp $configfile.$$ $configfile");
	}


}


#
# Disable IPv6. We don't use it.
# PCI-DSS 1.1.5
#
sub disable_ipv6
{
	showinfo("Disable IPv6 ...");

	#
	# Disable IPv6 networking whether it is currently enabled or not.
	# Make sure the following 2 lines are added:
	#	NETWORKING_IPV6=no
	#	IPV6INIT=no
	#
	# This tip from the National Security Agency publication:
	# "Hardening Tips for the Default Installation of Red Hat Enterprise Linux 5"
	#
	# Additionally, this should happen in execution of function setup_system_services():
	#	service ip6tables stop
	#	chkconfig ip6tables off
	#
	# There is no reason for us to be using ipv6, and, ipv6 hacks are becoming
	# a known vulnerability in the world of security.
	#

	my $configfile = '/etc/sysconfig/network';

	system("cp $configfile $configfile.$$");
	if (-s "$configfile.$$" > 0) {
		open(NEWFILE, "> $configfile");
		open(OLDFILE, "< $configfile.$$");
		while(<OLDFILE>) {
			next if(/^(\s*)(NETWORKING_IPV6)(\s*)(=)/);
			next if(/^(\s*)(IPV6INIT)(\s*)(=)/);
			print(NEWFILE);
		}
		print(NEWFILE "NETWORKING_IPV6=no\n");
		print(NEWFILE "IPV6INIT=no\n");
		close(OLDFILE);
		close(NEWFILE);
	}
	if (-s "$configfile" > 0) {
		unlink "$configfile.$$";
	} else {
		system("cp $configfile.$$ $configfile");
	}

	#
	# The ipv6 kernel module also needs to be prevented from loading.
	# To accomplish that, add the line "install ipv6 /bin/true" to the
	# end of the file /etc/modprobe.conf and reboot.
	#
	$configfile = '/etc/modprobe.conf';
	system("grep 'install.*ipv6.*/bin/true' $configfile > /dev/null 2> /dev/null");
	if ($? == 0) {
		showinfo("IPv6 alread disabled in $configfile. Will skip $configfile edits.");
		return;
	}

	system("echo 'install ipv6 /bin/true' >> $configfile");
	system("grep 'install.*ipv6.*/bin/true' $configfile > /dev/null 2> /dev/null");
	if ($? != 0) {
		showinfo("Can't modify $configfile - IPv6 is NOT disabled.");
	} else {
		showinfo("IPv6 will be disabled after next reboot.");
	}
}


#
# Use "tcpwarappers" to also guard against external access.
# PCI-DSS 1.1.5
# PCI-DSS 1.2.x
#
sub modify_host_access
{

	#
	# In order to use the "hosts_access(3)", the binary implementing the service
	# must be linked with the "libwrap" library.  Here is what testing determined...
	# pretty much the *only* service which observes this access is sshd:
	#
	# binary	platform	yes	no
	# cupsd		all			x
	# httpd		all			x
	# nmbd		all			x
	# ntpd		all			x
	# sshd		all		x
	#


	showinfo("Creating hosts.deny");
	open(DENYFILE, "> /etc/hosts.deny");
	print(DENYFILE << 'xxxEOFxxx');

sshd: ALL: spawn (echo "illegal connection attempt from %a to %d %p at `date` %u user" | mail server@rtimail.homelinux.com -s "Unauthorized ssh connection for `hostname`")
in.telnetd: ALL: spawn (echo "illegal connection attempt from %a to %d %p at `date` %u user" | mail server@rtimail.homelinux.com -s "Unauthorized ssh connection for `hostname`")
ALL: ALL
xxxEOFxxx



	close(DENYFILE);


	showinfo("Creating hosts.allow");
	open(ALLOWFILE, "> /etc/hosts.allow");
	print(ALLOWFILE "#\n");
	print(ALLOWFILE "# hosts.allow\n");
	print(ALLOWFILE "# Auto-generated by harden_linux.pl $CVS_REVISION\n");
	print(ALLOWFILE "# Do not hand-edit this file.\n");
	print(ALLOWFILE "#\n");
	print(ALLOWFILE << 'xxxEOFxxx');

# SSHd (single factor authenication) should only be allowed from the LAN.
sshd: 10.
sshd: 192.168.
sshd: 127.0.0.1
sshd: 65.245.5.36
sshd: 65.245.5.209
sshd: 65.198.163.36

# TFRemote (Two-factor authentication) allowed from anywhere.
tfremote: ALL

# Sendmail only used as a local MTA.
sendmail: 127.0.0.1

xxxEOFxxx

	system("chown root:root /etc/hosts.allow");
	system("chmod 644 /etc/hosts.allow");
	system("chown root:root /etc/hosts.deny");
	system("chmod 644 /etc/hosts.deny");
}


#
# As ssh is remote access, harden this up to disallow any 'bonk' items.
# PCI-DSS 8.3
#
sub modify_sshd
{
	my $TIMESTAMP = strftime("%Y%m%d%H%M%S", localtime());

	showinfo("Hardening SSHd....");
	open(OLDFILE, "< /etc/ssh/sshd_config");
	open(NEWFILE, "> /etc/ssh/sshd_config.$$");
	while(<OLDFILE>) {
		next if(/^(\s*)(#BEGIN code added by $0)/i);
		next if(/^(\s*)(#END code added by $0)/i);
		next if(/^(\s*)(ListenAddress)/i);
		next if(/^(\s*)(PermitRootLogin)/i);
		next if(/^(\s*)(Banner)/i);
		next if(/^(\s*)(AllowTcpForwarding)/i);
		next if(/^(\s*)(X11Forwarding)/i);
		next if(/^(\s*)(PasswordAuthentication)/i);

		print(NEWFILE);
	}
	close(OLDFILE);

	print(NEWFILE "#BEGIN code added by $0 $CVS_REVISION $TIMESTAMP\n");
	print(NEWFILE "ListenAddress 0.0.0.0\n");
	print(NEWFILE "PermitRootLogin no\n");
	print(NEWFILE "Banner /etc/motd\n");
	print(NEWFILE "AllowTcpForwarding yes\n");
	print(NEWFILE "X11Forwarding no\n");
	print(NEWFILE "PasswordAuthentication yes\n");
	print(NEWFILE "#END code added by $0 $CVS_REVISION $TIMESTAMP\n");

	close(NEWFILE);

	if(! -z "/etc/ssh/sshd_config.$$") {
		system("mv /etc/ssh/sshd_config.$$ /etc/ssh/sshd_config");
	}

	system ("/sbin/service sshd restart");
}


#
#Syncrhonize clock with network servers.
#PCI 10.4
#
sub modify_timeservers
{


	showinfo("Setup Time Servers....");
	if(! -d "/etc/ntp") {
		return(0);
	}

	loginfo("Setting NTP Servers.");
	open(FILE, "> /etc/ntp/step-tickers");
	print(FILE << 'EOF');
clock.redhat.com
time.nist.gov
EOF
	close(FILE);

	system("/sbin/service ntpd restart");
}








#
# Update Aide IDS databases, as, we have futzed with a lot of files.
# PCI-DSS 11.4
#
sub update_aide
{
	showinfo("Update Aide Databases.");
	if(-f "/var/lib/aide/aide.db.gz") {
		$pid = fork();
		if ($pid == 0) {
			showinfo("Update Aide Databases.");
			system("/usr/sbin/aide --init");
			system("mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz");
			exit(0);
		} else {
			showinfo("Update Aide Database started as PID $pid at ". localtime() . "\n");
		}
		exit(0);
	}
}



# 
# Show the user a message *and* log to syslog
#
sub showinfo
{
	my $message = $_[0];
	print("$message\r\n");
	loginfo($message);
}


# 
# Only Log actions to syslog, without display to screen.
#
sub loginfo
{
	my $message = $_[0];
	system("/usr/bin/logger -i -t \"$UID-harden_linux\.pl \" -- \"$message\"");
}
