#!/usr/bin/perl
###########################################################################
#									  #
# Harden SuSE Linux 6.0 - 7.2, Perl Script by Marc Heuse <mh@mh-sec.de>	  #
#									  #
#									  #
# If you need help for this script, please send an email with your	  #
# question to mh@mh-sec.de						  #
#									  #
# You don't need to configure anything in this perl script!		  #
# 									  #
# If you run this script with the commandline option "yes", an automatic  #
# yes to all questions about security settings is assumed.		  #
# You may also auto-answer all ten questions by putting ten commandline   #
# parameters. a "y"* is a YES to a question, anything else NO.		  #
#									  #
# To UNDO your changes, a perlscript called undo_harden_suse is put	  #
# put into the /etc directory (also the harden_suse.log file).		  #
#									  #
# TIPS:	Server:			"harden_suse y y y y y n y n y y"         #
#	User Workstation:	"harden_suse y n y y n n y n n y"         #
#	Firewall:		"harden_suse yes"                         #
#									  #
###########################################################################
#
# I need input and comments!
#
####
#
# Thanks to Burchard Steinbild, Werner Fink and Markus Schaefer for comments
#
# DEBUG
$debug = 0;     # 0 for normal use, 1 for debug mode where no changes are done!
# important defines
$version = "v3.3";
$date = "06-May-2001";
$script = "harden_suse";
$MAX_SUSE_VERSION="7.2";
$questions = 10;

if (! -f "/etc/rc.config") {
    print STDERR "/etc/rc.config doesn't exist! Is this really a SuSE Linux ?\n";
    exit 1;
}
unless($< == 0) {
    print STDERR "You must run this as root.\n";
    exit 1;
}  
if (! -f "/etc/SuSE-release") {
    print STDERR "Warning: /etc/SuSE-release not found. Is this really a SuSE Linux >= v6.0?";
    print STDERR "However, this script will work with any SuSE Linux version >= 5.0\007\n";
    print STDERR "NOTE: if you answer question 2 with yes, you need to add the groups \"xok\"\n";
    print STDERR "and \"trusted\" before running this script!\n";
} else {
    open SuSE, "< /etc/SuSE-release";
    while (<SuSE>) {
	if ( m/VERSION/ ) {
	    ($null,$null,$suse_version,$null) = split(/ /, $_, 4);
	    chomp($suse_version);
	}
    }
    close SuSE;
}
$suse_version = "5.3"	if (! defined $suse_version);

print "Guessing you are using SuSE version $suse_version\n";
if ($suse_version > $MAX_SUSE_VERSION) {
   print STDERR "\007
WARNING: You seemed to have installed a SuSE version which is not yet
supported by this hardening script! (Verified to work with 5.3 up to $MAX_SUSE_VERSION)
Try to get an update from ftp://ftp.suse.com or http://www.suse.de/~marc ...
Use at own risk!\n"
}

$yes = 1	if (defined $ARGV[0] && $ARGV[0] =~ m/yes/i && $#ARGV == 0);

if (-f "/etc/undo_harden_suse") {
    print STDERR "\007
WARNING: I found the undo file from a previous harden_suse run!
If you answer YES, the work is done but you can't restore your previous doing.
If you answer NO, this script aborts.\n
Make a wise choice. (Y/N)\n";
    if (! &askme()) {
	print "\nAborted.\n";
	exit 1;
    }
}

print "\007
Warning: This script will secure your system which means that it will disable
almost all services on your system and tamper with some configuration files.
You should know what you are doing!\n
Do you really want to run this script? (y/n)\n";
if (! &askme()) {
    print "\nAborted.\n";
    exit 1;
}

if (($#ARGV > 0 && $#ARGV != $questions - 1) || ($#ARGV == 0 && ! $yes)) {
    print STDERR "Warning: invalid number or wrong argument(s), ignoring them!\n";
}

# defines
chomp( $current = `/bin/date` );
$author = "Marc Heuse <mh\@mh-sec.de>";
$tmp_dir = "/tmp/.harden_suse.$$";
$current_q = 1;
$newname = "";
$timeline = "0-1";
# commands
$grep = "/usr/bin/grep";
$sort = "/usr/bin/sort";
$uniq = "/usr/bin/uniq";
$awk = "/usr/bin/awk";
$sed = "/usr/bin/sed";
$diff = "/usr/bin/diff";
$find = "/usr/bin/find";
$chmod = "/bin/chmod";
$mv = "/bin/mv";
$rm = "/bin/rm";
$cp = "/bin/cp";
# config files
$file_rc="/etc/rc.config";
$file_passwd="/etc/passwd";
$file_inetd="/etc/inetd.conf";
$file_tcpd="/etc/hosts.deny";
$file_login="/etc/login.defs";
$file_tty="/etc/securetty";
$file_ssh1="/etc/ssh_config";
$file_sshd1="/etc/sshd_config";
$file_ssh2="/etc/ssh/ssh_config";
$file_sshd2="/etc/ssh/sshd_config";
$file_profile="/etc/profile";
$file_csh="/etc/csh.login";
$file_issue="/etc/issue";
$file_issuenet="/etc/issue.net";
$file_motd="/etc/motd";
$file_lilo="/boot/message";
# misc
$HEADER ="#
# This config file was changed by the $script script to enforce a
# strict security on your system.
# This configuration was changed on the $current.
# $script $version ($date) by $author
#
# Think twice before changing an option.
#";

# init
$do_rc = $do_inetd = $do_ssh = $do_sid = $do_login = $do_user = $do_password = $do_write = $do_policy = 1;
$perms = "secure paranoid";
$start = "start";

print "\n\nWARNING: DEBUG option is activated! No changes will be done!!\007\n"	if ($debug > 0);

# Question 1
print "\n\nWe will now ask you $questions questions about the new configuration.
Any YES to a question means you made a decission for a better SECURITY.
Any NO  to a question means you need that configuration or you want convenience.
\n$current_q) Do you want to disable all daemons and secure system accounts (0>uid<100)?
   (except (untouched): acct, apmd, ciped, cron, gpm, isapnp, kerneld, quota,
                        scanlogd, sshd, syslogd, susefirewall and ipsec)";
$do_rc = 0		if (! &askme());
# Question 2
$perms = "secure local";
print "\n$current_q) Do you want permission settings secure?";
undef $perms	if (! &askme());
# Question 3
print "\n$current_q) Do you want to disable inetd & comment out all it's services + secure tcpd?";
$do_inetd = 0		if (! &askme());
# Question 4
print "\n$current_q) Do you want to secure the login procedure?
   (logging all login attempts, root login only from console, show lastlogin)";
$do_login = 0		if (! &askme());
# Question 5
print "\n$current_q) Do you want a strong password policy?
   (minimum 7 length, additional security checks, 40 day passwords, MD5)";
$do_password = 0	if (! &askme());
# Question 6
print "\n$current_q) Do you want strong permissions on user homedirs and umask + disable cores?";
#   (Type \"no\" if you want to use X or a big databank because of the limits.
#   But then you shouldn't run this script anyway)";
$do_user = 0		if (! &askme());
# Question 7
print "\n$current_q) Do you want a safe and secure config for SSH and SSHD?";
$do_ssh = 0		if (! &askme());
# Question 8
print "\n$current_q) Do you want to have all suid/sgid bits removed which are not in the
  $perms and local permission listings? (this is a lengthy process)";
$do_sid = 0		if (! &askme());
# Question 9
print "\n$current_q) Do you want to do a search for world writable files/dirs?
   (this will also remove user permissions from floppy & type. lengthy process)";
$do_write = 0		if (! &askme());
# Question 10
print "\n$current_q) Do you want to show legal disclaimers when users are logging in?";
$do_policy = 0		if (! &askme());

$timeline = "2-15"	if ($do_sid || $do_write);
$timeline = "2-20"	if ($do_sid && $do_write);
print "\nGreat! Sit back, this will take a while ($timeline minutes).\n\n";
sleep 3;

# security
umask 077;
system("$rm -rf $tmp_dir");
mkdir $tmp_dir, 0700;
if (! -O $tmp_dir) {
    print "Oops, cracker attack on $tmp_dir!\n";
    exit 1;
}

open LOG, "> $tmp_dir/harden_suse.log" or die("Can't create $tmp_dir/harden_suse.log");
open UNDO, ">$tmp_dir/undo_harden_suse" or die("Can't create $tmp_dir/undo_harden_suse");
print LOG "DEBUG MODE - NO CHANGES WERE DONE!\n"		if ($debug);
print LOG "Output from $script $version run on $current\n\n";
print UNDO "#!/usr/bin/perl
#
# UNDO script for the $script $version by $author
# run on $current
#
print STDOUT \"UNDO of changes in process now ...\\n\";\n";

if ($do_rc || $do_login || defined $perms) {
  open RC, "< $file_rc" or die("Can't open $file_rc");
  open NEW_RC, "> $tmp_dir/rc.config" or die("Can't create $tmp_dir/rc.config");
  print_log("Found $file_rc, working on it ...");
  print NEW_RC "$HEADER\n";
  while (<RC>) {
    &clean_line();
    if (m/^CHECK_PERMISSIONS=/ && defined $perms) {
     if (! m/=.*set/) {
	$_ = "CHECK_PERMISSIONS=set";
	&print_log("Permission will be SET, not just warnings generated.");
     }
    }
    if (m/^PERMISSION_SECURITY=/ && defined $perms) {
	$_ = "PERMISSION_SECURITY=\"$perms\"";
	&print_log("Permission Security set to \"$perms\".");
    }
    if ($do_rc || $do_login) {
     if (m/^SMTP=.*yes/ && $do_rc) {
 	$_ = "SMTP=\"no\"";
 	&print_log("Disabled service SMTP.");
     }
     if (m/^NFS_SERVER=.*yes/ && $do_rc) {
	$_ = "NFS_SERVER=\"no\"";
	&print_log("Disabled service NFS_SERVER.");
     }
     if (m/^ROOT_LOGIN_REMOTE=.*yes/ && $do_login) {
	$_ = "ROOT_LOGIN_REMOTE=\"no\"";
	&print_log("Remote root login prohibited.");
     }
     if (m/^CWD_IN_ROOT_PATH=.*yes/ && $do_rc) {
	$_ = "CWD_IN_ROOT_PATH=\"no\"";
	&print_log("Removed \"current\" directory from root's PATH.");
     }
     if (m/^CRON=.*yes/ && $do_rc) {
	&print_log("Warning: I'm leaving CRON enabled!");
     }
     if (m/^IP_TCP_SYNCOOKIES=.*no/ && $do_rc) {
	$_ = "IP_TCP_SYNCOOKIES=\"yes\"";
	&print_log("Warning: I'm enabling TCP SYN Cookies.");
     }
     if (m/^IP_FORWARD=.*yes/ && $do_rc) {
	$_ = "IP_FORWARD=\"no\"";
	&print_log("Disabled IP Forwarding.");
     }
     if (m/^PASSWD_USE_CRACKLIB=.*no/ && ($do_rc || $do_login || $do_password)) {
        $_ = "PASSWD_USE_CRACKLIB=\"yes\"";
	&print_log("Warning: I'm enabling CRACKLIB usage.");
     }
     if (m/^RUN_UPDATEDB_AS=/ && (! m/^RUN_UPDATEDB_AS=.*nobody/) && $do_rc) {
        $_ = "RUN_UPDATEDB_AS=\"nobody\"";
	&print_log("Warning: I'm changed updatedb to run as nobody.");
     }
     if (m/^START_/ && $do_rc) {
        s/^START_//;
	if (m/=.*yes/) {
	    s/=.*//;
	    if (m/PLIP/ || m/PPPOED/ || m/IRDA/ || m/IDEDMA/ || m/ADSL/ || m/LOOPBACK/ || m/ALSA/ || m/ALSA_SEQ/ || m/KERNELD/ || m/USB/ || m/ISAPNP/ || m/APMD/ || m/QUOTA/ || m/PCMCIA/ || m/NSCD/ ) {
	        # silently ignore
	        $_ = "START_$_=\"yes\"";
	    } else { if (m/AUTOFS/ || m/FTP_PROXY/ || m/SNORT/ || m/NESSUSD/ || m/GPM/ || m/ACCT/ || m/SSHD/ || m/SCANLOGD/ || m/CIPED/ || m/FW2/ || m/FW/ || m/IPSEC/ ) {
	        &print_log("Warning: I'm leaving $_ enabled!");
	        $_ = "START_$_=\"yes\"";
	    } else {
	        &print_log("Disabled service $_.");
	        $_ = "START_$_=\"no\"";
	    } }
	} else {
	    if (m/ACCT/ || m/SCANLOGD/) {
		s/=.*//;
		&print_log("Warning: I'm enabling service $_ !");
		$_ = "START_$_=\"yes\"";
	    } else {
		$_ = "START_$_";
	    }
        }
     }
    }
    print NEW_RC "$_\n";
  }
  close RC;
  close NEW_RC;
  print_log("Done with $file_rc\n");
}

if (-f "$file_passwd" && $do_rc) {
    &print_log("Found $file_passwd, going to disable system accounts (except uid 0) ...");
    open PASSWD, "< $file_passwd";
    open NEW_PASSWD, "> $tmp_dir/passwd";
    $count = 0;
    while (<PASSWD>) {
	$changed = 0;
        chomp($_);
        ($login, $pw, $uid, $gid, $geco, $home, $shell) = split(/:/, $_, 7);
#        warn $pw if 0;
        if (($uid > 0 && $uid < 100) && ($shell ne "/bin/false")) {
	   $shell = "/bin/false";
	   $changed = 1;
	   &print_log("Changed shell of user $login to /bin/false.");
	}
	if ($home eq "/tmp") {
	   $home = "/var/state";
	   $changed = 1;
	   &print_log("Changed home of user $login to /var/state.");
	}
        if ($changed == 1) {
	   $_ = $login . ":x:" . $uid . ":" . $gid . ":" . $geco . ":" . $home . ":" . $shell;
	   $count += 1;
	}
	print NEW_PASSWD "$_\n";
    }
    close PASSWD;
    close NEW_PASSWD;
    &print_log("Secured $count accounts.");
    &print_log("Done with $file_passwd\n");
}

# inetd was disabled above but we want to ensure that inetd.conf is secured
if (-f "$file_inetd" && $do_inetd) {
    &print_log("Found $file_inetd, working on it ...");
    open INETD, "< $file_inetd";
    open NEW_INETD, "> $tmp_dir/inetd.conf";
    print NEW_INETD "$HEADER\n";
    while (<INETD>) {
	if (m/^#/ || m/^\s*$/) {
	   print NEW_INETD "$_";
	} else {
	   $line = "# " . $_;
	   print NEW_INETD "$line";
	   chomp($_);
	   s/\s+.*//g;
	   &print_log("Disabled service $_.");
	}
    }
    close INETD;
    close NEW_INETD;
    &print_log("Done with $file_inetd\n");
}

# We set tcpd to standard deny to be sure that only allowed hosts get access
if (-f "$file_tcpd" && $do_inetd) {
    &print_log("Found $file_tcpd, working on it ...");
    open TCPD, "< $file_tcpd";
    open NEW_TCPD, "> $tmp_dir/hosts.deny";
    print NEW_TCPD "$HEADER\n";
    while (<TCPD>) {
	if (m/^\s*ALL\s*:.*ALL.*/) {
	    $tcpd_denyall = 1;
	}
	print NEW_TCPD "$_";
    }
    if (! defined $tcpd_denyall) {
	print NEW_TCPD "ALL: ALL EXCEPT localhost\n";
	&print_log("Set default to deny all except localhost.");
    }
    close TCPD;
    close NEW_TCPD;
    &print_log("Done with $file_tcpd\n");
}

$counter = 1;
foreach $file_sshd ( $file_sshd1, $file_sshd2 ) {
 if (-f "$file_sshd" && $do_ssh) {
    &print_log("Found $file_sshd, working on it ...");
    open SSHD, "< $file_sshd";
    open NEW_SSHD, "> $tmp_dir/sshd_config$counter";
    print NEW_SSHD "$HEADER\n";
    while (<SSHD>) {
	&clean_line();
	$orig_line = $_;
	s/#.*//g;
	$old_line = $_;
	if (m/permitrootlogin/i) {
	    if (m/permitrootlogin.*yes/i) {
		$_ = "PermitRootLogin no";
		&print_log("Disabled direct root login.");
	    }
	    $sshd_rootlogin = "1";
	}
	if (m/ignorerhosts/i) {
	    if (m/ignorerhosts.*no/i) {
		$_ = "IgnoreRhosts yes";
		&print_log("Enabled the \"ignore rhosts\" option.");
	    }
	    $sshd_rhosts = "1";
	}
	if (m/strictmodes.*no/i) {
	    $_ = "StrictModes yes";
	    &print_log("Enabled the \"strict modes\" option.");
	}
	if (m/x11forwarding/i) {
	    if (m/x11forwarding.*yes/i) {
		$_ = "X11Forwarding no";
		&print_log("Disabled X11 forwarding.");
	    }
	    $sshd_x11 = "1";
	}
	if (m/rhostsauthentication.*yes/i) {
	    $_ = "RhostsAuthentication no";
	    &print_log("Disabled rhosts authentication");
	}
        if (m/rhostsrsaauthentication/i) {
  	    if (m/rhostsrsaauthentication.*yes/i) {
	        $_ = "RhostsRSAAuthentication no";
	        &print_log("Disabled rhosts RSA authentication");
  	    }
	    $sshd_rhostrsa = "1";
        }
	if (m/permitemptypasswords/i) {
	    if (m/permitemptypasswords.*yes/i) {
		$_ = "PermitEmptyPasswords no";
		&print_log("Disabled access to accounts with no passwords");
	    }
	    $sshd_emptypw = "1";
	}
	if (m/logingracetime/i) {
	    $_ = "LoginGraceTime 300";	    
	    &print_log("Set login gracetime to 300 seconds.");
	    $sshd_login = "1";
	}
	if (m/printmotd.*no/i  && $do_policy) {
	    $_ = "PrintMotd yes";
	    &print_log("Will display motd upon login.");
	}
	if ($old_line eq $_) {
	    $_ = $orig_line;
	}
	print NEW_SSHD "$_\n";
    }
    # now we must handle default values we don't like and did not already fix
    &print_log("Changing defaults I don't like which were not in the config:");
    if (! defined $sshd_rootlogin) {
	print NEW_SSHD "PermitRootLogin no\n";
	&print_log("Disabled direct root login.");
    }
    if (! defined $sshd_rhosts) {
	print NEW_SSHD "IgnoreRhosts yes\n";
	&print_log("Enabled the \"ignore rhosts\" option.");
    }

    if (! defined $sshd_rhostrsa) {
	print NEW_SSHD "RhostsRSAAuthentication no\n";
	&print_log("Disbaled .rhosts RSA authentication.");
    }
    if (! defined $sshd_x11) {
	print NEW_SSHD "X11Forwarding no\n";
	&print_log("Disabled X11 forwarding.");
    }
    if (! defined $sshd_emptypw) {
	print NEW_SSHD "PermitEmptyPasswords no\n";
	&print_log("Disabled access to accounts with no passwords");
    }
    if (! defined $sshd_login) {
	print NEW_SSHD "LoginGraceTime 300\n";
	&print_log("Set login gracetime to 300 seconds.");
    }
    close SSHD;
    close NEW_SSHD;
    &print_log("Done with $file_sshd\n");
 }
 $counter += 1;
}

$counter = 1;
foreach $file_ssh ( $file_ssh1, $file_ssh2 ) {
 if (-f "$file_ssh" && $do_ssh) {
    &print_log("Found $file_ssh, working on it ...");
    open SSH, "< $file_ssh";
    open NEW_SSH, "> $tmp_dir/ssh_config$counter";
    print NEW_SSH "$HEADER\n";
    while (<SSH>) {
	&clean_line();
	$orig_line = $_;
	s/#.*//g;
	$old_line = $_;
	if (m/forwardagent/i) {
	    if (m/forwardagent.*yes/i) {
		$_ = "ForwardAgent no";
		&print_log("Disabled Authentication Agent forwarding.");
	    }
	    $ssh_agent = "1";
	}
	if (m/forwardx11/i) {
	    if (m/forwardx11.*yes/i) {
		$_ = "ForwardX11 no";
		&print_log("Disabled X11 forwarding.");
	    }
	    $ssh_x11 = "1";
	}
	if (m/fallbacktorsh/i) {
	    if (m/fallbacktorsh.*yes/i) {
		$_ = "FallBackToRsh no";
		&print_log("Disabled fallback to rsh.");
	    }
	    $ssh_rsh = "1";
	}
	# not sure if I should also config no rhosts authentication ...
	if ($old_line eq $_) {
	    $_ = $orig_line;
	}
	print NEW_SSH "$_\n";
    }
    # now we must handle default values we don't like and did not already fix
    &print_log("Changing defaults I don't like which were not in the config:");
    if (! defined $ssh_agent) {
	print NEW_SSH "ForwardAgent no\n";
	&print_log("Disabled Authentication Agent forwarding.");
    }
    if (! defined $ssh_x11) {
	print NEW_SSH "ForwardX11 no\n";
	&print_log("Disabled X11 forwarding.");
    }
    if (! defined $ssh_rsh) {
	print NEW_SSH "FallBackToRsh no\n";
	&print_log("Disabled fallback to rsh.");
    }
    close SSH;
    close NEW_SSH;
    &print_log("Done with $file_ssh\n");
 }
 $counter += 1;
}

if (-f "$file_tty" && $do_login) {
    &print_log("Found $file_tty, working on it ...");
    open TTY, "< $file_tty";
    open NEW_TTY, "> $tmp_dir/securetty";
    while (<TTY>) {
	&clean_line();
        if (m/^tty[0-9].*/ || m/^ttyS[0-9].*/ || m/^cua[0-9].*/) {
	    print NEW_TTY "$_\n";
	} else {
	    &print_log("Disabled root login on $_");
	}
    }
    close TTY;
    close NEW_TTY;
    &print_log("Done with $file_tty\n");
} else {
    if ($do_login) {
        open NEW_TTY, "> $tmp_dir/securetty";
        print NEW_TTY "tty1\ntty2\ntty3\ntty4\ntty5\ntty6\n";
	close NEW_TTY;
	&print_log("Created $file_tty with local virtual terminals (just in case)\n");
    }
}

if (! -f "$file_login") {
    &print_log("Warning: I can't find $file_login! There must be something wrong!\n");
} else {
  if ($do_login || $do_password || $do_user) {
    &print_log("Found $file_login, working on it ...");
    open LOGIN, "< $file_login";
    open NEW_LOGIN, "> $tmp_dir/login.defs";
    print NEW_LOGIN "$HEADER\n";
    while (<LOGIN>) {
	&clean_line();
	if (m/^FAILLOG_ENAB.*no/ && $do_login) {
	    $_ = "FAILLOG_ENAB\t\tyes";
	    &print_log("Enabled logging of failed logins.");
	}
	if (m/^LOG_OK_LOGINS.*no/ && $do_login) {
	    $_ = "LOG_OK_LOGINS\t\tyes";
	    &print_log("Enabled logging of successful logins.");
	}
	if (m/^LASTLOG_ENAB.*no && $do_login/) {
	    $_ = "LASTLOG_ENAB\t\tyes";
	    &print_log("Enabled display of the lastlogin message.");
	}
	if (m/^CONSOLE/ && $do_login) {
	    $login_console = "1";
	    &print_log("\007Please check that the following value is OK: $_");
	}
	if (m/^UMASK/ && $do_user) {
	    $_ = "UMASK\t\t077";
	    &print_log("Setting the default UMASK to 077.");
	}
	if (m/^ULIMIT/ && $do_user) {
	    $_ = "ULIMIT\t\t524288";
	    &print_log("Setting ulimit to 512mb (reduce this more if no user needs that.");
	}
	if (m/^DEFAULT_HOME.*yes/ && $do_user) {
	    $_ = "DEFAULT_HOME\t\tno";
	    &print_log("Disabled the login if the homedir doesn't exist.");
	}
	if (m/^PASS_ALWAYS_WARN.*no/ && $do_password) {
	    $_ = "PASS_ALWAYS_WARN\t\tyes";
	    &print_log("Enabled warning if a weak password ist found.");
	}
	if (m/^OBSCURE_CHECKS_ENAB.*no/ && $do_password) {
	    $_ = "OBSCURE_CHECKS_ENAB\t\tyes";
	    &print_log("Enabled additional password checks");
	}
	if (m/^PASS_MIN_LEN/ && $do_password) {
	    $_ = "PASS_MIN_LEN \t\t7";
	    &print_log("Set minimum password length to 7.");
	}
	if (m/^PASS_MAX_DAYS/ && $do_password) {
	    $_ = "PASS_MAX_DAYS\t\t40";
	    &print_log("Set maximum days of a password to 40.");
	}
	if (m/^CHFN_RESTRICT/ && $do_password) {
	    $_ = "#$_";
	    &print_log("Disabled the possbility to change the passwd geco field for a user.");
	}
	print NEW_LOGIN "$_\n";
    }
    if (! defined $login_console && $suse_version < "6.2" ) {
	print NEW_LOGIN "CONSOLE console:tty1\n";
	&print_log("Warning: CONSOLE was not set! Set to CONSOLE:tty1 .");
    }
    close LOGIN;
    close NEW_LOGIN;
    &print_log("Done with $file_login\n");
  }
}

# MD5 here
#foreach $dir () {
#if (-d "/usr/doc/packages/pam/md5.config") {
#
#}

if (-f "$file_profile" && $do_user) {
    &print_log("Found $file_profile, working on it ...");
    open PROFILE, "< $file_profile";
    open NEW_PROFILE, "> $tmp_dir/profile";
    print NEW_PROFILE "$HEADER\n";
    while (<PROFILE>) {
	&clean_line();
	$orig_line = $_;
	s/#.*//g;
	$old_line = $_;
	if (m/^umask/) {
	    $_ = "umask 077";
	    &print_log("Setting umask to 077");
	    $profile_umask = "1";
	}
	if (m/^ulimit/) {
	    $_ = ": # " . $_;
	    $profile_limit = "1";
	}
	if ($old_line eq $_) {
	    $_ = $orig_line;
	}
	print NEW_PROFILE "$_\n";
    }
    print NEW_PROFILE "\n#Additions by harden_suse:\n";
    if (! defined $profile_umask) {
	print NEW_PROFILE "umask 077\n";
	&print_log("Setting umask to 077.");
    }
    if (defined $profile_limit) {
	&print_log("Disabled user limits.");
    }
    print NEW_PROFILE "ulimit -H -c 20000  # hardlimit: no coredumps > 20mb\n";
    print NEW_PROFILE "ulimit -S -c 0      # softlimit: no coredumps\n";
#    print NEW_PROFILE "ulimit -f    512000 # hard/soft: no files > 512 mb in size allowed\n";
#    print NEW_PROFILE "ulimit -S -n 250    # softlimit: no more than 250 filedescriptors\n";
#    print NEW_PROFILE "ulimit -S -u 100    # softlimit: no more than 100 processes\n";
#    print NEW_PROFILE "ulimit -H -v 100000 # hardlimit: no more than 100mb memory\n";
#    print NEW_PROFILE "ulimit -S -v 50000  # softlimit: no more than 50mb memory (netscape needs ...)\n";
    &print_log("Disabled core dumps.");
    close PROFILE;
    close NEW_PROFILE;
    &print_log("Done with $file_profile\n");
}

if (-f "$file_csh" && $do_user) {
    &print_log("Found $file_csh, working on it ...");
    open CSH, "< $file_csh";
    open NEW_CSH, "> $tmp_dir/csh.login";
    print NEW_CSH "$HEADER\n";
    while (<CSH>) {
	&clean_line();
	$orig_line = $_;
	s/#.*//g;
	$old_line = $_;
	if (m/^umask/) {
	    $_ = "umask 077";
	    &print_log("Setting umask to 077");
	    $csh_umask = "1";
	}
	if (m/^limit/) {
	    $_ = ": #" . $_;
	    $csh_limit = "1";
	}
	if ($old_line eq $_) {
	    $_ = $orig_line;
	}
	print NEW_CSH "$_\n";
    }
    print NEW_CSH "\n#Additions by harden_suse:\n";
    if (! defined $csh_umask) {
        print NEW_CSH "umask 077\n";
        &print_log("Setting umask to 077.");
    }
    if (defined $csh_limit) {
        &print_log("Disabled user limits.");
    }
#    print NEW_CSH "limit -h filesize 512m # hardlimit: no files > 512mb creation allowed\n";
    print NEW_CSH "limit -h coredumpsize 20m # hardlimit: no coredumps > 20m allowed\n";
    print NEW_CSH "limit coredumpsize 0   # softlimit: no coredumps\n";
#    print NEW_CSH "limit descriptors 100  # softlimit: no more than 100 filedescriptors\n";
#    print NEW_CSH "limit maxproc 100      # softlimit: no more than 100 processes\n";
#    print NEW_CSH "limit -h memoryuse 50m # hardlimit: no more than 50mb memory\n";
#    print NEW_CSH "limit memoryuse 20m    # softlimit: no more than 20mb memory\n";
    &print_log("Disabled core dumps.");
    close CSH;
    close NEW_CSH;
    &print_log("Done with $file_csh\n");
}

# MD5 here
if ($do_password) {
    $dir="/usr/doc/packages/pam/md5.config" if (-d "/usr/doc/packages/pam/md5.config");
    $dir="/usr/share/doc/packages/pam/md5.config" if (-d "/usr/share/doc/packages/pam/md5.config");
    $pdir="/etc/pam.d";

    if (defined $dir) {
        open PAMPASSWD, "< $pdir/passwd";
        @passwd=<PAMPASSWD>;
        foreach $line (@passwd) {
	    $md5_installed=1 if ($line =~ /password .* md5/);
        }
        close PAMPASSWD;
        if ($md5_installed != 1) {
	    &print_log("Changing to MD5 passwords (people need to change passwords to be effective)");
            @files=<$dir/*>;
            foreach $f (@files) {
	        $of = $f;
	        $of =~ s#.*/##;
	        if (-e "$pdir/$of") { # create backup
	            &get_new_name("$pdir/$of");
	            system("$cp -f $pdir/$of $newname")       if (! $debug);
	            print UNDO "system(\"/bin/mv -fv $newname $pdir/$of\");\n";
                }
	        system("$cp -f $f $pdir/$of")       if (! $debug);
	        print "$f -> $of\n";
            }
	}
    }
}

if ($do_policy) {
    &print_log("Writing new policies for issue, issue.net and motd (all in /etc) ...");
    open NEW_POLICY, "> $tmp_dir/issue";
    print NEW_POLICY "
Warning: Unauthorized access to this system is forbidden and will be
         prosecuted by law! By accessing this system, you agree that your
         actions can be monitored if unauthorized usage is suspected.\n\n";
    close NEW_POLICY;
    system("/bin/cp $tmp_dir/issue $tmp_dir/issue.net");
    system("/bin/cp $tmp_dir/issue $tmp_dir/motd");
    system("/bin/cp $tmp_dir/issue $tmp_dir/message");
    &print_log("Done writing policies\n");
}

&print_log("Copying the changed configs (making backups of course)");
if ( -e "$tmp_dir/rc.config" ) {
    &get_new_name("$file_rc");
    system("$mv -f $file_rc $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_rc\");\n";
}
if ( -e "$tmp_dir/inetd.conf" ) {
    &get_new_name("$file_inetd");
    system("$mv -f $file_inetd $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_inetd\");\n";
}
if ( -e "$tmp_dir/hosts.deny" ) {
    &get_new_name("$file_tcpd");
    system("$mv -f $file_tcpd $newname")       if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_tcpd\");\n";
}
if ( -e "$tmp_dir/sshd_config1" ) {
    &get_new_name("$file_sshd1");
    system("$mv -f $file_sshd1 $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_sshd1\");\n";
}
if ( -e "$tmp_dir/ssh_config1" ) {
    &get_new_name("$file_ssh1");
    system("$mv -f $file_ssh1 $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_ssh1\");\n";
}
if ( -e "$tmp_dir/sshd_config2" ) {
    &get_new_name("$file_sshd2");
    system("$mv -f $file_sshd2 $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_sshd2\");\n";
}
if ( -e "$tmp_dir/ssh_config2" ) {
    &get_new_name("$file_ssh2");
    system("$mv -f $file_ssh2 $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_ssh2\");\n";
}
if ( -e "$tmp_dir/login.defs" ) {
    &get_new_name("$file_login");
    system("$mv -f $file_login $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_login\");\n";
}
if ( -e "$tmp_dir/securetty" ) {
    &get_new_name("$file_tty");
    system("$mv -f $file_tty $newname")		if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_tty\");\n";
}
if ( -e "$tmp_dir/profile" ) {
    &get_new_name("$file_profile");
    system("$mv -f $file_profile $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_profile\");\n";
}
if ( -e "$tmp_dir/csh.login" ) {
    &get_new_name("$file_csh");
    system("$mv -f $file_csh $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_csh\");\n";
}
if ( -e "$tmp_dir/issue" ) {
    &get_new_name("$file_issue");
    system("$mv -f $file_issue $newname") if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_issue\");\n";
}
if ( -e "$tmp_dir/issue.net" ) {
    &get_new_name("$file_issuenet");
    system("$mv -f $file_issuenet $newname") if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_issuenet\");\n";
}
if ( -e "$tmp_dir/motd" ) {
    &get_new_name("$file_motd");
    system("$mv -f $file_motd $newname") if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_motd\");\n";
}
$run_lilo = 0;
if ( -e "$tmp_dir/message" ) {
    &get_new_name("$file_lilo");
    system("$mv -f $file_lilo $newname") if (! $debug);
    if ( -e  $file_lilo ) {
	$run_lilo = 1;
    }
    print UNDO "system(\"/bin/mv -fv $newname $file_lilo\");\n";
}
if ( -e "$tmp_dir/passwd" ) {
    &get_new_name("$file_passwd");
    system("$mv -f $file_passwd $newname")	if (! $debug);
    print UNDO "system(\"/bin/mv -fv $newname $file_passwd\");\n";
}

system("$chmod 644 $tmp_dir/*");
system("cd $tmp_dir; $cp -fp rc.config passwd inetd.conf hosts.deny login.defs securetty profile csh.login issue issue.net motd /etc 2> /dev/null")	if (! $debug);
system("cd $tmp_dir; $cp -fp ssh_config1 /etc/ssh_config")	if ((!$debug) && -e "$tmp_dir/ssh_config1");
system("cd $tmp_dir; $cp -fp sshd_config1 /etc/sshd_config")	if ((!$debug) && -e "$tmp_dir/sshd_config1");
system("cd $tmp_dir; $cp -fp ssh_config2 /etc/ssh/ssh_config")	if ((!$debug) && -e "$tmp_dir/ssh_config2");
system("cd $tmp_dir; $cp -fp sshd_config2 /etc/ssh/sshd_config")	if ((!$debug) && -e "$tmp_dir/sshd_config2");
system("cd $tmp_dir; $cp -fp message /boot")   if (! $debug && -e "$tmp_dir/message");

if ($do_user) {
    &print_log("\nSetting /root and all directories in /home to mode 0700.");
    system("$chmod 700 /root /home/*")		if (! $debug);
    print UNDO "print STDERR \"Warning: I can't reset the directory permissions for: /root /home/*\\n\";\n";
}

if ($do_rc) {
    &print_log("\nRunning SuSEconfig now.");
    close(LOG);
    system("/sbin/SuSEconfig|/usr/bin/tee -a $tmp_dir/harden_suse.log")	if (! $debug);
    open LOG, ">> $tmp_dir/harden_suse.log";
    &print_log("Finished SuSEconfig.\n");
}
if ($run_lilo == 1) {
    &print_log("Running LILO now.");
    system("/sbin/lilo")	if (! $debug);
    &print_log("Finished LILO.\n");
}

if ($do_sid || $do_write) {
    system("$awk '{print \$1}' /etc/permissions > $tmp_dir/filelist.tmp");
    system("for i in $perms local; do $awk '{print \$1}' /etc/permissions.\$i >> $tmp_dir/filelist.tmp; done");
    if ( -d "/etc/permissions.d" ) {
	system("$awk '{print \$1}' /etc/permissions.d/* >> $tmp_dir/filelist.tmp");
#	print STDERR "Warning: /etc/permissions.d support is still BETA.\n";
    }
    system("$sort $tmp_dir/filelist.tmp|$uniq > $tmp_dir/filelist.ok");
}

if ($do_sid) {
    &print_log("Checking now for suid/sgid files, this may take a while ...");
    system("($find / -type f \\( -perm -04000 -o -perm -02000 \\) '!' -fstype nfs|$sort > $tmp_dir/setid) 2>/dev/null");
    system("$diff -u $tmp_dir/filelist.ok $tmp_dir/setid|$grep '^+'|$grep -v '^+++'|$sed 's/^+//' > $tmp_dir/setid.remove");
    &print_log("\nRemoving the SUID bit from the following files:");
    open REMOVE, "< $tmp_dir/setid.remove";
    while (<REMOVE>) {
	chomp;
	if (-u $_) {
	    system($chmod, "u-s", "--", $_)		if (! $debug);
	    &print_log($_);
	    print UNDO "system(\"$chmod\", \"-v\", \"u+s\", \"--\", \"$_\");\n";
	}
    }
    close REMOVE;
    &print_log("\nRemoving the SGID bit from the following files:");
    open REMOVE, "< $tmp_dir/setid.remove";
    while (<REMOVE>) {
	chomp;
	if (-g $_) {
	    system($chmod, "g-s", "--", $_)		if (! $debug);
	    &print_log($_);
	    print UNDO "system(\"$chmod\", \"-v\", \"g+s\", \"--\", \"$_\");\n";
	}
    }
    close REMOVE;
    &print_log("");
    open REMOVE, "< $tmp_dir/setid.remove";
    while (<REMOVE>) {
	chomp;
	if (! -e $_) {
	    &print_log("Warning: this file seems not to exist: $_");
	}
    }
    close REMOVE;
}

if ($do_write) {
    system("$chmod o-w /dev/fd*")			if (! $debug);
    system("$chmod o-w /dev/rmt* /dev/sr*")		if (! $debug);
    system("$chmod o-r /dev/rmt* /dev/sr*")		if (! $debug);
    &print_log("\nRemoving world write perms from floppy & tape devices + read perms from tapes");
    system("$chmod 622 /dev/*audio*")		if (! $debug);
    print UNDO "system(\"$chmod 0666 /dev/rmt* /dev/sr* /dev/fd* /dev/*audio*\");\n";
    &print_log("Removing permissions for anyone except root to make audio recordings.");
    &print_log("Checking now for world writable files, this may take a while ...");
    system("($find / -perm -2 \\( -type f -or \\( -type d -not -perm -01000 \\) \\) -not -fstype nfs|$sort 1>$tmp_dir/write) 2>/dev/null");
    system("$diff -u $tmp_dir/filelist.ok $tmp_dir/write|$grep '^+'|$grep -v '^+++'|$sed 's/^+//' > $tmp_dir/write.remove");
    &print_log("\nRemoving world writable permissions from the following files:");
    open REMOVE, "< $tmp_dir/write.remove";
    while (<REMOVE>) {
	chomp;
	if (-W $_) {
	    system($chmod, "o-w", "--", $_)		if (! $debug);
	    &print_log($_);
	    print UNDO "system(\"$chmod\", \"-v\", \"o+w\", \"--\", \"$_\");\n";
	} else {
	    &print_log("Warning: file was not found writable: $_");
	}
    }
    &print_log("");
}

# the following important security fix is enforced: (SuSE < 6.1)
system("$chmod 640 /dev/kmem")			if (! $debug);

print UNDO "system(\"/sbin/SuSEconfig\");\nprint STDOUT \"Renaming files:\\n\";\nsystem(\"$mv -v /etc/undo_harden_suse /etc/undo_harden_suse.OLD\");\nsystem(\"$mv -v /etc/harden_suse.log /etc/harden_suse.log.OLD\");\nprint STDOUT \"Finished.\\n\";\n";
close UNDO;
&print_log("Copying the undo file to /etc/undo_harden_suse");
system("$cp -f $tmp_dir/undo_harden_suse /etc/undo_harden_suse")	if (! $debug);
system("$chmod 740 /etc/undo_harden_suse")		if (! $debug);

&print_log("\n\n\n\n\n\nYou may enhance security by:
\t* keep your site as updated as possible! -> http://www.suse.de/security
\t* install and configure the SuSEfirewall.rpm package
\t* install the seccheck.rpm package
\t* install the secure_linux patch from www.openwall.com
\t* chroot every daemon and try to run them unpriv'ed or with caps
\t* use the mount options nosuid/noexec/nodev/ro where appropriate
and check http://www.suse.de/~marc and http://www.suse.de/security regulary.
");

&print_log("\nCopying the logfile to /etc/harden_suse.log");
close LOG;
system("$cp -f $tmp_dir/harden_suse.log /etc/harden_suse.log")	if (! $debug);
print STDOUT "Cleaning up ...\n";
system("$rm -rf $tmp_dir")		if (! $debug);
print STDOUT "\007\nFinished - you might need to reboot for the changes to take effect.\n";
print STDOUT "\nDEBUG: The files can be found in $tmp_dir\n"	if ($debug);
if ($do_inetd) {
    print STDOUT "WARNING: tcpd is configured not to allow any network access to daemons it\nprotects - this includes SSH! Therefore you might want to configure\n/etc/hosts.allow and /etc/hosts.deny!\n"
}
exit 0;
#
# END OF MAIN
#
sub askme {
    $current_q += 1;
    if (defined $yes) {
	print "\n==> YES\n";
	return 1;
    }
    if ($#ARGV == ($questions - 1) && !defined $start) {
	print "\n==> YES\n";
	return 1;
    }
    if (defined $questions && $#ARGV == ($questions - 1) && defined $start) {
	if ($ARGV[$current_q-2] =~ m/^y/i || $current_q == 1) {
	    print "\n==> YES\n";
	    return 1;
	} else {
	    print "\n==> NO\n";
	    return 0;
	}
    }
    do {
	print "\n==> ";
        chomp($answer = <STDIN>);
        if ($answer =~ m/^y/i) {
	    return 1;
	}
	if ($answer =~ m/^n/i) {
	    return 0;
	}
	print "Hey, I gimme a \"yes\" or \"no\"!\n";
    } while (1);
}
sub print_log {
    my($entry) = @_;
    print "$entry\n";
    print LOG "$entry\n";
}
sub clean_line {
    chomp;
    s/^\s+//;
}
sub get_new_name {
    my($file) = @_;
    $newname = $file . ".harden_suse";
    if (-f $newname) {
	$newname = $file . ".harden_suse.$$";
	if (-f $newname) {
	    $newname = `/bin/mktemp /etc/$file.harden_suse.XXXXXX`;
	}
    }
    &print_log("Saving $file as $newname .");
}
# END OF SCRIPT
