#!/usr/bin/perl
###############################################################################
# Copyright 2006-2012, Way to the Web Limited
# URL: http://www.configserver.com
# Email: sales@waytotheweb.com
###############################################################################
use Fcntl qw(:DEFAULT :flock);
use IPC::Open3;

system ("/bin/cp -f /etc/csf/csf.conf /etc/csf/csf.conf.preupdate");
&loadcsfconfig;

if ($config{GENERIC}) {
	exec "perl auto.generic.pl";
	exit;
}

if (-e "/etc/csf/csf.tempban") {
	sysopen (IN,"/etc/csf/csf.tempban", O_RDWR | O_CREAT);
	flock (IN, LOCK_EX);
	my @data = <IN>;
	chomp @data;
	seek (IN, 0, 0);
	truncate (IN, 0);
	foreach my $line (@data) {
		if ($line =~ /^\d+\:/) {$line =~ s/\:/\|/g}
		print IN "$line\n";
	}
	close (IN);
}
if (-e "/etc/csf/csf.tempallow") {
	sysopen (IN,"/etc/csf/csf.tempallow", O_RDWR | O_CREAT);
	flock (IN, LOCK_EX);
	my @data = <IN>;
	chomp @data;
	seek (IN, 0, 0);
	truncate (IN, 0);
	foreach my $line (@data) {
		if ($line =~ /^\d+\:/) {$line =~ s/\:/\|/g}
		print IN "$line\n";
	}
	close (IN);
}
if (-e "/etc/csf/reselleralert.txt") {
	sysopen (IN,"/etc/csf/reselleralert.txt", O_RDWR | O_CREAT);
	flock (IN, LOCK_EX);
	my @data = <IN>;
	chomp @data;
	seek (IN, 0, 0);
	truncate (IN, 0);
	my $text = 0;
	foreach my $line (@data) {
		if ($line =~ /\[text\]/) {$text = 1}
		print IN "$line\n";
	}
	unless ($text) {print IN "\n[text]\n"}
	close (IN);
}

open (IN,"</etc/chkserv.d/chkservd.conf");
my @chkservd = <IN>;
close (IN);
chomp @chkservd;

if (not grep {$_ =~ /^lfd/} @chkservd) {
	open (OUT,">/etc/chkserv.d/lfd");
	print OUT "service[lfd]=x,x,x,service lfd restart,lfd,root\n";
	close (OUT);

	open (OUT, ">>/etc/chkserv.d/chkservd.conf");
	print OUT "lfd:1\n";
	close OUT;

	system("/scripts/restartsrv_chkservd");
}

if ($config{TESTING}) {

	open (IN, "</etc/ssh/sshd_config") or die $!;
	flock (IN, LOCK_SH) or die $!;
	my @sshconfig = <IN>;
	close (IN);
	chomp @sshconfig;

	my $sshport = "22";
	foreach my $line (@sshconfig) {
		if ($line =~ /^Port (\d+)/) {$sshport = $1}
	}

	$config{TCP_IN} =~ s/\s//g;
	if ($config{TCP_IN} ne "") {
		foreach my $port (split(/\,/,$config{TCP_IN})) {
			if ($port eq $sshport) {$sshport = "22"}
		}
	}

	if ($sshport ne "22") {
		$config{TCP_IN} .= ",$sshport";
		open (IN, "</etc/csf/csf.conf") or die $!;
		flock (IN, LOCK_SH) or die $!;
		my @config = <IN>;
		close (IN);
		chomp @config;
		open (OUT, ">/etc/csf/csf.conf") or die $!;
		flock (OUT, LOCK_EX) or die $!;
		foreach my $line (@config) {
			if ($line =~ /^TCP_IN/) {
				print OUT "TCP_IN = \"$config{TCP_IN}\"\n";
				print "\n*** SSH port $sshport added to the TCP_IN port list\n\n";
			} else {
				print OUT $line."\n";
			}
		}
		close OUT;
		&loadcsfconfig;
	}

	if (-e $config{IP6TABLES}) {
		my ($childin, $childout);
		my $cmdpid = open3($childin, $childout, $childout, $config{IFCONFIG});
		my @ifconfig = <$childout>;
		waitpid ($cmdpid, 0);
		chomp @ifconfig;
		if (grep {$_ =~ /^\s*inet6 \w*:\s*(\S+\s*Scope:\w+)/} @ifconfig) {
			$config{IPV6} = 1;
			open (FH, "<", "/proc/sys/kernel/osrelease");
			my @data = <FH>;
			close (FH);
			chomp @data;
			if ($data[0] =~ /^(\d+)\.(\d+)\.(\d+)/) {
				my $maj = $1;
				my $mid = $2;
				my $min = $3;
				if (($maj > 2) or (($maj > 1) and ($mid > 6)) or (($maj > 1) and ($mid > 5) and ($min > 19))) {
					$config{IPV6_SPI} = 1;
				} else {
					$config{IPV6_SPI} = 0;
				}
			}
			open (IN, "</etc/csf/csf.conf") or die $!;
			flock (IN, LOCK_SH) or die $!;
			my @config = <IN>;
			close (IN);
			chomp @config;
			open (OUT, ">/etc/csf/csf.conf") or die $!;
			flock (OUT, LOCK_EX) or die $!;
			foreach my $line (@config) {
				if ($line =~ /^IPV6 =/) {
					print OUT "IPV6 = \"$config{IPV6}\"\n";
					print "\n*** IPV6 Enabled\n\n";
				}
				elsif ($line =~ /^IPV6_SPI =/) {
					print OUT "IPV6_SPI = \"$config{IPV6_SPI}\"\n";
					print "\n*** IPV6_SPI set to $config{IPV6_SPI}\n\n";
				} else {
					print OUT $line."\n";
				}
			}
			close OUT;
			&loadcsfconfig;
		}
	}

	my $exim = 0;
	opendir (DIR, "/etc/chkserv.d");
	while (my $file = readdir (DIR)) {
		if ($file =~ /exim-(\d+)/) {$exim = $1}
	}
	closedir (DIR);

	if ($exim ne "0") {
		$config{TCP_IN} .= ",$exim";
		$config{SMTP_PORTS} .= ",$exim";
		open (IN, "</etc/csf/csf.conf") or die $!;
		flock (IN, LOCK_SH) or die $!;
		my @config = <IN>;
		close (IN);
		chomp @config;
		open (OUT, ">/etc/csf/csf.conf") or die $!;
		flock (OUT, LOCK_EX) or die $!;
		foreach my $line (@config) {
			if ($line =~ /^TCP_IN =/) {
				print OUT "TCP_IN = \"$config{TCP_IN}\"\n";
				print "\n*** Additional Exim port $exim added to the TCP_IN port list\n\n";
			}
			elsif ($line =~ /^SMTP_PORTS =/) {
				print OUT "SMTP_PORTS = \"$config{SMTP_PORTS}\"\n";
				print "\n*** Additional Exim port $exim added to the SMTP_PORTS port list\n\n";
			} else {
				print OUT $line."\n";
			}
		}
		close OUT;
		&loadcsfconfig;
	}
}

open (IN, "<csf.conf") or die $!;
flock (IN, LOCK_SH) or die $!;
my @config = <IN>;
close (IN);
chomp @config;
open (OUT, ">/etc/csf/csf.conf") or die $!;
flock (OUT, LOCK_EX) or die $!;
foreach my $line (@config) {
	if ($line =~ /^\#/) {
		print OUT $line."\n";
		next;
	}
	if ($line !~ /=/) {
		print OUT $line."\n";
		next;
	}
	my ($name,$value) = split (/=/,$line);
	$name =~ s/\s//g;
	if ($value =~ /\"(.*)\"/) {
		$value = $1;
	} else {
		print "Invalid configuration line: [$line]\n"
	}
	if ($configsetting{$name}) {
		if ($name eq "LF_GLOBAL" and $config{$name} eq "") {$config{$name} = 0}
		print OUT "$name = \"$config{$name}\"\n";
	} else {
		print OUT $line."\n";
		print "New setting: $name\n";
	}
}
close OUT;

#open (IN, "</etc/csf/csf.pignore") or die $!;
#flock (IN, LOCK_SH) or die $!;
#my @pignore = <IN>;
#close (IN);
#chomp @pignore;
#foreach my $file ('exe:/usr/local/cpanel/bin/cppop-ssl','exe:/usr/local/cpanel/cpsrvd-ssl','exe:/usr/local/cpanel/3rdparty/bin/imapd','exe:/usr/local/apache/bin/httpd','exe:/usr/local/cpanel/bin/cppop','exe:/usr/sbin/sshd','exe:/usr/local/cpanel/3rdparty/bin/analog','exe:/usr/local/cpanel/3rdparty/bin/php','exe:/usr/sbin/proftpd','exe:/usr/local/urchin/bin/urchinwebd','exe:/usr/bin/spamc','exe:/usr/local/cpanel/bin/logrunner','exe:/usr/local/cpanel/cpdavd','exe:/usr/local/cpanel/bin/cpwrap','exe:/usr/libexec/gam_server','exe:/usr/sbin/named','exe:/usr/sbin/exim','exe:/usr/sbin/mysqld','exe:/usr/libexec/hald-addon-acpi','exe:/usr/sbin/hald','exe:/bin/dbus-daemon','user:mailnull','user:mailman','exe:/usr/bin/dbus-daemon-1','exe:/usr/libexec/hald-addon-keyboard','exe:/usr/libexec/dovecot/imap','exe:/usr/libexec/dovecot/pop3','exe:/usr/sbin/mysqld_safe', 'exe:/usr/sbin/nsd', 'exe:/usr/libexec/dovecot/imap-login', 'exe:/usr/libexec/dovecot/pop3-login','exe:/var/cpanel/3rdparty/bin/php','exe:/usr/bin/postgres') {
#	open (OUT, ">>/etc/csf/csf.pignore") or die $!;
#	flock (OUT, LOCK_EX) or die $!;
#	if (!grep {$_ =~ /^$file$/} @pignore) {
#		print OUT "$file\n";
#		print "\n$file - added to csf.pignore\n";
#	}
#	close OUT;
#}

#open (IN, "</etc/csf/csf.rignore") or die $!;
#flock (IN, LOCK_SH) or die $!;
#my @rignore = <IN>;
#close (IN);
#chomp @rignore;
#open (OUT, ">>/etc/csf/csf.rignore") or die $!;
#flock (OUT, LOCK_EX) or die $!;
#if (!grep {$_ =~ /^\.cpanel\.net$/} @rignore) {print OUT ".cpanel.net\n"}
#if ($config{GOOGLEBOT}) {print OUT ".googlebot.com\n"}
#close OUT;

if ($config{TESTING}) {
	my @netstat = `netstat -lpn`;
	chomp @netstat;
	my @tcpports;
	my @udpports;
	my @tcp6ports;
	my @udp6ports;
	foreach my $line (@netstat) {
		if ($line =~ /^(\w+).* (\d+\.\d+\.\d+\.\d+):(\d+)/) {
			if ($2 eq '127.0.0.1') {next}
			if ($1 eq "tcp") {
				push @tcpports, $3;
			}
			elsif ($1 eq "udp") {
				push @udpports, $3;
			}
		}
		if ($line =~ /^(\w+).* (::):(\d+) /) {
			if ($1 eq "tcp") {
				push @tcp6ports, $3;
			}
			elsif ($1 eq "udp") {
				push @udp6ports, $3;
			}
		}
	}

	@tcpports = sort { $a <=> $b } @tcpports;
	@udpports = sort { $a <=> $b } @udpports;
	@tcp6ports = sort { $a <=> $b } @tcp6ports;
	@udp6ports = sort { $a <=> $b } @udp6ports;

	print "\nTCP ports currently listening for incoming connections:\n";
	my $last = "";
	foreach my $port (@tcpports) {
		if ($port ne $last) {
			if ($port ne $tcpports[0]) {print ","}
			print $port;
			$last = $port;
		}
	}
	print "\n\nUDP ports currently listening for incoming connections:\n";
	$last = "";
	foreach my $port (@udpports) {
		if ($port ne $last) {
			if ($port ne $udpports[0]) {print ","}
			print $port;
			$last = $port;
		}
	}
	$opts = "TCP_*, UDP_*";
	if (@tcp6ports or @udp6ports) {
		$opts .= ", IPV6, TCP6_*, UDP6_*";
		print "\n\nIPv6 TCP ports currently listening for incoming connections:\n";
		my $last = "";
		foreach my $port (@tcp6ports) {
			if ($port ne $last) {
				if ($port ne $tcp6ports[0]) {print ","}
				print $port;
				$last = $port;
			}
		}
		print "\n";
		print "\nIPv6 UDP ports currently listening for incoming connections:\n";
		$last = "";
		foreach my $port (@udp6ports) {
			if ($port ne $last) {
				if ($port ne $udp6ports[0]) {print ","}
				print $port;
				$last = $port;
			}
		}
	}
	print "\n\nNote: The port details above are for information only, csf hasn't been auto-configured.\n\n";
	print "Don't forget to:\n";
	print "1. Configure the following options in the csf configuration to suite your server: $opts\n";
	print "2. Restart csf and lfd\n";
	print "3. Set TESTING to 0 once you're happy with the firewall\n";
}

if ($ENV{SSH_CLIENT}) {
	my $ip = (split(/ /,$ENV{SSH_CLIENT}))[0];
	if ($ip =~ /(\d+\.\d+\.\d+\.\d+)/) {
		print "\nAdding current SSH session IP address to the csf whitelist in csf.allow:\n";
		system("/etc/csf/csf.pl -a $1 csf SSH installation/upgrade IP address");
	}
}

exit;
###############################################################################
sub loadcsfconfig {
	open (IN, "</etc/csf/csf.conf") or die $!;
	flock (IN, LOCK_SH) or die $!;
	my @config = <IN>;
	close (IN);
	chomp @config;

	foreach my $line (@config) {
		if ($line =~ /^\#/) {next}
		if ($line !~ /=/) {next}
		my ($name,$value) = split (/=/,$line);
		$name =~ s/\s//g;
		if ($value =~ /\"(.*)\"/) {
			$value = $1;
		} else {
			&error(__LINE__,"Invalid configuration line");
		}
		$config{$name} = $value;
		$configsetting{$name} = 1;
	}
}
###############################################################################
