OwlCyberSecurity - MANAGER
Edit File: lfd
#!/usr/local/cpanel/3rdparty/bin/perl ############################################################################### # Copyright (C) 2006-2026 Jonathan Michaelson # # https://github.com/waytotheweb/scripts # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, see <https://www.gnu.org/licenses>. ############################################################################### use cPstrict; no warnings; use lib '/usr/local/csf/lib'; use Fcntl qw(:DEFAULT :flock); use IO::Handle; use IPC::Open3; use Net::CIDR::Lite; use POSIX qw(:sys_wait_h sysconf strftime setsid); use Socket; use Digest::MD5; use ConfigServer::Config; use ConfigServer::Slurp qw(slurp slurpee); use ConfigServer::CheckIP qw(checkip cccheckip); use ConfigServer::URLGet; use ConfigServer::GetIPs qw(getips); use ConfigServer::Service; use ConfigServer::AbuseIP (); use ConfigServer::GetEthDev; use ConfigServer::Sendmail; use ConfigServer::Logger qw(logfile); use ConfigServer::KillSSH; use ConfigServer::LookUpIP qw(iplookup); use Cpanel::Encoder::Tiny (); umask(0177); our ( $abuseip, $accept, $apache401timeout, $apache403timeout, $apache404timeout, $attimeout, $blocklisttimeout, $ccltimeout, $cctimeout, $childcnt, $childpid, $childproc, $cidr, $cidr6, $cleanreg, $clock_ticks, $clusterip, $count, $csftimeout, $cttimeout, $cxsreputation, $dirwatchfiletimeout, $dirwatchtimeout, $dyndnstimeout, $eth6devin, $eth6devout, $ethdevin, $ethdevout, $exploittimeout, $faststart, $gcidr, $gcidr6, $gdyndnstimeout, $globaltimeout, $hostname, $hostshort, $integritytimeout, $ipscidr, $ipscidr6, $ipv4reg, $ipv6reg, $loadtimeout, $locktimeout, $loginterval, $masterpid, $modsecipdbchecktimeout, $pid, $pidfile, $pidfile_fh, $pidino, $pstimeout, $ptchildpid, $pttimeout, $queuetimeout, $relaytimeout, $scripttimeout, $slurpreg, $smtptimeout, $sys_syslog, $syslogcheckcode, $syslogchecktimeout, $sysloggid, $syslogpid, $systemstatstimeout, $tar, $toomanymatches, $tz, $uidtimeout, $uiip, $urlget, $version, $messenger1, $messenger2, $messenger3 ); our ( %accounttracking, %adb, %adf, %ads, %apache401, %apache403, %apache404, %blockedips, %blocklists, %cfblocks, %config, %cpanelalert, %cpanelalertusers, %cpconfig, %cxsports, %db, %dirwatchfile, %forks, %gignoreips, %globlogs, %ifaces, %ignoreips, %ips, %logfiles, %loginproto, %logins, %logintimeout, %logscannerfiles, %messengerips, %messengerports, %newaccounttracking, %nofiles, %ports, %portscans, %psips, %pskip, %relayip, %relays, %rtignore, %scripts, %sfile, %skip, %skipfile, %skipscript, %skipuser, %suignore, %uidignore, %uidscans ); our ( @cccidrs, @cidrs, @faststart4, @faststart4nat, @faststart6, @faststart6nat, @faststartipset, @gcidrs, @ipset, @lfbuf, @lffd, @lfino, @lfsize, @logignore, @matchfile, @rdns, @suspicious ); # Run main script logic only when executed directly, not when loaded as module __PACKAGE__->run() unless caller; sub run { my $class = shift; # Initialize package variables $pidfile = "/var/run/lfd.pid"; if ( -e "/etc/csf/csf.disable" ) { print "csf and lfd have been disabled\n"; return 1; } my $csf_error = get_csf_error_message(); if ($csf_error) { print "\n$csf_error\n"; return 1; } my $config = ConfigServer::Config->loadconfig(); %config = $config->config(); my %configsetting = $config->configsetting(); $ipv4reg = $config->ipv4reg; $ipv6reg = $config->ipv6reg; $slurpreg = ConfigServer::Slurp->slurpreg; $cleanreg = ConfigServer::Slurp->cleanreg; unless ( $config{LF_DAEMON} ) { cleanup( __LINE__, "*Error* LF_DAEMON not enabled in /etc/csf/csf.conf" ) } if ( $config{TESTING} ) { cleanup( __LINE__, "*Error* lfd will not run with TESTING enabled in /etc/csf/csf.conf" ) } if ( $config{LF_DIRWATCH} ) { require File::Find; import File::Find; } if ( $config{SYSLOG} or $config{SYSLOG_CHECK} ) { eval('use Sys::Syslog;'); ## no critic (BuiltinFunctions::ProhibitStringyEval) - Optional module load - syslog support not required on all systems unless ($@) { $sys_syslog = 1 } } if ( $config{DEBUG} ) { require Time::HiRes; import Time::HiRes; } if ( $config{CLUSTER_SENDTO} or $config{CLUSTER_RECVFROM} ) { require Crypt::CBC; import Crypt::CBC; require File::Basename; import File::Basename; } if ( $config{CLUSTER_SENDTO} or $config{CLUSTER_RECVFROM} ) { require IO::Socket::INET; import IO::Socket::INET; } if ( $config{MESSENGER} ) { require ConfigServer::Messenger; import ConfigServer::Messenger; } if ( $config{CF_ENABLE} ) { require ConfigServer::CloudFlare; import ConfigServer::CloudFlare; } if ( -e "/etc/cxs/cxs.reputation" and -e "/usr/local/csf/lib/ConfigServer/cxs.pm" ) { require ConfigServer::cxs; import ConfigServer::cxs; $cxsreputation = 1; %cxsports = ConfigServer::cxs::Rports(); } $SIG{CHLD} = 'IGNORE'; if ( $pid = fork ) { exit 0; } elsif ( defined($pid) ) { $pid = $$; } else { die "*Error* Unable to fork: $!"; } chdir("/etc/csf"); close(STDIN); close(STDOUT); close(STDERR); open STDIN, "<", "/dev/null"; open STDOUT, ">", "/dev/null"; open STDERR, ">", "/dev/null"; setsid(); my $oldfh = select STDERR; ## no critic (InputOutput::ProhibitOneArgSelect) - Save current filehandle before STDERR redirection in daemon mode $| = 1; select $oldfh; ## no critic (InputOutput::ProhibitOneArgSelect) - Restore previous filehandle after setting autoflush on STDERR if ( $config{DEBUG} ) { open( STDERR, ">>", "/var/log/lfd.log" ); } if ( -e "/proc/sys/kernel/hostname" ) { open( my $IN, "<", "/proc/sys/kernel/hostname" ); flock( $IN, LOCK_SH ); $hostname = <$IN>; chomp $hostname; close($IN); } else { $hostname = "unknown"; } $hostshort = ( split( /\./, $hostname ) )[0]; $clock_ticks = sysconf( POSIX::_SC_CLK_TCK() ) || 100; $tz = strftime( "%z", localtime ); sysopen( $pidfile_fh, $pidfile, O_RDWR | O_CREAT ) or childcleanup( __LINE__, "*Error* unable to create lfd PID file [$pidfile] $!" ); flock( $pidfile_fh, LOCK_EX | LOCK_NB ) or childcleanup( __LINE__, "*Error* attempt to start lfd when it is already running" ); autoflush $pidfile_fh 1; seek( $pidfile_fh, 0, 0 ); truncate( $pidfile_fh, 0 ); print $pidfile_fh "$pid\n"; $pidino = ( stat($pidfile) )[1]; $masterpid = $pid; $0 = "lfd - starting"; $SIG{INT} = \&cleanup; $SIG{TERM} = \&cleanup; $SIG{HUP} = \&cleanup; $SIG{__DIE__} = sub { return if $^S; cleanup(@_); }; $SIG{CHLD} = 'IGNORE'; $SIG{PIPE} = 'IGNORE'; $ipscidr = Net::CIDR::Lite->new; $ipscidr6 = Net::CIDR::Lite->new; $cidr = Net::CIDR::Lite->new; $cidr6 = Net::CIDR::Lite->new; $gcidr = Net::CIDR::Lite->new; $gcidr6 = Net::CIDR::Lite->new; eval { $ipscidr6->add("::1/128") }; eval { $ipscidr->add("127.0.0.0/8") }; $faststart = 0; eval { $urlget = ConfigServer::URLGet->new( $config{URLGET}, "csf/$version", $config{URLPROXY} ); }; unless ( defined $urlget ) { if ( -e $config{CURL} or -e $config{WGET} ) { $config{URLGET} = 3; $urlget = ConfigServer::URLGet->new( $config{URLGET}, "csf/$version", $config{URLPROXY} ); logfile("*WARNING* URLGET set to use LWP but perl module is not installed, fallback to using CURL/WGET"); } else { $config{URLGET} = 1; $urlget = ConfigServer::URLGet->new( $config{URLGET}, "csf/$version", $config{URLPROXY} ); logfile("*WARNING* URLGET set to use LWP but perl module is not installed, CURL and WGET not installed - reverting to HTTP::Tiny"); } } if ( -e "/etc/wwwacct.conf" ) { foreach my $line ( slurp("/etc/wwwacct.conf") ) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } my ( $name, $value ) = split( / /, $line, 2 ); $cpconfig{$name} = $value; } } if ( -e "/usr/local/cpanel/version" ) { foreach my $line ( slurp("/usr/local/cpanel/version") ) { $line =~ s/$cleanreg//g; if ( $line =~ /\d/ ) { $cpconfig{version} = $line } } } if ( -e "/var/lib/csf/csf.tempconf" ) { unlink("/var/lib/csf/csf.tempconf") } if ( -e "/var/lib/csf/lfd.enable" ) { unlink "/var/lib/csf/lfd.enable" } if ( -e "/var/lib/csf/lfd.start" ) { unlink "/var/lib/csf/lfd.start" } if ( -e "/var/lib/csf/lfd.restart" ) { unlink "/var/lib/csf/lfd.restart" } if ( -e "/var/lib/csf/csf.4.saved" ) { unlink "/var/lib/csf/csf.4.saved" } if ( -e "/var/lib/csf/csf.4.ipsets" ) { unlink "/var/lib/csf/csf.4.ipsets" } if ( -e "/var/lib/csf/csf.6.saved" ) { unlink "/var/lib/csf/csf.6.saved" } if ( -e "/var/lib/csf/csf.dnscache" ) { unlink "/var/lib/csf/csf.dnscache" } if ( -e "/var/lib/csf/csf.gignore" ) { unlink "/var/lib/csf/csf.gignore" } getethdev(); ($version) = slurpee( "/etc/csf/version.txt", 'warn' => 0 ) or cleanup( __LINE__, "Unable to open version.txt" ); chomp $version; logfile("daemon started on $hostname - csf v$version (cPanel)"); if ( $config{DEBUG} >= 1 ) { logfile("Clock Ticks: $clock_ticks") } if ( $config{DEBUG} >= 1 ) { logfile("debug: **** DEBUG LEVEL $config{DEBUG} ENABLED ****") } unless ( -e $config{SENDMAIL} ) { logfile("*WARNING* Unable to send email reports - [$config{SENDMAIL}] not found"); } if ( ConfigServer::Service::type() eq "systemd" ) { my @reply = syscommand( __LINE__, $config{SYSTEMCTL}, "is-active", "firewalld" ); chomp @reply; if ( $reply[0] eq "active" or $reply[0] eq "activating" ) { cleanup( __LINE__, "*Error* firewalld found to be running. You must stop and disable firewalld when using csf" ); exit 1; } } require ConfigServer::RegexMain; import ConfigServer::RegexMain; if ( $config{RESTRICT_SYSLOG} == 1 ) { logfile("Restricted log file access (RESTRICT_SYSLOG)"); foreach ( qw{LF_SSHD LF_FTPD LF_IMAPD LF_POP3D LF_BIND LF_SUHOSIN LF_SSH_EMAIL_ALERT LF_SU_EMAIL_ALERT LF_CONSOLE_EMAIL_ALERT LF_DISTATTACK LF_DISTFTP LT_POP3D LT_IMAPD PS_INTERVAL UID_INTERVAL PORTKNOCKING_ALERT LF_SUDO_EMAIL_ALERT} ) { if ( $config{$_} != 0 ) { $config{$_} = 0; logfile("RESTRICT_SYSLOG: Option $_ *Disabled*"); } } } elsif ( $config{RESTRICT_SYSLOG} == 3 ) { logfile("Restricting syslog/rsyslog socket acccess to group [$config{RESTRICT_SYSLOG_GROUP}]..."); syslog_init(); } if ( $config{SYSLOG} or $config{SYSLOG_CHECK} ) { unless ($sys_syslog) { logfile("*Error* Cannot log to SYSLOG - Perl module Sys::Syslog required"); } } if ( -e "/etc/csf/csf.blocklists" ) { my @entries = slurp("/etc/csf/csf.blocklists"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $name, $interval, $max, $url ) = split( /\|/, $line ); if ( $name =~ /^\w+$/ ) { $name = substr( uc $name, 0, 25 ); if ( $name =~ /^CXS_/ ) { $name =~ s/^CXS_/X_CXS_/ } if ( !$interval || $interval < 3600 ) { $interval = 3600 } if ( !length $max ) { $max = 0 } $blocklists{$name}{interval} = $interval; $blocklists{$name}{max} = $max; $blocklists{$name}{url} = $url; } } } if ( $cxsreputation and -e "/etc/cxs/cxs.blocklists" ) { my $all = 0; my @lines = slurp("/etc/cxs/cxs.blocklists"); if ( grep { $_ =~ /^CXS_ALL/ } @lines ) { $all = 1; } foreach my $line (@lines) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } my ( $name, $interval, $max, $url ) = split( /\|/, $line ); $url //= ""; $url =~ s/download\.configserver\.com/$config{DOWNLOADSERVER}/g; if ( $all and $name ne "CXS_ALL" ) { next } if ( $name =~ /^\w+$/ ) { $name = substr( uc $name, 0, 25 ); if ( !length $max ) { $max = 0 } $blocklists{$name}{interval} = $interval; $blocklists{$name}{max} = $max; $blocklists{$name}{url} = $url; } } } if ( -e "/etc/csf/csf.ignore" ) { my @ignore = slurp("/etc/csf/csf.ignore"); foreach my $line (@ignore) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @ignore, @incfile; } } foreach my $line (@ignore) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $first, undef ) = split( /\s/, $line ); my ( $ip, $iscidr ) = split( /\//, $first ); $ip //= ""; if ( checkip( \$first ) ) { if ($iscidr) { push @cidrs, $first } else { $ignoreips{$ip} = 1 } } elsif ( $ip ne "127.0.0.1" ) { logfile("Invalid entry in csf.ignore: [$first]") } } foreach my $entry (@cidrs) { if ( checkip( \$entry ) == 6 ) { eval { $cidr6->add($entry) }; } else { eval { $cidr->add($entry) }; } if ($@) { logfile("Invalid entry in csf.ignore: $entry") } } } if ( -e "/etc/csf/csf.rignore" ) { my @entries = slurp("/etc/csf/csf.rignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } if ( $line =~ /^(\.|\w)/ ) { my ( $host, undef ) = split( /\s/, $line ); $host //= ""; if ( $host ne "" ) { push @rdns, $host } } } } if ( $config{IGNORE_ALLOW} and -e "/etc/csf/csf.allow" ) { my @ignore = slurp("/etc/csf/csf.allow"); foreach my $line (@ignore) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @ignore, @incfile; } } foreach my $line (@ignore) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $first, undef ) = split( /\s/, $line ); my ( $ip, $iscidr ) = split( /\//, $first ); $ip //= ""; if ( checkip( \$first ) ) { if ($iscidr) { push @cidrs, $first } else { $ignoreips{$ip} = 1 } } } foreach my $entry (@cidrs) { if ( checkip( \$entry ) == 6 ) { eval { $cidr6->add($entry) }; } else { eval { $cidr->add($entry) }; } if ($@) { logfile("Invalid CIDR in csf.allow: $entry") } } } if ( $config{LF_HTACCESS} or $config{LF_APACHE_404} or $config{LF_APACHE_403} or $config{LF_APACHE_401} or $config{LF_QOS} or $config{LF_SYMLINK} ) { globlog("HTACCESS_LOG") } if ( $config{LF_MODSEC} or $config{LF_CXS} ) { globlog("MODSEC_LOG") } if ( $config{LF_SUHOSIN} ) { globlog("SUHOSIN_LOG}") } if ( $config{LF_SMTPAUTH} or $config{LF_EXIMSYNTAX} ) { globlog("SMTPAUTH_LOG") } if ( $config{LF_POP3D} or $config{LT_POP3D} ) { globlog("POP3D_LOG") } if ( $config{LF_IMAPD} or $config{LT_IMAPD} ) { globlog("IMAPD_LOG") } if ( $config{LF_CPANEL} ) { globlog("CPANEL_LOG") } if ( $config{LF_SSHD} or $config{LF_SSH_EMAIL_ALERT} or $config{LF_CONSOLE_EMAIL_ALERT} ) { globlog("SSHD_LOG") } if ( $config{LF_FTPD} ) { globlog("FTPD_LOG") } if ( $config{LF_BIND} ) { globlog("BIND_LOG") } if ( $config{LF_CPANEL_ALERT} ) { globlog("CPANEL_ACCESSLOG") } if ( $config{SYSLOG_CHECK} and $sys_syslog ) { globlog("SYSLOG_LOG") } if ( $config{PS_INTERVAL} or $config{ST_ENABLE} or $config{UID_INTERVAL} ) { globlog("IPTABLES_LOG") } if ( $config{LF_SU_EMAIL_ALERT} ) { globlog("SU_LOG") } if ( $config{LF_SUDO_EMAIL_ALERT} ) { globlog("SUDO_LOG") } if ( $config{LF_SCRIPT_ALERT} ) { globlog("SCRIPT_LOG") } if ( $config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} ) { globlog("SMTPRELAY_LOG") } if ( $config{LT_IMAPD} ) { $loginproto{imapd} = $config{LT_IMAPD} } if ( $config{LT_POP3D} ) { $loginproto{pop3d} = $config{LT_POP3D} } for ( my $x = 1; $x < 10; $x++ ) { globlog("CUSTOM${x}_LOG") } if ( -e "/usr/local/cpanel/version" and -e "/etc/cpanel/ea4/is_ea4" and -e "/etc/cpanel/ea4/paths.conf" ) { my @file = slurp("/etc/cpanel/ea4/paths.conf"); foreach my $line (@file) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } if ( $line !~ /=/ ) { next } my ( $name, $value ) = split( /=/, $line, 2 ); $value //= ""; $value =~ s/^\s+//g; $value =~ s/\s+$//g; if ( $name eq "dir_logs" ) { if ( $config{LF_HTACCESS} or $config{LF_APACHE_404} or $config{LF_APACHE_403} or $config{LF_APACHE_401} or $config{LF_QOS} or $config{LF_SYMLINK} ) { delete $globlogs{HTACCESS_LOG}{ $config{HTACCESS_LOG} }; delete $logfiles{ $config{HTACCESS_LOG} }; $globlogs{HTACCESS_LOG}{"$value/error_log"} = 1; $logfiles{"$value/error_log"} = 1; logfile("EasyApache4, using $value/error_log instead of $config{HTACCESS_LOG} (Web Server)"); } if ( $config{LF_MODSEC} or $config{LF_CXS} ) { delete $globlogs{MODSEC_LOG}{ $config{MODSEC_LOG} }; delete $logfiles{ $config{MODSEC_LOG} }; $globlogs{MODSEC_LOG}{"$value/error_log"} = 1; $logfiles{"$value/error_log"} = 1; logfile("EasyApache4, using $value/error_log instead of $config{MODSEC_LOG} {ModSecurity}"); } } } } if ( $config{LOGSCANNER} ) { my @entries = slurp("/etc/csf/csf.logfiles"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $file (@entries) { $file && $file =~ s/$cleanreg//g; if ( !length $file ) { next } if ( $file =~ /^\s*\#|Include/ ) { next } if ( $file =~ /[*?\[]/ ) { foreach my $log ( glob $file ) { if ( -e $log ) { $logfiles{$log} = 1; $logscannerfiles{$log} = 1; } } } else { if ( -e $file ) { $logfiles{$file} = 1; $logscannerfiles{$file} = 1; } } } my @entries2 = slurp("/etc/csf/csf.logignore"); foreach my $line (@entries2) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries2, @incfile; } } foreach my $line (@entries2) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } if ( testregex($line) ) { push @logignore, $line } else { logfile("*Error* Invalid regex [$line] in csf.logignore") } } logfile("Log Scanner..."); } unless ( -d "/var/spool/exim" ) { $config{LF_QUEUE_ALERT} = 0 } $accept = "ACCEPT"; if ( $config{WATCH_MODE} ) { $accept = "LOGACCEPT"; $config{DROP_NOLOG} = ""; $config{DROP_LOGGING} = "1"; $config{DROP_IP_LOGGING} = "1"; $config{DROP_OUT_LOGGING} = "1"; $config{DROP_PF_LOGGING} = "1"; $config{PS_INTERVAL} = "0"; $config{DROP_ONLYRES} = "0"; logfile("WATCH_MODE enabled..."); } if ( -e "/var/lib/csf/csf.restart" ) { unlink "/var/lib/csf/csf.restart"; csfrestart(); } if ( $config{LF_CSF} ) { if ( -e "/var/lib/csf/cpanel.new" ) { unlink "/var/lib/csf/cpanel.new" } logfile("CSF Tracking..."); csfcheck(); $csftimeout = 0; } if ( $config{IPV6} ) { logfile("IPv6 Enabled..."); } if ($cxsreputation) { logfile("cxs Reputation Enabled..."); } if ( $config{PT_LOAD} ) { logfile("LOAD Tracking..."); loadcheck(); $loadtimeout = 0; } if ( $config{CF_ENABLE} and -e "/etc/csf/csf.cloudflare" ) { logfile("CloudFlare Firewall..."); $cfblocks{LF_MODSEC} = 1; $cfblocks{LF_CXS} = 1; } if ( $config{MESSENGER} ) { unless ( -e "/var/log/lfd_messenger.log" ) { open( my $OUT, ">", "/var/log/lfd_messenger.log" ); close($OUT); } system( "chown", "$config{MESSENGER_USER}:$config{MESSENGER_USER}", "/var/log/lfd_messenger.log" ); if ( !$config{MESSENGERV2} ) { messengerstop(2); } if ( !$config{MESSENGERV3} ) { messengerstop(3); } my ( undef, undef, $uid, $gid ) = getpwnam( $config{MESSENGER_USER} ); if ( ( $config{MESSENGER_USER} ne "" ) and ( $config{MESSENGER_USER} ne "root" ) and ( $uid > 0 ) and ( $gid > 0 ) ) { if ( $config{MESSENGER_HTTPS_DISABLED} ne "" ) { logfile( $config{MESSENGER_HTTPS_DISABLED} ); } if ( $config{MESSENGERV3} ) { $messenger3 = ConfigServer::Messenger->init(3); if ( -e "/var/cpanel/users/$config{MESSENGER_USER}" ) { logfile("*MESSENGERV3* - Cannot run service using a cPanel account:[$config{MESSENGER_USER}], MESSENGER service disabled"); $config{MESSENGER} = 0; $config{MESSENGERV3} = 0; messengerstop(3); } else { if ( $config{MESSENGER_HTTPS_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTTPS_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTTPS Service starting..."); } if ( $config{MESSENGER_HTML_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTML_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTML Service starting..."); } messengerv3(); } } elsif ( $config{MESSENGERV2} ) { $messenger2 = ConfigServer::Messenger->init(2); if ( -e "/var/cpanel/users/$config{MESSENGER_USER}" ) { logfile("*MESSENGERV2* - Cannot run service using a cPanel account:[$config{MESSENGER_USER}], MESSENGER service disabled"); $config{MESSENGER} = 0; $config{MESSENGERV2} = 0; messengerstop(2); } else { if ( $config{MESSENGER_HTTPS_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTTPS_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTTPS Service starting..."); } if ( $config{MESSENGER_HTML_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTML_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTML Service starting..."); } messengerv2(); } } else { $messenger1 = ConfigServer::Messenger->init(1); if ( $config{MESSENGER_HTTPS_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTTPS_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTTPS Service starting..."); messenger( $config{MESSENGER_HTTPS}, $config{MESSENGER_USER}, "HTTPS" ); } if ( $config{MESSENGER_HTML_IN} ne "" ) { foreach my $port ( split( /\,/, $config{MESSENGER_HTML_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger HTML Service starting..."); messenger( $config{MESSENGER_HTML}, $config{MESSENGER_USER}, "HTML" ); } } if ( $config{MESSENGER_TEXT_IN} ne "" ) { unless ( defined $messenger1 ) { $messenger1 = ConfigServer::Messenger->init(1); } foreach my $port ( split( /\,/, $config{MESSENGER_TEXT_IN} ) ) { $messengerports{$port} = 1 } logfile("Messenger TEXT Service starting..."); messenger( $config{MESSENGER_TEXT}, $config{MESSENGER_USER}, "TEXT" ); } } else { logfile("Messenger account [$config{MESSENGER_USER}] invalid, MESSENGER service *disabled*"); $config{MESSENGER} = 0; } } else { messengerstop(2); messengerstop(3); } if ( $config{CLUSTER_RECVFROM} ) { logfile("Cluster Service starting..."); if ( length $config{CLUSTER_KEY} < 8 ) { logfile("Failed: Cluster Service - CLUSTER_KEY too short"); $config{CLUSTER_RECVFROM} = 0; } else { if ( length $config{CLUSTER_KEY} < 20 ) { logfile("Cluster Service - CLUSTER_KEY should really be longer than 20 characters") } lfdserver(); } } if ( $config{DYNDNS} ) { logfile("DynDNS Tracking..."); dyndns(); $dyndnstimeout = 0; if ( $config{DYNDNS} < 60 ) { logfile("DYNDNS refresh increased to 300 to prevent looping (csf.conf setting: $config{DYNDNS})"); $config{DYNDNS} = 300; } } if ( $config{LF_GLOBAL} ) { if ( $config{GLOBAL_IGNORE} ) { logfile("Global Ignore Tracking...") } if ( $config{GLOBAL_ALLOW} ) { logfile("Global Allow Tracking...") } if ( $config{GLOBAL_DENY} ) { logfile("Global Deny Tracking...") } if ( $config{GLOBAL_DYNDNS} ) { logfile("Global DynDNS Tracking...") } global(); $globaltimeout = 0; if ( $config{LF_GLOBAL} < 60 ) { logfile("LF_GLOBAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_GLOBAL})"); $config{LF_GLOBAL} = 300; } if ( $config{GLOBAL_DYNDNS_INTERVAL} < 60 ) { logfile("GLOBAL_DYNDNS_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{GLOBAL_DYNDNS_INTERVAL})"); $config{GLOBAL_DYNDNS_INTERVAL} = 300; } } if ( scalar( keys %blocklists ) > 0 ) { logfile("Blocklist Tracking..."); blocklist(); $blocklisttimeout = 0; } if ( $config{CC_LOOKUPS} ) { if ( $config{CC_LOOKUPS} != 4 and $config{MM_LICENSE_KEY} eq "" and $config{CC_SRC} eq "1" ) { logfile("*ERROR*: Country Code Lookups setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue updating the MaxMind databases"); } logfile("Country Code Lookups..."); countrycodelookups(); $ccltimeout = 0; } if ( $config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_ALLOW_PORTS} or $config{CC_DENY_PORTS} or $config{CC_ALLOW_SMTPAUTH} ) { if ( $config{MM_LICENSE_KEY} eq "" and $config{CC_SRC} eq "1" ) { logfile("*ERROR*: Country Code Filters setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue updating the MaxMind databases"); } logfile("Country Code Filters..."); countrycode(); $cctimeout = 0; } if ( $config{CC_IGNORE} ) { if ( $config{CC_LOOKUPS} ) { logfile("Country Code Ignores..."); } else { logfile("Country Code Ignores requires CC_LOOKUPS to be enabled - disabled CC_IGNORE"); $config{CC_IGNORE} = ""; } } if ( $config{LF_INTEGRITY} ) { logfile("System Integrity Tracking..."); integrity(); $integritytimeout = 0; if ( $config{LF_INTEGRITY} < 120 ) { logfile("LF_INTEGRITY refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_INTEGRITY})"); $config{LF_INTEGRITY} = 300; } } if ( $config{LF_EXPLOIT} ) { if ( -e "/var/lib/csf/csf.tempexploit" ) { unlink("/var/lib/csf/csf.tempexploit") } if ( -e "/etc/csf/csf.suignore" ) { my @entries = slurp("/etc/csf/csf.suignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } $suignore{$line} = 1; } } logfile("Exploit Tracking..."); exploit(); $exploittimeout = 0; if ( $config{LF_EXPLOIT} < 60 ) { logfile("LF_EXPLOIT refresh increased to 60 to prevent looping (csf.conf setting: $config{LF_EXPLOIT})"); $config{LF_EXPLOIT} = 60; } } if ( $config{X_ARF} ) { if ( -e $config{HOST} ) { $abuseip = 1 } else { logfile("Binary location of HOST is incorrect in csf.conf") } } if ( $config{LF_DIRWATCH} ) { if ( -e "/etc/csf/csf.fignore" ) { my @entries = slurp("/etc/csf/csf.fignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } if ( $line =~ /[*\\]/ ) { if ( testregex($line) ) { push @matchfile, $line } else { logfile("*Error* Invalid regex [$line] in csf.fignore") } } elsif ( $line =~ /^user:(.*)/ ) { $skipuser{$1} = 1; } else { $skipfile{$line} = 1; } } } if ( -e "/var/lib/csf/csf.tempfiles" ) { unlink("/var/lib/csf/csf.tempfiles") } if ( -e "/var/lib/csf/csf.dwdisable" ) { unlink("/var/lib/csf/csf.dwdisable") } logfile("Directory Watching..."); $dirwatchtimeout = 0; } if ( $config{LF_DIRWATCH_FILE} ) { if ( -e "/etc/csf/csf.dirwatch" ) { logfile("Directory File Watching..."); my @entries = slurp("/etc/csf/csf.dirwatch"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } if ( -e $line ) { $dirwatchfile{$line} = 1; } else { logfile("Directory File Watching [$line] not found - ignoring"); } } dirwatchfile(); $dirwatchfiletimeout = 0; } } if ( $config{LF_SCRIPT_ALERT} ) { logfile("Email Script Tracking..."); if ( -e "/etc/csf/csf.signore" ) { my @entries = slurp("/etc/csf/csf.signore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } $skipscript{$line} = 1; } } } if ( $config{LF_QUEUE_ALERT} ) { logfile("Email Queue Tracking..."); queuecheck(); $queuetimeout = 0; if ( $config{LF_QUEUE_INTERVAL} < 30 ) { logfile("LF_QUEUE_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_QUEUE_INTERVAL})"); $config{LF_QUEUE_INTERVAL} = 300; } } if ( $config{LF_MODSECIPDB_ALERT} ) { logfile("ModSecurity IP D/B Tracking..."); modsecipdbcheck(); $modsecipdbchecktimeout = 0; } if ( $config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT} ) { logfile("Email Relay Tracking..."); if ( $config{RT_LOCALRELAY_ALERT} ) { if ( -e "/etc/csf/csf.mignore" ) { my @entries = slurp("/etc/csf/csf.mignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } $rtignore{$line} = 1; } } } } if ( $config{LF_PERMBLOCK} ) { logfile("Temp to Perm Block Tracking..."); } if ( $config{LF_NETBLOCK} ) { logfile("Netblock Tracking..."); } if ( $config{LF_PERMBLOCK} or $config{LF_NETBLOCK} ) { sysopen( my $TEMPIP, "/var/lib/csf/csf.tempip", O_RDWR | O_CREAT ); flock( $TEMPIP, LOCK_EX ); my @data = <$TEMPIP>; chomp @data; seek( $TEMPIP, 0, 0 ); truncate( $TEMPIP, 0 ); foreach my $line (@data) { my $old = 1; my ( $oip, $operm, $otime, $omessage ) = split( /\|/, $line, 4 ); $otime //= 0; my $interval = time - $otime; if ( $config{LF_PERMBLOCK} and $interval < ( $config{LF_PERMBLOCK_INTERVAL} * $config{LF_PERMBLOCK_COUNT} ) ) { $old = 0 } if ( $config{LF_NETBLOCK} and $interval < ( $config{LF_NETBLOCK_INTERVAL} * $config{LF_NETBLOCK_COUNT} ) ) { $old = 0 } unless ($old) { print $TEMPIP "$line\n" } } close($TEMPIP); } if ( $config{ST_SYSTEM} ) { logfile("System Statistics..."); my $time = time; sysopen( my $SYSSTATNEW, "/var/lib/csf/stats/system.new", O_RDWR | O_CREAT ); flock( $SYSSTATNEW, LOCK_EX ); seek( $SYSSTATNEW, 0, 0 ); truncate( $SYSSTATNEW, 0 ); sysopen( my $SYSSTAT, "/var/lib/csf/stats/system", O_RDWR | O_CREAT ); flock( $SYSSTAT, LOCK_EX ); while ( my $line = <$SYSSTAT> ) { chomp $line; my ( $thistime, undef ) = split( /\,/, $line ); if ( $time - $thistime > ( 86400 * $config{ST_SYSTEM_MAXDAYS} ) ) { next } print $SYSSTATNEW $line . "\n"; } close($SYSSTAT); close($SYSSTATNEW); rename "/var/lib/csf/stats/system.new", "/var/lib/csf/stats/system"; systemstats(); } if ( $config{PS_INTERVAL} ) { logfile("Port Scan Tracking..."); if ( $config{PS_INTERVAL} < 60 ) { logfile("PS_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{PS_INTERVAL})"); $config{PS_INTERVAL} = 60; } $pstimeout = 0; } if ( $config{UID_INTERVAL} ) { logfile("User ID Tracking..."); if ( $config{UID_INTERVAL} < 60 ) { logfile("UID_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{UID_INTERVAL})"); $config{UID_INTERVAL} = 60; } $uidtimeout = 0; my @entries = slurp("/etc/csf/csf.uidignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } $uidignore{$line} = 1; } } if ( $config{CT_LIMIT} ) { if ( $config{CT_STATES} ) { logfile("Connection Tracking ($config{CT_STATES})..."); } else { logfile("Connection Tracking..."); } connectiontracking(); $cttimeout = 0; if ( $config{CT_INTERVAL} < 10 ) { logfile("CT_INTERVAL refresh increased to 30 to prevent looping (csf.conf setting: $config{CT_INTERVAL})"); $config{CT_INTERVAL} = 30; } } if ( $config{PT_LIMIT} ) { if ( -e "/etc/csf/csf.pignore" ) { my @entries = slurp("/etc/csf/csf.pignore"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $item, $rule ) = split( /:/, $line, 2 ); $rule //= ""; $item //= ""; $rule =~ s/[\r\n]//g; $rule =~ s/\s*$//g; $item =~ s/\s//g; $item = lc $item; if ( $item =~ /^(cmd|exe|user)$/ ) { $skip{$item}{$rule} = 1; } elsif ( $item =~ /^(pcmd|pexe|puser)$/ ) { if ( testregex($rule) ) { $pskip{$item}{$rule} = 1 } else { logfile("*Error* Invalid regex [$line] in csf.pignore") } } } } if ( -e "/var/lib/csf/csf.temppids" ) { unlink("/var/lib/csf/csf.temppids") } if ( -e "/var/lib/csf/csf.tempusers" ) { unlink("/var/lib/csf/csf.tempusers") } logfile("Process Tracking..."); processtracking(); $pttimeout = 0; if ( $config{PT_INTERVAL} < 10 ) { logfile("PT_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{PT_INTERVAL})"); $config{PT_INTERVAL} = 60; } if ( $config{PT_SSHDHUNG} ) { logfile("SSHD Hung Session Tracking..."); } } if ( $config{AT_ALERT} ) { if ( $config{AT_ALERT} == 3 ) { my ( $user, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell ) = getpwnam("root"); $accounttracking{$user}{account} = 1; $accounttracking{$user}{passwd} = $passwd; $accounttracking{$user}{uid} = $uid; $accounttracking{$user}{gid} = $gid; $accounttracking{$user}{dir} = $dir; $accounttracking{$user}{shell} = $shell; } else { while ( my ( $user, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell ) = getpwent() ) { if ( ( $config{AT_ALERT} eq "2" ) and ( $uid ne "0" ) ) { next } $accounttracking{$user}{account} = 1; $accounttracking{$user}{passwd} = $passwd; $accounttracking{$user}{uid} = $uid; $accounttracking{$user}{gid} = $gid; $accounttracking{$user}{dir} = $dir; $accounttracking{$user}{shell} = $shell; } endpwent(); } logfile("Account Tracking..."); $attimeout = 0; if ( $config{AT_INTERVAL} < 10 ) { logfile("AT_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{AT_INTERVAL})"); $config{AT_INTERVAL} = 60; } } if ( $config{LF_SSH_EMAIL_ALERT} ) { logfile("SSH Tracking..."); } if ( $config{LF_SU_EMAIL_ALERT} ) { logfile("SU Tracking..."); } if ( $config{LF_SUDO_EMAIL_ALERT} ) { logfile("SUDO Tracking..."); } if ( $config{LF_CONSOLE_EMAIL_ALERT} ) { logfile("Console Tracking..."); } if ( $config{LF_CPANEL_ALERT} ) { $config{LF_CPANEL_ALERT_USERS} =~ s/\s//g; foreach my $user ( split( /\,/, $config{LF_CPANEL_ALERT_USERS} ) ) { $cpanelalertusers{$user} = 1; } logfile("WHM Tracking..."); } if ( $config{PORTKNOCKING} and $config{PORTKNOCKING_ALERT} ) { logfile("Port Knocking Tracking..."); } my $sshdef = $config{PORTS_sshd}; $ports{pop3d} = $config{PORTS_pop3d}; $ports{imapd} = $config{PORTS_imapd}; $ports{htpasswd} = $config{PORTS_htpasswd}; $ports{mod_security} = $config{PORTS_mod_security}; $ports{mod_qos} = $config{PORTS_mod_qos}; $ports{symlink} = $config{PORTS_symlink}; $ports{cxs} = $config{PORTS_cxs}; $ports{bind} = $config{PORTS_bind}; $ports{suhosin} = $config{PORTS_suhosin}; $ports{cpanel} = $config{PORTS_cpanel}; $ports{ftpd} = $config{PORTS_ftpd}; $ports{smtpauth} = $config{PORTS_smtpauth}; $ports{eximsyntax} = $config{PORTS_eximsyntax}; opendir( my $DIR, "/etc/chkserv.d" ); while ( my $file = readdir($DIR) ) { if ( $file =~ /exim-(\d+)/ ) { $ports{smtpauth} .= ",$1"; $ports{eximsyntax} .= ",$1"; } } closedir($DIR); if ( -e "/etc/ssh/sshd_config" ) { foreach my $line ( slurp("/etc/ssh/sshd_config") ) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } if ( $line =~ /^Port\s+(\d+)/i ) { my $port = $1; if ( $ports{sshd} ) { $ports{sshd} .= ",$port"; } else { $ports{sshd} = $port; } } } } unless ( $ports{sshd} ) { $ports{sshd} = $sshdef } if ( $config{LF_INTERVAL} < 60 ) { logfile("LF_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_INTERVAL})"); $config{LT_INTERVAL} = 300; } if ( $config{LF_PARSE} < 5 or $config{LF_PARSE} > 20 ) { logfile("LF_PARSE refresh reset to 5 to prevent looping (csf.conf setting: $config{LF_PARSE})"); $config{LF_PARSE} = 5; } my $lastline = ""; $scripttimeout = 0; my $duration = 0; my $maintimer = 0; while (1) { $0 = "lfd - processing"; $maintimer = time; seek( $pidfile_fh, 0, 0 ); my @piddata = <$pidfile_fh>; chomp @piddata; if ( ( $pid ne $piddata[0] ) or ( $pidino ne ( stat($pidfile) )[1] ) ) { cleanup( __LINE__, "*Error* pid mismatch or missing" ); } my $csf_error = get_csf_error_message(); if ($csf_error) { cleanup( __LINE__, $csf_error ); } my $perms = sprintf "%04o", ( stat("/etc/csf") )[2] & oct("07777"); if ( $perms != "0600" ) { chmod( 0600, "/etc/csf" ); logfile("*Permissions* on /etc/csf reset to 0600 [currently: $perms]"); } $perms = sprintf "%04o", ( stat("/var/lib/csf") )[2] & oct("07777"); if ( $perms != "0600" ) { chmod( 0600, "/var/lib/csf" ); logfile("*Permissions* on /var/lib/csf reset to 0600 [currently: $perms]"); } $perms = sprintf "%04o", ( stat("/usr/local/csf") )[2] & oct("07777"); if ( $perms != "0600" ) { chmod( 0600, "/usr/local/csf" ); logfile("*Permissions* on /usr/local/csf reset to 0600 [currently: $perms]"); } $locktimeout += $duration; if ( $locktimeout >= 60 ) { $locktimeout = 0; lockhang(); } if ( scalar( keys %forks ) > 200 ) { my $forkcnt = 0; foreach my $key ( keys %forks ) { if ( $key =~ /\d+/ and $key > 1 and kill( 0, $key ) ) { $forkcnt++; if ( $config{DEBUG} >= 3 ) { logfile("debug: fork:[$key]") } } else { delete $forks{$key}; } } if ( $config{DEBUG} >= 2 ) { logfile("debug: Forks:[$forkcnt]") } if ( $forkcnt > 200 ) { logfile("*Error* Excessive number of children ($forkcnt), restarting lfd..."); lfdrestart(); exit; } } if ( -e "/var/lib/csf/lfd.restart" ) { unlink "/var/lib/csf/lfd.restart"; lfdrestart(); exit; } if ( -e "/var/lib/csf/csf.restart" ) { unlink "/var/lib/csf/csf.restart"; csfrestart(); } if ( $config{LF_CSF} ) { $csftimeout += $duration; if ( $csftimeout >= 300 ) { $csftimeout = 0; csfcheck(); } } if ( $config{SYSLOG_CHECK} and $sys_syslog ) { $syslogchecktimeout += $duration; if ( $syslogchecktimeout >= $config{SYSLOG_CHECK} ) { $syslogchecktimeout = 0; if ( !length $syslogcheckcode ) { my @chars = ( '0' .. '9', 'a' .. 'z', 'A' .. 'Z' ); $syslogcheckcode = join '', map { $chars[ rand(@chars) ] } ( 1 .. ( 15 + int( rand(15) ) ) ); eval { openlog( 'lfd', 'ndelay,pid', 'user' ); syslog( 'info', "SYSLOG check [$syslogcheckcode]" ); closelog(); } } else { syslogcheck(); $syslogcheckcode = ""; } } } if ( $config{ST_SYSTEM} ) { $systemstatstimeout += $duration; if ( $systemstatstimeout >= 60 ) { $systemstatstimeout = 0; systemstats(); } } if ( $config{LT_POP3D} ) { $logintimeout{pop3d} += $duration; if ( $logintimeout{pop3d} >= 3600 ) { delete $logintimeout{pop3d}; delete $logins{pop3d}; } } if ( $config{LT_IMAPD} ) { $logintimeout{imapd} += $duration; if ( $logintimeout{imapd} >= 3600 ) { delete $logintimeout{imapd}; delete $logins{imapd}; } } if ( $config{PS_INTERVAL} ) { $pstimeout += $duration; if ( $pstimeout >= $config{PS_INTERVAL} ) { $pstimeout = 0; undef %portscans; } } if ( $config{UID_INTERVAL} ) { $uidtimeout += $duration; if ( $uidtimeout >= $config{UID_INTERVAL} ) { $uidtimeout = 0; undef %uidscans; } } if ( $config{LF_SCRIPT_ALERT} ) { $scripttimeout += $duration; if ( $scripttimeout >= 3600 ) { $scripttimeout = 0; undef %scripts; } } if ( $config{LF_APACHE_404} ) { $apache404timeout += $duration; if ( $apache404timeout >= $config{LF_INTERVAL} ) { $apache404timeout = 0; undef %apache404; } } if ( $config{LF_APACHE_403} ) { $apache403timeout += $duration; if ( $apache403timeout >= $config{LF_INTERVAL} ) { $apache403timeout = 0; undef %apache403; } } if ( $config{LF_APACHE_401} ) { $apache401timeout += $duration; if ( $apache401timeout >= $config{LF_INTERVAL} ) { $apache401timeout = 0; undef %apache401; } } if ( $config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT} ) { $relaytimeout += $duration; if ( $relaytimeout >= 3600 ) { $relaytimeout = 0; undef %relays; } } if ( -e "/var/lib/csf/csf.tempconf" ) { open( my $IN, "<", "/var/lib/csf/csf.tempconf" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; if ( $line =~ /^\#/ ) { next } if ( $line !~ /=/ ) { next } my ( $name, $value ) = split( /=/, $line, 2 ); $name //= ""; $value //= ""; $name =~ s/\s//g; if ( $value =~ /\"(.*)\"/ ) { $value = $1; } else { cleanup( __LINE__, "*Error* Invalid configuration line in csf.tempconf" ); } $config{$name} = $value; } close($IN); } if ( $config{GLOBAL_IGNORE} and -e "/var/lib/csf/csf.gignore" ) { undef @gcidrs; undef %gignoreips; undef $gcidr; undef $gcidr6; $gcidr = Net::CIDR::Lite->new; $gcidr6 = Net::CIDR::Lite->new; open( my $IN, "<", "/var/lib/csf/csf.gignore" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; if ( $line =~ /^([#\n\r\s])/ or $line eq "" ) { next } my ( $ip, undef ) = split( /\s/, $line ); my ( undef, $iscidr ) = split( /\//, $ip ); $ip //= ""; my $v = checkip( \$ip ); if ($v) { if ($iscidr) { push @gcidrs, $ip; undef $@; if ( $v == 6 ) { eval { $gcidr6->add($ip) }; } else { eval { $gcidr->add($ip) }; } if ($@) { logfile("Invalid entry in GLOBAL_IGNORE: $ip") } } else { $gignoreips{$ip} = 1 } } elsif ( $ip ne "127.0.0.1" ) { logfile("Invalid entry in GLOBAL_IGNORE: [$ip]") } } close($IN); unlink "/var/lib/csf/csf.gignore"; } $count = 0; $0 = "lfd - scanning log files"; undef %relayip; if ( $config{RELAYHOSTS} ) { open( my $IN, "<", "/etc/relayhosts" ); flock( $IN, LOCK_SH ); while ( my $ip = <$IN> ) { chomp $ip; if ( checkip( \$ip ) ) { $relayip{$ip} = 1 } } close($IN); } if ( $config{DYNDNS} and $config{DYNDNS_IGNORE} ) { open( my $IN, "<", "/var/lib/csf/csf.tempdyn" ); flock( $IN, LOCK_SH ); while ( my $ip = <$IN> ) { chomp $ip; if ( checkip( \$ip ) ) { $relayip{$ip} = 1 } } close($IN); } if ( $config{GLOBAL_DYNDNS} and $config{GLOBAL_DYNDNS_IGNORE} ) { open( my $IN, "<", "/var/lib/csf/csf.tempgdyn" ); flock( $IN, LOCK_SH ); while ( my $ip = <$IN> ) { chomp $ip; if ( checkip( \$ip ) ) { $relayip{$ip} = 1 } } close($IN); } if ( $config{RESTRICT_SYSLOG} == 3 ) { syslog_perms() } foreach my $lgfile ( keys %logfiles ) { if ( !length $lgfile ) { next } my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", $lgfile, $timer ) } my $totlines = 0; my @data; while ( my $line = getlogfile( $lgfile, $count, $totlines ) ) { if ( $line eq "reopen" ) { undef @data; last; } else { $totlines++; push @data, $line; } } if ( $config{DEBUG} >= 2 ) { logfile("debug: Parsing $lgfile ($totlines lines)") } foreach my $line (@data) { if ( ( $lastline ne "" ) and ( $line =~ /^\S+\s+\d+\s+\S+ \S+ last message repeated (\d+) times/ ) ) { my $hits = $1; if ( $hits > 100 ) { $hits = 100 } for ( my $x = 0; $x < $hits; $x++ ) { dochecks( $lastline, $lgfile ); } } else { dochecks( $line, $lgfile ); $lastline = $line; } } $lastline = ""; $count++; undef %psips; undef %blockedips; if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", $lgfile, $timer ) } } $0 = "lfd - processing"; if ( $config{CT_LIMIT} ) { $cttimeout += $duration; if ( $cttimeout >= $config{CT_INTERVAL} ) { $cttimeout = 0; connectiontracking(); } } if ( $config{DYNDNS} ) { $dyndnstimeout += $duration; if ( $dyndnstimeout >= $config{DYNDNS} ) { $dyndnstimeout = 0; dyndns(); } } if ( $config{GLOBAL_DYNDNS} ) { $gdyndnstimeout += $duration; if ( $gdyndnstimeout >= $config{GLOBAL_DYNDNS_INTERVAL} ) { $gdyndnstimeout = 0; globaldyndns(); } } if ( $config{LF_GLOBAL} ) { $globaltimeout += $duration; if ( $globaltimeout >= $config{LF_GLOBAL} ) { $globaltimeout = 0; global(); } } if ( scalar( keys %blocklists ) ) { $blocklisttimeout += $duration; if ( $blocklisttimeout >= 300 ) { $blocklisttimeout = 0; blocklist(); } } if ( $config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_ALLOW_PORTS} or $config{CC_DENY_PORTS} or $config{CC_ALLOW_SMTPAUTH} ) { $cctimeout += $duration; if ( $cctimeout >= 3600 ) { $cctimeout = 0; countrycode(); } } if ( $config{CC_LOOKUPS} ) { $ccltimeout += $duration; if ( $ccltimeout >= 3600 ) { $ccltimeout = 0; countrycodelookups(); } } if ( $config{LOGSCANNER} ) { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); my $lastrun; if ( -e "/var/lib/csf/csf.lastlogrun" ) { my @data = slurp("/var/lib/csf/csf.lastlogrun"); $loginterval = $lastrun = $data[0]; } if ( !length $loginterval ) { if ( $config{LOGSCANNER_INTERVAL} eq "hourly" ) { $loginterval = $hour } if ( $config{LOGSCANNER_INTERVAL} eq "daily" ) { $loginterval = $mday } } if ( -e "/var/lib/csf/csf.logrun" ) { unlink "/var/lib/csf/csf.logrun"; logscanner($hour); } elsif ( $config{LOGSCANNER_INTERVAL} eq "hourly" and $loginterval ne $hour ) { $loginterval = $hour; logscanner($hour); } elsif ( $config{LOGSCANNER_INTERVAL} eq "daily" and $loginterval ne $mday ) { $loginterval = $mday; logscanner($hour); } if ( $lastrun ne $loginterval ) { $lastrun = $loginterval; sysopen( my $LOGRUN, "/var/lib/csf/csf.lastlogrun", O_WRONLY | O_CREAT | O_TRUNC ); flock( $LOGRUN, LOCK_EX ); print $LOGRUN $loginterval; close($LOGRUN); } } if ( $config{LF_INTEGRITY} ) { $integritytimeout += $duration; if ( $integritytimeout >= $config{LF_INTEGRITY} ) { $integritytimeout = 0; integrity(); } } if ( $config{LF_QUEUE_ALERT} ) { $queuetimeout += $duration; if ( $queuetimeout >= $config{LF_QUEUE_INTERVAL} ) { $queuetimeout = 0; queuecheck(); } } if ( $config{LF_MODSECIPDB_ALERT} ) { $modsecipdbchecktimeout += $duration; if ( $modsecipdbchecktimeout >= 3600 ) { $modsecipdbchecktimeout = 0; modsecipdbcheck(); } } if ( $config{LF_EXPLOIT} ) { $exploittimeout += $duration; if ( $exploittimeout >= $config{LF_EXPLOIT} ) { $exploittimeout = 0; exploit(); } } if ( $config{LF_DIRWATCH} ) { $dirwatchtimeout += $duration; if ( not -e "/var/lib/csf/csf.dwdisable" ) { if ( $dirwatchtimeout >= $config{LF_DIRWATCH} ) { $dirwatchtimeout = 0; dirwatch(); } } } if ( $config{LF_DIRWATCH_FILE} ) { $dirwatchfiletimeout += $duration; if ( $dirwatchfiletimeout >= $config{LF_DIRWATCH_FILE} ) { dirwatchfile(); $dirwatchfiletimeout = 0; } } if ( $config{PT_LOAD} ) { $loadtimeout += $duration; if ( $loadtimeout >= $config{PT_LOAD} ) { $loadtimeout = 0; loadcheck(); } } if ( $config{PT_LIMIT} ) { $pttimeout += $duration; if ( $pttimeout >= $config{PT_INTERVAL} ) { $pttimeout = 0; processtracking(); } } if ( $config{MESSENGER} ) { unless ( -e "/var/log/lfd_messenger.log" ) { open( my $OUT, ">", "/var/log/lfd_messenger.log" ); close($OUT); } system( "chown", "$config{MESSENGER_USER}:$config{MESSENGER_USER}", "/var/log/lfd_messenger.log" ); foreach my $key ( keys %messengerips ) { if ( $messengerips{$key} > 1 and $config{"MESSENGER_${key}_IN"} ne "" ) { unless ( kill( 0, $messengerips{$key} ) ) { messenger( $config{"MESSENGER_${key}"}, $config{MESSENGER_USER}, $key ); logfile("Messenger $key Service died, restarted"); } } } messengerrecaptcha(); } if ( $config{CLUSTER_RECVFROM} ) { if ( $clusterip > 1 and !( kill( 0, $clusterip ) ) ) { lfdserver(); logfile("Cluster Service died, restarted"); } } if ( $config{AT_ALERT} ) { $attimeout += $duration; if ( $attimeout >= $config{AT_INTERVAL} ) { $attimeout = 0; undef %newaccounttracking; if ( $config{AT_ALERT} == 3 ) { my ( $user, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell ) = getpwnam("root"); $newaccounttracking{$user}{account} = 1; $newaccounttracking{$user}{passwd} = $passwd; $newaccounttracking{$user}{uid} = $uid; $newaccounttracking{$user}{gid} = $gid; $newaccounttracking{$user}{dir} = $dir; $newaccounttracking{$user}{shell} = $shell; } else { while ( my ( $user, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell ) = getpwent() ) { if ( ( $config{AT_ALERT} eq "2" ) and ( $uid ne "0" ) ) { next } $newaccounttracking{$user}{account} = 1; $newaccounttracking{$user}{passwd} = $passwd; $newaccounttracking{$user}{uid} = $uid; $newaccounttracking{$user}{gid} = $gid; $newaccounttracking{$user}{dir} = $dir; $newaccounttracking{$user}{shell} = $shell; } endpwent(); } accounttracking(); %accounttracking = %newaccounttracking; } } ipunblock(); $0 = "lfd - sleeping"; sleep( $config{LF_PARSE} ); $duration = time - $maintimer; if ( ( $config{DEBUG} >= 1 ) and ( $duration > ( $config{LF_PARSE} * 10 ) ) ) { logfile("debug: *Performance* log parsing taking $duration seconds"); } if ( $config{DEBUG} >= 2 ) { logfile("debug: Tick: $duration [$config{LF_PARSE}]") } } return 0; } # End of run() sub dochecks { my $line = shift; my $lgfile = shift; my $timenow = time; my $logscanner_skip = 0; my ( $reason, $ip, $app, $customtrigger, $customports, $customperm, $customcf ) = ConfigServer::RegexMain::processline( $line, $lgfile, \%globlogs ); my ( $gip, $account, $domain ) = split( /\|/, $ip, 3 ); $account //= ""; unless ( $account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processline) Account name [$account] is invalid") } $account = ""; } $ip = $gip; if ( ($ip) and ( $ip !~ /^127\./ ) and ( $ip ne "::1" ) ) { if ( ignoreip($ip) ) { logfile("$reason $ip - ignored"); } else { if ( $blockedips{$ip}{block} or ( $blockedips{$ip}{apps} =~ /\b$app\b/ ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( ( $app eq "pop3d" or $app eq "imapd" ) and $line =~ /\(auth failed, (\d+) attempts/ ) { $db{$ip}{text} .= "$line\n"; for ( 1 .. $1 ) { $db{$ip}{count}++; $db{$ip}{apps} .= $app . " "; $db{$ip}{appscount}{$app}++; $db{$ip}{mytime} .= "$timenow,"; $db{$ip}{appstime}{$app} .= "$timenow,"; $db{$ip}{domains} .= $domain . " "; } } else { $db{$ip}{count}++; $db{$ip}{text} .= "$line\n"; $db{$ip}{apps} .= $app . " "; $db{$ip}{appscount}{$app}++; $db{$ip}{mytime} .= "$timenow,"; $db{$ip}{appstime}{$app} .= "$timenow,"; $db{$ip}{domains} .= $domain . " "; } my $hits; my $trigger; my $setting; my @times; if ($customtrigger) { $trigger = "LF_CUSTOMTRIGGER"; $config{$trigger} = $customtrigger; $config{"$trigger\_PERM"} = $customperm; $ports{$app} = $customports; $cfblocks{LF_CUSTOMTRIGGER} = $customcf; } if ( $config{LF_TRIGGER} ) { @times = split( /\,/, $db{$ip}{mytime} ); $trigger = "LF_TRIGGER"; } else { @times = split( /\,/, $db{$ip}{appstime}{$app} ); if ( $app eq "sshd" ) { $trigger = "LF_SSHD" } elsif ( $app eq "pop3d" ) { $trigger = "LF_POP3D" } elsif ( $app eq "imapd" ) { $trigger = "LF_IMAPD" } elsif ( $app eq "ftpd" ) { $trigger = "LF_FTPD" } elsif ( $app eq "smtpauth" ) { $trigger = "LF_SMTPAUTH" } elsif ( $app eq "eximsyntax" ) { $trigger = "LF_EXIMSYNTAX" } elsif ( $app eq "htpasswd" ) { $trigger = "LF_HTACCESS" } elsif ( $app eq "mod_security" ) { $trigger = "LF_MODSEC" } elsif ( $app eq "bind" ) { $trigger = "LF_BIND" } elsif ( $app eq "suhosin" ) { $trigger = "LF_SUHOSIN" } elsif ( $app eq "cpanel" ) { $trigger = "LF_CPANEL" } elsif ( $app eq "whm" ) { $trigger = "LF_CPANEL" } elsif ( $app eq "webmail" ) { $trigger = "LF_CPANEL" } elsif ( $app eq "mod_qos" ) { $trigger = "LF_QOS" } elsif ( $app eq "symlink" ) { $trigger = "LF_SYMLINK" } elsif ( $app eq "cxs" ) { $trigger = "LF_CXS" } } my $newtimes; my $newcnt = 0; foreach my $time (@times) { if ( $timenow - $time <= $config{LF_INTERVAL} ) { $newtimes .= "$time,"; $newcnt++; } } if ( $config{LF_TRIGGER} ) { $db{$ip}{count} = $newcnt; $db{$ip}{mytime} = $newtimes; $hits = $db{$ip}{count}; } else { $db{$ip}{appscount}{$app} = $newcnt; $db{$ip}{appstime}{$app} = $newtimes; $hits = $db{$ip}{appscount}{$app}; } if ( $config{DEBUG} >= 1 ) { logfile("debug: $reason $ip - $hits failure(s) in the last $config{LF_INTERVAL} secs") } if ( $hits >= $config{$trigger} ) { my @text = split( /\n/, $db{$ip}{text} ); $db{$ip}{text} = ""; for ( -$hits .. -1 ) { $db{$ip}{text} .= "$text[$_]\n" } $0 = "lfd - blocking $ip"; block( $ip, $hits, $app, $config{"$trigger\_PERM"}, $trigger, $reason ); if ( $config{LF_SELECT} and !$config{LF_TRIGGER} ) { $db{$ip}{appscount}{$app} = 0; $db{$ip}{appstime}{$app} = ""; } else { delete $db{$ip}; } if ($cxsreputation) { ConfigServer::cxs::Rreport( $trigger, $ip, "$reason $ip - $hits failure(s) in the last $config{LF_INTERVAL} secs", $trigger ); } $0 = "lfd - scanning $lgfile"; } if ( $config{LF_DISTATTACK} and $account ) { $adb{$app}{$account}{$timenow}{ip} .= "$ip,"; $adb{$app}{$account}{$timenow}{text} .= "$line\n"; my @accountips; my $text; foreach my $key ( keys %{ $adb{$app}{$account} } ) { if ( $timenow - $key <= $config{LF_INTERVAL} ) { push @accountips, ( split( /\,/, $adb{$app}{$account}{$key}{ip} ) ); $text .= $adb{$app}{$account}{$key}{text}; } else { delete $adb{$app}{$account}{$key}; } } my %seen; my @uniqueips = grep { !$seen{$_}++ } @accountips; if ( $config{DEBUG} >= 1 ) { logfile( "debug: $reason " . @uniqueips . " ip(s) to account [$account] " . @accountips . " in the last $config{LF_INTERVAL} secs" ) } if ( ( @accountips >= $config{$trigger} ) and ( @uniqueips >= $config{LF_DISTATTACK_UNIQ} ) ) { delete $adb{$app}{$account}; blockaccount( \@uniqueips, $account, $#accountips + 1, $app, $text, $config{"$trigger\_PERM"}, $trigger ); } } } } } if ( $config{LF_DISTFTP} and ( $globlogs{FTPD_LOG}{$lgfile} ) ) { my ( $ip, $account ) = ConfigServer::RegexMain::processdistftpline($line); unless ( $account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processdistftpline) Account name [$account] is invalid") } $account = ""; } if ( $ip and $account and ( $ip !~ /^127\./ ) and ( $ip ne "::1" ) ) { if ( ignoreip($ip) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: Distributed FTP $ip - ignored") } } else { $adf{$account}{$timenow}{ip} .= "$ip,"; $adf{$account}{$timenow}{text} .= "$line\n"; my @accountips; my $text; foreach my $key ( keys %{ $adf{$account} } ) { if ( $timenow - $key <= $config{LF_DIST_INTERVAL} ) { push @accountips, ( split( /\,/, $adf{$account}{$key}{ip} ) ); $text .= $adf{$account}{$key}{text}; } else { delete $adf{$account}{$key}; } } my %seen; my @uniqueips = grep { !$seen{$_}++ } @accountips; if ( $config{DEBUG} >= 1 ) { logfile( "debug: FTP logins from " . @uniqueips . " ip(s) to account [$account] " . @accountips . " in the last $config{LF_INTERVAL} secs" ) } if ( ( @accountips >= $config{LF_DISTFTP} ) and ( @uniqueips >= $config{LF_DISTFTP_UNIQ} ) ) { delete $adf{$account}; blockdistftp( \@uniqueips, $account, $#accountips + 1, $text, $config{LF_DISTFTP_PERM} ); } } } } if ( $config{LF_DISTSMTP} and ( $globlogs{SMTPAUTH_LOG}{$lgfile} ) ) { my ( $ip, $account ) = ConfigServer::RegexMain::processdistsmtpline($line); unless ( $account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processdistsmtpline) Account name [$account] is invalid") } $account = ""; } if ( $ip and $account and ( $ip !~ /^127\./ ) and ( $ip ne "::1" ) ) { if ( ignoreip($ip) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: Distributed SMTP $ip - ignored") } } else { $ads{$account}{$timenow}{ip} .= "$ip,"; $ads{$account}{$timenow}{text} .= "$line\n"; my @accountips; my $text; foreach my $key ( keys %{ $ads{$account} } ) { if ( $timenow - $key <= $config{LF_DIST_INTERVAL} ) { push @accountips, ( split( /\,/, $ads{$account}{$key}{ip} ) ); $text .= $ads{$account}{$key}{text}; } else { delete $ads{$account}{$key}; } } my %seen; my @uniqueips = grep { !$seen{$_}++ } @accountips; if ( $config{DEBUG} >= 1 ) { logfile( "debug: SMTP logins from " . @uniqueips . " ip(s) to account [$account] " . @accountips . " in the last $config{LF_INTERVAL} secs" ) } if ( ( @accountips >= $config{LF_DISTSMTP} ) and ( @uniqueips >= $config{LF_DISTSMTP_UNIQ} ) ) { delete $ads{$account}; blockdistsmtp( \@uniqueips, $account, $#accountips + 1, $text, $config{LF_DISTSMTP_PERM} ); } } } } if ( $config{LF_APACHE_404} and ( $globlogs{HTACCESS_LOG}{$lgfile} ) ) { my ($ip) = ConfigServer::RegexMain::loginline404($line); if ( $ip and !ignoreip($ip) ) { $apache404{$ip}{count}++; $apache404{$ip}{text} .= "$line\n"; if ( $apache404{$ip}{count} > $config{LF_APACHE_404} ) { disable404( $ip, $apache404{$ip}{text} ); delete $apache404{$ip}; } } } if ( $config{LF_APACHE_403} and ( $globlogs{HTACCESS_LOG}{$lgfile} ) ) { my ($ip) = ConfigServer::RegexMain::loginline403($line); if ( $ip and !ignoreip($ip) ) { $apache403{$ip}{count}++; $apache403{$ip}{text} .= "$line\n"; if ( $apache403{$ip}{count} > $config{LF_APACHE_403} ) { disable403( $ip, $apache403{$ip}{text} ); delete $apache403{$ip}; } } } if ( $config{LF_APACHE_401} and ( $globlogs{HTACCESS_LOG}{$lgfile} ) ) { my ($ip) = ConfigServer::RegexMain::loginline401($line); if ( $ip and !ignoreip($ip) ) { $apache401{$ip}{count}++; $apache401{$ip}{text} .= "$line\n"; if ( $apache401{$ip}{count} > $config{LF_APACHE_401} ) { disable401( $ip, $apache401{$ip}{text} ); delete $apache401{$ip}; } } } if ( ( $config{LT_POP3D} or $config{LT_IMAPD} ) and ( ( $globlogs{POP3D_LOG}{$lgfile} ) or ( $globlogs{IMAPD_LOG}{$lgfile} ) ) ) { my ( $app, $account, $ip ) = ConfigServer::RegexMain::processloginline($line); unless ( $account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processloginline) Account name [$account] is invalid") } $account = ""; } if ( $account and $loginproto{$app} and !ignoreip( $ip, 1 ) ) { $logins{$app}{$account}{$ip}++; if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processloginline) Account name [$account] [$ip] [$logins{$app}{$account}{$ip}]") } if ( $logins{$app}{$account}{$ip} > $loginproto{$app} ) { $0 = "lfd - disabling $app logins for $account"; logindisable( $app, $ip, $logins{$app}{$account}{$ip}, $account ); delete $logins{$app}{$account}{$ip}; $0 = "lfd - scanning $lgfile"; } } } if ( $config{LF_SSH_EMAIL_ALERT} and ( ( $lgfile eq "/var/log/messages" ) or ( $lgfile eq "/var/log/secure" ) or ( $lgfile eq "/var/log/auth.log" ) or ( $globlogs{SSHD_LOG}{$lgfile} ) ) ) { my ( $account, $ip, $method ) = ConfigServer::RegexMain::processsshline($line); unless ( $account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $account and $config{DEBUG} >= 1 ) { logfile("debug: (processsshline) Account name [$account] is invalid") } $account = ""; } if ( $account and $ip and !ignoreip($ip) ) { sshalert( $account, $ip, $method, $line ); } elsif ( ignoreip($ip) ) { logfile("*SSH login* from $ip into the $account account using $method authentication - ignored") } } if ( $config{LF_SU_EMAIL_ALERT} and ( ( $lgfile eq "/var/log/messages" ) or ( $lgfile eq "/var/log/secure" ) or ( $lgfile eq "/var/log/auth.log" ) or ( $globlogs{SU_LOG}{$lgfile} ) ) ) { my ( $to, $from, $status ) = ConfigServer::RegexMain::processsuline($line); if ( ( $to and $from ) and ( $from ne "root" ) and ( $from ne 'root(uid=0)' ) and ( $from ne '(uid=0)' ) ) { sualert( $to, $from, $status, $line ); } } if ( $config{LF_SUDO_EMAIL_ALERT} and ( ( $lgfile eq "/var/log/messages" ) or ( $lgfile eq "/var/log/secure" ) or ( $lgfile eq "/var/log/auth.log" ) or ( $globlogs{SUDO_LOG}{$lgfile} ) ) ) { my ( $to, $from, $status ) = ConfigServer::RegexMain::processsudoline($line); if ( ( $to and $from ) and ( $from ne "root" ) and ( $from ne 'root(uid=0)' ) and ( $from ne '(uid=0)' ) ) { sudoalert( $to, $from, $status, $line ); } } if ( $config{LF_CONSOLE_EMAIL_ALERT} and ( ( $lgfile eq "/var/log/messages" ) or ( $lgfile eq "/var/log/secure" ) or ( $lgfile eq "/var/log/auth.log" ) or ( $globlogs{SU_LOG}{$lgfile} ) ) ) { my ($status) = ConfigServer::RegexMain::processconsoleline($line); if ($status) { consolealert($line); } } if ( $config{LF_CPANEL_ALERT} and ( $globlogs{CPANEL_ACCESSLOG}{$lgfile} ) ) { my ( $ip, $user ) = ConfigServer::RegexMain::processcpanelline($line); unless ( $user =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/ ) { if ( $user and $config{DEBUG} >= 1 ) { logfile("debug: (processcpanelline) Account name [$user] is invalid") } $user = ""; } if ( $ip and !ignoreip($ip) and $user and ( $cpanelalertusers{$user} or $cpanelalertusers{all} ) ) { if ( $cpanelalert{$ip}{$user} and ( time - $cpanelalert{$ip}{$user} < 3600 ) ) { $cpanelalert{$ip}{$user} = time; } else { $cpanelalert{$ip}{$user} = time; cpanelalert( $ip, $user, $line ); } } } if ( $config{LF_SCRIPT_ALERT} and ( $globlogs{SCRIPT_LOG}{$lgfile} ) ) { my $path = ConfigServer::RegexMain::scriptlinecheck($line); if ( $path ne "" ) { $scripts{$path}{cnt}++; if ( $scripts{$path}{cnt} <= 10 ) { $scripts{$path}{mails} .= "$line\n"; } if ( $scripts{$path}{cnt} > $config{LF_SCRIPT_LIMIT} ) { scriptalert( $path, $scripts{$path}{cnt}, $scripts{$path}{mails} ); delete $scripts{$path}; } } } if ( $config{PS_INTERVAL} and ( $globlogs{IPTABLES_LOG}{$lgfile} ) ) { my ( $ip, $port ) = ConfigServer::RegexMain::pslinecheck($line); if ( $port and $ip and !ignoreip($ip) ) { my $hit = 0; foreach my $ports ( split( /\,/, $config{PS_PORTS} ) ) { if ( $ports =~ /\:/ ) { my ( $start, $end ) = split( /\:/, $ports ); $start //= 0; $end //= 0; if ( $port >= $start and $port <= $end ) { $hit = 1 } } elsif ( $port == $ports ) { $hit = 1 } if ($hit) { last } } if ($hit) { $portscans{$ip}{count}++; $portscans{$ip}{blocks} .= "$line\n"; $portscans{$ip}{ports}{$port} = 1; if ( $portscans{$ip}{count} > $config{PS_LIMIT} ) { if ( $config{PS_DIVERSITY} > 1 and ( $config{PS_DIVERSITY} > scalar( keys %{ $portscans{$ip}{ports} } ) ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: *Port Scan* detected from $ip - but denied by PS_DIVERSITY") } } else { if ( $psips{$ip} ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: *Port Scan* detected from $ip - already blocked") } delete $portscans{$ip}; } else { portscans( $ip, $portscans{$ip}{count}, $portscans{$ip}{blocks} ); $psips{$ip} = 1; delete $portscans{$ip}; } } } } } elsif ( ( $config{DEBUG} >= 1 ) and $port and $ip and ignoreip($ip) ) { logfile("debug: PS count for $ip - ignored"); } } if ( $config{UID_INTERVAL} and ( $globlogs{IPTABLES_LOG}{$lgfile} ) ) { my ( $port, $uid ) = ConfigServer::RegexMain::uidlinecheck($line); if ( $port and $uid and !$uidignore{$uid} ) { my $hit = 0; foreach my $ports ( split( /\,/, $config{UID_PORTS} ) ) { if ( $ports =~ /\:/ ) { my ( $start, $end ) = split( /\:/, $ports ); $start //= 0; $end //= 0; if ( $port >= $start and $port <= $end ) { $hit = 1 } } elsif ( $port == $ports ) { $hit = 1 } if ($hit) { last } } if ($hit) { $uidscans{$uid}{count}++; $uidscans{$uid}{blocks} .= "$line\n"; if ( $uidscans{$uid}{count} > $config{UID_LIMIT} ) { uidscans( $uid, $uidscans{$uid}{count}, $uidscans{$uid}{blocks} ); delete $uidscans{$uid}; } } } elsif ( ( $config{DEBUG} >= 1 ) and $port and $uid and $uidignore{$uid} ) { logfile("debug: UID count for $uid - ignored"); } } if ( $config{ST_ENABLE} and ( $globlogs{IPTABLES_LOG}{$lgfile} ) ) { if ( ConfigServer::RegexMain::statscheck($line) ) { stats( $line, "iptables" ); } } if ( $config{SYSLOG_CHECK} and $sys_syslog and $syslogcheckcode and ( $globlogs{SYSLOG_LOG}{$lgfile} ) ) { if ( ConfigServer::RegexMain::syslogcheckline( $line, $syslogcheckcode ) ) { if ( $config{DEBUG} >= 2 ) { logfile("debug: SYSLOG_CHECK match [$syslogcheckcode]") } $syslogcheckcode = ""; $logscanner_skip = 1; } } if ( $config{PORTKNOCKING} and $config{PORTKNOCKING_ALERT} and ( $globlogs{IPTABLES_LOG}{$lgfile} ) ) { my ( $ip, $port ) = ConfigServer::RegexMain::portknockingcheck($line); if ( $port and $ip and !ignoreip($ip) ) { portknocking( $ip, $port ); } } if ( $config{LOGSCANNER} and $logscannerfiles{$lgfile} and !$logscanner_skip ) { my $hit = 1; foreach my $regex (@logignore) { if ( $line =~ /$regex/ ) { $hit = 0; last; } } if ($hit) { unless ( -e "/var/lib/csf/csf.logmax" ) { sysopen( my $LOGTEMP, "/var/lib/csf/csf.logtemp", O_RDWR | O_CREAT ); flock( $LOGTEMP, LOCK_EX ); my @data = <$LOGTEMP>; close($LOGTEMP); if ( @data > $config{LOGSCANNER_LINES} ) { open( my $OUT, ">", "/var/lib/csf/csf.logmax" ); close($OUT); } else { sysopen( my $LOGTEMP, "/var/lib/csf/csf.logtemp", O_WRONLY | O_APPEND | O_CREAT ); flock( $LOGTEMP, LOCK_EX ); print $LOGTEMP "$lgfile|$line\n"; close($LOGTEMP); } } } } if ( ( ( $config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT} ) ) and ( $globlogs{SMTPRELAY_LOG}{$lgfile} ) ) { my ( $ip, $check ) = ConfigServer::RegexMain::relaycheck($line); if ($ip) { if ( $check eq "RELAY" and !$relays{$ip}{check} ) { open( my $RELAYHOSTS, "<", "/etc/relayhosts" ); flock( $RELAYHOSTS, LOCK_SH ); my @relayhosts = <$RELAYHOSTS>; close($RELAYHOSTS); chomp @relayhosts; if ( grep { $_ =~ /^$ip$/ } @relayhosts ) { $check = "POPRELAY"; } open( my $ALWAYSRELAY, "<", "/etc/alwaysrelay" ); flock( $ALWAYSRELAY, LOCK_SH ); @relayhosts = <$ALWAYSRELAY>; close($ALWAYSRELAY); chomp @relayhosts; if ( grep { $_ =~ /^$ip$/ } @relayhosts ) { $check = "POPRELAY"; } } if ( $ips{$ip} or $ipscidr->find($ip) or $ipscidr6->find($ip) ) { $check = "LOCALHOSTRELAY" } if ( $config{ST_SYSTEM} ) { sysopen( my $EMAIL, "/var/lib/csf/stats/email", O_RDWR | O_CREAT ); flock( $EMAIL, LOCK_EX ); my $stats = <$EMAIL>; chomp $stats; my ( $sent, $recv ) = split( /\:/, $stats ); if ( $check eq "RELAY" ) { $recv++ } else { $sent++ } seek( $EMAIL, 0, 0 ); truncate( $EMAIL, 0 ); print $EMAIL "$sent:$recv"; close($EMAIL); } } if ( $ip and ( $ip ne "mailnull" ) and ( $ip ne "root" ) and ( !$rtignore{$ip} ) ) { my $tline = $line; $tline =~ s/".*"/""/g; my $start = 0; my $cnt = 0; foreach my $item ( split( /\s+/, $tline ) ) { if ( $item eq "for" ) { $start = 1; next } if ( $start and ( $item =~ /\@/ ) ) { $cnt++ } else { $start = 0 } } if ( $cnt > 0 ) { $relays{$ip}{cnt} += $cnt; } else { $relays{$ip}{cnt}++; } if ( $config{DEBUG} >= 1 ) { logfile("debug: RT\_$check\_LIMIT detected from $ip, count = $relays{$ip}{cnt}") } unless ( $relays{$ip}{check} ) { $relays{$ip}{check} = $check } my $mailcnt = 0; foreach my $mail ( split( /\n/, $relays{$ip}{mails} ) ) { $mailcnt++ } if ( $mailcnt < 10 ) { $relays{$ip}{mails} .= "$line\n"; } if ( ( $relays{$ip}{cnt} > $config{"RT\_$check\_LIMIT"} ) and ( $config{"RT\_$check\_ALERT"} ) ) { if ( ( $check eq "LOCALHOSTRELAY" ) or ( !ignoreip($ip) ) ) { relayalert( $ip, $relays{$ip}{cnt}, $relays{$ip}{check}, $relays{$ip}{mails} ); delete $relays{$ip}; } } } elsif ( ( $config{DEBUG} >= 1 ) and $ip and $rtignore{$ip} ) { logfile("debug: RT\_$check\_LIMIT detected from $ip - ignored"); } } return; } sub getlogfile { my $logfile = shift; my $lfn = shift; my $totlines = shift; my $ino; my $size; my $line; my $count; if ( !defined( $lffd[$lfn] ) ) { if ( openlogfile( $logfile, $lfn ) ) { return undef } } ( undef, $ino, undef, undef, undef, undef, undef, $size, undef ) = stat($logfile); if ( $ino != $lfino[$lfn] ) { logfile("$logfile rotated. Reopening log file"); if ( openlogfile( $logfile, $lfn ) ) { return undef } return "reopen"; } if ( $size < $lfsize[$lfn] ) { logfile("$logfile has been reset. Reopening log file"); if ( openlogfile( $logfile, $lfn ) ) { return undef } return "reopen"; } $lffd[$lfn]->clearerr(); $line = readline( $lffd[$lfn] ); if ( $totlines > ( $config{LF_PARSE} * 1000 ) ) { my $text = "*Error* Log line flooding/looping in $logfile. Reopening log file"; logfile("$text"); if ( $config{LOGFLOOD_ALERT} ) { my @alert = slurp("/usr/local/csf/tpl/logfloodalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( openlogfile( $logfile, $lfn ) ) { return undef } return "reopen"; } chomp $line; if ($line) { $lfsize[$lfn] = $size; return $line; } return undef; } sub openlogfile { my $logfile = shift; my $lfn = shift; if ( defined( $lffd[$lfn] ) ) { close( $lffd[$lfn] ); delete( $lffd[$lfn] ); } sysopen( $lffd[$lfn], $logfile, O_RDONLY | O_NONBLOCK ); if ( !defined( $lffd[$lfn] ) ) { logfile("*Error* Cannot open $logfile"); return 1; } if ( seek( $lffd[$lfn], 0, 2 ) == -1 ) { logfile("*Error* Cannot seek to end of $logfile"); return 1; } logfile("Watching $logfile..."); ( undef, $lfino[$lfn], undef, undef, undef, undef, undef, $lfsize[$lfn], undef ) = stat( $lffd[$lfn] ); return 0; } sub globlog { my $setting = shift; if ( $config{$setting} =~ /[*?\[]/ ) { foreach my $log ( glob $config{$setting} ) { $globlogs{$setting}{$log} = 1; $logfiles{$log} = 1; } } else { $globlogs{$setting}{ $config{$setting} } = 1; $logfiles{ $config{$setting} } = 1; } return; } sub lockhang { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "lockhang", $timer ) } $0 = "lfd - (child) checking for lock hang"; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); sysopen( my $COMMANDLOCK, "/var/lib/csf/lock/command.lock", O_RDWR | O_CREAT ) or logfile("open: $!"); flock( $COMMANDLOCK, LOCK_EX ) or logfile("lock: $!"); close($COMMANDLOCK); alarm(0); }; alarm(0); if ($@) { sysopen( my $COMMANDLOCK, "/var/lib/csf/lock/command.lock", O_RDWR | O_CREAT ); flock( $COMMANDLOCK, LOCK_SH ); my $pid = <$COMMANDLOCK>; chomp $pid; close($COMMANDLOCK); if ( $pid == $$ ) { logfile("*Hanging Lock* by main lfd process found for /var/lib/csf/lock/command.lock - restarting lfd"); open( my $LFDOUT, ">", "/var/lib/csf/lfd.restart" ); close($LFDOUT); } else { kill( 9, $pid ); logfile("*Hanging Lock* by $pid found for /var/lib/csf/lock/command.lock - terminated"); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "lockhang", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub syslog_init { local $SIG{CHLD} = 'DEFAULT'; my %syslogusers; my @entries = slurp("/etc/csf/csf.syslogusers"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } if ( $line =~ /^[a-zA-Z0-9]+([\_\s\-\.]?[a-zA-Z0-9])*$/ ) { $syslogusers{$line} = 1 } } $sysloggid = getgrnam( $config{RESTRICT_SYSLOG_GROUP} ); unless ($sysloggid) { syscommand( __LINE__, "/usr/sbin/groupadd", "-r", $config{RESTRICT_SYSLOG_GROUP} ) } $sysloggid = getgrnam( $config{RESTRICT_SYSLOG_GROUP} ); unless ($sysloggid) { logfile("RESTRICT_SYSLOG: *Error* Failed to create group: [$config{RESTRICT_SYSLOG_GROUP}], RESTRICT_SYSLOG disabled"); $config{RESTRICT_SYSLOG} = 0; return; } my ( undef, undef, $gid, $members ) = getgrgid($sysloggid); foreach my $name ( split( /\s+/, $members ) ) { $syslogusers{$name} = 0 } foreach my $name ( keys %syslogusers ) { if ( $syslogusers{$name} and getpwnam($name) ne "" ) { syscommand( __LINE__, "/usr/sbin/usermod", "-a", "-G", $config{RESTRICT_SYSLOG_GROUP}, $name ); if ( $config{DEBUG} >= 1 ) { logfile("debug: RESTRICT_SYSLOG: User $name added to group $config{RESTRICT_SYSLOG_GROUP}") } } } return; } sub syslog_perms { my $newpid = 1; my @socketids; my @sockets; if ( -S "/dev/log" ) { push @sockets, "/dev/log" } if ( -S "/usr/share/cagefs-skeleton/dev/log" ) { push @sockets, "/usr/share/cagefs-skeleton/dev/log" } if ($syslogpid) { if ( readlink("/proc/$syslogpid/exe") =~ m[^(/sbin/syslog)|(/sbin/rsyslog)|(/usr/sbin/syslog)|(/usr/sbin/rsyslog)] ) { $newpid = 0 } } if ($newpid) { opendir( my $PROCDIR, "/proc" ); while ( my $pid = readdir($PROCDIR) ) { if ( $pid !~ /^\d+$/ ) { next } my $exe = readlink("/proc/$pid/exe"); if ( $exe =~ m[^(/sbin/syslog)|(/sbin/rsyslog)|(/usr/sbin/syslog)|(/usr/sbin/rsyslog)] ) { $syslogpid = $pid; last; } } closedir($PROCDIR); } if ($syslogpid) { opendir( my $DIR, "/proc/$syslogpid/fd/" ); while ( my $file = readdir($DIR) ) { if ( readlink("/proc/$syslogpid/fd/$file") =~ /^socket:\[(\d*)\]$/ ) { push @socketids, $1 } } closedir($DIR); if (@socketids) { open( my $IN, "<", "/proc/net/unix" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my @data = split( /\s+/, $line, 8 ); $data[7] //= ""; foreach my $socket (@socketids) { if ( ( $socket == ( $data[6] // 0 ) ) and ( $data[7] ne "/dev/log" ) and ( $data[7] ne "/usr/share/cagefs-skeleton/dev/log" ) ) { push @sockets, $data[7] } } } close($IN); } else { if ( $config{DEBUG} >= 2 ) { logfile("debug: RESTRICT_SYSLOG: *Error* No additional unix sockets found") } } } else { if ( $config{DEBUG} >= 1 ) { logfile("debug: RESTRICT_SYSLOG: *Error* syslog/rsyslog process not found") } } if (@sockets) { my $fixme = 0; foreach my $socket (@sockets) { if ( -S $socket ) { my ( undef, undef, $mode, undef, $uid, $gid, undef ) = stat($socket); $mode = sprintf( "%04o", $mode & oct("07777") ); if ( $gid != $sysloggid or $mode ne "0660" ) { $fixme = 1; last; } } } if ($fixme) { chown( -1, $sysloggid, @sockets ); chmod( 0660, @sockets ); my $count = 0; logfile("RESTRICT_SYSLOG: Unix socket permissions reapplied. Reopening log files..."); foreach my $lgfile ( keys %logfiles ) { openlogfile( $lgfile, $count ); $count++; } if ( $config{DEBUG} >= 1 ) { logfile("debug: RESTRICT_SYSLOG: Fixed socket ownership/permissions") } } } else { logfile("RESTRICT_SYSLOG: *Error* No matching unix sockets found"); return; } return; } sub block { ## no critic (Subroutines::ProhibitManyArgs) - Legacy function signature with 8 parameters for comprehensive IP blocking control my $ip = shift; my $ipcount = shift; my $app = shift; my $temp = shift; my $active = shift; my $reason = shift; my $ipc = $ipcount; my $text = $db{$ip}{text}; my $apps = $db{$ip}{apps}; my $domains = $db{$ip}{domains}; unless ( $config{LF_TRIGGER} ) { $apps = $app } $blockedips{$ip}{block} = 1; $blockedips{$ip}{apps} .= "$app\,"; $text =~ s/\n+/\n/g; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "block", $timer ) } my %logapps; my $apptext; foreach my $app ( split( / /, $apps ) ) { $logapps{$app} = 1 } foreach my $key ( keys %logapps ) { if ( $apptext eq "" ) { $apptext = $key } else { $apptext .= ",$key" } } my $perm = 1; if ( $temp > 1 ) { $perm = 0 } $0 = "lfd - (child) blocking $ip"; my $tip = iplookup($ip); my $failtext = "Login failure/trigger from"; if ( keys %logapps == 1 ) { $failtext = $reason } my $cfid; if ( $config{CF_ENABLE} and $cfblocks{$active} ) { $perm = 0; $temp = $config{CF_TEMP}; cloudflare( "deny", $ip, $config{CF_BLOCK}, $domains ); $cfid = " (CF_ENABLE)"; } if ( $config{PT_SSHDKILL} and $logapps{sshd} ) { ConfigServer::KillSSH::find( $ip, $ports{sshd} ) } my $blocked = 0; if ( $config{LF_SELECT} and !$config{LF_TRIGGER} ) { if ( ipblock( $perm, "($apptext) $failtext $tip: $ipcount in the last $config{LF_INTERVAL} secs$cfid", $ip, $ports{$app}, "in", $temp, 0, $text, $active ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { $blocked = 1 } } else { if ( ipblock( $perm, "($apptext) $failtext $tip: $ipcount in the last $config{LF_INTERVAL} secs$cfid", $ip, "", "inout", $temp, 0, $text, $active ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { $blocked = 1 } } if ($blocked) { if ( $config{LF_EMAIL_ALERT} and ( $perm or ( !$perm and $config{LF_TEMP_EMAIL_ALERT} ) ) ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $temp seconds [$active]"; if ($perm) { $block = "Permanent Block [$active]" } my $allowip = allowip($ip); if ( $allowip == 1 ) { $block .= " (IP match in csf.allow, block may not work)" } if ( $allowip == 2 ) { $block .= " (IP match in GLOBAL_ALLOW, block may not work)" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$ipcount \($apptext\)/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: alert email sent for $ip") } } if ( $config{X_ARF} ) { $0 = "lfd - (child) sending X-ARF email for $ip"; my @alert = slurp("/usr/local/csf/tpl/x-arf.txt"); my @message; my $rfc3339 = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime ); my $boundary = time; my $reportedfrom = "root\@$hostname"; if ( $config{X_ARF_TO} ) { $config{LF_ALERT_TO} = $config{X_ARF_TO} } if ( $config{X_ARF_FROM} ) { $config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM} } my $iptype = "ipv" . checkip( \$ip ); my $abuseto = ""; my $abusemsg = ""; if ($abuseip) { ( $abuseto, $abusemsg ) = ConfigServer::AbuseIP::abuseip($ip); if ( $abuseto eq "" ) { $abusemsg = ""; } elsif ( $config{X_ARF_ABUSE} and $config{X_ARF_FROM} ) { if ( $config{LF_ALERT_TO} ) { $config{LF_ALERT_TO} .= "," . $abuseto; } else { $config{LF_ALERT_TO} = "root," . $abuseto; } } } foreach my $line (@alert) { $line =~ s/\[ip\]/$ip/ig; $line =~ s/\[abuseip\]/$abusemsg/ig; $line =~ s/\[iptype\]/$iptype/ig; $line =~ s/\[tip\]/$tip/ig; $line =~ s/\[ipcount\]/$ipc/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[service\]/$app/ig; $line =~ s/\[csfversion\]/$version/ig; $line =~ s/\[reportedfrom\]/$reportedfrom/ig; $line =~ s/\[reportedid\]/$boundary\@$hostname/ig; $line =~ s/\[boundary\]/$boundary/ig; $line =~ s/\[text\]/$text/ig; $line =~ s/\[RFC3339\]/$rfc3339/ig; push @message, $line; } ConfigServer::Sendmail::relay( $config{LF_ALERT_TO}, $config{LF_ALERT_FROM}, @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: X-ARF email sent for $ip") } } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "block", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub blockaccount { my $ipa = shift; my @ips = @$ipa; my $account = shift; my $ipcount = shift; my $app = shift; my $text = shift; my $temp = shift; my $trigger = shift; foreach my $ip (@ips) { $blockedips{$ip}{block} = 1; $blockedips{$ip}{apps} .= "$app\,"; } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "blockaccount", $timer ) } my $perm = 1; if ( $temp > 1 ) { $perm = 0 } $text .= "\nIP Addresses Blocked:\n\n"; foreach my $ip (@ips) { $0 = "lfd - (child) blocking $ip"; if ( $config{PT_SSHDKILL} and $app eq "sshd" ) { ConfigServer::KillSSH::find( $ip, $ports{sshd} ) } my $tip = iplookup($ip); if ( $config{LF_SELECT} and !$config{LF_TRIGGER} ) { if ( ipblock( $perm, "$tip, $ipcount distributed $app attacks on account [$account] in the last $config{LF_INTERVAL} secs", $ip, $ports{$app}, "in", $temp, 0, $text, "LF_DISTATTACK" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } } else { if ( ipblock( $perm, "$tip, $ipcount distributed $app attacks on account [$account] in the last $config{LF_INTERVAL} secs", $ip, "", "inout", $temp, 0, $text, "LF_DISTATTACK" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } } $text .= "$tip\n"; if ($cxsreputation) { ConfigServer::cxs::Rreport( $trigger, $ip, "$ipcount distributed $trigger attacks in the last $config{LF_INTERVAL} secs", $trigger ); } } if ( $config{LF_EMAIL_ALERT} ) { my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $temp seconds [LF_DISTATTACK]"; if ($perm) { $block = "Permanent Block [LF_DISTATTACK]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/distributed $app attack on account [$account]/ig; $line =~ s/\[ipcount\]/$ipcount/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "blockaccount", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub blockdistftp { my $ipa = shift; my @ips = @$ipa; my $account = shift; my $ipcount = shift; my $text = shift; my $temp = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "blockdistftp", $timer ) } my $perm = 1; if ( $temp > 1 ) { $perm = 0 } $text .= "\nIP Addresses Blocked:\n\n"; foreach my $ip (@ips) { $0 = "lfd - (child) blocking $ip"; my $tip = iplookup($ip); if ( ipblock( $perm, "$tip, $ipcount distributed FTP Logins on account [$account] in the last $config{LF_DIST_INTERVAL} secs", $ip, "", "inout", $temp, 0, $text, "LF_DISTFTP" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } $text .= "$tip\n"; } if ( $config{LF_DISTFTP_ALERT} ) { my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $temp seconds [LF_DISTFTP]"; if ($perm) { $block = "Permanent Block [LF_DISTFTP]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/distributed FTP Logins on account [$account]/ig; $line =~ s/\[ipcount\]/$ipcount/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( $config{LF_DIST_ACTION} and -e $config{LF_DIST_ACTION} and -x $config{LF_DIST_ACTION} ) { $0 = "lfd - (child) running LF_DIST_ACTION"; system( $config{LF_DIST_ACTION}, "LF_DISTFTP", $account, $text ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "blockdistftp", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub blockdistsmtp { my $ipa = shift; my @ips = @$ipa; my $account = shift; my $ipcount = shift; my $text = shift; my $temp = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "blockdistsmtp", $timer ) } my $perm = 1; if ( $temp > 1 ) { $perm = 0 } $text .= "\nIP Addresses Blocked:\n\n"; foreach my $ip (@ips) { $0 = "lfd - (child) blocking $ip"; my $tip = iplookup($ip); if ( ipblock( $perm, "$tip, $ipcount distributed SMTP Logins on account [$account] in the last $config{LF_DIST_INTERVAL} secs", $ip, "", "inout", $temp, 0, $text, "LF_DISTSMTP" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } $text .= "$tip\n"; } if ( $config{LF_DISTSMTP_ALERT} ) { my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $temp seconds [LF_DISTSMTP]"; if ($perm) { $block = "Permanent Block [LF_DISTSMTP]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/distributed SMTP Logins on account [$account]/ig; $line =~ s/\[ipcount\]/$ipcount/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( $config{LF_DIST_ACTION} and -e $config{LF_DIST_ACTION} and -x $config{LF_DIST_ACTION} ) { $0 = "lfd - (child) running LF_DIST_ACTION"; system( $config{LF_DIST_ACTION}, "LF_DISTSMTP", $account, $text ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "blockdistsmtp", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub disable404 { my $ip = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "disable404", $timer ) } my $tip = iplookup($ip); my $perm = 1; if ( $config{LF_APACHE_404_PERM} > 1 ) { $perm = 0 } if ( ipblock( $perm, "$tip, more than $config{LF_APACHE_404} Apache 404 hits in the last $config{LF_INTERVAL} secs", $ip, $ports{mod_security}, "in", $config{LF_APACHE_404_PERM}, 0, "", "LF_APACHE_404" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( $config{LT_EMAIL_ALERT} ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $config{LF_APACHE_404_PERM} seconds [LF_APACHE_404]"; if ($perm) { $block = "Permanent Block [LF_APACHE_404]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$config{LF_APACHE_404}/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "disable404", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub disable403 { my $ip = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "disable403", $timer ) } my $tip = iplookup($ip); my $perm = 1; if ( $config{LF_APACHE_403_PERM} > 1 ) { $perm = 0 } if ( ipblock( $perm, "$tip, more than $config{LF_APACHE_403} Apache 403 hits in the last $config{LF_INTERVAL} secs", $ip, $ports{mod_security}, "in", $config{LF_APACHE_403_PERM}, 0, "", "LF_APACHE_403" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( $config{LT_EMAIL_ALERT} ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $config{LF_APACHE_403_PERM} seconds [LF_APACHE_403]"; if ($perm) { $block = "Permanent Block [LF_APACHE_403]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$config{LF_APACHE_403}/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "disable403", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub disable401 { my $ip = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "disable401", $timer ) } my $tip = iplookup($ip); my $perm = 1; if ( $config{LF_APACHE_401_PERM} > 1 ) { $perm = 0 } if ( ipblock( $perm, "$tip, more than $config{LF_APACHE_401} Apache 401 hits in the last $config{LF_INTERVAL} secs", $ip, $ports{mod_security}, "in", $config{LF_APACHE_401_PERM}, 0, "", "LF_APACHE_401" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( $config{LT_EMAIL_ALERT} ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/alert.txt"); my $block = "Temporary Block for $config{LF_APACHE_401_PERM} seconds [LF_APACHE_401]"; if ($perm) { $block = "Permanent Block [LF_APACHE_401]" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$config{LF_APACHE_401}/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "disable401", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub logindisable { my $app = shift; my $ip = shift; my $logins = shift; my $account = shift; my $trigger = "LT_" . uc($app); my $port = "110"; my $sport = "995"; if ( $app eq "imapd" ) { $port = "143"; $sport = "993" } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "logindisable", $timer ) } my $flush = ( 3600 - $logintimeout{$app} ); my $tip = iplookup($ip); if ( $config{LT_SKIPPERMBLOCK} ) { $config{LF_PERMBLOCK} = 0 } if ( ipblock( 0, "$app - $logins logins in $logintimeout{$app} secs from $tip for $account exceeds $loginproto{$app}/hour", $ip, "$port,$sport", "in", $flush, 0, "", $trigger ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( $config{LT_EMAIL_ALERT} ) { $0 = "lfd - (child) sending alert email for $account"; my @alert = slurp("/usr/local/csf/tpl/tracking.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[app\]/$app/ig; $line =~ s/\[logins\]/$logins/ig; $line =~ s/\[account\]/$account/ig; $line =~ s/\[timeout\]/$logintimeout{$app}/ig; $line =~ s/\[flush\]/$flush/ig; $line =~ s/\[rate\]/$loginproto{$app}/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); logfile("tracking email sent for $account"); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "logindisable", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub portscans { my $ip = shift; my $count = shift; my $blocks = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "portscans", $timer ) } my $tip = iplookup($ip); if ( ipblock( $config{PS_PERMANENT}, "*Port Scan* detected from $tip. $count hits in the last $pstimeout seconds", $ip, "", "in", $config{PS_BLOCK_TIME}, 0, $blocks, "PS_LIMIT" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } else { if ( $config{PS_EMAIL_ALERT} ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/portscan.txt"); my $block = "Temporary Block for $config{PS_BLOCK_TIME} seconds [PS_LIMIT]"; if ( $config{PS_PERMANENT} ) { $block = "Permanent Block [PS_LIMIT]" } my $allowip = allowip($ip); if ( $allowip == 1 ) { $block .= " (IP match in csf.allow, block may not work)" } if ( $allowip == 2 ) { $block .= " (IP match in GLOBAL_ALLOW, block may not work)" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[count\]/$count/ig; $line =~ s/\[blocks\]/$blocks/ig; $line =~ s/\[temp\]/$block/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: alert email sent for $ip") } if ( $config{X_ARF} ) { $0 = "lfd - (child) sending X-ARF email for $ip"; my @alert = slurp("/usr/local/csf/tpl/x-arf.txt"); my @message; my $rfc3339 = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime ); my $boundary = time; my $reportedfrom = "root\@$hostname"; if ( $config{X_ARF_TO} ) { $config{LF_ALERT_TO} = $config{X_ARF_TO} } if ( $config{X_ARF_FROM} ) { $config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM} } my $iptype = "ipv" . checkip( \$ip ); my $abuseto = ""; my $abusemsg = ""; if ( $iptype eq "ipv4" and $abuseip ) { ( $abuseto, $abusemsg ) = ConfigServer::AbuseIP::abuseip($ip); if ( $abuseto eq "" ) { $abusemsg = ""; } elsif ( $config{X_ARF_ABUSE} and $config{X_ARF_FROM} ) { if ( $config{LF_ALERT_TO} ) { $config{LF_ALERT_TO} .= "," . $abuseto; } else { $config{LF_ALERT_TO} = "root," . $abuseto; } } } foreach my $line (@alert) { $line =~ s/\[ip\]/$ip/ig; $line =~ s/\[abuseip\]/$abusemsg/ig; $line =~ s/\[iptype\]/$iptype/ig; $line =~ s/\[tip\]/$tip/ig; $line =~ s/\[ipcount\]/$count/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[service\]/firewall/ig; $line =~ s/\[csfversion\]/$version/ig; $line =~ s/\[reportedfrom\]/$reportedfrom/ig; $line =~ s/\[reportedid\]/$boundary\@$hostname/ig; $line =~ s/\[boundary\]/$boundary/ig; $line =~ s/\[text\]/$blocks/ig; $line =~ s/\[RFC3339\]/$rfc3339/ig; push @message, $line; } ConfigServer::Sendmail::relay( $config{LF_ALERT_TO}, $config{LF_ALERT_FROM}, @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: X-ARF email sent for $ip") } } } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "portscans", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub uidscans { my $uid = shift; my $count = shift; my $blocks = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "uidscans", $timer ) } $0 = "lfd - (child) sending alert email for UID $uid"; my $user = getpwuid($uid); if ( $user eq "" ) { $user = $uid } logfile("*UID Tracking* $count blocks for UID $uid ($user)"); my @alert = slurp("/usr/local/csf/tpl/uidscan.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[uid\]/$uid ($user)/ig; $line =~ s/\[count\]/$count/ig; $line =~ s/\[ports\]/$blocks/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: alert email sent for UID $uid ($user)") } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "uidscans", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub csfrestart { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "csfrestart", $timer ) } $0 = "lfd - (re)starting csf..."; logfile("csf (re)start requested - running *csf startup*..."); syscommand( __LINE__, "/usr/sbin/csf", "-sf" ); logfile("csf (re)start completed"); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "csfrestart", $timer ) } $0 = "lfd - processing"; return; } sub lfdrestart { $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $SIG{CHLD} = 'IGNORE'; $0 = "lfd - stopping"; logfile("daemon restart requested"); close($pidfile_fh); unlink $pidfile; $SIG{HUP} = 'IGNORE'; kill HUP => -$$; exec("/usr/sbin/lfd"); exit 0; } sub csfcheck { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "csfcheck", $timer ) } my @ipdata = syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -L LOCALINPUT -n" ); chomp @ipdata; if ( $ipdata[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @ipdata } if ( $ipdata[0] =~ /xtables lock/ ) { logfile("*Error*: Unable to check csf due to xtables lock, enable WAITLOCK in csf.conf"); } else { if ( $ipdata[0] !~ /^Chain LOCALINPUT/ ) { $0 = "lfd - starting csf..."; logfile("iptables appears to have been flushed - running *csf startup*..."); syscommand( __LINE__, "/usr/sbin/csf", "-sf" ); logfile("csf startup completed"); $0 = "lfd - processing"; } if ( -e "/usr/local/cpanel/version" ) { my $skip; if ( -e "/var/run/upcp.pid" ) { open( my $IN, "<", "/var/run/upcp.pid" ); flock( $IN, LOCK_SH ); my $upcp = <$IN>; close($IN); chomp($upcp); if ( -d "/proc/$upcp" ) { if ( $config{DEBUG} >= 1 ) { logfile("cPanel upcp is running, skipped version check") } $skip = 1; } } if ( -e "/var/lib/csf/cpanel.new" ) { my $mtime = ( stat("/var/lib/csf/cpanel.new") )[9]; if ( time - $mtime < 3600 ) { $skip = 1 } } unless ($skip) { my $current; foreach my $line ( slurp("/usr/local/cpanel/version") ) { $line =~ s/$cleanreg//g; if ( $line =~ /\d/ ) { $current = $line } } if ( $current ne $cpconfig{version} ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { local $SIG{CHLD} = 'DEFAULT'; my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "cpanelcheck", $timer ) } $0 = "lfd - (child) cPanel upgraded..."; my $lockstr = "LF_CSF"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; logfile("cPanel upgrade detected, restarting ConfigServer services..."); if ( -e "/var/lib/csf/cpanel.new" ) { unlink "/var/lib/csf/cpanel.new" } open( my $CPANELNEW, ">", "/var/lib/csf/cpanel.new" ); flock( $CPANELNEW, LOCK_EX ); print $CPANELNEW time; close($CPANELNEW); if ( -e "/etc/cxs/cxs.pl" ) { logfile("cPanel upgrade detected, restarting cxs Watch (if running)"); open( my $OUT, ">", "/etc/cxs/newusers/cxswatchrestart" ); close($OUT); logfile("cPanel upgrade detected, restarting cxs pure-uploadscript (if running)"); eval { local $SIG{'ALRM'} = sub { die }; alarm(30); syscommand( __LINE__, "/sbin/service", "pure-uploadscript", "restart" ); syscommand( __LINE__, "/scripts/restartsrv_ftpserver" ); alarm(0); }; alarm(0); } if ( -e "/etc/osm/osmd.pl" ) { logfile("cPanel upgrade detected, restarting osmd"); eval { local $SIG{'ALRM'} = sub { die }; alarm(30); syscommand( __LINE__, "/sbin/service", "osmd", "restart" ); alarm(0); }; alarm(0); } if ( -e "/etc/exim_outgoing.conf" and ( -e "/etc/init.d/MailScanner" or -e "/etc/systemd/system/multi-user.target.wants/MailScanner.service" ) ) { logfile("cPanel upgrade detected, restarting MailScanner"); eval { local $SIG{'ALRM'} = sub { die }; alarm(30); syscommand( __LINE__, "/sbin/service", "MailScanner", "restart" ); alarm(0); }; alarm(0); } logfile("cPanel upgrade detected, restarting lfd"); open( my $LFDOUT, ">", "/var/lib/csf/lfd.restart" ); close($LFDOUT); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "cpanelcheck", $timer ) } $0 = "lfd - child closing"; exit; } } } } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "csfcheck", $timer ) } return; } sub loadcheck { if ( -e "/var/lib/csf/csf.load" ) { open( my $IN, "<", "/var/lib/csf/csf.load" ); flock( $IN, LOCK_SH ); my $start = <$IN>; close($IN); chomp $start; if ( time - $start < $config{PT_LOAD_SKIP} ) { return; } else { unlink("/var/lib/csf/csf.load"); } } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "loadcheck", $timer ) } $0 = "lfd - (child) checking load..."; my $lockstr = "PT_LOAD"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; open( my $IN, "<", "/proc/loadavg" ); flock( $IN, LOCK_SH ); my $loadavg = <$IN>; close($IN); chomp $loadavg; my @load = split( /\s+/, $loadavg ); my $reportload = $load[1]; if ( $config{PT_LOAD_AVG} == 1 ) { $reportload = $load[0] } elsif ( $config{PT_LOAD_AVG} == 15 ) { $reportload = $load[2] } else { $config{PT_LOAD_AVG} = 5 } if ( $reportload >= $config{PT_LOAD_LEVEL} ) { logfile("*LOAD* $config{PT_LOAD_AVG} minute load average is $reportload, threshold is $config{PT_LOAD_LEVEL} - email sent"); sysopen( my $LOAD, "/var/lib/csf/csf.load", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot write to file: $!" ); flock( $LOAD, LOCK_EX ); seek( $LOAD, 0, 0 ); truncate( $LOAD, 0 ); print $LOAD time; close($LOAD); if ( $config{PT_LOAD_ACTION} and -e "$config{PT_LOAD_ACTION}" and -x "$config{PT_LOAD_ACTION}" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $ptchildpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } unless ($ptchildpid) { system( $config{PT_LOAD_ACTION} ); exit; } } my @proclist; eval { local $SIG{'ALRM'} = sub { die }; alarm(15); @proclist = syscommand( __LINE__, $config{PS}, "axuf" ); alarm(0); }; alarm(0); if ($@) { push @proclist, "Unable to obtain process output within 15 seconds - Timed out" } my @vmstat; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); @vmstat = syscommand( __LINE__, $config{VMSTAT} ); alarm(0); }; alarm(0); if ($@) { push @vmstat, "Unable to obtain vmstat output within 10 seconds - Timed out" } my @netstat; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); @netstat = syscommand( __LINE__, $config{NETSTAT}, "-autpn" ); alarm(0); }; alarm(0); if ($@) { push @netstat, "Unable to obtain netstat output within 10 seconds - Timed out" } my $url = $config{PT_APACHESTATUS}; my $key = _get_whm_server_status_key(); if ( defined $key ) { $url .= "?key=$key"; } my $log_url = $url; $log_url =~ s/([?&])key=[^&]*/$1key=REDACTED/; my ( $status, $apache ) = $urlget->urlget($url); if ($status) { $apache = "Unable to retrieve Apache Server Status [$log_url] - $apache" } my @alert = slurp("/usr/local/csf/tpl/loadalert.txt"); my $boundary = "csf" . time; my @message; foreach my $line (@alert) { $line =~ s/\[loadavg1\]/$load[0]/ig; $line =~ s/\[loadavg5\]/$load[1]/ig; $line =~ s/\[loadavg15\]/$load[2]/ig; $line =~ s/\[loadavg\]/$config{PT_LOAD_AVG}/ig; $line =~ s/\[reportload\]/$reportload/ig; $line =~ s/\[totprocs\]/$load[3]/ig; $line =~ s/\[processlist\]/@proclist/ig; $line =~ s/\[vmstat\]/@vmstat/ig; $line =~ s/\[netstat\]/@netstat/ig; $line =~ s/\[apache\]/$apache/ig; $line =~ s/\[boundary\]/$boundary/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "loadcheck", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub denycheck { my $ip = shift; my $port = shift; my $perm = shift; my $ipstring = quotemeta($ip); my $skip = 0; my @deny = slurp("/etc/csf/csf.deny"); foreach my $line (@deny) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @deny, @incfile; } } my $denymatches = scalar( grep { $_ =~ /^$ipstring\b/i } @deny ); if ( $config{LF_REPEATBLOCK} and $denymatches < $config{LF_REPEATBLOCK} ) { $denymatches = 0 } unless ( $denymatches == 0 ) { $skip = 1 } open( my $IN, "<", "/var/lib/csf/csf.tempban" ); flock( $IN, LOCK_SH ); @deny = <$IN>; close($IN); chomp @deny; if ( grep { $_ =~ /^\d+\|$ipstring\|$port\|/i } @deny ) { unless ($perm) { $skip = 1 } } return $skip; } sub queuecheck { if ( -e "/var/lib/csf/csf.queue" ) { open( my $IN, "<", "/var/lib/csf/csf.queue" ); flock( $IN, LOCK_SH ); my $start = <$IN>; close($IN); chomp $start; if ( time - $start < $config{LF_FLUSH} ) { return; } else { unlink("/var/lib/csf/csf.queue"); } } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "queuecheck", $timer ) } $0 = "lfd - (child) checking mail queue..."; my $lockstr = "LF_QUEUE_INTERVAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my $queue; my $msqueue; my $timeout = ""; eval { local $SIG{'ALRM'} = sub { die }; alarm(30); $queue = ( syscommand( __LINE__, "/usr/sbin/exim", "-bpc" ) )[0]; alarm(0); }; alarm(0); if ($@) { $timeout = "Unable to obtain exim queue length within 30 seconds - Timed out" } chomp $queue; if ( -e "/etc/exim_outgoing.conf" ) { $msqueue = $queue; eval { local $SIG{'ALRM'} = sub { die }; alarm(30); $queue = ( syscommand( __LINE__, "/usr/sbin/exim", "-C", "/etc/exim_outgoing.conf", "-bpc" ) )[0]; alarm(0); }; alarm(0); if ($@) { $timeout = "Unable to obtain exim_outgoing.conf queue length within 30 seconds - Timed out" } chomp $queue; } if ( ( $queue > $config{LF_QUEUE_ALERT} ) or ( $msqueue > $config{LF_QUEUE_ALERT} ) or ( $timeout ne "" ) ) { my $report = "The exim delivery queue size is $queue"; if ($msqueue) { $report .= ", the MailScanner pending queue size is $msqueue" } if ($timeout) { $report = $timeout } logfile("*Email Queue* $report"); sysopen( my $QUEUE, "/var/lib/csf/csf.queue", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot write to file: $!" ); flock( $QUEUE, LOCK_EX ); seek( $QUEUE, 0, 0 ); truncate( $QUEUE, 0 ); print $QUEUE time; close($QUEUE); my @alert = slurp("/usr/local/csf/tpl/queuealert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[text\]/$report/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "queuecheck", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub modsecipdbcheck { if ( -e "/var/lib/csf/csf.modsecipdbcheck" ) { open( my $IN, "<", "/var/lib/csf/csf.modsecipdbcheck" ); flock( $IN, LOCK_SH ); my $start = <$IN>; close($IN); chomp $start; if ( time - $start < $config{LF_FLUSH} ) { return; } else { unlink("/var/lib/csf/csf.modsecipdbcheck"); } } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "modsecipdbcheck", $timer ) } $0 = "lfd - (child) checking modsec ip db..."; my $lockstr = "LF_MODSECIPDB_ALERT"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my $size = ( stat $config{LF_MODSECIPDB_FILE} )[7] / ( 1024 * 1024 * 1024 ); if ( $size > $config{LF_MODSECIPDB_ALERT} ) { $size = sprintf( "%.2f", $size ); my $report = "ModSecurity persistent IP database ($config{LF_MODSECIPDB_FILE}) size is ${size}GB"; sysopen( my $QUEUE, "/var/lib/csf/csf.modsecipdbcheck", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot write to file: $!" ); flock( $QUEUE, LOCK_EX ); seek( $QUEUE, 0, 0 ); truncate( $QUEUE, 0 ); print $QUEUE time; close($QUEUE); my @alert = slurp("/usr/local/csf/tpl/modsecipdbalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[text\]/$report/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "modsecipdbcheck", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub connectiontracking { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "connectiontracking", $timer ) } $0 = "lfd - (child) connection tracking..."; my $lockstr = "CT_INTERVAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my %ipcnt; my %iptext; my %subcnt; my %subtext; my $alarm = int( $config{CT_INTERVAL} / 10 ) + 10; my $start = time; my $tfail = 0; my %states; if ( $config{CT_STATES} ) { foreach my $state ( split( /\,/, $config{CT_STATES} ) ) { $states{$state} = 1; } } my %countports; if ( $config{CT_PORTS} ) { foreach my $port ( split( /\,/, $config{CT_PORTS} ) ) { $countports{$port} = 1; } } my %net; my %tcpstates = ( "01" => "ESTABLISHED", "02" => "SYN_SENT", "03" => "SYN_RECV", "04" => "FIN_WAIT1", "05" => "FIN_WAIT2", "06" => "TIME_WAIT", "07" => "CLOSE", "08" => "CLOSE_WAIT", "09" => "LAST_ACK", "0A" => "LISTEN", "0B" => "CLOSING" ); foreach my $proto ( "tcp", "udp", "tcp6", "udp6" ) { open( my $IN, "<", "/proc/net/$proto" ); flock( $IN, LOCK_SH ); while (<$IN>) { my @rec = split(); if ( $rec[9] =~ /uid/ ) { next } my ( $dip, $dport ) = split( /:/, $rec[1] ); $dport //= 0; $dport = hex($dport); my ( $sip, $sport ) = split( /:/, $rec[2] ); $sport //= 0; $sport = hex($sport); $dip = hex2ip($dip); if ( $dip =~ /^0:0:0:0:0:ffff:(.*)$/ ) { my $embed = ipv4in6($dip); if ( $embed =~ /^$ipv4reg$/ ) { $dip = $embed } } $sip = hex2ip($sip); if ( $sip =~ /^0:0:0:0:0:ffff:(.*)$/ ) { my $embed = ipv4in6($sip); if ( $embed =~ /^$ipv4reg$/ ) { $sip = $embed } } my $state = $tcpstates{ $rec[3] }; if ( $config{DEBUG} >= 4 ) { logfile("debug: CT $proto: $sip:$sport -> $dip:$dport state:[$state]") } if ( $config{CT_SKIP_TIME_WAIT} and ( $state eq "TIME_WAIT" ) ) { next } if ( $config{CT_STATES} and ( $states{$state} != 1 ) ) { next } if ( $config{CT_PORTS} and ( $countports{$dport} != 1 ) ) { next } if ( $state eq "LISTEN" ) { next } if ( $dip =~ /^127\./ ) { next } if ( $dip =~ /^0\.0\.0\.1/ ) { next } checkip( \$sip ); $ipcnt{$sip}++; $iptext{$sip} .= "$proto: $sip:$sport -> $dip:$dport ($state)\n"; if ( $config{CT_SUBNET_LIMIT} > 0 ) { my $subnet; if ( $sip =~ /^(\d+\.\d+\.\d+)\.\d+$/ ) { $subnet = $1; if ( $subnet ne "0.0.0" and $subnet ne "255.255.255" ) { $subcnt{$subnet}++; $subtext{$subnet} .= "$proto: $sip:$sport -> $dip:$dport ($state)\n"; if ( $config{DEBUG} >= 2 ) { logfile("debug: CT $proto: $sip:$sport -> $dip:$dport state:[$state] count:[$ipcnt{$sip}] subnet:[$subnet] subnet count:[$subcnt{$subnet}]") } } } } else { if ( $config{DEBUG} >= 2 ) { logfile("debug: CT $proto: $sip:$sport -> $dip:$dport state:[$state] count:[$ipcnt{$sip}]") } } } close($IN); } foreach my $ip ( keys %ipcnt ) { if ( ( $ipcnt{$ip} > $config{CT_LIMIT} ) and !ignoreip($ip) ) { my $tip = iplookup($ip); if ( ipblock( $config{CT_PERMANENT}, "(CT) IP $tip found to have $ipcnt{$ip} connections", $ip, "", "inout", $config{CT_BLOCK_TIME}, 0, $iptext{$ip}, "CT_LIMIT" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: CT $ip already blocked") } } else { if ( $config{CT_EMAIL_ALERT} ) { $0 = "lfd - (child) (CT) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/connectiontracking.txt"); my $block = "Temporary Block for $config{CT_BLOCK_TIME} seconds [CT_LIMIT]"; if ( $config{CT_PERMANENT} ) { $block = "Permanent Block [CT_LIMIT]" } my $allowip = allowip($ip); if ( $allowip == 1 ) { $block .= " (IP match in csf.allow, block may not work)" } if ( $allowip == 2 ) { $block .= " (IP match in GLOBAL_ALLOW, block may not work)" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$ipcnt{$ip}/ig; $line =~ s/\[iptext\]/$iptext{$ip}/ig; $line =~ s/\[temp\]/$block/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{X_ARF} ) { $0 = "lfd - (child) sending X-ARF email for $ip"; my @alert = slurp("/usr/local/csf/tpl/x-arf.txt"); my @message; my $rfc3339 = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime ); my $boundary = time; my $reportedfrom = "root\@$hostname"; if ( $config{X_ARF_TO} ) { $config{LF_ALERT_TO} = $config{X_ARF_TO} } if ( $config{X_ARF_FROM} ) { $config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM} } my $iptype = "ipv" . checkip( \$ip ); my $abuseto = ""; my $abusemsg = ""; if ( $iptype eq "ipv4" and $abuseip ) { ( $abuseto, $abusemsg ) = ConfigServer::AbuseIP::abuseip($ip); if ( $abuseto eq "" ) { $abusemsg = ""; } elsif ( $config{X_ARF_ABUSE} and $config{X_ARF_FROM} ) { if ( $config{LF_ALERT_TO} ) { $config{LF_ALERT_TO} .= "," . $abuseto; } else { $config{LF_ALERT_TO} = "root," . $abuseto; } } } foreach my $line (@alert) { $line =~ s/\[ip\]/$ip/ig; $line =~ s/\[abuseip\]/$abusemsg/ig; $line =~ s/\[iptype\]/$iptype/ig; $line =~ s/\[tip\]/$tip/ig; $line =~ s/\[ipcount\]/$ipcnt{$ip}/ig; $line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig; $line =~ s/\[service\]/port-flood/ig; $line =~ s/\[csfversion\]/$version/ig; $line =~ s/\[reportedfrom\]/$reportedfrom/ig; $line =~ s/\[reportedid\]/$boundary\@$hostname/ig; $line =~ s/\[boundary\]/$boundary/ig; $line =~ s/\[text\]/$iptext{$ip}/ig; $line =~ s/\[RFC3339\]/$rfc3339/ig; push @message, $line; } ConfigServer::Sendmail::relay( $config{LF_ALERT_TO}, $config{LF_ALERT_FROM}, @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: CT X-ARF email sent for $ip") } } if ( $config{DEBUG} >= 1 ) { logfile("debug: CT alert email sent for $ip") } } } } } foreach my $subnet ( keys %subcnt ) { if ( $config{DEBUG} >= 2 ) { logfile("debug: CT $subnet processing $subcnt{$subnet} entries") } my $fullsubnet = $subnet . ".0/24"; if ( ( $subcnt{$subnet} > $config{CT_SUBNET_LIMIT} ) and !ignoreip($fullsubnet) ) { my $tip = iplookup($fullsubnet); if ( ipblock( $config{CT_PERMANENT}, "(CT) subnet $tip found to have $subcnt{$subnet} connections", $fullsubnet, "", "inout", $config{CT_BLOCK_TIME}, 0, $subtext{$subnet}, "CT_SUBNET_LIMIT" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: CT $subnet already blocked") } } else { if ( $config{CT_EMAIL_ALERT} ) { $0 = "lfd - (child) (CT) sending alert email for $subnet"; my @alert = slurp("/usr/local/csf/tpl/connectiontracking.txt"); my $block = "Temporary Block for $config{CT_BLOCK_TIME} seconds [CT_LIMIT]"; if ( $config{CT_PERMANENT} ) { $block = "Permanent Block [CT_LIMIT]" } my $allowip = allowip($fullsubnet); if ( $allowip == 1 ) { $block .= " (subnet match in csf.allow, block may not work)" } if ( $allowip == 2 ) { $block .= " (subnet match in GLOBAL_ALLOW, block may not work)" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[ipcount\]/$subcnt{$subnet}/ig; $line =~ s/\[iptext\]/$subtext{$subnet}/ig; $line =~ s/\[temp\]/$block/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: CT alert email sent for $fullsubnet") } } } } } if ($tfail) { $config{CT_INTERVAL} = $config{CT_INTERVAL} * 1.5; sysopen( my $TEMPCONF, "/var/lib/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPCONF, LOCK_EX ); print $TEMPCONF "CT_INTERVAL = \"$config{CT_INTERVAL}\"\n"; close($TEMPCONF); logfile("CT_INTERVAL taking $alarm seconds, temporarily throttled to run every $config{CT_INTERVAL} seconds"); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "connectiontracking", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub accounttracking { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "accounttracking", $timer ) } $0 = "lfd - (child) account tracking..."; my $lockstr = "AT_INTERVAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my $report = ""; foreach my $user ( keys %newaccounttracking ) { if ( ( $config{AT_ALERT} eq "2" ) and ( $newaccounttracking{$user}{uid} ne "0" ) ) { next } if ( $accounttracking{$user}{account} != 1 ) { if ( $config{AT_NEW} ) { $report .= "New account [$user] has been created with uid:[$newaccounttracking{$user}{uid}] gid:[$newaccounttracking{$user}{gid}] login:[$newaccounttracking{$user}{dir}] shell:[$newaccounttracking{$user}{shell}]\n"; } } else { if ( $config{AT_PASSWD} and ( $newaccounttracking{$user}{passwd} ne $accounttracking{$user}{passwd} ) ) { $report .= "Account [$user] password has changed\n"; } if ( $config{AT_UID} and ( $newaccounttracking{$user}{uid} ne $accounttracking{$user}{uid} ) ) { $report .= "Account [$user] uid has changed from [$accounttracking{$user}{uid}] to [$newaccounttracking{$user}{uid}]\n"; } if ( $config{AT_GID} and ( $newaccounttracking{$user}{gid} ne $accounttracking{$user}{gid} ) ) { $report .= "Account [$user] gid has changed from [$accounttracking{$user}{gid}] to [$newaccounttracking{$user}{gid}]\n"; } if ( $config{AT_DIR} and ( $newaccounttracking{$user}{dir} ne $accounttracking{$user}{dir} ) ) { $report .= "Account [$user] login directory has changed from [$accounttracking{$user}{dir}] to [$newaccounttracking{$user}{dir}]\n"; } if ( $config{AT_SHELL} and ( $newaccounttracking{$user}{shell} ne $accounttracking{$user}{shell} ) ) { $report .= "Account [$user] login shell has changed from [$accounttracking{$user}{shell}] to [$newaccounttracking{$user}{shell}]\n"; } } } foreach my $user ( keys %accounttracking ) { if ( ( $config{AT_ALERT} eq "2" ) and ( $accounttracking{$user}{uid} ne "0" ) ) { next } if ( $config{AT_OLD} and ( $newaccounttracking{$user}{account} != 1 ) ) { $report .= "Existing account [$user] has been removed. Old settings uid:[$accounttracking{$user}{uid}] gid:[$accounttracking{$user}{gid}] login:[$accounttracking{$user}{dir}] shell:[$accounttracking{$user}{shell}]\n"; } } if ( $report ne "" ) { logfile("*Account Modification* Email sent"); my @alert = slurp("/usr/local/csf/tpl/accounttracking.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[report\]/$report/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "accounttracking", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub syslogcheck { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "syslogcheck", $timer ) } $0 = "lfd - (child) SYSLOG check..."; my $lockstr = "SYSLOG_CHECK"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; logfile("*SYSLOG CHECK* Failed to detect check line [$syslogcheckcode] sent to SYSLOG"); my @alert = slurp("/usr/local/csf/tpl/syslogalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[code\]/$syslogcheckcode/ig; $line =~ s/\[log\]/$config{SYSLOG_LOG}/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "syslogcheck", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub processtracking { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "processtracking", $timer ) } $0 = "lfd - (child) process tracking..."; my $lockstr = "PT_INTERVAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my %users; my %net; opendir( my $DIR, "/var/cpanel/users" ); while ( my $user = readdir($DIR) ) { if ( $user =~ /^\./ ) { next } $users{$user} = 1; } closedir($DIR); $users{nobody} = 1; foreach my $proto ( "udp", "tcp", "udp6", "tcp6" ) { open( my $IN, "<", "/proc/net/$proto" ); flock( $IN, LOCK_SH ); while (<$IN>) { my @rec = split(); if ( $rec[9] =~ /uid/ ) { next } my ( $dip, $dport ) = split( /:/, $rec[2] ); $dport //= 0; $dport = hex($dport); my ( $sip, $sport ) = split( /:/, $rec[1] ); $sport //= 0; $sport = hex($sport); if ( $dip == 0 or $sip == 0 ) { next } $dip = hex2ip($dip); if ( $dip =~ /^0:0:0:0:0:ffff:(.*)$/ ) { my $embed = ipv4in6($dip); if ( $embed =~ /^$ipv4reg$/ ) { $dip = $embed } } $sip = hex2ip($sip); if ( $sip =~ /^0:0:0:0:0:ffff:(.*)$/ ) { my $embed = ipv4in6($sip); if ( $embed =~ /^$ipv4reg$/ ) { $sip = $embed } } if ( $sip eq '0.0.0.1' ) { next } $net{ $rec[9] }{proto} = $proto; $net{ $rec[9] }{sport} = $sport; $net{ $rec[9] }{sip} = $sip; $net{ $rec[9] }{dport} = $dport; $net{ $rec[9] }{dip} = $dip; if ( $config{DEBUG} >= 4 ) { logfile("debug: PT $proto: $sip:$sport -> $dip:$dport") } } close($IN); } open( my $IN, "<", "/proc/uptime" ); flock( $IN, LOCK_SH ); my @up = <$IN>; close($IN); chomp @up; my ( $upsecs, undef ) = split( /\s/, $up[0] ); my %pids; if ( !-z "/var/lib/csf/csf.temppids" ) { open( my $IN, "<", "/var/lib/csf/csf.temppids" ); flock( $IN, LOCK_SH ); my @data = <$IN>; close($IN); chomp @data; foreach my $line (@data) { my ( $itemttl, $item ) = split( /:/, $line ); if ( time - $itemttl < $config{LF_FLUSH} ) { $pids{$item} = 1; } } } my %ignoreusers; if ( !-z "/var/lib/csf/csf.tempusers" ) { open( my $IN, "<", "/var/lib/csf/csf.tempusers" ); flock( $IN, LOCK_SH ); my @data = <$IN>; close($IN); chomp @data; foreach my $line (@data) { my ( $itemttl, $item ) = split( /:/, $line ); if ( time - $itemttl < $config{LF_FLUSH} ) { $ignoreusers{$item} = 1; } } } my %totproc; my %procres; my %sessions; opendir( my $PROCDIR, "/proc" ); while ( my $pid = readdir($PROCDIR) ) { if ( $pid !~ /^\d+$/ ) { next } open( my $IN, "<", "/proc/$pid/status" ) or next; flock( $IN, LOCK_SH ); my @status = <$IN>; close($IN); chomp @status; my $user; my $uid; my $vmsize = 0; my $vmrss = 0; my $ppid = $pid; foreach my $line (@status) { if ( $line =~ /^Uid:(.*)/ ) { my $uidline = $1; my @uids; foreach my $bit ( split( /\s/, $uidline ) ) { if ( $bit =~ /^(\d*)$/ ) { push @uids, $1 } } $uid = $uids[-1]; $user = getpwuid($uid); } if ( $line =~ /^VmSize:\s+(\d+) kB$/ ) { $vmsize = $1 } if ( $line =~ /^VmRSS:\s+(\d+) kB$/ ) { $vmrss = $1 } if ( $line =~ /^PPid:\s+(\d+)$/ ) { $ppid = $1; if ( $ppid == 1 ) { $ppid = $pid } } } if ( $users{$user} or $config{PT_ALL_USERS} ) { if ( $pids{$pid} ) { next } if ( $skip{user}{$user} ) { next } my $pmatch = 0; foreach my $item ( keys %{ $pskip{puser} } ) { if ( $user =~ /^$item$/ ) { $pmatch = 1; last; } } if ($pmatch) { next } my %printable = ( ( map { chr($_), unpack( 'H2', chr($_) ) } ( 0 .. 255 ) ), "\\" => '\\', "\r" => 'r', "\n" => 'n', "\t" => 't', "\"" => '"' ); my $exe = readlink("/proc/$pid/exe"); my $cwd = readlink("/proc/$pid/cwd"); $exe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg; $cwd =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg; if ( $exe eq "" ) { next } if ( $config{DEBUG} >= 4 ) { logfile("debug: PT exe = $exe") } my $exet = $exe; my $deleted = 0; if ( $exe =~ /\(deleted\)/ ) { if ( $ppid and ( $ppid != $pid ) and $pids{$ppid} ) { my $pexe = readlink("/proc/$ppid/exe"); $pexe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg; if ( $pexe =~ /\(deleted\)/ ) { if ( $config{DEBUG} >= 2 ) { logfile("Process Tracking - Parent PID $ppid already reported for deleted $pid - ignored") } next; } } $deleted = 1; if ( $config{PT_DELETED} ) { $exet .= "\n\nThe file system shows this process is running an executable file that has been deleted. This typically happens when the original file has been replaced by a new file when the application is updated. To prevent this being reported again, restart the process that runs this excecutable file. See csf.conf and the PT_DELETED text for more information about the security implications of processes running deleted executable files."; } else { next } } open( my $CMDLINE, "<", "/proc/$pid/cmdline" ); flock( $CMDLINE, LOCK_SH ); my $cmdline = <$CMDLINE>; close($CMDLINE); chomp $cmdline; $cmdline =~ s/\0$//g; $cmdline =~ s/\0/ /g; $cmdline =~ s/\s+$//g; $cmdline =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg; open( my $STAT, "<", "/proc/$pid/stat" ) or next; flock( $STAT, LOCK_SH ); my $pstatline = <$STAT>; close($STAT); chomp $pstatline; my @pstat; if ( $pstatline =~ /^\d+\s\(.*\)\s(.*)$/ ) { @pstat = split( /\s/, $1 ); } else { next } my $jiffsecs = ( $pstat[19] // 0 ) / $clock_ticks; my $uptime = int( $upsecs - $jiffsecs ); if ( $config{PT_SSHDHUNG} ) { if ( $cmdline =~ /^sshd:\s+unknown\s+\[net|priv\]\s*$/ ) { if ( $uptime > 60 ) { kill( 9, $pid ); logfile("*PT_SSHDHUNG* process pid:[$pid] cmd:[$cmdline] Uptime:[$uptime], killed"); next; } } } if ( $skip{exe}{$exe} ) { next } $pmatch = 0; foreach my $item ( keys %{ $pskip{pexe} } ) { if ( $exe =~ /^$item$/ ) { $pmatch = 1; last; } } if ($pmatch) { next } if ( $skip{cmd}{$cmdline} ) { next } $pmatch = 0; foreach my $item ( keys %{ $pskip{pcmd} } ) { if ( $cmdline =~ /^$item$/ ) { $pmatch = 1; last; } } if ($pmatch) { next } if ( ( $config{MESSENGER} and $user eq $config{MESSENGER_USER} ) and ( $cmdline =~ /^lfd (HTML|TEXT|HTTPS) messenger/ ) ) { next } if ( $config{PT_FORKBOMB} ) { my $sid = $pstat[3]; if ( $sid > 1 ) { $sessions{$sid}{count}++; $sessions{$sid}{text} .= "PID:$pid PPID:$ppid SID:$sid User:$user EXE:$exe CMD:$cmdline\n"; if ( $sessions{$sid}{count} >= $config{PT_FORKBOMB} ) { logfile("*PT_FORKBOMB* PID:$pid SID:$sid User:$user EXE:$exe CMD:$cmdline"); my $text = $sessions{$sid}{text}; delete $sessions{$sid}; kill 9, "-$sid"; my @alert = slurp("/usr/local/csf/tpl/forkbombalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[level\]/$config{PT_FORKBOMB}/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); next; } } } if ( $user eq "root" ) { next } if ( $config{PT_SKIP_HTTP} ) { my $pgrp = $pstat[2]; my $pgrpexe = readlink("/proc/$pgrp/exe"); if ( ( $pid ne $pgrp ) and ( $pgrpexe eq "/usr/local/apache/bin/httpd" ) ) { next } if ( ( $pid ne $pgrp ) and ( $pgrpexe eq "/usr/local/bin/httpd" ) ) { next } if ( ( $pid ne $pgrp ) and ( $pgrpexe eq "/usr/bin/httpd" ) ) { next } } if ( $user ne "nobody" ) { unless ($deleted) { $totproc{$user}{count}++; if ( $totproc{$user}{pids} eq "" ) { $totproc{$user}{pids} = $pid; } else { $totproc{$user}{pids} .= ",$pid"; } $totproc{$user}{text} .= "User:$user PID:$pid PPID:$ppid Run Time:$uptime(secs) Memory:$vmsize(kb) RSS:$vmrss(kb) exe:$exe cmd:$cmdline\n"; $procres{$pid}{vmsize} = $vmsize; $procres{$pid}{vmrss} = $vmrss; $procres{$pid}{uptime} = $uptime; $procres{$pid}{user} = $user; $procres{$pid}{exe} = $exe; $procres{$pid}{exet} = $exet; $procres{$pid}{cmd} = $cmdline; $procres{$pid}{ppid} = $ppid; } } if ( $uptime > $config{PT_LIMIT} ) { my $suspect = 0; my @fd; opendir( my $DIR, "/proc/$pid/fd" ) or next; while ( my $file = readdir($DIR) ) { if ( $file =~ /^\./ ) { next } push( @fd, readlink("/proc/$pid/fd/$file") ); } closedir($DIR); my $files; my $sockets; foreach my $file (@fd) { if ( $file =~ /^socket:\[?([0-9]+)\]?$/ ) { my $ino = $1; if ( $net{$ino} ) { $sockets .= "$net{$ino}{proto}: $net{$ino}{sip}:$net{$ino}{sport} -> $net{$ino}{dip}:$net{$ino}{dport}\n"; if ( $suspect != 2 ) { $suspect = 1 } if ( $config{PT_SKIP_HTTP} and $net{$ino}{sport} =~ /^(80|443)$/ ) { $suspect = 2 } } } if ( $file =~ /^socket|pipe/ ) { next } $files .= $file . "\n"; } if ( $suspect == 2 ) { $suspect = 0 } if ( $suspect or $deleted ) { my $sexe = readlink("/proc/$pid/exe"); if ( $sexe eq "" ) { next } logfile("*Suspicious Process* PID:$pid PPID:$ppid User:$user Uptime:$uptime secs EXE:$exe CMD:$cmdline"); sysopen( my $TEMPPIDS, "/var/lib/csf/csf.temppids", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPPIDS, LOCK_EX ); print $TEMPPIDS time . ":$pid\n"; if ( $deleted and $ppid and ( $ppid != $pid ) ) { my $pexe = readlink("/proc/$ppid/exe"); $pexe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg; if ( $pexe =~ /\(deleted\)/ ) { print $TEMPPIDS time . ":$ppid\n"; $pids{$ppid} = 1; } } close($TEMPPIDS); $0 = "lfd - (child) (PT) sending alert email for process $pid"; open( my $IN, "<", "/proc/$pid/maps" ); flock( $IN, LOCK_SH ); my @maps = <$IN>; close($IN); chomp @maps; my $maps; foreach my $line (@maps) { $maps .= $line . "\n" } my @alert = slurp("/usr/local/csf/tpl/processtracking.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[pid\]/$pid (Parent PID:$ppid)/ig; $line =~ s/\[user\]/$user/ig; $line =~ s/\[uptime\]/$uptime/ig; $line =~ s/\[sockets\]/$sockets/ig; $line =~ s/\[files\]/$files/ig; $line =~ s/\[maps\]/$maps/ig; $line =~ s/\[exe\]/$exet/ig; $line =~ s/\[cmdline\]/$cmdline/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $deleted and $config{PT_DELETED_ACTION} and -e "$config{PT_DELETED_ACTION}" and -x "$config{PT_DELETED_ACTION}" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $ptchildpid = fork ) ) { childcleanup( __LINE__, "*Error* cannot fork: $!" ); } unless ($ptchildpid) { system( $config{PT_DELETED_ACTION}, $exe, $pid, $user, $ppid ); logfile("Executed PT_DELETED_ACTION for PID:$pid"); exit; } } } } } } closedir($PROCDIR); if ( $config{PT_USERPROC} ) { $0 = "lfd - (child) (PT) checking user processes"; foreach my $user ( keys %totproc ) { if ( $ignoreusers{$user} ) { next } if ( $totproc{$user}{count} > $config{PT_USERPROC} ) { my $kill = "Not killed"; if ( $config{PT_USERKILL} ) { foreach my $pid ( split( /\,/, $totproc{$user}{pids} ) ) { kill( 9, $pid ); } $kill = "Killed"; } else { sysopen( my $TEMPUSERS, "/var/lib/csf/csf.tempusers", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPUSERS, LOCK_EX ); print $TEMPUSERS time . ":$user\n"; close($TEMPUSERS); } logfile("*Excessive Processes* User:$user Kill:$config{PT_USERKILL} Process Count:$totproc{$user}{count}"); if ( !$config{PT_USERKILL} or ( $config{PT_USERKILL} and $config{PT_USERKILL_ALERT} ) ) { my @alert = slurp("/usr/local/csf/tpl/usertracking.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[user\]/$user/ig; $line =~ s/\[count\]/$totproc{$user}{count} \($kill\)/ig; $line =~ s/\[text\]/$totproc{$user}{text}/ig; $line =~ s/\[kill\]/$kill/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( $config{PT_USER_ACTION} and -e "$config{PT_USER_ACTION}" and -x "$config{PT_USER_ACTION}" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $ptchildpid = fork ) ) { childcleanup( __LINE__, "*Error* cannot fork: $!" ); } unless ($ptchildpid) { system( $config{PT_USER_ACTION}, $totproc{$user}{pids} ); exit; } } } } } if ( $config{PT_USERMEM} or $config{PT_USERRSS} or $config{PT_USERTIME} ) { foreach my $pid ( keys %procres ) { my $report = 0; my $resource; my $level; if ( $config{PT_USERMEM} and ( $procres{$pid}{vmsize} > ( $config{PT_USERMEM} * 1024 ) ) ) { $report = 1; $resource = "Virtual Memory Size"; my $memsize = int( $procres{$pid}{vmsize} / 1024 ); $level = "$memsize > $config{PT_USERMEM} (MB)"; logfile("*User Processing* PID:$pid Kill:$config{PT_USERKILL} User:$procres{$pid}{user} VM:$memsize(MB) EXE:$procres{$pid}{exe} CMD:$procres{$pid}{cmd}"); } if ( $config{PT_USERRSS} and ( $procres{$pid}{vmrss} > ( $config{PT_USERRSS} * 1024 ) ) ) { $report = 1; $resource = "RSS Memory Size"; my $memsize = int( $procres{$pid}{vmrss} / 1024 ); $level = "$memsize > $config{PT_USERRSS} (MB)"; logfile("*User Processing* PID:$pid Kill:$config{PT_USERKILL} User:$procres{$pid}{user} RSS:$memsize(MB) EXE:$procres{$pid}{exe} CMD:$procres{$pid}{cmd}"); } if ( $config{PT_USERTIME} and ( $procres{$pid}{uptime} > $config{PT_USERTIME} ) ) { $report = 1; $resource = "Process Time"; $level = "$procres{$pid}{uptime} > $config{PT_USERTIME} (seconds)"; logfile("*User Processing* PID:$pid Kill:$config{PT_USERKILL} User:$procres{$pid}{user} Time:$procres{$pid}{uptime} EXE:$procres{$pid}{exe} CMD:$procres{$pid}{cmd}"); } if ($report) { my $kill = "No"; if ( $config{PT_USERKILL} ) { kill( 9, $pid ); $kill = "Yes"; } else { sysopen( my $TEMPPIDS, "/var/lib/csf/csf.temppids", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPPIDS, LOCK_EX ); print $TEMPPIDS time . ":$pid\n"; close($TEMPPIDS); } if ( !$config{PT_USERKILL} or ( $config{PT_USERKILL} and $config{PT_USERKILL_ALERT} ) ) { my @alert = slurp("/usr/local/csf/tpl/resalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[user\]/$procres{$pid}{user}/ig; $line =~ s/\[cmd\]/$procres{$pid}{cmd}/ig; $line =~ s/\[exe\]/$procres{$pid}{exet}/ig; $line =~ s/\[resource\]/$resource/ig; $line =~ s/\[level\]/$level/ig; $line =~ s/\[kill\]/$kill/ig; $line =~ s/\[pid\]/$pid (Parent PID:$procres{$pid}{ppid})/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } if ( $config{PT_USER_ACTION} and -e "$config{PT_USER_ACTION}" and -x "$config{PT_USER_ACTION}" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $ptchildpid = fork ) ) { childcleanup( __LINE__, "*Error* cannot fork: $!" ); } unless ($ptchildpid) { system( $config{PT_USER_ACTION}, $pid ); exit; } } } } } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "processtracking", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub sshalert { my $account = shift; my $ip = shift; my $method = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "sshalert", $timer ) } logfile("*SSH login* from $ip into the $account account using $method authentication"); $0 = "lfd - (child) sending SSH login alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/sshalert.txt"); my $tip = iplookup($ip); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[account\]/$account/ig; $line =~ s/\[method\]/$method/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "sshalert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub sualert { my $suto = shift; my $sufrom = shift; my $status = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "sualert", $timer ) } logfile("*SU login* from account $sufrom to account $suto: $status"); $0 = "lfd - (child) sending SU login alert email from $sufrom to $suto"; my @alert = slurp("/usr/local/csf/tpl/sualert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[to\]/$suto/ig; $line =~ s/\[from\]/$sufrom/ig; $line =~ s/\[status\]/$status/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "sualert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub sudoalert { my $suto = shift; my $sufrom = shift; my $status = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "sudoalert", $timer ) } logfile("*SUDO login* from account $sufrom to account $suto: $status"); $0 = "lfd - (child) sending SU login alert email from $sufrom to $suto"; my @alert = slurp("/usr/local/csf/tpl/sudoalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[to\]/$suto/ig; $line =~ s/\[from\]/$sufrom/ig; $line =~ s/\[status\]/$status/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "sudoalert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub consolealert { my $logline = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "consolealert", $timer ) } logfile("*CONSOLE login* to root"); $0 = "lfd - (child) sending console login alert email"; my @alert = slurp("/usr/local/csf/tpl/consolealert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[line\]/$logline/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "consolealert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub cpanelalert { my $ip = shift; my $user = shift; my $text = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "cpanelalert", $timer ) } logfile("*WHM/cPanel $user access* from $ip"); $0 = "lfd - (child) sending WHM/cPanel access alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/cpanelalert.txt"); my $tip = iplookup($ip); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[user\]/$user/ig; $line =~ s/\[text\]/$text/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{LF_CPANEL_ALERT_ACTION} and -e "$config{LF_CPANEL_ALERT_ACTION}" and -x "$config{LF_CPANEL_ALERT_ACTION}" ) { $0 = "lfd - (child) running LF_CPANEL_ALERT_ACTION"; system( $config{LF_CPANEL_ALERT_ACTION}, $ip, $user, $tip ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "cpanelalert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub scriptalert { my $path = shift; my $count = shift; my $mails = shift; my $text; my $files; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "scriptalert", $timer ) } if ( $skipscript{$path} ) { logfile("*Script Alert* - A script in '$path' has sent an email $count times within the last hour - ignored"); exit; } logfile("*Script Alert* - A script in '$path' has sent an email $count times within the last hour"); $0 = "lfd - (child) identifying possible email scripts"; opendir( my $DIR, "$path" ); while ( my $file = readdir($DIR) ) { if ( $file =~ /\.(php([\ds]?)|phtml|cgi|pl|pm|sh|py)$/ ) { open( my $IN, "<", "$path/$file" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; if ( $line =~ /mail\s*\(/ ) { $files .= "'$path/$file'\n"; last; } if ( $line =~ /sendmail/ ) { $files .= "'$path/$file'\n"; last; } if ( $line =~ /exim/ ) { $files .= "'$path/$file'\n"; last; } } close($IN); } } closedir($DIR); if ( $config{LF_SCRIPT_PERM} ) { if ( -l $path ) { logfile("'$path' is a symlink - *not* disabled by LF_SCRIPT_PERM"); $files .= "\nDirectory '$path' is a symlink - *not* disabled\n"; } else { my $perms = sprintf "%04o", ( stat($path) )[2] & oct("00777"); $files .= "\nDirectory '$path' has been disabled with 000 permissions.\n\nTo restore the permissions use:\nchattr -i $path\nchmod $perms $path\n"; chmod( 0000, $path ); system( $config{CHATTR}, "+i", $path ); logfile("'$path' has been disabled"); } } $0 = "lfd - (child) sending script alert"; my @alert = slurp("/usr/local/csf/tpl/scriptalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[path\]/\'$path\'/ig; $line =~ s/\[count\]/$count/ig; $line =~ s/\[emails\]/$mails/ig; $line =~ s/\[scripts\]/$files/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{LF_SCRIPT_ACTION} and -e $config{LF_SCRIPT_ACTION} and -x $config{LF_SCRIPT_ACTION} ) { $0 = "lfd - (child) running LF_SCRIPT_ACTION"; system( $config{LF_SCRIPT_ACTION}, $path, $count, $mails, $files ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "scriptalert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub relayalert { my $ip = shift; my $cnt = shift; my $check = shift; my $mails = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "relayalert", $timer ) } logfile("*Exceeded $check limit* from $ip ($cnt in the last hour)"); $0 = "lfd - (child) reporting exceeded $check limit"; my $tip = $ip; my $type = "$check, Local Account"; if ( $ip =~ /^127\./ ) { $type = "$check, IPv4 localhost"; } elsif ( $ip eq "::1" ) { $type = "$check, IPv6 localhost"; } elsif ( checkip( \$ip ) ) { $tip = iplookup($ip); $type = "$check, Remote IP"; } if ( $config{"RT\_$check\_BLOCK"} ) { if ( checkip( \$ip ) and !ignoreip($ip) ) { my $perm = 0; if ( $config{"RT\_$check\_BLOCK"} == 1 ) { $perm = 1 } if ( ipblock( $perm, "$tip $check limit exceeded", $ip, $ports{smtpauth}, "in", $config{"RT\_$check\_BLOCK"}, 0, $mails, "RT\_$check\_LIMIT" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: $ip already blocked") } } } } my @alert = slurp("/usr/local/csf/tpl/relayalert.txt"); my $block = "No"; if ( $config{"RT\_$check\_BLOCK"} == 1 ) { $block = "Permanent Block [RT\_$check\_LIMIT]" } if ( $config{"RT\_$check\_BLOCK"} > 1 ) { $block = "Temporary Block for " . $config{"RT\_$check\_BLOCK"} . " seconds [RT\_$check\_LIMIT]" } my $allowip = allowip($ip); if ( $allowip == 1 and $block ne "No" ) { $block .= " (IP match in csf.allow, block may not work)" } if ( $allowip == 2 and $block ne "No" ) { $block .= " (IP match in GLOBAL_ALLOW, block may not work)" } my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[block\]/$block/ig; $line =~ s/\[check\]/$check/ig; $line =~ s/\[type\]/$type/ig; $line =~ s/\[count\]/$cnt/ig; $line =~ s/\[emails\]/$mails/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{RT_ACTION} and -e "$config{RT_ACTION}" and -x "$config{RT_ACTION}" ) { $0 = "lfd - (child) running RT_ACTION"; system( $config{RT_ACTION}, $ip, $check, $block, $cnt, $mails ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "relayalert", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub portknocking { my $ip = shift; my $port = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "portknocking", $timer ) } logfile("*Port Knocking* port $port opened by $ip"); $0 = "lfd - (child) sending Port Knocking alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/portknocking.txt"); my $tip = iplookup($ip); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[port\]/$port/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "portknocking", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub blocklist { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "blocklist", $timer ) } $0 = "lfd - retrieving blocklists"; my $lockstr = "BLOCKLISTS"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - retrieving blocklists (waiting for list lock)"; listlock("lock"); my $skipcxs; foreach my $name ( keys %blocklists ) { if ($skipcxs) { next } my $getlist = 0; my $verbose = 1; if ( $name =~ /^CXS_/ and $blocklists{$name}{url} =~ /download\.configserver\.com/ ) { $blocklists{$name}{interval} = 600; $verbose = 0; } if ( -e "/var/lib/csf/csf.block.$name" ) { my $mtime = ( stat("/var/lib/csf/csf.block.$name") )[9]; my $listtime = ( time - $mtime ); if ( $listtime >= $blocklists{$name}{interval} ) { $getlist = 1 } } else { $getlist = 1 } if ( $getlist and ( $name eq "SPAMDROP" or $name eq "SPAMEDROP" ) ) { my $tmpfile = "/var/lib/csf/$name.tmp"; if ( -e $tmpfile ) { my $mtime = ( stat($tmpfile) )[9]; my $listtime = ( time - $mtime ); if ( $listtime < 7200 ) { logfile( "Unable to retrieve blocklist $name for the next " . ( 7200 - $listtime ) . " secs" ); $getlist = 0; } else { unlink $tmpfile } } else { sysopen( my $OUT, $tmpfile, O_WRONLY | O_CREAT ); flock( $OUT, LOCK_EX ); print $OUT time; close($OUT); } } if ($getlist) { $0 = "lfd - retrieving blocklist $name"; my ( $status, $text ) = $urlget->urlget( $blocklists{$name}{url} ); if ($status) { logfile("Unable to retrieve blocklist $name - $text"); if ( $name =~ /^CXS_/ and $text =~ /Forbidden/ ) { logfile("CXS Reputation service disabled [$text]"); $skipcxs = 1; unlink "/etc/cxs/cxs.reputation"; } next; } my $blcidr = Net::CIDR::Lite->new; eval { $blcidr->add_any("127.0.0.1") }; my $blcidr6 = Net::CIDR::Lite->new; eval { $blcidr6->add("::1/128") }; foreach my $bl ( keys %blocklists ) { if ( $bl eq $name ) { next } if ( -e "/var/lib/csf/csf.block.$bl" ) { sysopen( my $BLOCK, "/var/lib/csf/csf.block.$bl", O_RDWR | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $BLOCK, LOCK_SH ); while ( my $ipstr = <$BLOCK> ) { chomp $ipstr; my $iptype = checkip( \$ipstr ); if ( $iptype == 4 ) { eval { $blcidr->add_any($ipstr) }; } elsif ( $iptype == 6 and $config{IPV6} ) { eval { $blcidr6->add_any($ipstr) }; } } close($BLOCK); } } if ( csflock() ) { lockfail("BLOCKLIST") } if ($verbose) { logfile("Retrieved and blocking blocklist $name IP address ranges") } my $drop = $config{DROP}; if ( $config{DROP_IP_LOGGING} ) { $drop = "BLOCKDROP" } if ( $text =~ m[^PK\x03\x04] or $text =~ m[^PK\x05\x06] or $text =~ m[^PK\x07\x08] ) { sysopen( my $BLOCK, "/var/lib/csf/csf.block.${name}.zip", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $BLOCK, LOCK_EX ); print $BLOCK $text; close($BLOCK); my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{UNZIP}, "-p", "/var/lib/csf/csf.block.${name}.zip" ); alarm(0); }; alarm(0); if ($@) { logfile("CC Error: Unable to unzip Blocklist $name [/var/lib/csf/csf.block.${name}.zip] - timeout"); $text = ""; } else { logfile("CC: Unzipped Blocklist $name [/var/lib/csf/csf.block.${name}.zip]"); $text = join( "\n", @data ); } } sysopen( my $BLOCK, "/var/lib/csf/csf.block.$name", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $BLOCK, LOCK_EX ); seek( $BLOCK, 0, 0 ); truncate( $BLOCK, 0 ); my $count = 0; my @blocklist = split( /\n/, $text ); my %seen; my @uniqueips = grep { !$seen{$_}++ } @blocklist; if ( $config{FASTSTART} ) { $faststart = 1 } foreach my $line (@uniqueips) { if ( $line =~ /^\#/ ) { next } if ( $line =~ /($ipv4reg(\/\d+)?)/ ) { my $iprange = $1; if ( $name eq "DSHIELD" and $iprange !~ /\/24/ ) { $iprange .= "/24" } if ( checkip( \$iprange ) ) { my $skip = 0; eval { $skip = $blcidr->find($iprange) }; if ($skip) { if ( $config{DEBUG} >= 1 ) { logfile("debug: BLOCKLIST [$name] duplicate skipped: [$iprange]") } next; } $count++; if ( $blocklists{$name}{max} > 0 and $count > $blocklists{$name}{max} ) { last } print $BLOCK "$iprange\n"; } } elsif ( $line =~ /($ipv6reg(\/\d+)?)/ ) { my $iprange = $1; if ( checkip( \$iprange ) ) { my $skip = 0; eval { $skip = $blcidr6->find($iprange) }; if ($skip) { if ( $config{DEBUG} >= 1 ) { logfile("debug: BLOCKLIST [$name] duplicate skipped: [$iprange]") } next; } $count++; if ( $blocklists{$name}{max} > 0 and $count > $blocklists{$name}{max} ) { last } print $BLOCK "$iprange\n"; } } } close($BLOCK); if ( $config{LF_IPSET} ) { open( my $BLOCK, "<", "/var/lib/csf/csf.block.$name" ); flock( $BLOCK, LOCK_SH ); my @ipset6; while ( my $line = <$BLOCK> ) { chomp $line; if ( $line =~ /^\#/ ) { next } if ( $line =~ /($ipv4reg(\/\d+)?)/ ) { my $iprange = $1; push @ipset, "add new_$name $iprange\n"; } elsif ( $line =~ /($ipv6reg(\/\d+)?)/ ) { my $iprange = $1; push @ipset6, "add new_6_$name $iprange\n"; } } close($BLOCK); ipsetrestore("new_$name"); ipsetswap( "new_$name", "bl_$name" ); if ( $config{IPV6} ) { @ipset = @ipset6; ipsetrestore("new_6_$name"); ipsetswap( "new_6_$name", "bl_6_$name" ); } } else { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEW$name" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F $name" ); } if ( $config{IPV6} ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEW$name" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F $name" ); } } open( my $BLOCK, "<", "/var/lib/csf/csf.block.$name" ); flock( $BLOCK, LOCK_SH ); while ( my $line = <$BLOCK> ) { chomp $line; if ( $line =~ /^\#/ ) { next } if ( $line =~ /($ipv4reg(\/\d+)?)/ ) { my $iprange = $1; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEW$name -s $iprange -j $drop" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A $name -s $iprange -j $drop" ); } } elsif ( $line =~ /($ipv6reg(\/\d+)?)/ ) { my $iprange = $1; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEW$name -s $iprange -j $drop" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A $name -s $iprange -j $drop" ); } } } close($BLOCK); if ( $config{FASTSTART} ) { faststart("Blocklist [$name]") } $config{LF_BOGON_SKIP} =~ s/\s//g; if ( $name eq "BOGON" and $config{LF_BOGON_SKIP} ne "" ) { foreach my $device ( split( /\,/, $config{LF_BOGON_SKIP} ) ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I NEWBOGON -i $device -j RETURN" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I BOGON -i $device -j RETURN" ); } } } if ( $config{SAFECHAINUPDATE} ) { if ( $cxsreputation and $name =~ /^CXS_/ and $name ne "CXS_ALL" and $cxsports{$name} ne "" ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT -p tcp -m multiport --dport $cxsports{$name} $ethdevin -j NEW$name" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT -p tcp -m multiport --dport $cxsports{$name} $ethdevin -j $name" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NEW$name" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j $name" ); } iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F $name" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X $name" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEW$name $name" ); if ( $config{IPV6} ) { if ( $cxsreputation and $name =~ /^CXS_/ and $name ne "CXS_ALL" and $cxsports{$name} ne "" ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT -p tcp -m multiport --dport $cxsports{$name} $ethdevin -j NEW$name" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT -p tcp -m multiport --dport $cxsports{$name} $ethdevin -j $name" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NEW$name" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j $name" ); } iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F $name" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X $name" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEW$name $name" ); } } } } } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "blocklist", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub countrycode { my $force = shift; if ( $config{MM_LICENSE_KEY} eq "" and $config{CC_SRC} eq "1" ) { logfile("CC Error: Country Code Lookups setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue using the MaxMind databases"); return; } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "countrycode", $timer ) } $0 = "lfd - retrieving countrycode lists"; my $lockstr = "COUNTRYCODE"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - retrieving countrycode lists (waiting for list lock)"; listlock("lock"); $0 = "lfd - retrieving countrycode lists"; my $drop = $config{DROP}; if ( $config{DROP_IP_LOGGING} ) { $drop = "CCDROP" } my $redo_deny = 0; my $redo_allow = 0; my $redo_allow_filter = 0; my $redo_allow_ports = 0; my $redo_deny_ports = 0; my $redo_allow_smtpauth = 0; $config{CC_DENY} =~ s/\s//g; $config{CC_ALLOW} =~ s/\s//g; $config{CC_ALLOW_FILTER} =~ s/\s//g; $config{CC_ALLOW_PORTS} =~ s/\s//g; $config{CC_DENY_PORTS} =~ s/\s//g; $config{CC_ALLOW_SMTPAUTH} =~ s/\s//g; my $getgeo = 0; my %cclist; if ( $config{CC_SRC} eq "" or $config{CC_SRC} eq "1" ) { unless ( -e "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ) { $getgeo = 1 } if ( -z "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ) { $getgeo = 1 } unless ( -e "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ) { $getgeo = 1 } if ( -z "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ) { $getgeo = 1 } if ( -e "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ) { my $mtime = ( stat("/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv") )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1 } } else { $getgeo = 1 } if ($getgeo) { unless ( -e $config{UNZIP} ) { logfile("Error: unzip binary ($config{UNZIP}) does not exist"); exit; } logfile("CC: Retrieving $config{cc_src} Country database [$config{cc_country}]"); my ( $status, $text ) = $urlget->urlget( "$config{cc_country}", "/var/lib/csf/Geo/GeoLite2-Country-CSV.zip" ); if ($status) { logfile("CC Error: Unable to retrieve $config{cc_src} Country database [$config{cc_country}] - $text"); } else { if ( -e "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ) { unlink "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-Country-CSV.zip" ); alarm(0); }; alarm(0); if ($@) { logfile("CC Error: Unable to unzip $config{cc_src} Country database /var/lib/csf/Geo/GeoLite2-Country-CSV.zip - timeout"); } if ( -z "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" or !( -e "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ) ) { logfile("CC Error: GeoLite2-Country-Blocks-IPv4.csv empty or missing"); } foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ( $cc and length($cc) == 2 ) { $cc = lc $cc; $cclist{$cc} = 1; } } } logfile("CC: Retrieving $config{asn_src} ASN database [$config{cc_asn}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_asn}", "/var/lib/csf/Geo/GeoLite2-ASN-CSV.zip" ); if ($status) { logfile("CC Error: Unable to retrieve $config{asn_src} ASN database [$config{cc_asn}] - $text"); } else { if ( -e "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ) { unlink "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-ASN-CSV.zip" ); alarm(0); }; alarm(0); if ($@) { logfile("CC Error: Unable to unzip $config{cc_src} Country database /var/lib/csf/Geo/GeoLite2-ASN-CSV.zip - timeout"); } if ( -z "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" or !( -e "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ) ) { logfile("CC Error: GeoLite2-ASN-Blocks-IPv4.csv empty or missing"); } foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ( $cc and length($cc) > 2 ) { $cc = lc $cc; $cclist{$cc} = 1; } } } unlink glob "/var/lib/csf/Geo/*.zip"; unlink glob "/var/lib/csf/Geo/*-Locations-de.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-es.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-fr.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-ja.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-pt-BR.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-ru.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-zh-CN.csv"; unlink glob "/var/lib/csf/Geo/*.dat"; unlink glob "/var/lib/csf/zone/*.zip"; unlink glob "/var/lib/csf/zone/*.csv"; unlink "/var/lib/csf/Geo/GeoIPv6.csv"; } $0 = "lfd - processing countrycode lists"; foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ($cc) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { my $mtime = ( stat("/var/lib/csf/zone/$cc.zone") )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1; $cclist{$cc} = 1 } } else { $getgeo = 1; $cclist{$cc} = 1 } if ( -z "/var/lib/csf/zone/$cc.zone" ) { $getgeo = 1; $cclist{$cc} = 1 } if ( $cclist{$cc} ) { if ( $config{CC_DENY} =~ /\b$cc\b/i ) { $redo_deny = 1 } if ( $config{CC_ALLOW} =~ /\b$cc\b/i ) { $redo_allow = 1 } if ( $config{CC_ALLOW_FILTER} =~ /\b$cc\b/i ) { $redo_allow_filter = 1 } if ( $config{CC_ALLOW_PORTS} =~ /\b$cc\b/i ) { $redo_allow_ports = 1 } if ( $config{CC_DENY_PORTS} =~ /\b$cc\b/i ) { $redo_deny_ports = 1 } if ( $config{CC_ALLOW_SMTPAUTH} =~ /\b$cc\b/i ) { $redo_allow_smtpauth = 1 } } } } if ($getgeo) { logfile("CC: Processing $config{cc_src} Country/ASN database"); my %dcidr; my %geoid; open( my $GEO, "<", "/var/lib/csf/Geo/GeoLite2-Country-Locations-en.csv" ); flock( $GEO, LOCK_SH ); while ( my $record = <$GEO> ) { chomp $record; $record =~ s/\"//g; my ( $geoname_id, undef, undef, undef, $country_iso_code, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( uc $cc eq uc $country_iso_code ) { $geoid{$cc}{$geoname_id} = 1; } } } close($GEO); open( my $IN, "<", "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $range, $geoname_id, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( $geoid{$cc}{$geoname_id} ) { $dcidr{$cc}{$range} = 1; } } } close($IN); open( $IN, "<", "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $range, $asn, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( uc($cc) =~ /AS(\d+)/ ) { if ( $1 eq $asn ) { $dcidr{$cc}{$range} = 1; } } } } close($IN); foreach my $cc ( keys %cclist ) { logfile( "CC: Extracting zone from $config{cc_src} Country/ASN database for [" . uc($cc) . "]" ); if ( keys %{ $dcidr{$cc} } eq 0 ) { if ( length($cc) == 2 ) { logfile( "CC: No entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv" ); } else { logfile( "CC: No entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv" ); } } else { sysopen( my $CIDROUT, "/var/lib/csf/zone/$cc.zone", O_WRONLY | O_CREAT ); flock( $CIDROUT, LOCK_EX ); seek( $CIDROUT, 0, 0 ); truncate( $CIDROUT, 0 ); foreach my $key ( keys %{ $dcidr{$cc} } ) { print $CIDROUT "$key\n" } close($CIDROUT); } } } } elsif ( $config{CC_SRC} eq "2" ) { unless ( -e "/var/lib/csf/Geo/ip2asn-combined.tsv" ) { $getgeo = 1 } if ( -z "/var/lib/csf/Geo/ip2asn-combined.tsv" ) { $getgeo = 1 } if ($getgeo) { unless ( -e $config{UNZIP} ) { logfile("Error: unzip binary ($config{UNZIP}) does not exist"); exit; } logfile("CC: Retrieving $config{asn_src} ASN database [$config{cc_asn}]"); my ( $status, $text ) = $urlget->urlget( "$config{cc_asn}", "/var/lib/csf/Geo/ip2asn-combined.tsv.gz" ); if ($status) { logfile("CC Error: Unable to retrieve $config{asn_src} ASN database [$config{cc_asn}] - $text"); } else { if ( -e "/var/lib/csf/Geo/ip2asn-combined.tsv" ) { unlink "/var/lib/csf/Geo/ip2asn-combined.tsv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{GUNZIP}, "/var/lib/csf/Geo/ip2asn-combined.tsv.gz" ); alarm(0); }; alarm(0); if ($@) { logfile("CC Error: Unable to unzip $config{cc_src} Country database /var/lib/csf/Geo/ip2asn-combined.tsv.gz - timeout"); } if ( -z "/var/lib/csf/Geo/ip2asn-combined.tsv" or !( -e "/var/lib/csf/Geo/ip2asn-combined.tsv" ) ) { logfile("CC Error: ip2asn-combined.tsv empty or missing"); } foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ( $cc and length($cc) > 2 ) { $cc = lc $cc; $cclist{$cc} = 1; } } } } $0 = "lfd - processing countrycode lists"; foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ($cc) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { my $mtime = ( stat("/var/lib/csf/zone/$cc.zone") )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1; $cclist{$cc} = 1 } } else { $getgeo = 1; $cclist{$cc} = 1 } if ( -z "/var/lib/csf/zone/$cc.zone" ) { $getgeo = 1; $cclist{$cc} = 1 } if ( $cclist{$cc} ) { if ( $config{CC_DENY} =~ /\b$cc\b/i ) { $redo_deny = 1 } if ( $config{CC_ALLOW} =~ /\b$cc\b/i ) { $redo_allow = 1 } if ( $config{CC_ALLOW_FILTER} =~ /\b$cc\b/i ) { $redo_allow_filter = 1 } if ( $config{CC_ALLOW_PORTS} =~ /\b$cc\b/i ) { $redo_allow_ports = 1 } if ( $config{CC_DENY_PORTS} =~ /\b$cc\b/i ) { $redo_deny_ports = 1 } if ( $config{CC_ALLOW_SMTPAUTH} =~ /\b$cc\b/i ) { $redo_allow_smtpauth = 1 } } } } if ($getgeo) { logfile("CC: Processing $config{asn_src} ASN database"); my %dcidr; open( my $IN, "<", "/var/lib/csf/Geo/ip2asn-combined.tsv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $start, $end, $asn, undef ) = split( /\t/, $record ); if ( checkip($start) == 6 ) { last } foreach my $cc ( keys %cclist ) { if ( uc($cc) =~ /AS(\d+)/ ) { if ( $1 eq $asn ) { my $ipscidr = Net::CIDR::Lite->new; eval { $ipscidr->add_range("$start-$end") }; my @cidr_list = $ipscidr->list; foreach my $list (@cidr_list) { $dcidr{$cc}{$list} = 1; } } } } } close($IN); foreach my $cc ( keys %cclist ) { if ( length($cc) > 2 ) { logfile( "CC: Extracting zone from $config{asn_src} ASN database for [" . uc($cc) . "]" ); if ( keys %{ $dcidr{$cc} } eq 0 ) { logfile( "CC: No entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/ip2asn-combined.tsv" ); } else { sysopen( my $CIDROUT, "/var/lib/csf/zone/$cc.zone", O_WRONLY | O_CREAT ); flock( $CIDROUT, LOCK_EX ); seek( $CIDROUT, 0, 0 ); truncate( $CIDROUT, 0 ); foreach my $key ( keys %{ $dcidr{$cc} } ) { print $CIDROUT "$key\n" } close($CIDROUT); } } elsif ( length($cc) == 2 ) { logfile( "CC: Retrieving $config{ccl_src} Country Code Zone [" . uc($cc) . "] from https://www.ipdeny.com" ); my ( $status, $text ) = $urlget->urlget( "https://www.ipdeny.com/ipblocks/data/aggregated/${cc}-aggregated.zone", "/var/lib/csf/zone/$cc.zone" ); if ($status) { logfile( "CC Error: Unable to retrieve $config{ccl_src} Country Code Zone [" . uc($cc) . "] from https://www.ipdeny.com/ - $text" ); } else { logfile( "CC: Country Code Zone [" . uc($cc) . "] retrieved" ); } sleep 1; } } } } if ($force) { $redo_deny = 1; $redo_allow = 1; $redo_allow_filter = 1; $redo_allow_ports = 1; $redo_deny_ports = 1; $redo_allow_smtpauth = 1; } if ( $config{LF_IPSET} ) { my $cclist; my $cnt = 0; if ($redo_deny) { $cclist .= $config{CC_DENY} . "," } if ($redo_allow) { $cclist .= $config{CC_ALLOW} . "," } if ($redo_allow_filter) { $cclist .= $config{CC_ALLOW_FILTER} . "," } if ($redo_allow_ports) { $cclist .= $config{CC_ALLOW_PORTS} . "," } if ($redo_deny_ports) { $cclist .= $config{CC_DENY_PORTS} } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ) } foreach my $cc ( split( /\,/, $cclist ) ) { if ( $cc eq "" ) { next } undef @ipset; $cc = lc $cc; if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -m set --match-set cc_$cc src -j RETURN" ); } if ( -e "/var/lib/csf/zone/$cc.zone" ) { logfile( "CC: Repopulating ipset cc_$cc with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } if ( cccheckip( \$ip ) ) { push @ipset, "add new_$cc $ip"; $cnt++; } } ipsetrestore("new_$cc"); ipsetswap( "new_$cc", "cc_$cc" ); } } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter and $cnt > 0 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -j $drop" ); if ( $config{LF_SPI} ) { my $statemodule = "-m state --state"; if ( $config{USE_CONNTRACK} ) { $statemodule = "-m conntrack --ctstate" } if ( $config{USE_FTPHELPER} ) { syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule RELATED -m helper --helper ftp -j $accept" ); syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED -j $accept" ); } else { syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED,RELATED -j $accept" ); } } } } else { if ( $config{CC_DENY} and $redo_deny ) { if ( csflock() ) { lockfail("CC_DENY") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWCC_DENY" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_DENY" ); } foreach my $cc ( split( /\,/, $config{CC_DENY} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_DENY with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWCC_DENY -s $ip -j $drop" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_DENY -s $ip -j $drop" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_DENY [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_DENY with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NEWCC_DENY" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_DENY" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_DENY" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X CC_DENY" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWCC_DENY CC_DENY" ); } } if ( $config{CC_ALLOW} and $redo_allow ) { if ( csflock() ) { lockfail("CC_ALLOW") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWCC_ALLOW" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOW" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOW with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWCC_ALLOW -s $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOW -s $ip -j $accept" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOW with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWCC_ALLOW" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOW" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOW" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X CC_ALLOW" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWCC_ALLOW CC_ALLOW" ); } } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_ALLOW_FILTER") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NCC_ALLOWF" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW_FILTER} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOWF with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NCC_ALLOWF -s $ip -j RETURN" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -s $ip -j RETURN" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW_FILTER [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOWF with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { if ( $cnt > 0 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NCC_ALLOWF -j $drop" ) } iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_ALLOWF" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NCC_ALLOWF CC_ALLOWF" ); } else { if ( $cnt > 0 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -j $drop" ); if ( $config{LF_SPI} ) { my $statemodule = "-m state --state"; if ( $config{USE_CONNTRACK} ) { $statemodule = "-m conntrack --ctstate" } if ( $config{USE_FTPHELPER} ) { syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule RELATED -m helper --helper ftp -j $accept" ); syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED -j $accept" ); } else { syscommand( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED,RELATED -j $accept" ); } } } } } if ( $config{CC_ALLOW_PORTS} and $redo_allow_ports ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_ALLOW_PORTS") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NCC_ALLOWP" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOWP" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW_PORTS} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOWP with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NCC_ALLOWP -s $ip -j CC_ALLOWPORTS" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_ALLOWP -s $ip -j CC_ALLOWPORTS" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW_PORTS [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOWP with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_ALLOWP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NCC_ALLOWP CC_ALLOWP" ); } } if ( $config{CC_DENY_PORTS} and $redo_deny_ports ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_DENY_PORTS") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NCC_DENYP" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_DENYP" ); } foreach my $cc ( split( /\,/, $config{CC_DENY_PORTS} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_DENYP with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NCC_DENYP -s $ip -j CC_DENYPORTS" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A CC_DENYP -s $ip -j CC_DENYPORTS" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_DENY_PORTS [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_DENYP with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_DENYP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_DENYP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F CC_DENYP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X CC_DENYP" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NCC_DENYP CC_DENYP" ); } } } if ( $config{CC6_LOOKUPS} and $config{IPV6} ) { countrycode6($force); } if ( $config{CC_ALLOW_SMTPAUTH} and $config{SMTPAUTH_RESTRICT} and $redo_allow_smtpauth ) { sysopen( my $SMTPAUTH, "/etc/exim.smtpauth", O_WRONLY | O_CREAT ); flock( $SMTPAUTH, LOCK_EX ); seek( $SMTPAUTH, 0, 0 ); truncate( $SMTPAUTH, 0 ); print $SMTPAUTH "# DO NOT EDIT THIS FILE\n#\n"; print $SMTPAUTH "# Modify /etc/csf/csf.smtpauth and then restart csf and then lfd\n\n"; print $SMTPAUTH "127.0.0.0/8\n"; print $SMTPAUTH "\"::1\"\n"; print $SMTPAUTH "\"::1/128\"\n"; if ( -e "/etc/csf/csf.smtpauth" ) { my @entries = slurp("/etc/csf/csf.smtpauth"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $ip, undef ) = split( /\s/, $line, 2 ); my $status = checkip( \$ip ); if ( $status == 4 ) { print $SMTPAUTH "$ip\n" } elsif ( $status == 6 ) { print $SMTPAUTH "\"$ip\"\n" } } } foreach my $cc ( split( /\,/, $config{CC_ALLOW_SMTPAUTH} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone" ) { print $SMTPAUTH "\n# IPv4 addresses for [" . uc($cc) . "]:\n"; foreach my $line ( slurp("/var/lib/csf/zone/$cc.zone") ) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } my $status = cccheckip( \$ip ); if ( $status == 4 ) { print $SMTPAUTH "$ip\n" } elsif ( $status == 6 ) { print $SMTPAUTH "\"$ip\"\n" } } logfile( "CC: Finished repopulating /etc/exim.smtpauth with IPv4 addresses from [" . uc($cc) . "]" ); } if ( $config{CC6_LOOKUPS} and -e "/var/lib/csf/zone/$cc.zone6" ) { print $SMTPAUTH "\n# IPv6 addresses for [" . uc($cc) . "]:\n"; foreach my $line ( slurp("/var/lib/csf/zone/$cc.zone6") ) { $line =~ s/$cleanreg//g; if ( $line =~ /^(\s|\#|$)/ ) { next } my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } my $status = cccheckip( \$ip ); if ( $status == 4 ) { print $SMTPAUTH "$ip\n" } elsif ( $status == 6 ) { print $SMTPAUTH "\"$ip\"\n" } } logfile( "CC: Finished repopulating /etc/exim.smtpauth with IPv6 addresses from [" . uc($cc) . "]" ); } } close($SMTPAUTH); chmod( 0644, "/etc/exim.smtpauth" ); } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "countrycode", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub countrycodelookups { my $force = shift; if ( $config{CC_LOOKUPS} == 4 ) { return } if ( $config{MM_LICENSE_KEY} eq "" and $config{CC_SRC} eq "1" ) { logfile("CC Error: Country Code Filters setting MM_LICENSE_KEY must be set in /etc/csf/csf.conf to continue using the MaxMind databases"); return; } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "countrycodelookups", $timer ) } $0 = "lfd - retrieving countrycode lookups"; my $lockstr = "CC_LOOKUPS"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - retrieving countrycodelookups lists (waiting for list lock)"; listlock("lock"); $0 = "lfd - retrieving countrycodelookups lists"; if ( $config{CC_SRC} eq "" or $config{CC_SRC} eq "1" ) { my $getgeo = 0; my $geofile = "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv4.csv"; if ( $config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3 ) { $geofile = "/var/lib/csf/Geo/GeoLite2-City-Blocks-IPv4.csv" } if ( -e $geofile ) { if ( -z $geofile ) { $getgeo = 1 } my $mtime = ( stat($geofile) )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1 } if ( $config{CC_LOOKUPS} == 3 ) { my $geofile = "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv4.csv"; if ( -z $geofile ) { $getgeo = 1 } my $mtime = ( stat($geofile) )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1 } } } else { $getgeo = 1 } if ($getgeo) { my $status; my $text; if ( $config{CC_LOOKUPS} == 3 ) { logfile("CCL: Retrieving $config{asn_src} ASN database [$config{cc_asn}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_asn}", "/var/lib/csf/Geo/GeoLite2-ASN-CSV.zip" ); logfile("CCL: Retrieving $config{cc_src} City database [$config{cc_city}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_city}", "/var/lib/csf/Geo/GeoLite2-City-CSV.zip" ); } elsif ( $config{CC_LOOKUPS} == 2 ) { logfile("CCL: Retrieving $config{cc_src} City database [$config{cc_city}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_city}", "/var/lib/csf/Geo/GeoLite2-City-CSV.zip" ); } else { logfile("CCL: Retrieving $config{cc_src} Country database [$config{cc_country}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_country}", "/var/lib/csf/Geo/GeoLite2-Country-CSV.zip" ); } if ($status) { if ( $config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3 ) { logfile("CCL Error: Unable to retrieve $config{cc_src} City database [$config{cc_city}] - $text"); } else { logfile("CCL Error: Unable to retrieve $config{cc_src} Country database [$config{cc_country}] - $text"); } } else { my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); my ( $childin, $childout, $cmdpid ); if ( $config{CC_LOOKUPS} == 3 ) { @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-ASN-CSV.zip" ); @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-City-CSV.zip" ); } elsif ( $config{CC_LOOKUPS} == 2 ) { @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-City-CSV.zip" ); } else { @data = syscommand( __LINE__, $config{UNZIP}, "-DDjod", "/var/lib/csf/Geo/", "/var/lib/csf/Geo/GeoLite2-Country-CSV.zip" ); } alarm(0); }; alarm(0); if ($@) { if ( $config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3 ) { logfile("CCL Error: Unable to unzip $config{cc_src} City database /var/lib/csf/Geo/GeoLite2-City-CSV.zip - timeout"); } else { logfile("CCL Error: Unable to unzip $config{cc_src} Country database /var/lib/csf/Geo/GeoLite2-Country-CSV.zip - timeout"); } } if ( !( -e $geofile ) or -z $geofile ) { logfile("CCL Error: $geofile empty or missing"); } else { my $now = time; utime( $now, $now, $geofile ); logfile("CCL: Retrieved $config{cc_src} IP database"); open( my $OUT, ">", "/var/lib/csf/csf.cclookup" ); close($OUT); } } unlink glob "/var/lib/csf/Geo/*.zip"; unlink glob "/var/lib/csf/Geo/*-Locations-de.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-es.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-fr.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-ja.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-pt-BR.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-ru.csv"; unlink glob "/var/lib/csf/Geo/*-Locations-zh-CN.csv"; unlink glob "/var/lib/csf/Geo/*.dat"; unlink glob "/var/lib/csf/zone/*.zip"; unlink glob "/var/lib/csf/zone/*.csv"; unlink "/var/lib/csf/Geo/GeoIPv6.csv"; } } elsif ( $config{CC_SRC} eq "2" ) { my $getgeo = 0; my $geofile = "/var/lib/csf/Geo/dbip-country-lite.csv"; if ( $config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3 ) { $geofile = "/var/lib/csf/Geo/dbip-city-lite.csv" } if ( -e $geofile ) { if ( -z $geofile ) { $getgeo = 1 } my $mtime = ( stat($geofile) )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1 } if ( $config{CC_LOOKUPS} == 3 ) { my $geofile = "/var/lib/csf/Geo/ip2asn-combined.tsv"; if ( -z $geofile ) { $getgeo = 1 } my $mtime = ( stat($geofile) )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1 } } } else { $getgeo = 1 } if ($getgeo) { unless ( -e $config{GUNZIP} ) { logfile("Error: gunzip binary ($config{GUNZIP}) does not exist"); exit; } logfile("CCL: Retrieving CC Lookup database [$config{cc_cc}]"); my ( $status, $text ) = $urlget->urlget( "$config{cc_cc}", "/var/lib/csf/Geo/countryInfo.txt" ); if ($status) { logfile("CCL Error: Unable to retrieve $config{cc_src} CC Lookup database [$config{cc_cc}] - $text"); } else { if ( -z "/var/lib/csf/Geo/countryInfo.txt" or !( -e "/var/lib/csf/Geo/countryInfo.txt" ) ) { logfile("CC Error: countryInfo.txt empty or missing"); } } if ( $config{CC_LOOKUPS} == 2 or $config{CC_LOOKUPS} == 3 ) { logfile("CCL: Retrieving $config{cc_src} City database [$config{cc_city}]"); my ( $status, $text ) = $urlget->urlget( "$config{cc_city}", "/var/lib/csf/Geo/dbip-city-lite.csv.gz" ); if ($status) { logfile("CCL Error: Unable to retrieve $config{cc_src} City database [$config{cc_city}] - $text"); } else { if ( -e "/var/lib/csf/Geo/dbip-city-lite.csv" ) { unlink "/var/lib/csf/Geo/dbip-city-lite.csv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{GUNZIP}, "/var/lib/csf/Geo/dbip-city-lite.csv.gz" ); alarm(0); }; alarm(0); if ($@) { logfile("CCL Error: Unable to gunzip $config{cc_src} City database /var/lib/csf/Geo/dbip-city-lite.csv.gz - timeout"); } if ( -z "/var/lib/csf/Geo/dbip-city-lite.csv" or !( -e "/var/lib/csf/Geo/dbip-city-lite.csv" ) ) { logfile("CCL Error: dbip-city-lite.csv empty or missing"); } } if ( $config{CC_LOOKUPS} == 3 ) { logfile("CCL: Retrieving $config{asn_src} ASN database [$config{cc_asn}]"); ( $status, $text ) = $urlget->urlget( "$config{cc_asn}", "/var/lib/csf/Geo/ip2asn-combined.tsv.gz" ); if ($status) { logfile("CCL Error: Unable to retrieve $config{asn_src} ASN database [$config{cc_asn}] - $text"); } else { if ( -e "/var/lib/csf/Geo/ip2asn-combined.tsv" ) { unlink "/var/lib/csf/Geo/ip2asn-combined.tsv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{GUNZIP}, "/var/lib/csf/Geo/ip2asn-combined.tsv.gz" ); alarm(0); }; alarm(0); if ($@) { logfile("CCL Error: Unable to unzip $config{cc_src} Country database /var/lib/csf/Geo/ip2asn-combined.tsv.gz - timeout"); } if ( -z "/var/lib/csf/Geo/ip2asn-combined.tsv" or !( -e "/var/lib/csf/Geo/ip2asn-combined.tsv" ) ) { logfile("CCL Error: ip2asn-combined.tsv empty or missing"); } } } } else { logfile("CC: Retrieving $config{cc_src} Country database [$config{cc_country}]"); my ( $status, $text ) = $urlget->urlget( "$config{cc_country}", "/var/lib/csf/Geo/dbip-country-lite.csv.gz" ); if ($status) { logfile("CCL Error: Unable to retrieve $config{cc_src} Country database [$config{cc_country}] - $text"); } else { if ( -e "/var/lib/csf/Geo/dbip-country-lite.csv" ) { unlink "/var/lib/csf/Geo/dbip-country-lite.csv" } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(180); @data = syscommand( __LINE__, $config{GUNZIP}, "/var/lib/csf/Geo/dbip-country-lite.csv.gz" ); alarm(0); }; alarm(0); if ($@) { logfile("CC Error: Unable to gunzip $config{cc_src} Country database /var/lib/csf/Geo/dbip-country-lite.csv.gz - timeout"); } if ( -z "/var/lib/csf/Geo/dbip-country-lite.csv" or !( -e "/var/lib/csf/Geo/dbip-country-lite.csv" ) ) { logfile("CC Error: dbip-country-lite.csv empty or missing"); } } } } } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "countrycodelookups", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub countrycode6 { my $force = shift; my $getgeo; my %cclist; my $redo_deny = 0; my $redo_allow = 0; my $redo_allow_filter = 0; my $redo_allow_ports = 0; my $redo_deny_ports = 0; my $redo_allow_smtpauth = 0; my $drop = $config{DROP}; if ( $config{DROP_IP_LOGGING} ) { $drop = "CCDROP" } $config{CC_DENY} =~ s/\s//g; $config{CC_ALLOW} =~ s/\s//g; $config{CC_ALLOW_FILTER} =~ s/\s//g; $config{CC_ALLOW_PORTS} =~ s/\s//g; $config{CC_DENY_PORTS} =~ s/\s//g; $config{CC_ALLOW_SMTPAUTH} =~ s/\s//g; if ($force) { $redo_deny = 1; $redo_allow = 1; $redo_allow_filter = 1; $redo_allow_ports = 1; $redo_deny_ports = 1; $redo_allow_smtpauth = 1; } $0 = "lfd - processing countrycode6 lists"; foreach my $cc ( split( /\,/, "$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_ALLOW_PORTS},$config{CC_DENY_PORTS},$config{CC_ALLOW_SMTPAUTH}" ) ) { if ( $cc eq "" ) { next } $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { my $mtime = ( stat("/var/lib/csf/zone/$cc.zone6") )[9]; my $days = int( ( time - $mtime ) / 86400 ); if ( $days >= $config{CC_INTERVAL} ) { $getgeo = 1; $cclist{$cc} = 1 } } else { $getgeo = 1; $cclist{$cc} = 1 } if ( -z "/var/lib/csf/zone/$cc.zone6" ) { $getgeo = 1; $cclist{$cc} = 1 } if ( $cclist{$cc} ) { if ( $config{CC_DENY} =~ /\b$cc\b/i ) { $redo_deny = 1 } if ( $config{CC_ALLOW} =~ /\b$cc\b/i ) { $redo_allow = 1 } if ( $config{CC_ALLOW_FILTER} =~ /\b$cc\b/i ) { $redo_allow_filter = 1 } if ( $config{CC_ALLOW_PORTS} =~ /\b$cc\b/i ) { $redo_allow_ports = 1 } if ( $config{CC_DENY_PORTS} =~ /\b$cc\b/i ) { $redo_deny_ports = 1 } if ( $config{CC_ALLOW_SMTPAUTH} =~ /\b$cc\b/i ) { $redo_allow_smtpauth = 1 } } } if ( $config{CC_SRC} eq "" or $config{CC_SRC} eq "1" ) { if ($getgeo) { logfile("CC: Processing $config{cc_src} Country/ASN IPv6 database"); my %dcidr; my %geoid; open( my $GEO, "<", "/var/lib/csf/Geo/GeoLite2-Country-Locations-en.csv" ); flock( $GEO, LOCK_SH ); while ( my $record = <$GEO> ) { chomp $record; $record =~ s/\"//g; my ( $geoname_id, undef, undef, undef, $country_iso_code, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( uc $cc eq uc $country_iso_code ) { $geoid{$cc}{$geoname_id} = 1; } } } close($GEO); open( my $IN, "<", "/var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv6.csv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $range, $geoname_id, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( $geoid{$cc}{$geoname_id} ) { $dcidr{$cc}{$range} = 1; } } } close($IN); open( $IN, "<", "/var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv6.csv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $range, $asn, undef ) = split( /\,/, $record ); foreach my $cc ( keys %cclist ) { if ( uc($cc) =~ /AS(\d+)/ ) { if ( $1 eq $asn ) { $dcidr{$cc}{$range} = 1; } } } } close($IN); foreach my $cc ( keys %cclist ) { logfile( "CC: Extracting zone from $config{cc_src} Country/ASN IPv6 database for [" . uc($cc) . "]" ); if ( keys %{ $dcidr{$cc} } eq 0 ) { if ( length($cc) == 2 ) { logfile( "CC: No IPv6 entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/GeoLite2-Country-Blocks-IPv6.csv" ); } else { logfile( "CC: No IPv6 entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/GeoLite2-ASN-Blocks-IPv6.csv" ); } } else { sysopen( my $CIDROUT, "/var/lib/csf/zone/$cc.zone6", O_WRONLY | O_CREAT ); flock( $CIDROUT, LOCK_EX ); seek( $CIDROUT, 0, 0 ); truncate( $CIDROUT, 0 ); foreach my $key ( keys %{ $dcidr{$cc} } ) { print $CIDROUT "$key\n" } close($CIDROUT); } } } } elsif ( $config{CC_SRC} eq "2" ) { if ($getgeo) { logfile("CC: Processing $config{cc_src} Country/ASN IPv6 database"); my %dcidr; open( my $IN, "<", "/var/lib/csf/Geo/ip2asn-combined.tsv" ); flock( $IN, LOCK_SH ); while ( my $record = <$IN> ) { chomp $record; $record =~ s/\"//g; my ( $start, $end, $asn, undef ) = split( /\t/, $record ); if ( checkip($start) == 4 ) { next } foreach my $cc ( keys %cclist ) { if ( uc($cc) =~ /AS(\d+)/ ) { if ( $1 eq $asn ) { my $ipscidr = Net::CIDR::Lite->new; eval { $ipscidr->add_range("$start-$end") }; my @cidr_list = $ipscidr->list; foreach my $list (@cidr_list) { $dcidr{$cc}{$list} = 1; } } } } } close($IN); foreach my $cc ( keys %cclist ) { if ( length($cc) > 2 ) { logfile( "CC: Extracting IPv6 zone from $config{asn_src} ASN database for [" . uc($cc) . "]" ); if ( keys %{ $dcidr{$cc} } eq 0 ) { logfile( "CC: No entries found for [" . uc($cc) . "] in /var/lib/csf/Geo/ip2asn-combined.tsv" ); } else { sysopen( my $CIDROUT, "/var/lib/csf/zone/$cc.zone6", O_WRONLY | O_CREAT ); flock( $CIDROUT, LOCK_EX ); seek( $CIDROUT, 0, 0 ); truncate( $CIDROUT, 0 ); foreach my $key ( keys %{ $dcidr{$cc} } ) { print $CIDROUT "$key\n" } close($CIDROUT); } } elsif ( length($cc) == 2 ) { logfile( "CC: Retrieving $config{ccl_src} Country Code IPv6 Zone [" . uc($cc) . "] from https://www.ipdeny.com" ); my ( $status, $text ) = $urlget->urlget( "https://www.ipdeny.com/ipv6/ipaddresses/aggregated/${cc}-aggregated.zone", "/var/lib/csf/zone/$cc.zone6" ); if ($status) { logfile( "CC Error: Unable to retrieve $config{ccl_src} Country Code IPv6 Zone [" . uc($cc) . "] from https://www.ipdeny.com - $text" ); } else { logfile( "CC: Country Code IPv6 Zone [" . uc($cc) . "] retrieved" ); } sleep 1; } } } } if ( $config{LF_IPSET} ) { my $cclist; my $cnt = 0; if ($redo_deny) { $cclist .= $config{CC_DENY} . "," } if ($redo_allow) { $cclist .= $config{CC_ALLOW} . "," } if ($redo_allow_filter) { $cclist .= $config{CC_ALLOW_FILTER} . "," } if ($redo_allow_ports) { $cclist .= $config{CC_ALLOW_PORTS} . "," } if ($redo_deny_ports) { $cclist .= $config{CC_DENY_PORTS} } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ) } foreach my $cc ( split( /\,/, $cclist ) ) { if ( $cc eq "" ) { next } undef @ipset; $cc = lc $cc; if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -m set --match-set cc_6_$cc src -j RETURN" ) } if ( -e "/var/lib/csf/zone/$cc.zone6" ) { logfile( "CC: Repopulating ipset cc_6_$cc with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( $config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33 ) { my ( $drop_ip, $drop_cidr ) = split( /\//, $ip ); if ( !length $drop_cidr ) { $drop_cidr = "32" } if ( $drop_cidr > $config{CC_DROP_CIDR} ) { next } } if ( cccheckip( \$ip ) ) { push @ipset, "add new_6_$cc $ip"; $cnt++; } } ipsetrestore("new_6_$cc"); ipsetswap( "new_6_$cc", "cc_6_$cc" ); } } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter and $cnt > 0 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -j $drop" ); if ( $config{IPV6_SPI} ) { my $statemodule = "-m state --state"; if ( $config{USE_FTPHELPER} ) { syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule RELATED -m helper --helper ftp -j $accept" ); syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED -j $accept" ); } else { syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED,RELATED -j $accept" ); } syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $eth6devin -p icmpv6 -j $accept" ); } } } else { if ( $config{CC_DENY} and $redo_deny ) { if ( csflock() ) { lockfail("CC_DENY") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWCC_DENY" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_DENY" ); } foreach my $cc ( split( /\,/, $config{CC_DENY} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_DENY with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWCC_DENY -s $ip -j $drop" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_DENY -s $ip -j $drop" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_DENY [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_DENY with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NEWCC_DENY" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_DENY" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_DENY" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X CC_DENY" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWCC_DENY CC_DENY" ); } } if ( $config{CC_ALLOW} and $redo_allow ) { if ( csflock() ) { lockfail("CC_ALLOW") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWCC_ALLOW" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOW" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOW with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWCC_ALLOW -s $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOW -s $ip -j $accept" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOW with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWCC_ALLOW" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOW" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOW" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X CC_ALLOW" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWCC_ALLOW CC_ALLOW" ); } } if ( $config{CC_ALLOW_FILTER} and $redo_allow_filter ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_ALLOW_FILTER") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NCC_ALLOWF" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW_FILTER} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOWF with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NCC_ALLOWF -s $ip -j RETURN" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -s $ip -j RETURN" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW_FILTER [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOWF with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { if ( $cnt > 0 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NCC_ALLOWF -j $drop" ) } iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_ALLOWF" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X CC_ALLOWF" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NCC_ALLOWF CC_ALLOWF" ); } else { if ( $cnt > 0 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOWF -j $drop" ); if ( $config{IPV6_SPI} ) { my $statemodule = "-m state --state"; if ( $config{USE_FTPHELPER} ) { syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule RELATED -m helper --helper ftp -j $accept" ); syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED -j $accept" ); } else { syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $ethdevin $statemodule ESTABLISHED,RELATED -j $accept" ); } syscommand( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I CC_ALLOWF $eth6devin -p icmpv6 -j $accept" ); } } } } if ( $config{CC_ALLOW_PORTS} and $redo_allow_ports ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_ALLOW_PORTS") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NCC_ALLOWP" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOWP" ); } foreach my $cc ( split( /\,/, $config{CC_ALLOW_PORTS} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_ALLOWP with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NCC_ALLOWP -s $ip -j CC_ALLOWPORTS" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_ALLOWP -s $ip -j CC_ALLOWPORTS" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_ALLOW_PORTS [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_ALLOWP with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_ALLOWP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X CC_ALLOWP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NCC_ALLOWP CC_ALLOWP" ); } } if ( $config{CC_DENY_PORTS} and $redo_deny_ports ) { my $cnt = 0; if ( csflock() ) { lockfail("CC_DENY_PORTS") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NCC_DENYP" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_DENYP" ); } foreach my $cc ( split( /\,/, $config{CC_DENY_PORTS} ) ) { $cc = lc $cc; if ( -e "/var/lib/csf/zone/$cc.zone6" ) { if ( $config{FASTSTART} ) { $faststart = 1 } logfile( "CC: Repopulating CC_DENYP with IP addresses from [" . uc($cc) . "]" ); open( my $IN, "<", "/var/lib/csf/zone/$cc.zone6" ); flock( $IN, LOCK_SH ); while ( my $line = <$IN> ) { chomp $line; my ( $ip, undef ) = split( /\s/, $line, 2 ); if ( cccheckip( \$ip ) ) { $cnt++; if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NCC_DENYP -s $ip -j CC_DENYPORTS" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A CC_DENYP -s $ip -j CC_DENYPORTS" ); } } } close($IN); if ( $config{FASTSTART} ) { faststart( "CC_DENY_PORTS [" . uc($cc) . "]" ) } logfile( "CC: Finished repopulating CC_DENYP with IP addresses from [" . uc($cc) . "]" ); } } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NCC_DENYP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j CC_DENYP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F CC_DENYP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X CC_DENYP" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NCC_DENYP CC_DENYP" ); } } } return; } sub global { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "global", $timer ) } $0 = "lfd - retrieving global lists"; my $lockstr = "LF_GLOBAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - retrieving global lists (waiting for list lock)"; listlock("lock"); $0 = "lfd - retrieving global lists"; if ( $config{GLOBAL_ALLOW} ) { my ( $status, $text ) = $urlget->urlget( $config{GLOBAL_ALLOW} ); if ($status) { logfile("Unable to retrieve global allow list - $text"); } else { if ( csflock() ) { lockfail("GLOBAL_ALLOW") } logfile("Global Allow - retrieved and allowing IP address ranges"); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGALLOWOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGALLOWIN -m set --match-set chain_GALLOW src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGALLOWOUT -m set --match-set chain_GALLOW dst -j $accept" ); ipsetcreate("chain_NEWGALLOW"); } if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGALLOWOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGALLOWIN -m set --match-set chain_6_GALLOW src -j $accept" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGALLOWOUT -m set --match-set chain_6_GALLOW dst -j $accept" ); ipsetcreate("chain_6_NEWGALLOW"); } } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GALLOWOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GALLOWIN -m set --match-set chain_GALLOW src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GALLOWOUT -m set --match-set chain_GALLOW dst -j $accept" ); ipsetflush("chain_GALLOW"); } if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GALLOWOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GALLOWIN -m set --match-set chain_6_GALLOW src -j $accept" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GALLOWOUT -m set --match-set chain_6_GALLOW dst -j $accept" ); ipsetflush("chain_6_GALLOW"); } } } sysopen( my $GALLOW, "/var/lib/csf/csf.gallow", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $GALLOW, LOCK_EX ); seek( $GALLOW, 0, 0 ); truncate( $GALLOW, 0 ); if ( $config{FASTSTART} ) { $faststart = 1 } foreach my $line ( split( /\n/, $text ) ) { if ( $line =~ /^\#/ ) { next } my ( $ip, $comment ) = split( /\s/, $line, 2 ); print $GALLOW "$ip\n"; if ( $config{SAFECHAINUPDATE} ) { linefilter( $ip, "allow", "NEWGALLOW" ); } else { linefilter( $ip, "allow", "GALLOW" ); } } if ( $config{FASTSTART} ) { faststart("GLOBAL_ALLOW") } close($GALLOW); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWGALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $ethdevout -j NEWGALLOWOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j GALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j GALLOWOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GALLOWOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GALLOWOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGALLOWIN GALLOWIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGALLOWOUT GALLOWOUT" ); if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALINPUT $eth6devin -j NEWGALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $eth6devout -j NEWGALLOWOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $eth6devin -j GALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $eth6devout -j GALLOWOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GALLOWOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GALLOWOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGALLOWIN GALLOWIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGALLOWOUT GALLOWOUT" ); } if ( $config{LF_IPSET} ) { ipsetswap( "chain_NEWGALLOW", "chain_GALLOW" ); if ( $config{IPV6} ) { ipsetswap( "chain_6_NEWGALLOW", "chain_6_GALLOW" ); } } } } } if ( $config{GLOBAL_DENY} ) { my ( $status, $text ) = $urlget->urlget( $config{GLOBAL_DENY} ); if ($status) { logfile("Unable to retrieve global deny list - $text"); } else { if ( csflock() ) { lockfail("GLOBAL_DENY") } logfile("Global Deny - retrieved and blocking IP address ranges"); my $drop = $config{DROP}; if ( $config{DROP_IP_LOGGING} ) { $drop = "BLOCKDROP" } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGDENYOUT" ); if ( $config{LF_IPSET} ) { my $pktin = $config{DROP}; my $pktout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $pktin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $pktout = "LOGDROPOUT" } iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGDENYIN -m set --match-set chain_GDENY src -j $pktin" ); unless ( $config{LF_BLOCKINONLY} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGDENYOUT -m set --match-set chain_GDENY dst -j $pktout" ) } ipsetcreate("chain_NEWGDENY"); } if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGDENYOUT" ); if ( $config{LF_IPSET} ) { my $pktin = $config{DROP}; my $pktout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $pktin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $pktout = "LOGDROPOUT" } iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGDENYIN -m set --match-set chain_6_GDENY src -j $pktin" ); unless ( $config{LF_BLOCKINONLY} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGDENYOUT -m set --match-set chain_6_GDENY dst -j $pktout" ) } ipsetcreate("chain_6_NEWGDENY"); } } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDENYOUT" ); if ( $config{LF_IPSET} ) { my $pktin = $config{DROP}; my $pktout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $pktin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $pktout = "LOGDROPOUT" } iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GDENYIN -m set --match-set chain_GDENY src -j $pktin" ); unless ( $config{LF_BLOCKINONLY} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GDENYOUT -m set --match-set chain_GDENY dst -j $pktout" ) } ipsetflush("chain_GDENY"); } if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDENYOUT" ); if ( $config{LF_IPSET} ) { my $pktin = $config{DROP}; my $pktout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $pktin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $pktout = "LOGDROPOUT" } iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GDENYIN -m set --match-set chain_6_GDENY src -j $pktin" ); unless ( $config{LF_BLOCKINONLY} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GDENYOUT -m set --match-set chain_6_GDENY dst -j $pktout" ) } ipsetflush("chain_6_GDENY"); } } } sysopen( my $GDENY, "/var/lib/csf/csf.gdeny", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $GDENY, LOCK_EX ); seek( $GDENY, 0, 0 ); truncate( $GDENY, 0 ); if ( $config{FASTSTART} ) { $faststart = 1 } foreach my $line ( split( /\n/, $text ) ) { if ( $line =~ /^\#/ ) { next } my ( $ip, $comment ) = split( /\s/, $line, 2 ); print $GDENY "$ip\n"; if ( $config{SAFECHAINUPDATE} ) { linefilter( $ip, "deny", "NEWGDENY" ); } else { linefilter( $ip, "deny", "GDENY" ); } } if ( $config{FASTSTART} ) { faststart("GLOBAL_DENY") } close($GDENY); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALINPUT $ethdevin -j NEWGDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A LOCALOUTPUT $ethdevout -j NEWGDENYOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j GDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j GDENYOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDENYOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GDENYOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGDENYIN GDENYIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGDENYOUT GDENYOUT" ); if ( $config{IPV6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALINPUT $eth6devin -j NEWGDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A LOCALOUTPUT $eth6devout -j NEWGDENYOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $eth6devin -j GDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $eth6devout -j GDENYOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDENYOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GDENYOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGDENYIN GDENYIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGDENYOUT GDENYOUT" ); } if ( $config{LF_IPSET} ) { ipsetswap( "chain_NEWGDENY", "chain_GDENY" ); if ( $config{IPV6} ) { ipsetswap( "chain_6_NEWGDENY", "chain_6_GDENY" ); } } } } } if ( $config{GLOBAL_IGNORE} ) { my ( $status, $text ) = $urlget->urlget( $config{GLOBAL_IGNORE} ); if ($status) { logfile("Unable to retrieve global ignore list - $text"); } else { logfile("Global Ignore - retrieved and ignoring"); sysopen( my $GIGNORE, "/var/lib/csf/csf.gignore", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $GIGNORE, LOCK_EX ); seek( $GIGNORE, 0, 0 ); truncate( $GIGNORE, 0 ); foreach my $line ( split( /\n/, $text ) ) { if ( $line =~ /^\#/ ) { next } my ( $ip, $comment ) = split( /\s/, $line, 2 ); print $GIGNORE "$ip\n"; } close($GIGNORE); } } if ( $config{GLOBAL_DYNDNS} ) { my ( $status, $text ) = $urlget->urlget( $config{GLOBAL_DYNDNS} ); if ($status) { logfile("Unable to retrieve global dyndns list - $text"); } else { logfile("Global DynDNS - retrieved and allowing IP addresses"); sysopen( my $GDYNDNS, "/var/lib/csf/csf.gdyndns", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $GDYNDNS, LOCK_EX ); seek( $GDYNDNS, 0, 0 ); truncate( $GDYNDNS, 0 ); foreach my $line ( split( /\n/, $text ) ) { if ( $line =~ /^\#/ ) { next } my ( $ip, $comment ) = split( /\s/, $line, 2 ); print $GDYNDNS "$ip\n"; } close($GDYNDNS); globaldyndns(); } } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "global", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub dyndns { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "dyndns", $timer ) } $0 = "lfd - resolving dyndns IP addresses"; my $lockstr = "DYNDNS"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - resolving dyndns IP addresses (waiting for list lock)"; listlock("lock"); $0 = "lfd - resolving dyndns IP addresses"; my @dyndns; my @entries = slurp("/etc/csf/csf.dyndns"); foreach my $line (@entries) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @entries, @incfile; } } foreach my $line (@entries) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } push @dyndns, $line; } if ( csflock() ) { lockfail("DYNDNS") } if ( $config{DEBUG} >= 1 ) { logfile("DynDNS - update IP addresses") } if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWALLOWDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWALLOWDYNIN -m set --match-set chain_ALLOWDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWALLOWDYNOUT -m set --match-set chain_ALLOWDYN dst -j $accept" ); } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F ALLOWDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A ALLOWDYNIN -m set --match-set chain_ALLOWDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A ALLOWDYNOUT -m set --match-set chain_ALLOWDYN dst -j $accept" ); } } if ( $config{IPV6} ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWALLOWDYNOUT" ); } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F ALLOWDYNOUT" ); } } sysopen( my $TEMPDYN, "/var/lib/csf/csf.tempdyn", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $TEMPDYN, LOCK_EX ); seek( $TEMPDYN, 0, 0 ); truncate( $TEMPDYN, 0 ); foreach my $line (@dyndns) { my $adport; my ( $fqdn, undef ) = split( /\s/, $line, 2 ); if ( $fqdn =~ /^(.*([sd])=)(.*)$/ ) { $adport = $1; $fqdn = $3 } my @results = getips($fqdn); if (@results) { foreach my $ip (@results) { if ($adport) { $ip = $adport . $ip } if ( $config{SAFECHAINUPDATE} ) { linefilter( $ip, "allow", "NEWALLOWDYN" ); } else { linefilter( $ip, "allow", "ALLOWDYN" ); } print $TEMPDYN "$ip\n"; } } else { logfile("DynDNS: Lookup for [$fqdn] failed"); } } close($TEMPDYN); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $ethdevout -j NEWALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWALLOWDYNIN ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWALLOWDYNOUT ALLOWDYNOUT" ); } if ( $config{IPV6} ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $ethdevout -j NEWALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X ALLOWDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWALLOWDYNIN ALLOWDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWALLOWDYNOUT ALLOWDYNOUT" ); } } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "dyndns", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub globaldyndns { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "globaldyndns", $timer ) } $0 = "lfd - resolving global dyndns IP addresses"; my $lockstr = "GLOBAL_DYNDNS_INTERVAL"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - resolving global dyndns IP addresses (waiting for list lock)"; listlock("lock"); $0 = "lfd - resolving global dyndns IP addresses"; open( my $IN, "<", "/var/lib/csf/csf.gdyndns" ); flock( $IN, LOCK_SH ); my @dyndns = <$IN>; close($IN); chomp @dyndns; if ( csflock() ) { lockfail("GLOBAL_DYNDNS_INTERVAL") } logfile("Global DynDNS - update IP addresses"); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -N NEWGDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGDYNIN -m set --match-set chain_GDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A NEWGDYNOUT -m set --match-set chain_GDYN dst -j $accept" ); } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GDYNIN -m set --match-set chain_GDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A GDYNOUT -m set --match-set chain_GDYN dst -j $accept" ); } } if ( $config{IPV6} ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -N NEWGDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGDYNIN -m set --match-set chain_6_GDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A NEWGDYNOUT -m set --match-set chain_6_GDYN dst -j $accept" ); } } else { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDYNOUT" ); if ( $config{LF_IPSET} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GDYNIN -m set --match-set chain_6_GDYN src -j $accept" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A GDYNOUT -m set --match-set chain_6_GDYN dst -j $accept" ); } } } sysopen( my $TEMPDYN, "/var/lib/csf/csf.tempgdyn", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $TEMPDYN, LOCK_EX ); seek( $TEMPDYN, 0, 0 ); truncate( $TEMPDYN, 0 ); foreach my $line (@dyndns) { if ( $line =~ /^\#/ ) { next } if ( $line eq "" ) { next } if ( $line =~ /^\n/ ) { next } if ( $line =~ /^\r/ ) { next } if ( $line =~ /^\s/ ) { next } my $ip; my $adport; my ( $fqdn, undef ) = split( /\s/, $line, 2 ); if ( $fqdn =~ /(.*:([sd])=)(.*)$/ ) { $adport = $1; $fqdn = $3 } my @results = getips($fqdn); if (@results) { foreach my $ip (@results) { if ($adport) { $ip = $adport . $ip } if ( $config{SAFECHAINUPDATE} ) { linefilter( $ip, "allow", "NEWGDYN" ); } else { linefilter( $ip, "allow", "GDYN" ); } print $TEMPDYN "$ip\n"; } } else { logfile("Global DynDNS: Lookup for [$fqdn] failed"); } } close($TEMPDYN); if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWGDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $ethdevout -j NEWGDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j GDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j GDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -F GDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -X GDYNOUT" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGDYNIN GDYNIN" ); iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -E NEWGDYNOUT GDYNOUT" ); } if ( $config{IPV6} ) { if ( $config{SAFECHAINUPDATE} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALINPUT $ethdevin -j NEWGDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -I LOCALOUTPUT $ethdevout -j NEWGDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALINPUT $ethdevin -j GDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D LOCALOUTPUT $ethdevout -j GDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -F GDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -X GDYNOUT" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGDYNIN GDYNIN" ); iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -E NEWGDYNOUT GDYNOUT" ); } } listlock("unlock"); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "globaldyndns", $timer ) } $0 = "lfd - child closing"; exit; } return; } my $listlock_fh; ## no critic (ControlStructures::ProhibitUnreachableCode) - Package-level variable, not unreachable code sub listlock { my $state = shift; if ( $state eq "lock" ) { sysopen( $listlock_fh, "/var/lib/csf/lock/list.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/list.lock"); flock( $listlock_fh, LOCK_EX ) or childcleanup("*Lock Error* [listlock] unable to lock"); print $listlock_fh time; } else { close($listlock_fh); } return; } sub dirwatch { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "dirwatch", $timer ) } $0 = "lfd - checking directories"; my $lockstr = "LF_DIRWATCH"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; my $alarm = int( $config{LF_DIRWATCH} / 10 ) + 10; my $start = time; my $tfail = 0; undef %nofiles; if ( !-z "/var/lib/csf/csf.tempfiles" ) { open( my $IN, "<", "/var/lib/csf/csf.tempfiles" ); flock( $IN, LOCK_SH ); my @data = <$IN>; close($IN); chomp @data; foreach my $line (@data) { my ( $itemttl, $item ) = split( /:/, $line ); if ( time - $itemttl < $config{LF_FLUSH} ) { $nofiles{$item} = 1; } } } undef @suspicious; my @dirs = ( '/tmp', '/dev/shm', '/usr/local/apache/proxy', '/etc/cron.d', '/etc/cron.daily', '/etc/cron.hourly', '/etc/cron.weekly' ); my $tmpino = ( stat("/tmp") )[1]; my $ino = ( lstat("/var/tmp") )[1]; if ( $ino ne $tmpino ) { push @dirs, '/var/tmp' } $ino = ( lstat("/usr/tmp") )[1]; if ( $ino ne $tmpino ) { push @dirs, '/usr/tmp' } eval { local $SIG{'ALRM'} = sub { die }; alarm($alarm); find( \&dirfiles, @dirs ); alarm(0); }; alarm(0); if ($@) { logfile("Directory Watching terminated after $alarm seconds"); $tfail = 1; } else { if (@suspicious) { $0 = "lfd - reporting directory watch results"; my @alert = slurp("/usr/local/csf/tpl/filealert.txt"); my $matches = 0; foreach my $file (@suspicious) { if ( $nofiles{$file} ) { next } unless ( -e $file ) { next } $nofiles{$file} = 1; my ( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ) = stat($file); if ( -l $file ) { ( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ) = lstat($file) } if ( $file !~ /\/core\./ ) { if ( $uid eq "0" ) { next } } my $tuid = getpwuid($uid); my $tgid = getgrgid($gid); if ( $file !~ /\/core\./ ) { if ( ( $uid eq "postgres" ) and ( $gid eq "postgres" ) ) { next } if ( $skipuser{$uid} ) { next } } $matches++; if ( $matches > 10 ) { logfile("Too many hits for *LF_DIRWATCH* - Directory Watching disabled"); sysopen( my $DWDISABLE, "/var/lib/csf/csf.dwdisable", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $DWDISABLE, LOCK_EX ); print $DWDISABLE "disabled\n"; close($DWDISABLE); my @newalert = @alert; my @message; foreach my $line (@newalert) { $line =~ s/\[file\]//ig; $line =~ s/\[reason\]//ig; $line =~ s/\[owner\]//ig; $line =~ s/\[action\]/Too many hits for \*LF_DIRWATCH\* - Directory Watching disabled/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); exit; } my $owner = "$tuid:$tgid ($uid:$gid)"; my $line = "*Suspicious File* $file [$owner] - $sfile{$file}{reason}"; my $action = "No action taken"; if ( $config{LF_DIRWATCH_DISABLE} ) { if ( -l $file ) { unlink($file); $action = "Symlink removed"; $line .= " - symlink removed"; delete $nofiles{$file}; } elsif ( -f $file ) { system( $config{TAR}, "-rf", "/var/lib/csf/suspicious.tar", $file ); unlink($file); $line .= " - removed"; $action = "Moved into /var/lib/csf/suspicious.tar"; delete $nofiles{$file}; } } logfile($line); $0 = "lfd - (child) suspicious file alert for $file"; my @newalert = @alert; my @message; foreach my $line (@newalert) { $line =~ s/\[file\]/$file/ig; $line =~ s/\[reason\]/$sfile{$file}{reason}/ig; $line =~ s/\[owner\]/$owner/ig; $line =~ s/\[action\]/$action/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( !$config{LF_DIRWATCH_DISABLE} ) { sysopen( my $TEMPFILES, "/var/lib/csf/csf.tempfiles", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPFILES, LOCK_EX ); print $TEMPFILES time . ":$file\n"; close($TEMPFILES); } } } } if ($tfail) { $config{LF_DIRWATCH} = $config{LF_DIRWATCH} * 3; sysopen( my $TEMPCONF, "/var/lib/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPCONF, LOCK_EX ); print $TEMPCONF "LF_DIRWATCH = \"$config{LF_DIRWATCH}\"\n"; close($TEMPCONF); logfile("LF_DIRWATCH taking $alarm seconds, temporarily throttled to run every $config{LF_DIRWATCH} seconds"); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "dirwatch", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub dirfiles { if ( $skipfile{$File::Find::name} ) { return } if ( $nofiles{$File::Find::name} ) { return } if ( ( -d $File::Find::name ) and ( $_ =~ /^(\.|\.\.)$/ ) ) { return } my $skip = 0; foreach my $match (@matchfile) { if ( $File::Find::name =~ /$match/ ) { $skip = 1; last; } } if ($skip) { return } if ( -l $File::Find::name ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Suspicious symlink (->" . readlink($File::Find::name) . ")"; return; } elsif ( ( -d $File::Find::name ) and ( $_ =~ /^(\.|\s)/ ) ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Suspicious directory"; return; } elsif ( -f $File::Find::name ) { if ( $File::Find::name =~ /^\/etc\/cron/ ) { if ( $_ =~ /^core\./ ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Core dump found - possible root exploit attack"; return; } else { return } } if ( $File::Find::name =~ /[\;\|\`\\]/ ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Suspicious file name"; return; } if ( $File::Find::name =~ /^\-/ ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Suspicious file name"; return; } if ( $_ =~ /\.(pl|cgi|ph.*|py|sh|bash)$/ ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Script, file extension"; return; } if ( $_ =~ /^(udp\.pl|r0nin|dc\.pl|bind|bindz|inetd|z|httpd|sshd|ssh|cron|crond|su)$/ ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Known exploit"; return; } open( my $FILETYPE, "<", $File::Find::name ); flock( $FILETYPE, LOCK_SH ); read( $FILETYPE, my $filedata, 1024 ); close($FILETYPE); if ( $filedata =~ m[^\177ELF] ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Linux Binary"; return; } if ( $filedata =~ m[^\#\!] ) { push @suspicious, $File::Find::name; $sfile{$File::Find::name}{reason} = "Script, starts with \#\!"; return; } } return; } sub dirwatchfile { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "dirwatchfile", $timer ) } my $lockstr = "LF_DIRWATCH_FILE"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - checking files and directories"; undef %nofiles; if ( -e "/var/lib/csf/csf.tempwatch" ) { open( my $IN, "<", "/var/lib/csf/csf.tempwatch" ); flock( $IN, LOCK_SH ); my @data = <$IN>; close($IN); chomp @data; foreach my $line (@data) { my ( $file, $md5sum ) = split( /:/, $line ); if ( $dirwatchfile{$file} ) { $dirwatchfile{$file} = $md5sum } } } my @alert = slurp("/usr/local/csf/tpl/watchalert.txt"); foreach my $file ( keys %dirwatchfile ) { unless ( -e $file ) { logfile("Directory *File Watching* [$file] does not exist"); next; } my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); @data = syscommand( __LINE__, $config{LS}, "--full-time", "-lARt", $file ); alarm(0); }; alarm(0); if ($@) { logfile( "Directory File Watching terminated after 10 seconds for $file: " . $@ ); exit; } chomp @data; my $md5current = Digest::MD5->new; my $output; foreach my $line (@data) { $md5current->add($line); $output .= $line . "\n"; } my $md5sum = $md5current->b64digest; if ( $dirwatchfile{$file} ne "1" ) { if ( $md5sum ne $dirwatchfile{$file} ) { $0 = "lfd - (child) suspicious file alert for $file"; logfile("Directory *File Watching* has detected a change in $file"); $dirwatchfile{$file} = $md5sum; my @newalert = @alert; my @message; foreach my $line (@newalert) { $line =~ s/\[file\]/$file/ig; $line =~ s/\[output\]/$output/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } } else { $dirwatchfile{$file} = $md5sum; } } sysopen( my $TEMPWATCH, "/var/lib/csf/csf.tempwatch", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot write out file: $!" ); flock( $TEMPWATCH, LOCK_EX ); seek( $TEMPWATCH, 0, 0 ); truncate( $TEMPWATCH, 0 ); foreach my $file ( keys %dirwatchfile ) { print $TEMPWATCH "$file:$dirwatchfile{$file}\n"; } close($TEMPWATCH); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "dirwatchfile", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub integrity { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "integrity", $timer ) } my $lockstr = "LF_INTEGRITY"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - checking system integrity"; my $integrity = '/usr/bin/* /usr/sbin/* /bin/* /sbin/* /usr/local/bin/* /usr/local/sbin/* /etc/init.d/* /etc/xinetd.d/* /etc/rc.local'; my $alarm = int( $config{LF_INTEGRITY} / 10 ) + 10; my $start = time; my $tfail = 0; my $action; if ( -z "/var/lib/csf/csf.tempint" ) { $action = "start" } unless ( -e "/var/lib/csf/csf.tempint" ) { $action = "start" } if ( $action eq "start" ) { eval { local $SIG{'ALRM'} = sub { die }; alarm($alarm); syscommand( __LINE__, "$config{MD5SUM} $integrity > /var/lib/csf/csf.tempint" ); alarm(0); }; alarm(0); if ($@) { logfile("System Integrity start terminated after $alarm seconds"); $tfail = 1; } } else { my @data; eval { local $SIG{'ALRM'} = sub { die }; alarm($alarm); @data = syscommand( __LINE__, "$config{MD5SUM} --check /var/lib/csf/csf.tempint" ); alarm(0); }; alarm(0); if ($@) { logfile("System Integrity check terminated after $alarm seconds"); $tfail = 1; } else { chomp @data; my $report; my $files; foreach my $line (@data) { my ( $file, $text ) = split( /:/, $line ); if ( $text =~ /FAILED/ ) { $report .= "$line\n"; $files .= " $file"; } } if ($report) { logfile("*System Integrity* has detected modified file(s):$files"); $0 = "lfd - (child) system integrity alert"; my @alert = slurp("/usr/local/csf/tpl/integrityalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\r//; $line =~ s/\[text\]/$report/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); unlink "/var/lib/csf/csf.tempint"; eval { local $SIG{'ALRM'} = sub { die }; alarm($alarm); syscommand( __LINE__, "$config{MD5SUM} $integrity > /var/lib/csf/csf.tempint" ); alarm(0); }; alarm(0); if ($@) { logfile("System Integrity start terminated after $alarm seconds"); $tfail = 1; } } } } if ($tfail) { $config{LF_INTEGRITY} = $config{LF_INTEGRITY} * 1.5; sysopen( my $TEMPCONF, "/var/lib/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPCONF, LOCK_EX ); print $TEMPCONF "LF_INTEGRITY = \"$config{LF_INTEGRITY}\"\n"; close($TEMPCONF); logfile("LF_INTEGRITY taking $alarm seconds, temporarily throttled to run every $config{LF_INTEGRITY} seconds"); } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "integrity", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub logscanner { my $hour = shift; if ( length $hour == 1 ) { $hour = "0$hour:00" } else { $hour = "$hour:00" } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "logscanner", $timer ) } my $lockstr = "LOGSCANNER"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - log scanner"; unless ( -z "/var/lib/csf/csf.logtemp" and $config{LOGSCANNER_EMPTY} == 0 ) { my $text; my %loglines; my $total = 0; my $max = 0; sysopen( my $LOGTEMP, "/var/lib/csf/csf.logtemp", O_RDWR | O_CREAT ); flock( $LOGTEMP, LOCK_EX ); my @data = <$LOGTEMP>; seek( $LOGTEMP, 0, 0 ); truncate( $LOGTEMP, 0 ); if ( -e "/var/lib/csf/csf.logmax" ) { unlink "/var/lib/csf/csf.logmax"; $max = 1; } close($LOGTEMP); chomp @data; if ( $config{LOGSCANNER_STYLE} == "1" ) { foreach my $line (@data) { my ( $logfile, $logline ) = split( /\|/, $line ); $loglines{$logfile} .= "$logline\n"; $total++; } foreach my $logfile ( keys %loglines ) { $text .= "$logfile:\n$loglines{$logfile}\n"; } } else { foreach my $line (@data) { my ( $logfile, $logline ) = split( /\|/, $line ); $text .= "$logline\n"; $total++; } } if ($max) { $text .= "\n...Report truncated as it exceeded $config{LOGSCANNER_LINES} of log lines...\n" } if ( $text eq "" ) { $text = "...No log lines to report..." } my @alert = slurp("/usr/local/csf/tpl/logalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\r//; $line =~ s/\[text\]/$text/ig; $line =~ s/\[lines\]/$total/ig; $line =~ s/\[hour\]/$hour/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("LOGSCANNER report sent") } } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "logscanner", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub exploit { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "exploit", $timer ) } my $lockstr = "LF_EXPLOIT"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; $0 = "lfd - checking system exploit"; my %exploit_tests; foreach my $test ( split( /\,/, $config{LF_EXPLOIT_IGNORE} ) ) { $exploit_tests{$test} = 1 } my $report = ""; unless ( $exploit_tests{SUPERUSER} ) { while ( my ( $name, undef, $uid ) = getpwent() ) { if ( ( $uid == 0 ) and ( $name ne "root" ) and ( $suignore{$name} != 1 ) ) { $report .= "Possible root compromise: User account $name is a superuser (UID 0)\n"; logfile("*System Exploit* has detected a possible root compromise ($name = UID 0)"); } } endpwent(); } if ($report) { $0 = "lfd - (child) system exploit alert"; sysopen( my $TEMPEXPLOIT, "/var/lib/csf/csf.tempexploit", O_WRONLY | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot open out file: $!" ); flock( $TEMPEXPLOIT, LOCK_EX ); print $TEMPEXPLOIT time; close($TEMPEXPLOIT); my @alert = slurp("/usr/local/csf/tpl/exploitalert.txt"); my @message; foreach my $line (@alert) { $line =~ s/\[text\]/$report/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); unlink "/var/lib/csf/csf.tempexp"; } close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "exploit", $timer ) } $0 = "lfd - child closing"; exit; } return; } sub getethdev { my $ethdev = ConfigServer::GetEthDev->new(); my %g_ifaces = $ethdev->ifaces; my %g_ipv4 = $ethdev->ipv4; my %g_ipv6 = $ethdev->ipv6; foreach my $key ( keys %g_ifaces ) { $ifaces{$key} = 1; } foreach my $key ( keys %g_ipv4 ) { $ips{$key} = 1; } if ( $config{IPV6} ) { foreach my $key ( keys %g_ipv6 ) { eval { $ipscidr6->add($key); }; } } ( $config{ETH_DEVICE}, undef ) = split( /:/, $config{ETH_DEVICE}, 2 ); if ( !length $config{ETH_DEVICE} ) { $ethdevin = "! -i lo"; $ethdevout = "! -o lo"; } else { $ethdevin = "-i $config{ETH_DEVICE}"; $ethdevout = "-o $config{ETH_DEVICE}"; } if ( $config{ETH6_DEVICE} eq "" ) { $eth6devin = $ethdevin; $eth6devout = $ethdevout; } else { $eth6devin = "-i $config{ETH6_DEVICE}"; $eth6devout = "-o $config{ETH6_DEVICE}"; } return; } sub hex2ip { my $bin = pack "C*" => map { hex($_) } $_[0] =~ /../g; my @l = unpack "L*", $bin; if ( @l == 4 ) { return join ':', map { sprintf "%x:%x", $_ >> 16, $_ & 0xffff } @l; } elsif ( @l == 1 ) { return join '.', map { $_ >> 24, ( $_ >> 16 ) & 0xff, ( $_ >> 8 ) & 0xff, $_ & 0xff } @l; } } sub ipv4in6 { my ($in) = @_; my @ipv6 = split( ":", $in ); my $v6part1 = hex( $ipv6[6] ); my $v6part2 = hex( $ipv6[7] ); my $ip41 = scalar( $v6part1 >> 8 ); my $ip42 = scalar( $v6part1 & 0xff ); my $ip43 = scalar( $v6part2 >> 8 ); my $ip44 = scalar( $v6part2 & 0xff ); my $out = $ip41 . "." . $ip42 . "." . $ip43 . "." . $ip44; return $out; } sub cleanup { $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $SIG{HUP} = 'IGNORE'; my $line = shift; my $message = shift; if ( ( $message eq "" ) and $line ) { $message = "Main Process: $line"; $line = ""; } $0 = "lfd - stopping"; if ($message) { if ( $line ne "" ) { $message .= ", at line $line" } logfile("$message"); } logfile("daemon stopped"); if ( $pidfile_fh and fileno($pidfile_fh) ) { close($pidfile_fh); unlink $pidfile; } kill( 9, -$$ ); exit 0; ## no critic (Cpanel::NoExitsFromSubroutines) - Program cleanup termination } sub childcleanup { $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; $SIG{HUP} = 'IGNORE'; my $line = shift; my $message = shift; if ( ( $message eq "" ) and $line ne "" ) { $message = "Child $childproc: $line"; $line = ""; } $0 = "child - aborting"; if ($message) { if ( $line ne "" ) { $message .= ", at line $line" } logfile("$message"); } exit; ## no critic (Cpanel::NoExitsFromSubroutines) - Child process termination } sub get_csf_error_message { my $error_file = "/etc/csf/csf.error"; # File may be deleted by csf between invocations - treat as non-error if missing/empty my $error_content = slurpee( $error_file, 'fatal' => 0, 'wantarray' => 0, 'warn' => 0 ); if ( length $error_content ) { chomp $error_content; unlink($error_file); # Note - this is intentional even if behavior change. return "*Error* csf reported an error: $error_content. *lfd stopped*"; } return; } sub ignoreip { my $ip = shift; my $skip = shift; if ( $ip eq "" ) { return 0 } if ( $ips{$ip} or $ipscidr->find($ip) or $ipscidr6->find($ip) ) { return 1 } if ( $ignoreips{$ip} ) { return 1 } if ( $gignoreips{$ip} ) { return 1 } if ( $config{CC_IGNORE} ) { my ( $cc, $asn ) = iplookup( $ip, 1 ); ( $asn, undef ) = split( /\s+/, $asn ); if ( length $cc and $config{CC_IGNORE} =~ /$cc/i ) { return 1 } if ( length $asn and $config{CC_IGNORE} =~ /$asn/i ) { return 1 } } if ( $relayip{$ip} and !$skip ) { return 1 } if (@cidrs) { if ( $cidr->find($ip) ) { return 1 } if ( $cidr6->find($ip) ) { return 1 } } if (@gcidrs) { if ( $gcidr->find($ip) ) { return 1 } if ( $gcidr6->find($ip) ) { return 1 } } if ( @rdns and !$skip ) { my $matchdomain; my $matchip; my $dnsip; my $dnsrip; my $dnshost; my $cachehit; open( my $DNS, "<", "/var/lib/csf/csf.dnscache" ); flock( $DNS, LOCK_SH ); while ( my $line = <$DNS> ) { chomp $line; ( $dnsip, $dnsrip, $dnshost ) = split( /\|/, $line ); $dnsip //= ""; if ( $ip eq $dnsip ) { $cachehit = 1; last; } } close($DNS); if ($cachehit) { $matchip = $dnsrip; $matchdomain = $dnshost; if ( $config{DEBUG} >= 2 ) { logfile("debug: (ignoreip) [cached] [$ip]:[$matchip] [$matchdomain]") } } else { eval { local $SIG{'ALRM'} = sub { die }; alarm(8); $matchip = inet_aton($ip); $matchdomain = gethostbyaddr( $matchip, AF_INET ); if ( $matchdomain ne "" ) { $matchip = gethostbyname($matchdomain); $matchip = inet_ntoa($matchip); } alarm(0); }; alarm(0); unless ( checkip( \$matchip ) ) { $matchip = "" } sysopen( my $DNS, "/var/lib/csf/csf.dnscache", O_WRONLY | O_APPEND | O_CREAT ); flock( $DNS, LOCK_EX ); print $DNS "$ip|$matchip|$matchdomain\n"; close($DNS); if ( $config{DEBUG} >= 2 ) { logfile("debug: (ignoreip) [not cached] [$ip]:[$matchip] [$matchdomain]") } } if ( $ip eq $matchip ) { foreach my $host (@rdns) { if ( ( $host =~ /\./ ) and ( $matchdomain =~ /$host$/ ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: (ignoreip) [$ip]:[$matchip] [$host]:[$matchdomain]") } return 1; } if ( $matchdomain eq $host ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: (ignoreip) [$ip]:[$matchip] [$host]:[$matchdomain]") } return 1; } } } } return 0; } sub linefilter { my $line = shift; my $ad = shift; my $chain = shift; my $delete = shift; my $pktin = "$accept"; my $pktout = "$accept"; my $inadd = "-I"; my $verbose = ""; my $localin = "ALLOWIN"; my $localout = "ALLOWOUT"; if ( $ad eq "deny" ) { $inadd = "-A"; $pktin = $config{DROP}; $pktout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $pktin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $pktout = "LOGDROPOUT" } $localin = "DENYIN"; $localout = "DENYOUT"; } my $chainin = $chain . "IN"; my $chainout = $chain . "OUT"; $line =~ s/[\n\r]//g; $line = lc $line; if ( $line =~ /^\#/ ) { return } if ( $line =~ /^Include/ ) { return } if ( $line eq "" ) { return } my $checkip = checkip( \$line ); my $iptables = $config{IPTABLES}; my $ipv4 = 1; my $ipv6 = 0; my $linein = $ethdevin; my $lineout = $ethdevout; if ( $checkip == 6 ) { if ( $config{IPV6} ) { $iptables = $config{IP6TABLES}; $linein = $eth6devin; $lineout = $eth6devout; $ipv4 = 0; $ipv6 = 1; } else { return } } if ($checkip) { if ($chain) { if ( $config{LF_IPSET} ) { if ($ipv4) { ipsetadd( "chain_$chainin", $line ) } else { ipsetadd( "chain_6_${chainin}", $line ) } } else { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -A $chainin $linein -s $line -j $pktin" ); if ( ( $ad eq "deny" and !$config{LF_BLOCKINONLY} ) or ( $ad ne "deny" ) ) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -A $chainout $lineout -d $line -j $pktout" ) } } } else { if ($delete) { if ( $config{LF_IPSET} ) { if ($ipv4) { ipsetdel( "chain_$localin", $line ) } else { ipsetdel( "chain_6_${localin}", $line ) } } else { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -D $localin $linein -s $line -j $pktin" ); if ( ( $ad eq "deny" and !$config{LF_BLOCKINONLY} ) or ( $ad ne "deny" ) ) { syscommand( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -D $localout $lineout -d $line -j $pktout" ) } } if ( ( $ad eq "deny" ) and ( $ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM} ) ) { domessenger( $line, "D" ) } if ( ( $ad eq "deny" ) and ( $ipv6 and $config{MESSENGER6} and $config{MESSENGER_PERM} ) ) { domessenger( $line, "D" ) } } else { if ( $config{LF_IPSET} ) { if ($ipv4) { ipsetadd( "chain_$localin", $line ) } else { ipsetadd( "chain_6_${localin}", $line ) } } else { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose $inadd $localin $linein -s $line -j $pktin" ); if ( ( $ad eq "deny" and !$config{LF_BLOCKINONLY} ) or ( $ad ne "deny" ) ) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose $inadd $localout $lineout -d $line -j $pktout" ) } } if ( ( $ad eq "deny" ) and ( $ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM} ) ) { domessenger( $line, "A" ) } if ( ( $ad eq "deny" ) and ( $ipv6 and $config{MESSENGER6} and $config{MESSENGER_PERM} ) ) { domessenger( $line, "A" ) } } } } elsif ( $line =~ /[:|]/ ) { if ( $line !~ /\|/ ) { $line =~ s/\:/\|/g } my $sip; my $dip; my $sport; my $dport; my $protocol = "-p tcp"; my $inout; my $from = 0; my $uid; my $gid; my $iptype; my @ll = split( /\|/, $line ); if ( $ll[0] eq "tcp" ) { $protocol = "-p tcp"; $from = 1; } elsif ( $ll[0] eq "udp" ) { $protocol = "-p udp"; $from = 1; } elsif ( $ll[0] eq "icmp" ) { $protocol = "-p icmp"; $from = 1; } for ( my $x = $from; $x < 2; $x++ ) { if ( ( $ll[$x] eq "out" ) ) { $inout = "out"; $from = $x + 1; last; } elsif ( ( $ll[$x] eq "in" ) ) { $inout = "in"; $from = $x + 1; last; } } for ( my $x = $from; $x < 3; $x++ ) { if ( ( $ll[$x] =~ /d=(.*)/ ) ) { $dport = "--dport $1"; $dport =~ s/_/:/g; if ( $protocol eq "-p icmp" ) { $dport = "--icmp-type $1" } if ( $dport =~ /,/ ) { $dport = "-m multiport " . $dport } $from = $x + 1; last; } elsif ( ( $ll[$x] =~ /s=(.*)/ ) ) { $sport = "--sport $1"; $sport =~ s/_/:/g; if ( $protocol eq "-p icmp" ) { $sport = "--icmp-type $1" } if ( $sport =~ /,/ ) { $sport = "-m multiport " . $sport } $from = $x + 1; last; } } for ( my $x = $from; $x < 4; $x++ ) { if ( ( $ll[$x] =~ /d=(.*)/ ) ) { my $ip = $1; my $status = checkip( \$ip ); if ($status) { $iptype = $status; $dip = "-d $1"; } last; } elsif ( ( $ll[$x] =~ /s=(.*)/ ) ) { my $ip = $1; my $status = checkip( \$ip ); if ($status) { $iptype = $status; $sip = "-s $1"; } last; } } for ( my $x = $from; $x < 5; $x++ ) { if ( ( $ll[$x] =~ /u=(.*)/ ) ) { $uid = "--uid-owner $1"; last; } elsif ( ( $ll[$x] =~ /g=(.*)/ ) ) { $gid = "--gid-owner $1"; last; } } if ( ( $sip or $dip ) and ( $dport or $sport ) ) { my $iptables = $config{IPTABLES}; if ( $iptype == 6 ) { if ( $config{IPV6} ) { $iptables = $config{IP6TABLES}; } else { return; } } if ( ( $inout eq "" ) or ( $inout eq "in" ) ) { my $bport = $dport; $bport =~ s/--dport //o; my $bip = $sip; $bip =~ s/-s //o; if ($chain) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -A $chainin $linein $protocol $dip $sip $dport $sport -j $pktin" ); } else { if ($delete) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -D $localin $linein $protocol $dip $sip $dport $sport -j $pktin" ); if ( $messengerports{$bport} and ( $ad eq "deny" ) and ( $ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM} ) ) { domessenger( $bip, "D", "$bport" ) } if ( $messengerports{$bport} and ( $ad eq "deny" ) and ( $ipv6 and $config{MESSENGER6} and $config{MESSENGER_PERM} ) ) { domessenger( $bip, "D", "$bport" ) } } else { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose $inadd $localin $linein $protocol $dip $sip $dport $sport -j $pktin" ); if ( $messengerports{$bport} and ( $ad eq "deny" ) and ( $ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM} ) ) { domessenger( $bip, "A", "$bport" ) } if ( $messengerports{$bport} and ( $ad eq "deny" ) and ( $ipv6 and $config{MESSENGER6} and $config{MESSENGER_PERM} ) ) { domessenger( $bip, "A", "$bport" ) } } } } if ( $inout eq "out" ) { if ($chain) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -A $chainout $lineout $protocol $dip $sip $dport $sport -j $pktout" ); } else { if ($delete) { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose -D $localout $lineout $protocol $dip $sip $dport $sport -j $pktout" ); } else { iptablescmd( __LINE__, "$iptables $config{IPTABLESWAIT} $verbose $inadd $localout $lineout $protocol $dip $sip $dport $sport -j $pktout" ); } } } } } return; } sub iptablescmd { my $line = shift; my $command = shift; $command =~ s/;`|//g; my $status = 0; my $iptableslock = 0; if ( $command =~ /^($config{IPTABLES}|$config{IP6TABLES})/ ) { $iptableslock = 1 } if ($faststart) { if ( $command =~ /^$config{IPTABLES}\s+(.*)$/ ) { my $fastcmd = $1; $fastcmd =~ s/-v//; $fastcmd =~ s/--wait//; if ( $fastcmd =~ /-t\s+nat/ ) { $fastcmd =~ s/-t\s+nat//; push @faststart4nat, $fastcmd; } else { push @faststart4, $fastcmd; } } if ( $command =~ /^$config{IP6TABLES}\s+(.*)$/ ) { my $fastcmd = $1; $fastcmd =~ s/-v//; $fastcmd =~ s/--wait//; if ( $fastcmd =~ /-t\s+nat/ ) { $fastcmd =~ s/-t\s+nat//; push @faststart6nat, $fastcmd; } else { push @faststart6, $fastcmd; } } return; } my $csf_error = get_csf_error_message(); if ($csf_error) { cleanup( __LINE__, $csf_error ); exit 1; ## no critic (Cpanel::NoExitsFromSubroutines) - Fatal csf error termination } if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { if ( $config{DEBUG} >= 2 ) { logfile("debug[$line]: Command:$command") } if ( csflock() ) { logfile("csf is currently restarting - command [$command] skipped on line $line"); unless ( $masterpid == $$ ) { exit } } if ($iptableslock) { iptableslock("lock") } my @output; if ( $iptableslock and $config{WAITLOCK} ) { eval { local $SIG{'ALRM'} = sub { die "alarm\n" }; alarm( $config{WAITLOCK_TIMEOUT} ); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $command ); @output = <$childout>; waitpid( $cmdpid, 0 ); alarm(0); }; alarm(0); if ( $@ eq "alarm\n" ) { cleanup( __LINE__, "*Error* timeout after iptables --wait for $config{WAITLOCK_TIMEOUT} seconds - WAITLOCK" ); } } else { my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $command ); @output = <$childout>; waitpid( $cmdpid, 0 ); } if ($iptableslock) { iptableslock("unlock") } chomp @output; if ( $output[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @output } if ( $output[0] =~ /^deny failed\:/ ) { logfile("*Error*: csf output: $output[0]") } if ( $output[0] =~ /^Error\:/ ) { logfile("*Error*: csf output: $output[0]") } if ( $output[0] =~ /xtables lock/ ) { logfile("*Error*: Unable to check csf due to xtables lock, enable WAITLOCK in csf.conf") } if ( $output[0] =~ /(^iptables: Unknown error 4294967295)|(xtables lock)/ and !$config{WAITLOCK} ) { my $cnt = 0; my $repeat = 6; while ( $cnt < $repeat ) { sleep 1; if ( $config{DEBUG} >= 1 ) { logfile( "debug[$line]: Retry (" . ( $cnt + 1 ) . ") [$command] due to [$output[0]]" ) } if ($iptableslock) { iptableslock("lock") } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $command ); my @output = <$childout>; waitpid( $cmdpid, 0 ); if ($iptableslock) { iptableslock("unlock") } chomp @output; if ( $output[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @output } $cnt++; if ( $output[0] =~ /(^iptables: Unknown error 4294967295)|(xtables lock)/ and $cnt == $repeat ) { logfile("*Error* processing command for line [$line] ($repeat times): [$output[0]]"); } unless ( $output[0] =~ /(^iptables: Unknown error 4294967295)|(xtables lock)/ ) { $cnt = $repeat } } } elsif ( $config{DEBUG} >= 1 and $output[0] =~ /^iptables|ip6tables|xtables|Bad|Another/ ) { logfile("*Error* processing command for line [$line]: [$output[0]]"); } } return; } sub syscommand { my ( $line, @cmd ) = @_; my $cmdline = join( " ", @cmd ); my $status = 0; my @output; my $csf_error = get_csf_error_message(); if ($csf_error) { cleanup( __LINE__, $csf_error ); exit 1; ## no critic (Cpanel::NoExitsFromSubroutines) - Fatal csf error termination } if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { if ( $config{DEBUG} >= 2 ) { logfile("debug[$line]: Command:$cmdline") } if ( csflock() ) { logfile("csf is currently restarting - command [$cmdline] skipped on line $line"); unless ( $masterpid == $$ ) { exit } } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, @cmd ); @output = <$childout>; waitpid( $cmdpid, 0 ); if ( $output[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @output } if ( $output[0] =~ /^deny failed\:/ ) { logfile("*Error*: csf output: $output[0]") } if ( $output[0] =~ /^Error\:/ ) { logfile("*Error*: csf output: $output[0]") } } return @output; } my $iptableslock_fh; ## no critic (ControlStructures::ProhibitUnreachableCode) - Package-level variable, not unreachable code sub iptableslock { my $lock = shift; my $iptablesx = shift; if ( $lock eq "lock" ) { sysopen( $iptableslock_fh, "/var/lib/csf/lock/command.lock", O_RDWR | O_CREAT ); flock( $iptableslock_fh, LOCK_EX ); autoflush $iptableslock_fh 1; seek( $iptableslock_fh, 0, 0 ); truncate( $iptableslock_fh, 0 ); print $iptableslock_fh $$; } else { close($iptableslock_fh); } return; } sub timer { my $status = shift; my $check = shift; my $start = shift; if ( $status eq "start" ) { logfile("debug: TIMER start: $check"); return Time::HiRes::gettimeofday(); } else { if ( $start == 0 ) { return } my $diff = sprintf '%.6f', ( Time::HiRes::gettimeofday() - $start ); logfile("debug: TIMER stop: $check ($diff secs)"); } return; } sub csflock { my $ret = 0; sysopen( my $CSFLOCKFILE, "/var/lib/csf/csf.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open csf lock file"); flock( $CSFLOCKFILE, LOCK_SH | LOCK_NB ) or $ret = 1; close($CSFLOCKFILE); return $ret; } sub lockfail { my $section = shift; logfile("csf is currently restarting - section [$section] skipped"); exit; ## no critic (Cpanel::NoExitsFromSubroutines) - Lock failure termination } sub ipblock { ## no critic (Subroutines::ProhibitManyArgs) - Legacy function signature with 6 parameters for IP/port blocking configuration my $perm = shift; my $message = shift; my $ip = shift; my $port = shift; my $inout = shift; my $timeout = shift; my $cluster = shift; my $logs = shift; my $active = shift; unless ($active) { $active = "other" } my $return = 0; if ( $message =~ / exceeds / ) { $cluster = 1 } my @report; $report[0] = $ip; if ($port) { $report[1] = "$port" } else { $report[1] = "*" } if ($perm) { $report[2] = "1" } else { $report[2] = "0" } if ($inout) { $report[3] = "$inout" } else { $report[3] = "inout" } if ($timeout) { $report[4] = "$timeout" } else { $report[4] = "0" } if ($message) { $report[5] = "$message" } else { $report[5] = " " } if ($logs) { $report[6] = "$logs" } else { $report[6] = " " } if ($active) { $report[7] = "$active" } my $ipv = checkip( \$ip ); unless ($ipv) { return 1 } my ( undef, $iscidr ) = split( /\//, $ip ); if ( $iscidr and !$cluster and ( $active ne "CT_SUBNET_LIMIT" ) ) { if ( $config{DEBUG} >= 1 ) { logfile("debug: IP [$ip] not blocked, contains a CIDR (ignore already blocked message)") } return 1; } $0 = "lfd - (child) blocking $ip"; my $blocked = 0; if ( !$iscidr and ( $config{LF_PERMBLOCK} or $config{LF_NETBLOCK} ) ) { if ( denycheck($ip) ) { $return = 2; } else { my $ips; my $ipmatch; my $ptext; my $nips; my $ntext; my $nipmatch; my $ipblock; my @newdata; my %skipip; my %skipnip; my $block_interval = $config{LF_PERMBLOCK_INTERVAL}; if ( $config{LF_NETBLOCK_INTERVAL} > $config{LF_PERMBLOCK_INTERVAL} ) { $block_interval = $config{LF_NETBLOCK_INTERVAL} } if ( $ipv == 4 ) { if ( $config{LF_NETBLOCK_CLASS} eq "A" ) { if ( $ip =~ /^(\d+)/ ) { $ipblock = "$1\.0\.0\.0/8" } } elsif ( $config{LF_NETBLOCK_CLASS} eq "B" ) { if ( $ip =~ /^(\d+\.\d+)/ ) { $ipblock = "$1\.0\.0/16" } } elsif ( $config{LF_NETBLOCK_CLASS} eq "C" ) { if ( $ip =~ /^(\d+\.\d+\.\d+)/ ) { $ipblock = "$1\.0/24" } } } elsif ( $ipv == 6 and $config{LF_NETBLOCK_IPV6} ne "" ) { if ( $config{LF_NETBLOCK_IPV6} eq "/64" or $config{LF_NETBLOCK_IPV6} eq "/56" or $config{LF_NETBLOCK_IPV6} eq "/48" or $config{LF_NETBLOCK_IPV6} eq "/32" or $config{LF_NETBLOCK_IPV6} eq "24" ) { eval { my $netip = $ip . $config{LF_NETBLOCK_IPV6}; my $status = checkip( \$netip ); if ( $status == 6 ) { $ipblock = $netip } }; } } sysopen( my $TEMPIP, "/var/lib/csf/csf.tempip", O_RDWR | O_CREAT ); flock( $TEMPIP, LOCK_EX ); my @data = <$TEMPIP>; chomp @data; foreach my $line (@data) { my ( $oip, $operm, $otime, $omessage ) = split( /\|/, $line, 4 ); if ( time - $otime < $block_interval ) { push @newdata, $line; } if ( $config{LF_PERMBLOCK} and !$perm ) { if ( ( $ip eq $oip ) and ( $operm != 1 ) and ( time - $otime < $config{LF_PERMBLOCK_INTERVAL} ) ) { $ips++; $ptext .= localtime($otime) . " $omessage\n"; $skipip{$line} = 1; } if ( $operm and ( $ip eq $oip ) ) { $ipmatch = 1; } } if ( $config{LF_NETBLOCK} and ( $ipv == 4 ) and $ipblock ) { if ( time - $otime < $config{LF_NETBLOCK_INTERVAL} ) { my $block = ""; if ( $config{LF_NETBLOCK_CLASS} eq "A" ) { if ( $oip =~ /^(\d+)/ ) { $block = "$1\.0\.0\.0/8" } } elsif ( $config{LF_NETBLOCK_CLASS} eq "B" ) { if ( $oip =~ /^(\d+\.\d+)/ ) { $block = "$1\.0\.0/16" } } elsif ( $config{LF_NETBLOCK_CLASS} eq "C" ) { if ( $oip =~ /^(\d+\.\d+\.\d+)/ ) { $block = "$1\.0/24" } } if ( $block ne "" and ( $ipblock eq $block ) ) { unless ( $oip eq $ip ) { $nips++; $ntext .= localtime($otime) . " $omessage\n"; $skipnip{$line} = 1; } } # if ($block eq $oip) {$nipmatch = 1} } } if ( $config{LF_NETBLOCK} and ( $ipv == 6 ) and $config{LF_NETBLOCK_IPV6} ne "" and $ipblock ) { if ( time - $otime < $config{LF_NETBLOCK_INTERVAL} ) { my $block = ""; eval { my $netip = $oip . $config{LF_NETBLOCK_IPV6}; my $status = checkip( \$netip ); if ( $status == 6 ) { $block = $netip } }; if ( $block ne "" and ( $ipblock eq $block ) ) { unless ( $oip eq $ip ) { $nips++; $ntext .= localtime($otime) . " $omessage\n"; $skipnip{$line} = 1; } } # if ($block eq $oip) {$nipmatch = 1} } } } if ($ipmatch) { $ips = 0; undef %skipip; if ( $config{DEBUG} >= 1 ) { logfile("debug: $message - already PERM blocked") } } else { $ips++; $ptext .= localtime(time) . " $message\n"; } if ($nipmatch) { $nips = 0; undef %skipnip; if ( $config{DEBUG} >= 1 ) { logfile("debug: $message - already NET blocked") } } else { $nips++; $ntext .= localtime(time) . " $message\n"; } if ( $nips > $config{LF_NETBLOCK_COUNT} ) { my $status = 0; if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { my $tip = iplookup($ipblock); $message = "(NETBLOCK) $tip has had more than $config{LF_NETBLOCK_COUNT} blocks in the last $config{LF_NETBLOCK_INTERVAL} secs"; syscommand( __LINE__, "/usr/sbin/csf", "-d", $ipblock, "lfd: $message" ); logfile("$message - *Blocked in csf* [$active]"); if ( $config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster ) { lfdclient( 1, $message, $ipblock, "", "inout", "0" ) } if ( $config{BLOCK_REPORT} ) { block_report( $ipblock, "*", "1", "inout", "0", $message, "", "LF_NETBLOCK_COUNT" ) } if ( $config{ST_ENABLE} ) { stats_report( $ipblock, "*", "1", "inout", "0", $message, "", "LF_NETBLOCK_COUNT" ) } $blocked = 1; } if ( $config{LF_NETBLOCK_ALERT} ) { $0 = "lfd - (child) sending alert email for $ipblock"; my @alert = slurp("/usr/local/csf/tpl/netblock.txt"); my @message; my $tip = iplookup($ipblock); foreach my $line (@alert) { $line =~ s/\[block\]/$tip/ig; $line =~ s/\[count\]/$nips/ig; if ( checkip( \$ipblock ) == 4 ) { $line =~ s/\[class\]/$config{LF_NETBLOCK_CLASS}/ig; } else { $line =~ s/\[class\]/$config{LF_NETBLOCK_IPV6}/ig; } $line =~ s/\[ips\]/$ntext/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } $ip = $ipblock; $perm = 1; $blocked = 1; } elsif ( $ips > $config{LF_PERMBLOCK_COUNT} ) { my $status = 0; if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { my $tip = iplookup($ip); $message = "(PERMBLOCK) $tip has had more than $config{LF_PERMBLOCK_COUNT} temp blocks in the last $config{LF_PERMBLOCK_INTERVAL} secs"; syscommand( __LINE__, "/usr/sbin/csf", "-tr", $ip ); syscommand( __LINE__, "/usr/sbin/csf", "-d", $ip, "lfd: $message" ); logfile("$message - *Blocked in csf* [$active]"); if ( $config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster ) { lfdclient( 1, $message, $ip, "", "inout", "0" ) } if ( $config{BLOCK_REPORT} ) { block_report( $ip, "*", "1", "inout", "0", $message, "", "LF_PERMBLOCK_COUNT" ) } if ( $config{ST_ENABLE} ) { stats_report( $ip, "*", "1", "inout", "0", $message, "", "LF_PERMBLOCK_COUNT" ) } $blocked = 1; } if ( $config{LF_PERMBLOCK_ALERT} ) { $0 = "lfd - (child) sending alert email for $ip"; my @alert = slurp("/usr/local/csf/tpl/permblock.txt"); my $tip = iplookup($ip); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[count\]/$ips/ig; $line =~ s/\[blocks\]/$ptext/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); } $perm = 1; $blocked = 1; } seek( $TEMPIP, 0, 0 ); truncate( $TEMPIP, 0 ); foreach my $line (@newdata) { if ( ( $ips > $config{LF_PERMBLOCK_COUNT} ) and ( $skipip{$line} ) ) { next } if ( ( $nips > $config{LF_NETBLOCK_COUNT} ) and ( $skipnip{$line} ) ) { next } print $TEMPIP "$line\n"; } print $TEMPIP "$ip|$perm|" . time . "|$message\n"; close($TEMPIP); } } if ( !$blocked and !$return ) { if ($perm) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my $status = 0; if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { if ( denycheck( "tcp|in|d=$dport|s=$ip", undef, $perm ) ) { $return = 2; } else { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } syscommand( __LINE__, "/usr/sbin/csf", "-d", "$proto|in|d=$dport|s=$ip", "lfd: $message" ); logfile("$message - *Blocked in csf* port=$dport [$active]"); $blocked = 1; } } } if ($blocked) { $return = 0 } } else { my $status = 0; if ( $config{VPS} ) { $status = checkvps() } if ($status) { logfile($status); } else { if ( denycheck( $ip, undef, $perm ) ) { $blocked = 0; $return = 2; } else { $blocked = 1; syscommand( __LINE__, "/usr/sbin/csf", "-d", $ip, "lfd: $message" ); logfile("$message - *Blocked in csf* [$active]"); } } } if ($blocked) { if ( $config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster ) { lfdclient( 1, $message, $ip, $port, $inout, "0" ) } if ( $config{BLOCK_REPORT} ) { block_report(@report) } if ( $config{ST_ENABLE} ) { stats_report(@report) } } } else { if ( denycheck( $ip, $port, $perm ) ) { $return = 2; } else { my $dropin = $config{DROP}; my $dropout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $dropin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $dropout = "LOGDROPOUT" } if ( $timeout < 2 ) { $timeout = 3600 } my $iptype = checkip( \$ip ); if ( $inout =~ /in/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A DENYIN $eth6devin -p $proto --dport $dport -s $ip -j $dropin" ); if ( $messengerports{$dport} and $config{MESSENGER6} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "A", $dport ) } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A DENYIN $ethdevin -p $proto --dport $dport -s $ip -j $dropin" ); if ( $messengerports{$dport} and $config{MESSENGER} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "A", $dport ) } } } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A DENYIN $eth6devin -s $ip -j $dropin" ); if ( $config{MESSENGER6} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "A" ) } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A DENYIN $ethdevin -s $ip -j $dropin" ); if ( $config{MESSENGER} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "A" ) } } } } if ( $inout =~ /out/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A DENYOUT $eth6devout -p $proto --dport $dport -d $ip -j $dropout" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A DENYOUT $ethdevout -p $proto --dport $dport -d $ip -j $dropout" ); } } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -A DENYOUT $eth6devout -d $ip -j $dropout" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -A DENYOUT $ethdevout -d $ip -j $dropout" ); } } } sysopen( my $TEMPBAN, "/var/lib/csf/csf.tempban", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPBAN, LOCK_EX ); print $TEMPBAN time . "|$ip|$port|$inout|$timeout|lfd - $message\n"; close($TEMPBAN); if ($message) { logfile("$message - *Blocked in csf* for $timeout secs [$active]") } if ( $config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster ) { lfdclient( $perm, $message, $ip, $port, $inout, $timeout ) } if ( $config{BLOCK_REPORT} ) { block_report(@report) } if ( $config{ST_ENABLE} ) { stats_report(@report) } } } } return $return; } sub ipunblock { if ( !-z "/var/lib/csf/csf.tempban" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { $0 = "lfd - processing temporary bans"; my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "ipunblock", $timer ) } sysopen( my $TEMPBAN, "/var/lib/csf/csf.tempban", O_RDWR | O_CREAT ) or childcleanup( __LINE__, "Unable to open /var/lib/csf/csf.tempban: $!" ); unless ( flock( $TEMPBAN, LOCK_EX | LOCK_NB ) ) { if ( $config{DEBUG} >= 3 ) { logfile("debug: Unable to lock csf.tempban in ipunblock") } } else { my @data = <$TEMPBAN>; chomp @data; my $cnt = @data; my @newdata; foreach my $line (@data) { my $unblock = 0; my $logmess = ""; if ( $config{DENY_TEMP_IP_LIMIT} and ( $cnt > $config{DENY_TEMP_IP_LIMIT} ) ) { $unblock = 1; $logmess = "(too many temporary bans in list)"; } my ( $time, $ip, $port, $inout, $timeout, $message ) = split( /\|/, $line ); my $iptype = checkip( \$ip ); if ( ( ( ( time - $time ) >= $timeout ) and $ip ) or $unblock ) { my $dropin = $config{DROP}; my $dropout = $config{DROP_OUT}; if ( $config{DROP_IP_LOGGING} ) { $dropin = "LOGDROPIN" } if ( $config{DROP_OUT_LOGGING} ) { $dropout = "LOGDROPOUT" } if ( $inout =~ /in/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D DENYIN $eth6devin -p $proto --dport $dport -s $ip -j $dropin" ); if ( $messengerports{$dport} and $config{MESSENGER6} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "D", $dport ) } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D DENYIN $ethdevin -p $proto --dport $dport -s $ip -j $dropin" ); if ( $messengerports{$dport} and $config{MESSENGER} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "D", $dport ) } } logfile("Incoming IP $ip:$dport temporary block removed"); if ( $config{UNBLOCK_REPORT} ) { unblock_report( $ip, $dport ) } } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D DENYIN $eth6devin -s $ip -j $dropin" ); if ( $config{MESSENGER6} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "D" ) } } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D DENYIN $ethdevin -s $ip -j $dropin" ); if ( $config{MESSENGER} and $config{MESSENGER_TEMP} ) { domessenger( $ip, "D" ) } } logfile("Incoming IP $ip temporary block removed"); if ( $config{UNBLOCK_REPORT} ) { unblock_report($ip) } } } if ( $inout =~ /out/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D DENYOUT $eth6devout -p $proto --dport $dport -d $ip -j $dropout" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D DENYOUT $ethdevout -p $proto --dport $dport -d $ip -j $dropout" ); } logfile("Outgoing IP $ip:$dport temporary block removed"); if ( $config{UNBLOCK_REPORT} ) { unblock_report( $ip, $dport ) } } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D DENYOUT $eth6devout -d $ip -j $dropout" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D DENYOUT $ethdevout -d $ip -j $dropout" ); } logfile("Outgoing IP $ip temporary block removed"); } if ( $config{UNBLOCK_REPORT} ) { unblock_report($ip) } } if ( $config{CF_ENABLE} and $message =~ /\(CF_ENABLE\)/ ) { cloudflare( "remove", $ip, $config{CF_BLOCK} ) } } else { push @newdata, $line; } $cnt--; } eval { local $SIG{'ALRM'} = sub { die }; alarm(60); local $SIG{INT} = 'IGNORE'; local $SIG{TERM} = 'IGNORE'; local $SIG{HUP} = 'IGNORE'; seek( $TEMPBAN, 0, 0 ); truncate( $TEMPBAN, 0 ); foreach my $line (@newdata) { print $TEMPBAN "$line\n" } }; alarm(0); } close($TEMPBAN); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "ipunblock", $timer ) } $0 = "lfd - (child) closing"; exit; } } if ( !-z "/var/lib/csf/csf.tempallow" ) { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { $0 = "lfd - processing temporary allows"; my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "ipunblock", $timer ) } sysopen( my $TEMPALLOW, "/var/lib/csf/csf.tempallow", O_RDWR | O_CREAT ) or childcleanup( __LINE__, "Enable to open /var/lib/csf/csf.tempallow: $!" ); unless ( flock( $TEMPALLOW, LOCK_EX | LOCK_NB ) ) { if ( $config{DEBUG} >= 3 ) { logfile("debug: Unable to lock csf.tempallow in ipunblock") } } else { my @data = <$TEMPALLOW>; chomp @data; my $cnt = @data; my @newdata; foreach my $line (@data) { my ( $time, $ip, $port, $inout, $timeout, $message ) = split( /\|/, $line ); my $iptype = checkip( \$ip ); if ( ( ( ( time - $time ) >= $timeout ) and $ip ) ) { if ( $inout =~ /in/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D ALLOWIN $eth6devin -p $proto --dport $dport -s $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D ALLOWIN $ethdevin -p $proto --dport $dport -s $ip -j $accept" ); } logfile("Incoming IP $ip:$dport temporary allow removed"); } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D ALLOWIN $eth6devin -s $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D ALLOWIN $ethdevin -s $ip -j $accept" ); } logfile("Incoming IP $ip temporary allow removed"); } } if ( $inout =~ /out/ ) { if ($port) { foreach my $dport ( split( /\,/, $port ) ) { my ( $tport, $proto ) = split( /\;/, $dport ); $dport = $tport; if ( $proto eq "" ) { $proto = "tcp" } if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D ALLOWOUT $eth6devout -p $proto --dport $dport -d $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D ALLOWOUT $ethdevout -p $proto --dport $dport -d $ip -j $accept" ); } logfile("Outgoing IP $ip:$dport temporary allow removed"); } } else { if ( $iptype == 6 ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -D ALLOWOUT $eth6devout -d $ip -j $accept" ); } else { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -D ALLOWOUT $ethdevout -d $ip -j $accept" ); } logfile("Outgoing IP $ip temporary allow removed"); } } if ( $config{CF_ENABLE} and $message =~ /\(CF_ENABLE\)/ ) { cloudflare( "remove", $ip, "whitelist" ) } } else { push @newdata, $line; } $cnt--; } eval { local $SIG{'ALRM'} = sub { die }; alarm(60); local $SIG{INT} = 'IGNORE'; local $SIG{TERM} = 'IGNORE'; local $SIG{HUP} = 'IGNORE'; seek( $TEMPALLOW, 0, 0 ); truncate( $TEMPALLOW, 0 ); foreach my $line (@newdata) { print $TEMPALLOW "$line\n" } }; alarm(0); } close($TEMPALLOW); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "ipunblock", $timer ) } $0 = "lfd - (child) closing"; exit; } } return; } sub cloudflare { my $action = shift; my $ip = shift; my $mode = shift; my $domains = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "cloudflare", $timer ) } $0 = "lfd - (child) CloudFlare $action..."; if ( $action eq "remove" ) { ConfigServer::CloudFlare::action( "remove", $ip, $mode ); } elsif ( $action eq "deny" ) { ConfigServer::CloudFlare::action( "deny", $ip, $config{CF_BLOCK}, "", $domains, 1 ); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "cloudflare", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub block_report { my @report = @_; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "block_report", $timer ) } $0 = "lfd - (child) Block Report..."; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); if ( $config{DEBUG} >= 1 ) { logfile("debug: BLOCK_REPORT [$config{BLOCK_REPORT}] triggered") } system( $config{BLOCK_REPORT}, @report ); alarm(0); }; alarm(0); if ($@) { logfile("BLOCK_REPORT timed out after 10 seconds"); } else { if ( $config{DEBUG} >= 3 ) { logfile("debug: BLOCK_REPORT [$config{BLOCK_REPORT}] for ['$report[0]' '$report[1]' '$report[2]' '$report[3]' '$report[4]' '$report[5]']") } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "block_report", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub unblock_report { my $ip = shift; my $port = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "unblock_report", $timer ) } $0 = "lfd - (child) Block Report..."; eval { local $SIG{'ALRM'} = sub { die }; alarm(10); system( $config{UNBLOCK_REPORT}, $ip, $port ); alarm(0); }; alarm(0); if ($@) { logfile("UNBLOCK_REPORT timed out after 10 seconds"); } else { if ( $config{DEBUG} >= 3 ) { logfile("debug: UNBLOCK_REPORT [$config{UNBLOCK_REPORT}] for [$ip] [$port]") } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "unblock_report", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub stats_report { my @report = @_; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "stats_report", $timer ) } $0 = "lfd - (child) Stats Report..."; my $lockstr = "ST_ENABLE_report"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); unless ( flock( $THISLOCK, LOCK_EX | LOCK_NB ) ) { if ( $config{DEBUG} >= 1 ) { childcleanup("debug: *Lock Error* [$lockstr] still active - section skipped"); } else { childcleanup(); } } print $THISLOCK time; #[0-23] hour, [24-54] day, [55-57] month my $STATS; if ( -e "/var/lib/csf/stats/lfdstats" ) { sysopen( $STATS, "/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT ); } elsif ( -e "/var/lib/csf/stats/lfdmain" ) { sysopen( my $OLDSTATS, "/var/lib/csf/stats/lfdmain", O_RDWR | O_CREAT ); flock( $OLDSTATS, LOCK_EX ); my @stats = <$OLDSTATS>; chomp @stats; my @newstats; my $cnt = 0; foreach my $line (@stats) { if ( $cnt == 55 ) { push @newstats, "" } push @newstats, $line; $cnt++; } sysopen( my $STATS, "/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT ); flock( $STATS, LOCK_EX ); seek( $STATS, 0, 0 ); truncate( $STATS, 0 ); foreach my $line (@newstats) { print $STATS "$line\n"; } close($STATS); rename "/var/lib/csf/stats/lfdmain", "/var/lib/csf/stats/lfdmain." . time; close($OLDSTATS); sysopen( $STATS, "/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT ); } else { sysopen( $STATS, "/var/lib/csf/stats/lfdstats", O_RDWR | O_CREAT ); } flock( $STATS, LOCK_EX ); my @stats = <$STATS>; chomp @stats; my $perm = $report[2]; my $trigger = $report[7]; my $time = time; my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime($time); my @line; my %triggers; my $permdate; my $permcount; my $tempdate; my $tempcount; my $loop; @line = split( /\,/, $stats[$hour] ); $permdate = $line[0]; $permcount = $line[1]; $tempdate = $line[2]; $tempcount = $line[3]; for ( $loop = 4; $loop < @line; $loop += 2 ) { if ( $time - $line[$loop] > ( 24 * 60 * 60 ) ) { next } my ( $triggerstat, $triggercount ) = split( /\:/, $line[ $loop + 1 ] ); $triggers{$triggerstat}{date} = $line[$loop]; $triggers{$triggerstat}{count} = $triggercount; } $triggers{$trigger}{date} = $time; $triggers{$trigger}{count}++; if ( $time - $permdate > ( 24 * 60 * 60 ) ) { $permdate = 0; $permcount = 0 } if ( $time - $tempdate > ( 24 * 60 * 60 ) ) { $tempdate = 0; $tempcount = 0 } if ($perm) { $permdate = $time; $permcount++ } else { $tempdate = $time; $tempcount++ } $stats[$hour] = "$permdate,$permcount,$tempdate,$tempcount"; foreach my $key ( keys %triggers ) { $stats[$hour] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}" } @line = split( /\,/, $stats[ $mday + 24 ] ); undef %triggers; $permdate = $line[0]; $permcount = $line[1]; $tempdate = $line[2]; $tempcount = $line[3]; for ( $loop = 4; $loop < @line; $loop += 2 ) { if ( $time - $line[$loop] > ( 29 * 24 * 60 * 60 ) ) { next } my ( $triggerstat, $triggercount ) = split( /\:/, $line[ $loop + 1 ] ); $triggers{$triggerstat}{date} = $line[$loop]; $triggers{$triggerstat}{count} = $triggercount; } $triggers{$trigger}{date} = $time; $triggers{$trigger}{count}++; if ( $time - $permdate > ( 29 * 24 * 60 * 60 ) ) { $permdate = 0; $permcount = 0 } if ( $time - $tempdate > ( 29 * 24 * 60 * 60 ) ) { $tempdate = 0; $tempcount = 0 } if ($perm) { $permdate = $time; $permcount++ } else { $tempdate = $time; $tempcount++ } $stats[ $mday + 24 ] = "$permdate,$permcount,$tempdate,$tempcount"; foreach my $key ( keys %triggers ) { $stats[ $mday + 24 ] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}" } @line = split( /\,/, $stats[ $mon + 56 ] ); undef %triggers; $permdate = $line[0]; $permcount = $line[1]; $tempdate = $line[2]; $tempcount = $line[3]; for ( $loop = 4; $loop < @line; $loop += 2 ) { if ( $time - $line[$loop] > ( 364 * 24 * 60 * 60 ) ) { next } my ( $triggerstat, $triggercount ) = split( /\:/, $line[ $loop + 1 ] ); $triggers{$triggerstat}{date} = $line[$loop]; $triggers{$triggerstat}{count} = $triggercount; } $triggers{$trigger}{date} = $time; $triggers{$trigger}{count}++; if ( $time - $permdate > ( 364 * 24 * 60 * 60 ) ) { $permdate = 0; $permcount = 0 } if ( $time - $tempdate > ( 364 * 24 * 60 * 60 ) ) { $tempdate = 0; $tempcount = 0 } if ($perm) { $permdate = $time; $permcount++ } else { $tempdate = $time; $tempcount++ } $stats[ $mon + 56 ] = "$permdate,$permcount,$tempdate,$tempcount"; foreach my $key ( keys %triggers ) { $stats[ $mon + 56 ] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}" } if ( $config{CC_LOOKUPS} ) { my $cc = "**"; my %ccs; @line = split( /\,/, $stats[69] ); if ( $report[5] =~ /\s\((\w\w)\// ) { $cc = $1 } for ( my $x = 0; $x < @line; $x += 2 ) { $ccs{ $line[$x] } = $line[ $x + 1 ] } $ccs{$cc}++; $stats[69] = ""; foreach my $key ( keys %ccs ) { $stats[69] .= "$key,$ccs{$key}," } } seek( $STATS, 0, 0 ); truncate( $STATS, 0 ); foreach my $line (@stats) { print $STATS "$line\n"; } close($STATS); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "stats_report", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub checkvps { if ( -e "/proc/user_beancounters" and !( -e "/proc/vz/version" ) ) { open( my $INVPS, "<", "/proc/user_beancounters" ); flock( $INVPS, LOCK_SH ); my @data = <$INVPS>; close($INVPS); chomp @data; foreach my $line (@data) { if ( $line =~ /^\s*numiptent\s+(\d*)\s+(\d*)\s+(\d*)\s+(\d*)/ ) { if ( $1 > $4 - 10 ) { return "The VPS iptables rule limit (numiptent) is too low ($1/$4) - *IP not blocked*" } } } } return 0; } sub messengerrecaptcha { my $timer = time; my ( undef, undef, $uid, $gid, undef, undef, undef, $homedir ) = getpwnam( $config{MESSENGER_USER} ); if ( -z "$homedir/unblock.txt" ) { return } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "messengerrecaptcha", $timer ) } $0 = "lfd - reCAPTCHA csf..."; $childproc = "Messenger (Recaptcha)"; $SIG{INT} = \&childcleanup; $SIG{TERM} = \&childcleanup; $SIG{HUP} = \&childcleanup; $SIG{__DIE__} = sub { return if $^S; childcleanup(@_); }; if ( -f "$homedir/unblock.txt" ) { my @alert = slurp("/usr/local/csf/tpl/recaptcha.txt"); sysopen( my $UNBLOCK, "$homedir/unblock.txt", O_RDWR | O_CREAT ); flock( $UNBLOCK, LOCK_EX ); while ( my $line = <$UNBLOCK> ) { chomp $line; my ( $unblockip, $host, $hostip ) = split( /;/, $line ); if ( checkip( \$unblockip ) ) { logfile("reCAPTCHA: Unblocking client [$unblockip] on domain [$host ($hostip)]"); syscommand( __LINE__, "/usr/sbin/csf", "-dr", $unblockip ); syscommand( __LINE__, "/usr/sbin/csf", "-tr", $unblockip ); if ( $config{RECAPTCHA_ALERT} ) { my $tip = iplookup($unblockip); my @message; foreach my $line (@alert) { $line =~ s/\[ip\]/$tip/ig; $line =~ s/\[host\]/$host ($hostip)/ig; push @message, $line; } ConfigServer::Sendmail::relay( "", "", @message ); if ( $config{DEBUG} >= 1 ) { logfile("debug: recaptcha email sent for $unblockip") } } } } seek( $UNBLOCK, 0, 0 ); truncate( $UNBLOCK, 0 ); close($UNBLOCK); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "messengerrecaptcha", $timer ) } exit; } return; } sub messengerstop { my $version = shift; if ( $version == 1 ) { return; } elsif ( $version == 2 ) { if ( -e "/etc/apache2/conf.d/csf.messenger.conf" ) { unlink("/etc/apache2/conf.d/csf.messenger.conf"); system("/scripts/restartsrv_httpd"); logfile("*MESSENGERV2* Removed /etc/apache2/conf.d/csf.messenger.conf"); } } elsif ( $version == 3 ) { if ( -d $config{MESSENGERV3LOCATION} ) { if ( -e $config{MESSENGERV3LOCATION} . "/csf.messenger.conf" ) { unlink( $config{MESSENGERV3LOCATION} . "/csf.messenger.conf" ); system( $config{MESSENGERV3RESTART} ); logfile( "*MESSENGERV3* Removed " . $config{MESSENGERV3LOCATION} . "/csf.messenger.conf" ); } } elsif ( -f $config{MESSENGERV3LOCATION} ) { my @conf = slurp( $config{MESSENGERV3LOCATION} ); if ( grep { $_ =~ m[^Include /var/lib/csf/csf.conf]i } @conf ) { sysopen( my $FILE, $config{MESSENGERV3LOCATION}, O_WRONLY | O_CREAT | O_TRUNC ); flock( $FILE, LOCK_EX ); foreach my $line (@conf) { $line =~ s/$cleanreg//g; if ( $line =~ m[^Include /var/lib/csf/csf.conf]i ) { next } print $FILE $line . "\n"; } close($FILE); system( $config{MESSENGERV3RESTART} ); logfile("*MESSENGERV3* Removed from $config{MESSENGERV3LOCATION}"); } } } return; } sub messenger { my $port = shift; my $user = shift; my $type = shift; my $timer = time; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; $messengerips{$type} = $childpid; unless ($childpid) { if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "messenger", $timer ) } $0 = "lfd - messenger csf..."; $SIG{INT} = \&childcleanup; $SIG{TERM} = \&childcleanup; $SIG{HUP} = \&childcleanup; $SIG{__DIE__} = sub { return if $^S; childcleanup(@_); }; $childproc = "Messenger ($type)"; my ( $status, $reason ) = $messenger1->start( $port, $user, $type ); if ($status) { logfile("*MESSENGER*: Error starting $type service: $reason"); sysopen( my $TEMPCONF, "/var/lib/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPCONF, LOCK_EX ); print $TEMPCONF "MESSENGER_${type}_IN = \"\"\n"; close($TEMPCONF); logfile("*MESSENGER*: $type service temporarily *DISABLED*"); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "messenger", $timer ) } exit; } return; } sub messengerv2 { my $timer = time; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "messengerv2", $timer ) } $0 = "lfd - messenger csf..."; my ( $status, $reason ) = $messenger2->start(); if ($status) { logfile("*MESSENGERV2* Error: $reason"); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "messengerv2", $timer ) } exit; } return; } sub messengerv3 { my $timer = time; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "messengerv3", $timer ) } $0 = "lfd - messenger csf..."; my ( $status, $reason ) = $messenger3->start(); if ($status) { logfile("*MESSENGERV3* Error: $reason"); } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "messengerv3", $timer ) } exit; } return; } sub domessenger { my $ip = shift; my $delete = shift; my $ports = shift; if ( $ports eq "" ) { $ports = "$config{MESSENGER_HTTPS_IN},$config{MESSENGER_HTML_IN},$config{MESSENGER_TEXT_IN}" } my $iptype = checkip( \$ip ); if ( $config{CC_MESSENGER_ALLOW} or $config{CC_MESSENGER_DENY} ) { my ( $cc, $asn ) = iplookup( $ip, 1 ); ( $asn, undef ) = split( /\s+/, $asn ); if ( $config{CC_MESSENGER_ALLOW} ) { my $allow = 0; if ( $cc ne "" and $config{CC_MESSENGER_ALLOW} =~ /$cc/i ) { $allow = 1 } if ( $asn ne "" and $config{CC_MESSENGER_ALLOW} =~ /$asn/i ) { $allow = 1 } unless ($allow) { return 1 } } if ( $config{CC_MESSENGER_DENY} ) { if ( $cc ne "" and $config{CC_MESSENGER_DENY} =~ /$cc/i ) { return 1 } if ( $asn ne "" and $config{CC_MESSENGER_DENY} =~ /$asn/i ) { return 1 } } } my $del = "-A"; if ( $delete eq "D" ) { $del = "-D" } my %textin; my %htmlin; my %httpsin; foreach my $port ( split( /\,/, $config{MESSENGER_HTTPS_IN} ) ) { $httpsin{$port} = 1 } foreach my $port ( split( /\,/, $config{MESSENGER_HTML_IN} ) ) { $htmlin{$port} = 1 } foreach my $port ( split( /\,/, $config{MESSENGER_TEXT_IN} ) ) { $textin{$port} = 1 } my $textports; my $htmlports; my $httpsports; foreach my $port ( split( /\,/, $ports ) ) { if ( $httpsin{$port} ) { if ( $httpsports eq "" ) { $httpsports = "$port" } else { $httpsports .= ",$port" } } if ( $htmlin{$port} ) { if ( $htmlports eq "" ) { $htmlports = "$port" } else { $htmlports .= ",$port" } } if ( $textin{$port} ) { if ( $textports eq "" ) { $textports = "$port" } else { $textports .= ",$port" } } } if ( $config{LF_IPSET} ) { if ( $delete eq "D" ) { if ( $iptype == 4 ) { ipsetdel( "MESSENGER", $ip ); } if ( $iptype == 6 and $config{MESSENGER6} ) { ipsetdel( "MESSENGER_6", $ip ); } } else { if ( $iptype == 4 ) { ipsetadd( "MESSENGER", $ip ); } if ( $iptype == 6 and $config{MESSENGER6} ) { ipsetadd( "MESSENGER_6", $ip ); } } } else { if ( $httpsports ne "" ) { if ( $iptype == 4 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $httpsports -j REDIRECT --to-ports $config{MESSENGER_HTTPS}" ); } if ( $iptype == 6 and $config{MESSENGER6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $httpsports -j REDIRECT --to-ports $config{MESSENGER_HTTPS}" ); } } if ( $htmlports ne "" ) { if ( $iptype == 4 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $htmlports -j REDIRECT --to-ports $config{MESSENGER_HTML}" ); } if ( $iptype == 6 and $config{MESSENGER6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $htmlports -j REDIRECT --to-ports $config{MESSENGER_HTML}" ); } } if ( $textports ne "" ) { if ( $iptype == 4 ) { iptablescmd( __LINE__, "$config{IPTABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $textports -j REDIRECT --to-ports $config{MESSENGER_TEXT}" ); } if ( $iptype == 6 and $config{MESSENGER6} ) { iptablescmd( __LINE__, "$config{IP6TABLES} $config{IPTABLESWAIT} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $textports -j REDIRECT --to-ports $config{MESSENGER_TEXT}" ); } } } return; } sub lfdserver { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; $clusterip = $childpid; unless ($childpid) { $childproc = "Cluster"; my $cipher = Crypt::CBC->new( -key => $config{CLUSTER_KEY}, -cipher => 'Blowfish' ); my %cmembers; foreach my $cip ( split( /\,/, $config{CLUSTER_RECVFROM} ) ) { $cmembers{$cip} = 1 } if ( $config{CLUSTER_MASTER} ) { $cmembers{ $config{CLUSTER_MASTER} } = 1 } $0 = "lfd Cluster Server"; $SIG{INT} = \&childcleanup; $SIG{TERM} = \&childcleanup; $SIG{HUP} = \&childcleanup; $SIG{__DIE__} = sub { return if $^S; childcleanup(@_); }; my $server = IO::Socket::INET->new( LocalPort => $config{CLUSTER_PORT}, Type => SOCK_STREAM, ReuseAddr => 1, Listen => $config{CLUSTER_CHILDREN}, ) or childcleanup( __LINE__, "*Error* cannot open server on port $config{CLUSTER_PORT}: $!" ); while (1) { while ( my ( $client, $c_addr ) = $server->accept() ) { $SIG{CHLD} = 'IGNORE'; my $pid = fork; if ( $pid == 0 ) { eval { local $SIG{'ALRM'} = sub { die }; alarm(10); close $server; my ( $cport, $iaddr ) = sockaddr_in($c_addr); my $peeraddress = inet_ntoa($iaddr); my $pip = iplookup($peeraddress); if ( $cmembers{$peeraddress} ) { binmode $client; $| = 1; my $line; my $grep; my $tip; while ( $line !~ /END\n$/ ) { my $char; $client->read( $char, 1 ) or last; $line .= $char; } $line =~ s/END\n$//; my $decrypted = $cipher->decrypt($line); if ( $config{DEBUG} >= 2 ) { logfile("debug: Cluster member $peeraddress said [$decrypted]") } my ( $command, $ip, $perm, $ports, $inout, $timeout, $message ) = split( /\s/, $decrypted, 7 ); $timeout //= 0; # TODO: what should the default timeout be if we don't see it? Does this happen? if ( !length $perm ) { $perm = 1 } if ( !defined $ports or $ports eq "*" ) { $ports = "" } if ( checkip( \$ip ) ) { $tip = iplookup($ip) } if ( !length $message ) { $message = "Not provided - $tip" } if ( $command eq "D" ) { ipblock( $perm, "Cluster member $pip said, DENY $ip, Reason:[$message]", $ip, $ports, $inout, $timeout, 1, "", "LF_CLUSTER" ); } elsif ( $command eq "TD" ) { ipblock( $perm, "Cluster member $pip said, TEMPDENY $ip, Reason:[$message]", $ip, $ports, $inout, $timeout, 1, "", "LF_CLUSTER" ); } elsif ( $command eq "A" and checkip( \$ip ) ) { logfile("Cluster member $pip said, ALLOW $ip, [$message]"); syscommand( __LINE__, "/usr/sbin/csf", "-a", $ip, "Cluster member $pip said, ALLOW $ip, Reason:[$message]" ); } elsif ( $command eq "TA" ) { logfile("Cluster member $pip said, TEMPALLOW $ip, Reason:[$message]"); syscommand( __LINE__, "/usr/sbin/csf", "-ta", $ip, $timeout, "-p", $ports, "-d", $inout, "Cluster member $pip said, TEMPALLOW $ip, Reason:[$message]" ); } elsif ( $command eq "AR" and checkip( \$ip ) ) { logfile("Cluster member $pip said, REMOVE ALLOW $tip"); syscommand( __LINE__, "/usr/sbin/csf", "-ar", $ip ); syscommand( __LINE__, "/usr/sbin/csf", "-tr", $ip ); } elsif ( $command eq "R" and checkip( \$ip ) ) { logfile("Cluster member $pip said, REMOVE DENY $tip"); syscommand( __LINE__, "/usr/sbin/csf", "-dr", $ip ); syscommand( __LINE__, "/usr/sbin/csf", "-tr", $ip ); } elsif ( $command eq "I" and checkip( \$ip ) ) { my $ignorematches; my @ignore = slurp("/etc/csf/csf.ignore"); foreach my $line (@ignore) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @ignore, @incfile; } } foreach my $line (@ignore) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $ipd, $commentd ) = split( /\s/, $line, 2 ); checkip( \$ipd ); if ( $ipd eq $ip ) { $ignorematches = 1; last; } } sysopen( my $IGNORE, "/etc/csf/csf.ignore", O_RDWR | O_CREAT ); flock( $IGNORE, LOCK_EX ); my $text = join( "", <$IGNORE> ); @ignore = split( /$slurpreg/, $text ); chomp @ignore; unless ($ignorematches) { print $IGNORE "$ip # Cluster member $pip said, IGNORE $ip, Reason:[$message] - " . localtime(time) . "\n"; logfile("Cluster member $pip said, IGNORE $ip, [$message]"); logfile("Cluster - lfd restarting..."); open( my $LFDOUT, ">", "/var/lib/csf/lfd.restart" ); close($LFDOUT); } else { logfile("Cluster member $pip said, IGNORE $ip, [$message], however it is already being ignored"); } close($IGNORE); } elsif ( $command eq "IR" and checkip( \$ip ) ) { my $hit; sysopen( my $IGNORE, "/etc/csf/csf.ignore", O_RDWR | O_CREAT ); flock( $IGNORE, LOCK_EX ); my $text = join( "", <$IGNORE> ); my @ignore = split( /$slurpreg/, $text ); chomp @ignore; seek( $IGNORE, 0, 0 ); truncate( $IGNORE, 0 ); foreach my $line (@ignore) { $line =~ s/$cleanreg//g; my ( $ipd, $commentd ) = split( /\s/, $line, 2 ); checkip( \$ipd ); if ( $ipd eq $ip ) { $hit = 1; next; } else { print $IGNORE $line . "\n"; } } close($IGNORE); if ($hit) { logfile("Cluster member $pip said, REMOVE IGNORE $tip"); logfile("Cluster - lfd restarting..."); open( my $LFDOUT, ">", "/var/lib/csf/lfd.restart" ); close($LFDOUT); } else { logfile("Cluster member $pip said, REMOVE IGNORE $tip, however it is not in csf.ignore"); } } elsif ( $command eq "PING" ) { logfile("Cluster member $pip said PING!"); } elsif ( $command eq "G" ) { logfile("Cluster member $pip said GREP $tip"); my @output = syscommand( __LINE__, "/usr/sbin/csf", "-g", $ip ); $grep = join( "", @output ); } elsif ( $command eq "C" ) { my ( undef, $name, $value ) = split( /\s/, $decrypted, 3 ); if ( $config{CLUSTER_MASTER} and ( $config{CLUSTER_MASTER} eq $peeraddress ) ) { $value =~ s/["=]//g; $value =~ s/(^\s*)|(\s*$)//g; if ( $config{CLUSTER_CONFIG} ) { logfile("Cluster member $pip said set [$name = \"$value\"]"); updateconfig( $name, $value ); } else { logfile("Cluster member $pip said set [$name = \"$value\"], however CLUSTER_CONFIG disabled"); } } else { logfile("*Cluster* member $pip said set [$name = \"$value\"], however it is not the CLUSTER_MASTER"); } } elsif ( $command eq "FILE" ) { my ( undef, $filename ) = split( /\s/, $decrypted ); my ( $file, $filedir ) = File::Basename::fileparse($filename); if ( $config{CLUSTER_MASTER} and ( $config{CLUSTER_MASTER} eq $peeraddress ) ) { if ( $config{CLUSTER_CONFIG} ) { my ( undef, $content ) = split( /\n/, $decrypted, 2 ); logfile("Cluster member $pip said store file [$file]"); open( my $FH, ">", "/etc/csf/$file" ); flock( $FH, LOCK_EX ); binmode($FH); print $FH $content; close($FH); } else { logfile("*Cluster* member $pip said store file [$file], however CLUSTER_CONFIG disabled"); } } else { logfile("*Cluster* member $pip said store file [$file], however it is not the CLUSTER_MASTER"); } } elsif ( $command eq "RESTART" ) { if ( $config{CLUSTER_MASTER} and ( $config{CLUSTER_MASTER} eq $peeraddress ) ) { if ( $config{CLUSTER_CONFIG} ) { logfile("Cluster member $pip said restart csf and lfd"); logfile("Cluster - csf restarting..."); syscommand( __LINE__, "/usr/sbin/csf", "-sf" ); logfile("Cluster - lfd restarting..."); open( my $LFDOUT, ">", "/var/lib/csf/lfd.restart" ); close($LFDOUT); } else { logfile("*Cluster* member $pip said restart csf and lfd, however CLUSTER_CONFIG disabled"); } } else { logfile("*Cluster* member $pip said restart csf and lfd, however it is not the CLUSTER_MASTER"); } } else { logfile("*WARNING* Cluster member $pip talking nonsense"); } if ( $command eq "PING" ) { print $client "PONG!\n"; } elsif ( $command eq "G" ) { print $client "$grep\n"; } else { print $client "Received\n"; } } else { logfile("*WARNING* $pip attempted to connect to the Cluster but not a member!"); } alarm(0); }; alarm(0); shutdown( $client, 2 ); $client->close(); exit; } $client->close(); } } } return; } sub lfdclient { ## no critic (Subroutines::ProhibitManyArgs) - Legacy function signature with 6 parameters for cluster communication my $perm = shift; my $message = shift; my $ip = shift; my $port = shift; my $inout = shift; my $timeout = shift; if ( $port eq "" ) { $port = "*" } $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "lfdclient", $timer ) } $0 = "lfd - Cluster client"; my $cipher = Crypt::CBC->new( -key => $config{CLUSTER_KEY}, -cipher => 'Blowfish' ); my $text; if ($perm) { $text = "D $ip $perm $port $inout $timeout $message"; } else { $text = "TD $ip $perm $port $inout $timeout $message"; } my $encrypted = $cipher->encrypt($text) . "END\n"; foreach my $cip ( split( /\,/, $config{CLUSTER_SENDTO} ) ) { if ( $ips{$cip} or $ipscidr->find($cip) or $ipscidr6->find($cip) or ( $cip eq $config{CLUSTER_NAT} ) ) { next } my $localaddr = "0.0.0.0"; if ( $config{CLUSTER_LOCALADDR} ) { $localaddr = $config{CLUSTER_LOCALADDR} } my $tip = iplookup($cip); my $sock; eval { $sock = IO::Socket::INET->new( PeerAddr => $cip, PeerPort => $config{CLUSTER_PORT}, LocalAddr => $localaddr, Timeout => '10' ); }; unless ( defined $sock ) { logfile("Cluster: Failed to connect to $tip"); } else { my $status = send( $sock, $encrypted, 0 ); unless ($status) { logfile("Cluster: Failed for $tip - $status"); } else { if ($perm) { logfile("Cluster: DENY $ip sent to $tip"); } else { logfile("Cluster: TEMPDENY $ip sent to $tip"); } } shutdown( $sock, 2 ); } } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "lfdclient", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub _get_whm_server_status_key { my $whm_server_status_key_file = '/var/cpanel/whm_server_status_key'; return unless -e $whm_server_status_key_file; my ($key) = slurp($whm_server_status_key_file); unless ( defined $key ) { logfile('Unable to read Apache server-status key file'); return; } if ( defined $key ) { $key =~ s/\s+//g; } return unless length $key; return $key; } sub updateconfig { my $chname = shift; my $chvalue = shift; sysopen( my $OUT, "/etc/csf/csf.conf", O_RDWR | O_CREAT ); flock( $OUT, LOCK_EX ); my @confdata = <$OUT>; chomp @confdata; seek( $OUT, 0, 0 ); truncate( $OUT, 0 ); for ( my $x = 0; $x < @confdata; $x++ ) { if ( ( $confdata[$x] !~ /^\#/ ) and ( $confdata[$x] =~ /=/ ) ) { my ( $name, $value ) = split( /=/, $confdata[$x], 2 ); $name //= ""; $value //= ""; $name =~ s/\s*//g; if ( $name eq $chname ) { print $OUT "$name = \"$chvalue\"\n"; } else { print $OUT "$confdata[$x]\n"; } } else { print $OUT "$confdata[$x]\n"; } } close($OUT); return; } sub stats { my $line = shift; my $type = shift; $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "stats", $timer ) } $0 = "lfd - (child) Statistics..."; eval { local $SIG{'ALRM'} = sub { die }; alarm(15); if ( $type eq "iptables" ) { my ( $in, $out, $src, $dst, $text ); if ( $line =~ /IN=(\S+)/ ) { $in = $1 } if ( $line =~ /OUT=(\S+)/ ) { $out = $1 } if ( $line =~ /SRC=(\S+)/ ) { $src = $1 } if ( $line =~ /DST=(\S+)/ ) { $dst = $1 } if ( $config{ST_LOOKUP} ) { if ( $in and $src ) { $text = iplookup($src) } elsif ( $out and $dst ) { $text = iplookup($dst) } } sysopen( my $IPTABLES, "/var/lib/csf/stats/iptables_log", O_WRONLY | O_APPEND | O_CREAT ); flock( $IPTABLES, LOCK_EX ); print $IPTABLES "$text|$line\n"; close($IPTABLES); if ( ( stat("/var/lib/csf/stats/iptables_log") )[7] > ( 2048 * $config{ST_IPTABLES} ) ) { my $lockstr = "ST_IPTABLES"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); unless ( flock( $THISLOCK, LOCK_EX | LOCK_NB ) ) { if ( $config{DEBUG} >= 1 ) { childcleanup("debug: *Lock Error* [$lockstr] still active - section skipped"); } else { childcleanup(); } } print $THISLOCK time; sysopen( my $IPTABLES, "/var/lib/csf/stats/iptables_log", O_RDWR | O_CREAT ); flock( $IPTABLES, LOCK_EX ); my @iptables = <$IPTABLES>; chomp @iptables; my @last = @iptables[ -$config{ST_IPTABLES} .. -1 ]; seek( $IPTABLES, 0, 0 ); truncate( $IPTABLES, 0 ); foreach my $line (@last) { print $IPTABLES "$line\n"; } close($IPTABLES); } if ( $config{DEBUG} >= 2 ) { logfile("debug: STATS added iptables [$src -> $dst] log line") } } alarm(0); }; alarm(0); if ($@) { logfile("STATS: 15 sec. timeout performing iptables_log") } if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "stats", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub systemstats { $SIG{CHLD} = 'IGNORE'; unless ( defined( $childpid = fork ) ) { cleanup( __LINE__, "*Error* cannot fork: $!" ); } $forks{$childpid} = 1; unless ($childpid) { my $timer = time; if ( $config{DEBUG} >= 3 ) { $timer = timer( "start", "systemstats", $timer ) } $0 = "lfd - (child) System Statistics..."; my $lockstr = "ST_SYSTEM"; sysopen( my $THISLOCK, "/var/lib/csf/lock/$lockstr.lock", O_RDWR | O_CREAT ) or childcleanup("*Error* Unable to open /var/lib/csf/lock/$lockstr.lock"); flock( $THISLOCK, LOCK_EX | LOCK_NB ) or childcleanup("*Lock Error* [$lockstr] still active - section skipped"); print $THISLOCK time; local $SIG{__DIE__} = undef; my $time = time; my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime($time); my $cputotal; my $cpuidle; my $cpuiowait; my $memtotal; my $memfree; my $memswaptotal; my $memswapfree; my $netin; my $netout; my $diskread; my $diskwrite; my $mailin; my $mailout; my $cputemp; my $mysqlin; my $mysqlout; my $mysqlq; my $mysqlsq; my $mysqlcn; my $mysqlth; my $apachecpu; my $apacheacc; my $apachebwork; my $apacheiwork; my $diskw; my $memcached; open( my $STAT, "<", "/proc/stat" ); flock( $STAT, LOCK_SH ); my $line = <$STAT>; close($STAT); chomp $line; my @cpu = split( /\s+/, $line ); shift @cpu; foreach (@cpu) { $cputotal += $_ } $cpuidle = $cpu[3]; $cpuiowait = $cpu[4]; open( my $MEMINFO, "<", "/proc/meminfo" ); flock( $MEMINFO, LOCK_SH ); my @memdata = <$MEMINFO>; close($MEMINFO); chomp @memdata; foreach my $line (@memdata) { if ( $line =~ /^MemTotal:\s+(\d+)\s+/ ) { $memtotal = $1 } if ( $line =~ /^MemFree:\s+(\d+)\s+/ ) { $memfree = $1 } if ( $line =~ /^SwapTotal:\s+(\d+)\s+/ ) { $memswaptotal = $1 } if ( $line =~ /^SwapFree:\s+(\d+)\s+/ ) { $memswapfree = $1 } if ( $line =~ /^Cached:\s+(\d+)\s+/ ) { $memcached = $1 } } open( my $LOADAVG, "<", "/proc/loadavg" ); flock( $LOADAVG, LOCK_SH ); my $loadavg = <$LOADAVG>; close($LOADAVG); chomp $loadavg; my @load = split( /\s+/, $loadavg ); opendir( my $DIR, "/sys/class/net" ); while ( my $dir = readdir($DIR) ) { if ( $dir eq "." or $dir eq ".." or $dir eq "lo" ) { next } open( my $IN, "<", "/sys/class/net/$dir/operstate" ); flock( $IN, LOCK_SH ); my $state = <$IN>; close($IN); chomp $state; if ( $state ne "down" ) { open( my $RX_BYTES, "<", "/sys/class/net/$dir/statistics/rx_bytes" ); flock( $RX_BYTES, LOCK_SH ); my $datain = <$RX_BYTES>; close($RX_BYTES); chomp $datain; $netin += $datain; open( my $TX_BYTES, "<", "/sys/class/net/$dir/statistics/tx_bytes" ); flock( $TX_BYTES, LOCK_SH ); my $dataout = <$TX_BYTES>; close($TX_BYTES); chomp $dataout; $netout += $dataout; } } closedir($DIR); if ( -e "/proc/diskstats" ) { open( my $IN, "<", "/proc/diskstats" ); flock( $IN, LOCK_SH ); my @diskdata = <$IN>; close($IN); chomp @diskdata; foreach my $line (@diskdata) { my @item = split( /\s+/, $line ); if ( $item[3] =~ /^[[:alpha:]]+$/ ) { $diskread += $item[4]; $diskwrite += $item[8]; } } if ( $diskread < 1 ) { foreach my $line (@diskdata) { my @item = split( /\s+/, $line ); if ( $item[3] =~ /^[[:alpha:]]+\d+$/ ) { $diskread += $item[4]; $diskwrite += $item[8]; } } } } my $dotemp = 0; if ( -e "/sys/devices/platform/coretemp.0/temp3_input" ) { $dotemp = 3 } if ( -e "/sys/devices/platform/coretemp.0/temp2_input" ) { $dotemp = 2 } if ( -e "/sys/devices/platform/coretemp.0/temp1_input" ) { $dotemp = 1 } if ($dotemp) { opendir( my $DIR, "/sys/devices/platform" ); while ( my $dir = readdir($DIR) ) { unless ( $dir =~ /^coretemp/ ) { next } open( my $IN, "<", "/sys/devices/platform/$dir/temp" . $dotemp . "_input" ); flock( $IN, LOCK_SH ); my $temp = <$IN>; close($IN); chomp $temp; if ( $temp > $cputemp ) { $cputemp = $temp } } closedir($DIR); $cputemp = sprintf( "%.2f", $cputemp / 1000 ); } sysopen( my $EMAIL, "/var/lib/csf/stats/email", O_RDWR | O_CREAT ); flock( $EMAIL, LOCK_EX ); my $stats = <$EMAIL>; chomp $stats; ( $mailout, $mailin ) = split( /\:/, $stats ); seek( $EMAIL, 0, 0 ); truncate( $EMAIL, 0 ); print $EMAIL "0:0"; close($EMAIL); if ( $config{ST_MYSQL} ) { eval('use DBI;'); ## no critic (BuiltinFunctions::ProhibitStringyEval) - Optional module load - database support not required on all systems if ($@) { sysopen( my $TEMPCONF, "/var/lib/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT ) or childcleanup( __LINE__, "*Error* Cannot append out file: $!" ); flock( $TEMPCONF, LOCK_EX ); print $TEMPCONF "ST_MYSQL = \"0\"\n"; close($TEMPCONF); logfile("STATS: DBI Perl Module missing - ST_MYSQL has been temporarily disabled. You should disable ST_MYSQL and restart lfd if you do not use this feature"); } else { eval { local $SIG{'ALRM'} = sub { die }; alarm(15); my $dbuser = $config{ST_MYSQL_USER}; my $dbpass = $config{ST_MYSQL_PASS}; my $dbhost = $config{ST_MYSQL_HOST}; if ( $dbpass eq "" and $dbuser eq "root" ) { open( my $DBS, "<", "/root/.my.cnf" ); flock( $DBS, LOCK_SH ); while (<$DBS>) { chomp; if (/^pass(word)?=(\S+)/) { $dbpass = $2; $dbpass =~ s/^\"|\"$//g; } if (/^host=(\S+)/) { $dbhost = $1; $dbhost =~ s/^\"|\"$//g; } } close($DBS); } my $status; my $dbh = DBI->connect( "DBI:mysql:hostname=" . $dbhost, $dbuser, $dbpass, { PrintError => 0 } ) or $status = $DBI::errstr; if ($status) { logfile("STATS: Unable to connect to MySQL: [$DBI::errstr] - You should disable ST_MYSQL and restart lfd if you do not use this feature"); } else { my $sth = $dbh->prepare('SHOW /*!50002 GLOBAL */ STATUS'); $sth->execute(); while ( my ( $key, $val ) = $sth->fetchrow_array() ) { if ( $key eq "Bytes_received" ) { $mysqlin = $val } if ( $key eq "Bytes_sent" ) { $mysqlout = $val } if ( $key eq "Queries" ) { $mysqlq = $val } if ( $key eq "Slow_queries" ) { $mysqlsq = $val } if ( $key eq "Connections" ) { $mysqlcn = $val } if ( $key eq "Threads_connected" ) { $mysqlth = $val } } } alarm(0); }; alarm(0); if ($@) { logfile("STATS: 15 sec. timeout performing ST_MYSQL") } } } if ( $config{ST_APACHE} ) { eval { local $SIG{'ALRM'} = sub { die }; alarm(15); my $url = $config{PT_APACHESTATUS} . "?auto"; my $key = _get_whm_server_status_key(); if ( length $key ) { $url .= "&key=$key"; } my $log_url = $url; $log_url =~ s/([?&])key=[^&]*/$1key=REDACTED/; my ( $status, $apache ) = $urlget->urlget($url); if ($status) { logfile("STATS: Unable to retrieve Apache Server Status [$log_url] - $apache"); } else { foreach my $line ( split( /\n/, $apache ) ) { my ( $item, $val ) = split( /:\s*/, $line ); $item //= ""; if ( $item eq "CPULoad" ) { $apachecpu = $val } if ( $item eq "Total Accesses" ) { $apacheacc = $val } if ( $item eq "BusyWorkers" ) { $apachebwork = $val } if ( $item eq "IdleWorkers" ) { $apacheiwork = $val } } } alarm(0); }; alarm(0); if ($@) { logfile("STATS: 15 sec. timeout performing ST_APACHE") } } if ( $config{ST_DISKW} ) { my $skip = 0; if ( -e "/var/lib/csf/csf.tempdisk" ) { open( my $ST_DISKW, "<", "/var/lib/csf/csf.tempdisk" ); flock( $ST_DISKW, LOCK_SH ); my $line = <$ST_DISKW>; chomp $line; close($ST_DISKW); my ( $time, $rate ) = split( /\:/, $line ); if ( $config{ST_DISKW_FREQ} < 1 ) { $config{ST_DISKW_FREQ} = 1 } if ( time - $time < ( 60 * $config{ST_DISKW_FREQ} ) ) { $skip = 1; $diskw = $rate; } } unless ($skip) { eval { local $SIG{'ALRM'} = sub { die }; alarm(15); my @dddata = syscommand( __LINE__, "$config{DD} $config{ST_DISKW_DD}" ); chomp @dddata; foreach my $line (@dddata) { if ( $line =~ / (\d+(\.\d*)?) MB\/s$/ ) { $diskw = $1; last; } if ( $line =~ / (\d+(\.\d*)?) GB\/s$/ ) { $diskw = $1 * 1024; last; } } alarm(0); }; alarm(0); if ($@) { $diskw = 0; logfile("STATS: 15 sec. timeout performing ST_DISKW"); } sysopen( my $ST_DISKW, "/var/lib/csf/csf.tempdisk", O_WRONLY | O_CREAT ); flock( $ST_DISKW, LOCK_EX ); print $ST_DISKW time . ":$diskw\n"; close($ST_DISKW); } } sysopen( my $SYSSTAT, "/var/lib/csf/stats/system", O_WRONLY | O_APPEND | O_CREAT ); flock( $SYSSTAT, LOCK_EX ); print $SYSSTAT "$time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load[0],$load[1],$load[2],$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork,$diskw,$memcached\n"; close($SYSSTAT); close($THISLOCK); if ( $config{DEBUG} >= 3 ) { $timer = timer( "stop", "systemstats", $timer ) } $0 = "lfd - (child) closing"; exit; } return; } sub allowip { my $ipmatch = shift; my @allow = slurp("/etc/csf/csf.allow"); foreach my $line (@allow) { if ( $line =~ /^Include\s*(.*)$/ ) { my @incfile = slurp($1); push @allow, @incfile; } } foreach my $line (@allow) { $line =~ s/$cleanreg//g; if ( $line eq "" ) { next } if ( $line =~ /^\s*\#|Include/ ) { next } my ( $ipd, $commentd ) = split( /\s/, $line, 2 ); $ipd //= ""; if ( $ipd eq $ipmatch ) { return 1; } elsif ( $ipd =~ /(\S+\/\d+)/ ) { my $cidrhit = $1; if ( checkip( \$cidrhit ) ) { my $dcidr = Net::CIDR::Lite->new; eval { $dcidr->add($cidrhit) }; if ($@) { logfile("Invalid CIDR in csf.allow: $cidrhit") } if ( $dcidr->find($ipmatch) ) { return 1; } } } } if ( $config{GLOBAL_ALLOW} and -e "/var/lib/csf/csf.gallow" ) { open( my $IN, "<", "/var/lib/csf/csf.gallow" ); flock( $IN, LOCK_SH ); my @allow = <$IN>; close($IN); chomp @allow; foreach my $line (@allow) { if ( $line eq "" ) { next } if ( $line =~ /^\s*\#/ ) { next } my ( $ipd, $commentd ) = split( /\s/, $line, 2 ); $ipd //= ""; if ( $ipd eq $ipmatch ) { return 2; } elsif ( $ipd =~ /(\S+\/\d+)/ ) { my $cidrhit = $1; if ( checkip( \$cidrhit ) ) { my $dcidr = Net::CIDR::Lite->new; eval { $dcidr->add($cidrhit) }; if ($@) { logfile("Invalid CIDR in csf.gallow: $cidrhit") } if ( $dcidr->find($ipmatch) ) { return 2; } } } } } return; } sub testregex { my $match = shift; eval { my $test =~ /$match/; }; if ($@) { return 0 } return 1; } sub faststart { my $text = shift; $faststart = 0; my $verbose = 1; if ( $text =~ /^Blocklist \[CXS_/ ) { $verbose = 0 } if (@faststart4) { if ( $config{DEBUG} >= 1 and $verbose ) { logfile("FASTSTART loading $text (IPv4)") } my $status; if ( $config{VPS} ) { $status = fastvps( scalar @faststart4 ) } if ($status) { logfile($status); } else { iptableslock("lock"); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, "$config{IPTABLES_RESTORE} $config{IPTABLESWAIT} -n" ); print $childin "*filter\n" . join( "\n", @faststart4 ) . "\nCOMMIT\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @results } if ( $results[0] =~ /^(iptables|ip6tables|xtables|Bad|Another)/ ) { my $cmd; if ( $results[1] =~ /^Error occurred at line: (\d+)$/ ) { $cmd = $faststart4[ $1 - 1 ] } logfile("*Error* FASTSTART: ($text IPv4) [$cmd] [$results[0]]"); } iptableslock( "unlock", 1 ); } } if (@faststart4nat) { if ( $config{DEBUG} >= 1 and $verbose ) { logfile("FASTSTART loading $text (IPv4 nat)") } my $status; if ( $config{VPS} ) { $status = fastvps( scalar @faststart4nat ) } if ($status) { logfile($status); } else { iptableslock("lock"); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, "$config{IPTABLES_RESTORE} $config{IPTABLESWAIT} -n" ); print $childin "*nat\n" . join( "\n", @faststart4nat ) . "\nCOMMIT\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @results } if ( $results[0] =~ /^(iptables|ip6tables|xtables|Bad|Another)/ ) { my $cmd; if ( $results[1] =~ /^Error occurred at line: (\d+)$/ ) { $cmd = $faststart4[ $1 - 1 ] } logfile("*Error* FASTSTART: ($text IPv4nat) [$cmd] [$results[0]]"); } iptableslock( "unlock", 1 ); } } if (@faststart6) { if ( $config{DEBUG} >= 1 and $verbose ) { logfile("FASTSTART loading $text (IPv6)") } my $status; if ( $config{VPS} ) { $status = fastvps( scalar @faststart6 ) } if ($status) { logfile($status); } else { iptableslock("lock"); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, "$config{IP6TABLES_RESTORE} $config{IPTABLESWAIT} -n" ); print $childin "*filter\n" . join( "\n", @faststart6 ) . "\nCOMMIT\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @results } if ( $results[0] =~ /^(iptables|ip6tables|xtables|Bad|Another)/ ) { my $cmd; if ( $results[1] =~ /^Error occurred at line: (\d+)$/ ) { $cmd = $faststart4[ $1 - 1 ] } logfile("*Error* FASTSTART: ($text IPv6) [$cmd] [$results[0]]"); } iptableslock( "unlock", 1 ); } } if (@faststart6nat) { if ( $config{DEBUG} >= 1 and $verbose ) { logfile("FASTSTART loading $text (IPv6 nat)") } my $status; if ( $config{VPS} ) { $status = fastvps( scalar @faststart6nat ) } if ($status) { logfile($status); } else { iptableslock("lock"); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, "$config{IP6TABLES_RESTORE} $config{IPTABLESWAIT} -n" ); print $childin "*nat\n" . join( "\n", @faststart6nat ) . "\nCOMMIT\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /# Warning: iptables-legacy tables present/ ) { shift @results } if ( $results[0] =~ /^(iptables|ip6tables|xtables|Bad|Another)/ ) { my $cmd; if ( $results[1] =~ /^Error occurred at line: (\d+)$/ ) { $cmd = $faststart6[ $1 - 1 ] } logfile("*Error* FASTSTART: ($text IPv6nat) [$cmd] [$results[0]]"); } iptableslock( "unlock", 1 ); } } if (@faststartipset) { if ( $config{DEBUG} >= 1 and $verbose ) { logfile("FASTSTART loading $text (IPSET)") } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "restore" ); print $childin join( "\n", @faststartipset ) . "\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("FASTSTART: (IPSET) Error:[$results[0]]"); } } undef @faststart4; undef @faststart4nat; undef @faststart6; undef @faststart6nat; undef @faststartipset; return; } sub fastvps { my $size = shift; if ( -e "/proc/user_beancounters" and !( -e "/proc/vz/version" ) ) { open( my $INVPS, "<", "/proc/user_beancounters" ); flock( $INVPS, LOCK_SH ); my @data = <$INVPS>; close($INVPS); chomp @data; foreach my $line (@data) { if ( $line =~ /^\s*numiptent\s+(\d*)\s+(\d*)\s+(\d*)\s+(\d*)/ ) { if ( $1 > $4 - ( $size + 10 ) ) { return "The VPS iptables rule limit (numiptent) is too low to add $size rules ($1/$4) - *IPs not added*" } } } } return 0; } sub ipsetcreate { my $set = shift; my $family = "inet"; if ( $set =~ /_6/ ) { $family = "inet6" } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "create", "-exist", $set, "hash:net", "family", $family, "hashsize", $config{LF_IPSET_HASHSIZE}, "maxelem", $config{LF_IPSET_MAXELEM} ); close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* IPSET: [$results[0]]"); } return; } sub ipsetrestore { my $set = shift; my $verbose = 1; if ( $set =~ /^new_(6_)?CXS_/ ) { $verbose = 0 } my $family = "inet"; if ( $set =~ /_6/ ) { $family = "inet6" } if ($verbose) { logfile( "IPSET: loading set $set with " . scalar(@ipset) . " entries" ) } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "restore" ); print $childin "create -exist $set hash:net family $family hashsize $config{LF_IPSET_HASHSIZE} maxelem $config{LF_IPSET_MAXELEM}\n" . join( "\n", @ipset ) . "\n"; close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* [$set] IPSET: [$results[0]]"); } undef @ipset; return; } sub ipsetswap { my $from = shift; my $to = shift; my $verbose = 1; if ( $from =~ /^new_(6_)?CXS_/ ) { $verbose = 0 } if ($verbose) { logfile("IPSET: switching set $from to $to") } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "swap", $from, $to ); close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* IPSET: [$results[0]]"); } $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "flush", $from ); close $childin; @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* IPSET: [$results[0]]"); } $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "destroy", $from ); close $childin; @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* [$from] [$to] IPSET: [$results[0]]"); } return; } sub ipsetadd { my $set = shift; my $ip = shift; if ( $set =~ /GDENY/ or $set =~ /GALLOW/ ) { if ( $set =~ /^(\w+)(IN|OUT)$/ ) { $set = $1 } } else { if ( $set =~ /^chain(_6)?_NEW(\w+)$/ ) { $set = "chain" . $1 . "_" . $2 } if ( $set =~ /^bl(_6)?_NEW(\w+)$/ ) { $set = "bl" . $1 . "_" . $2 } if ( $set =~ /^(\w+)(IN|OUT)$/ ) { $set = $1 } } if ( $set eq "" or $ip eq "" ) { return } if ($faststart) { push @faststartipset, "add -exist $set $ip"; return; } if ( $config{DEBUG} >= 1 ) { logfile("debug: IPSET adding [$ip] to set [$set]") } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "add", "-exist", $set, $ip ); close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* [$set] IPSET: [$results[0]]"); } return; } sub ipsetdel { my $set = shift; my $ip = shift; if ( $set =~ /^chain(_6)?_NEW(\w+)$/ ) { $set = "chain" . $1 . "_" . $2 } if ( $set =~ /^bl(_6)?_NEW(\w+)$/ ) { $set = "bl" . $1 . "_" . $2 } if ( $set =~ /^(\w+)(IN|OUT)$/ ) { $set = $1 } if ( $set eq "" or $ip eq "" ) { return } if ( $config{DEBUG} >= 1 ) { logfile("debug: IPSET deleting [$ip] from set [$set]") } my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "del", $set, $ip ); close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* [$set] IPSET: [$results[0]]"); } return; } sub ipsetflush { my $set = shift; logfile("IPSET: flushing set $set"); my ( $childin, $childout ); my $cmdpid = open3( $childin, $childout, $childout, $config{IPSET}, "flush", $set ); close $childin; my @results = <$childout>; waitpid( $cmdpid, 0 ); chomp @results; if ( $results[0] =~ /^ipset/ ) { logfile("*Error* IPSET: [$results[0]]"); } return; } 1;