* * This file is part of "System Health and Security Probe". * * "System Health and Security Probe" 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 . */ if (file_exists(dirname(__FILE__) . '/shsp-config.php')) { require(dirname(__FILE__) . '/shsp-config.php'); if (preg_match('/^[0-9]+$/', $time_in_hours)) { $runinterval = $time_in_hours * 3600; } else { exit("Error. 'System Health and Security Probe' needs a valid \$time_in_hours parameter.\n"); } if (!empty($db_name)) { $bannedipsdb = $db_name; } else { exit("Error. 'System Health and Security Probe' needs a valid \$db_name parameter.\n"); } if (!empty($db_user)) { $bannedipsuser = $db_user; } else { exit("Error. 'System Health and Security Probe' needs a valid \$db_user parameter.\n"); } if (!empty($db_password)) { $bannedipspswd = $db_password; } else { exit("Error. 'System Health and Security Probe' needs a valid \$db_password parameter.\n"); } if (!empty($report_from)) { $emailsender = $report_from; } else { exit("Error. 'System Health and Security Probe' needs a valid \$report_from parameter.\n"); } if (!empty($report_to)) { $emailto = str_replace(" ", "", $report_to); $emailtoarr = explode(",", $emailto); $emailtofirst = $emailtoarr[0]; } else { exit("Error. 'System Health and Security Probe' needs a valid \$report_to parameter.\n"); } if (!empty($report_subject)) { $subject = $report_subject; } else { $subject = "System Health and Security Report"; } if (!empty($phpmyadmin_log)) { $phpmyadminlog = $phpmyadmin_log; $phpmyadminlogsec = $phpmyadminlog . ".1"; } else { $phpmyadminlog = ""; $phpmyadminlogsec = ""; } if (!empty($mailman_log)) { $mailmanlog = $mailman_log; $mailmanlogsec = $mailmanlog . ".1"; } else { $mailmanlog = ""; $mailmanlogsec = ""; } if (!empty($dolibarr_log)) { $dolibarrlog = $dolibarr_log; $dolibarrlogsec = $dolibarrlog . ".1"; } else { $dolibarrlog = ""; $dolibarrlogsec = ""; } if (!empty($phplist_log)) { $phplistlog = $phplist_log; $phplistlogsec = $phplistlog . ".1"; } else { $phplistlog = ""; $phplistlogsec = ""; } if (!empty($roundcube_log)) { $roundcubelog = $roundcube_log; $roundcubelogsec = $roundcubelog . ".1"; } else { $roundcubelog = ""; $roundcubelogsec = ""; } if (!empty($matomo_log)) { $matomolog = $matomo_log; $matomologsec = $matomolog . ".1"; } else { $matomolog = ""; $matomologsec = ""; } if (!empty($looladmin_log)) { $looladminlog = $looladmin_log; $looladminlogsec = $looladminlog . ".1"; } else { $looladminlog = ""; $looladminlogsec = ""; } if (!empty($wordpress_log)) { $wprexpl = explode(",", $wordpress_log); $wordpresslog = array_filter(array_map('trim', $wprexpl)); $wordpresslogsec = array_map(function($wplog) { return $wplog . ".1"; }, $wordpresslog); } else { $wordpresslog = []; $wordpresslogsec = []; } if (!empty($asterisk_log)) { $asterisklog = $asterisk_log; $asterisklogsec = $asterisklog . ".1"; } else { $asterisklog = ""; $asterisklogsec = ""; } if (!empty($bind_log)) { $bindlog = $bind_log; $bindlogsec = $bindlog . ".0"; } else { $bindlog = ""; $bindlogsec = ""; } if (!empty($nextcloud_log)) { $nextcloudlog = $nextcloud_log; $nextcloudlogsec = $nextcloudlog . ".1"; } else { $nextcloudlog = ""; $nextcloudlogsec = ""; } if (!empty($dovecot_log)) { $dovecotlog = $dovecot_log; $dovecotlogsec = $dovecotlog . ".1"; } else { $dovecotlog = ""; $dovecotlogsec = ""; } if (!empty($postfix_log)) { $postfixlog = $postfix_log; $postfixlogsec = $postfixlog . ".1"; } else { $postfixlog = ""; $postfixlogsec = ""; } if (!empty($postfix_sasl_log)) { $postfixsasllog = $postfix_sasl_log; $postfixsasllogsec = $postfixsasllog . ".1"; } else { $postfixsasllog = ""; $postfixsasllogsec = ""; } if (!empty($proftpd_log)) { $proftpdlog = $proftpd_log; $proftpdlogsec = $proftpdlog . ".1"; } else { $proftpdlog = ""; $proftpdlogsec = ""; } if (!empty($sshd_log)) { $sshdlog = $sshd_log; $sshdlogsec = $sshdlog . ".1"; } else { $sshdlog = ""; $sshdlogsec = ""; } if (!empty($openvpn_log)) { $openvpnlog = $openvpn_log; $openvpnlogsec = $openvpnlog . ".1"; } else { $openvpnlog = ""; $openvpnlogsec = ""; } if (!empty($postfix_admin_log)) { $postfixadminlog = $postfix_admin_log; $postfixadminlogsec = $postfixadminlog . ".1"; } else { $postfixadminlog = ""; $postfixadminlogsec = ""; } if (!empty($roundpin_log)) { $roundpinlog = $roundpin_log; $roundpinlogsec = $roundpinlog . ".1"; } else { $roundpinlog = ""; $roundpinlogsec = ""; } if (!empty($mybb_log)) { $mybblog = $mybb_log; $mybblogsec = $mybblog . ".1"; } else { $mybblog = ""; $mybblogsec = ""; } if (!empty($friendica_log)) { $friendicalog = $friendica_log; $friendicalogsec = $friendicalog . ".1"; } else { $friendicalog = ""; $friendicalogsec = ""; } if (!empty($redscarfsuite_panel_log)) { $redscarfsuitepanellog = $redscarfsuite_panel_log; $redscarfsuitepanellogsec = $redscarfsuitepanellog . ".1"; } else { $redscarfsuitepanellog = ""; $redscarfsuitepanellogsec = ""; } if (!empty($disk_threshold)) { $diskthreshold = $disk_threshold; } else { $diskthreshold = "3145728"; } if (!empty($clamav_report_dir)) { if (mb_substr($clamav_report_dir, -1) == "/") { $clamavreportdir = substr($clamav_report_dir, 0, -1); } else { $clamavreportdir = $clamav_report_dir; } } else { $clamavreportdir = ""; } if (!empty($backup_directory)) { if (mb_substr($backup_directory, -1) == "/") { $backupdirectory = substr($backup_directory, 0, -1); } else { $backupdirectory = $backup_directory; } } else { $backupdirectory = ""; } if (!empty($automatic_emails_to_isp)) { $automaticemail = $automatic_emails_to_isp; } else { $automaticemail = "no"; } if (!empty($excluded_jails)) { $excludedjails = $excluded_jails; } else { $excludedjails = []; } if (!empty($excluded_ips)) { $excludedips = str_replace(" ", "", $excluded_ips); } else { $excludedips = ""; } if (!empty($sysadmin_name)) { $sysadminname = $sysadmin_name; } else { $sysadminname = ""; } if (!empty($abuse_reports_to_admin)) { $abusereportstoadmin = $abuse_reports_to_admin; } else { $abusereportstoadmin = "no"; } if (count($wordpresslog) > 1) { $wordpressfirst = implode(' or ', $wordpresslog); $wordpresssec = implode(' or ', $wordpresslogsec); } else { $wordpressfirst = $wordpresslog[0]; $wordpresssec = $wordpresslogsec[0]; } // Log paths $jaillogarr = ['asterisk' => $asterisklog, 'named-refused' => $bindlog, 'dovecot' => $dovecotlog, 'looladmin' => $looladminlog, 'mailman' => $mailmanlog, 'nextcloud' => $nextcloudlog, 'dolibarr' => $dolibarrlog, 'phplist' => $phplistlog, 'phpmyadmin' => $phpmyadminlog, 'postfix' => $postfixlog, 'postfix-sasl' => $postfixsasllog, 'proftpd' => $proftpdlog, 'roundcube' => $roundcubelog, 'sshd' => $sshdlog, 'openvpn' => $openvpnlog, 'postfixadmin' => $postfixadminlog, 'roundpin' => $roundpinlog, 'mybb' => $mybblog, 'matomo' => $matomolog, 'friendica' => $friendicalog, 'redscarfsuitepanel' => $redscarfsuitepanellog,'wordpress' => [$wordpressfirst, $wordpresssec]]; $jaillogarrsec = ['asterisk' => $asterisklogsec, 'named-refused' => $bindlogsec, 'dovecot' => $dovecotlogsec, 'looladmin' => $looladminlogsec, 'mailman' => $mailmanlogsec, 'nextcloud' => $nextcloudlogsec, 'dolibarr' => $dolibarrlogsec, 'phplist' => $phplistlogsec, 'phpmyadmin' => $phpmyadminlogsec, 'postfix' => $postfixlogsec, 'postfix-sasl' => $postfixsasllogsec, 'proftpd' => $proftpdlogsec, 'roundcube' => $roundcubelogsec, 'matomo' => $matomologsec, 'sshd' => $sshdlogsec, 'openvpn' => $openvpnlogsec, 'postfixadmin' => $postfixadminlogsec, 'roundpin' => $roundpinlogsec, 'mybb' => $mybblogsec, 'friendica' => $friendicalogsec, 'redscarfsuitepanel' => $redscarfsuitepanellogsec]; // Jail ports $jailsports = ['asterisk' => '5060', 'named-refused' => '53', 'dovecot' => '143', 'looladmin' => '443', 'mailman' => '443', 'nextcloud' => '443', 'dolibarr' => '443', 'phplist' => '443', 'phpmyadmin' => '443', 'postfix' => '25', 'postfix-sasl' => '25', 'proftpd' => '21', 'roundcube' => '443', 'matomo' => '443', 'sshd' => '22', 'openvpn' => '1194', 'postfixadmin' => '443', 'roundpin' => '443', 'wordpress' => '443']; // Connect to the database mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $mysqli = new mysqli('localhost', $bannedipsuser, $bannedipspswd, $bannedipsdb); if ($mysqli->connect_error) { exit('Error connecting to the database !'); } $mysqli->set_charset("utf8mb4"); $query0 = "SHOW TABLES LIKE 'bannedipstable';"; $result0 = $mysqli->query($query0); // Create the 4 tables if it's the first run of this script if (mysqli_num_rows($result0) == 0) { // Create the table for the IPs banned by Fail2ban $query1 = " CREATE TABLE IF NOT EXISTS bannedipstable ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, IP VARCHAR (400) DEFAULT NULL, jail VARCHAR (300) DEFAULT NULL, first_ban DATETIME DEFAULT NULL, last_ban DATETIME DEFAULT NULL, ban_number INT DEFAULT NULL, emails_isp INT DEFAULT 0 ); "; $result1 = $mysqli->query($query1); // Create the table for the last sent email $query2 = " CREATE TABLE IF NOT EXISTS lastsentemail ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, old_message LONGTEXT DEFAULT NULL ); "; $result2 = $mysqli->query($query2); /** * Create the table for the last sent Nextcloud and mail log lines containing virus detections ('mail_loglines' and 'nextcloud_loglines'), and the last * read log lines ('lastmailline' and 'lastnextline') */ $query3 = " CREATE TABLE IF NOT EXISTS sentreadlines ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, mail_loglines TEXT DEFAULT NULL, nextcloud_loglines TEXT DEFAULT NULL, lastmailline TEXT DEFAULT NULL, lastnextline TEXT DEFAULT NULL, lastpersdetmail TEXT DEFAULT NULL, lastpersdetnext TEXT DEFAULT NULL ); "; $result3 = $mysqli->query($query3); // Create the table to store the jails and their respective banned IPs sent in the last email or banned in the past $query4 = " CREATE TABLE IF NOT EXISTS jailsandips ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, jail VARCHAR (300) DEFAULT NULL, bannedips LONGTEXT DEFAULT NULL ); "; $result4 = $mysqli->query($query4); } // Search for failed processes and format the data $getfailedservinit = shell_exec('systemctl --failed --all'); $getfailedservsec = str_replace("To show all installed unit files use 'systemctl list-unit-files'.", "", $getfailedservinit); $failedservsplit = preg_split('/\r\n|\r|\n/', $getfailedservsec); $countlines = count($failedservsplit); $getfailedservbr = implode("
", $failedservsplit); $getfailedservtert = $failedservsplit[$countlines - 3]; // If there are failed processes, list them in a paragraph in the email to be sent if ($getfailedservtert[0] != 0) { $getfailedserv = ""; } else { $getfailedserv = null; } /** * Read the periodic virus scanning reports to see if there were any detections during the last scan */ // Create the ClamAV reports directory if it hasn't been already created if (!is_dir($clamavreportdir)) { exec("mkdir -p " . $clamavreportdir); } $maildetectrep = $clamavreportdir . "/clamav_mail_report"; $nextdetectrep = $clamavreportdir . "/clamav_nextcloud_report"; $targetstr = "Infected files: "; // Search for the infected files lines in the mail scan report if (is_file($maildetectrep)) { $readmaildetrep = file_get_contents($maildetectrep); $infectedfl = strstr($readmaildetrep, $targetstr); $txtbreakline = preg_split('/\r\n|\r|\n/', $infectedfl); $nbofinfectfls = explode("Infected files: ", $txtbreakline[0]); $nbinfctdflsmail = $nbofinfectfls[1]; if ($nbinfctdflsmail != 0) { $getdetlines = explode("\n", $readmaildetrep); $getdetmaillnarr = []; $gosel = false; $p = 0; foreach ($getdetlines as $lnkey => $lnvalue) { if (strpos($lnvalue, "SCAN SUMMARY") !== false) { $gosel = false; } if ($gosel) { $getdetmaillnarr[$p] = $lnvalue; $p++; } if (strpos($lnvalue, "---------------------------") !== false) { $gosel = true; } } $maildetlnbr = implode("

", $getdetmaillnarr); $maildetlnbr_enc = base64_encode($maildetlnbr); } else { $maildetlnbr_enc = null; } } else { $nbinfctdflsmail = 0; $maildetlnbr_enc = null; } // Search for the infected files lines in the Nextcloud scan report if (is_file($nextdetectrep)) { $readnextdetrep = file_get_contents($nextdetectrep); $infectedfltxt = strstr($readnextdetrep, $targetstr); $txtbreakln = preg_split('/\r\n|\r|\n/', $infectedfltxt); $nbofinfectedinit = explode("Infected files: ", $txtbreakln[0]); $nbinfctdflsnext = $nbofinfectedinit[1]; if ($nbinfctdflsnext != 0) { $getdetnextlines = explode("\n", $readnextdetrep); $getdetnextlinesarr = []; $goselnext = false; $b = 0; foreach ($getdetnextlines as $lnextkey => $lnextvalue) { if (strpos($lnextvalue, "SCAN SUMMARY") !== false) { $goselnext = false; } if ($goselnext) { $getdetnextlinesarr[$b] = $lnextvalue; $b++; } if (strpos($lnextvalue, "---------------------------") !== false) { $goselnext = true; } } $nextdetlnbr = implode("

", $getdetnextlinesarr); $nextdetlnbr_enc = base64_encode($nextdetlnbr); } else { $nextdetlnbr_enc = null; } } else { $nbinfctdflsnext = 0; $nextdetlnbr_enc = null; } // Get the last sent Nextcloud log lines and mail log lines containing virus detections, the last mail log line and Nextcloud log line read by the script in the previous run // and the last sent mail virus detections and Nextcloud virus detections $queryvr = "SELECT id, mail_loglines, nextcloud_loglines, lastmailline, lastnextline, lastpersdetmail, lastpersdetnext FROM sentreadlines WHERE id = 1;"; $resultvr = $mysqli->query($queryvr); $result_fetchvr = $resultvr->fetch_array(); $old_maildetections_enc = $result_fetchvr[1]; $old_nextdetections_enc = $result_fetchvr[2]; $lastreadmaillineinit = $result_fetchvr[3]; $lastreadnextlineinit = $result_fetchvr[4]; $old_mailvirdetect_enc = $result_fetchvr[5]; $old_nextvirdetect_enc = $result_fetchvr[6]; $lastreadmailline = base64_decode($lastreadmaillineinit); $lastreadnextline = base64_decode($lastreadnextlineinit); if ($nbinfctdflsmail != 0 && $nbinfctdflsnext != 0 && $maildetlnbr_enc != $old_mailvirdetect_enc && $nextdetlnbr_enc != $old_nextvirdetect_enc) { $clamavperiodicdetect = "
"; } elseif ($nbinfctdflsmail != 0 && $nbinfctdflsnext == 0 && $maildetlnbr_enc != $old_mailvirdetect_enc) { $clamavperiodicdetect = "
"; } elseif ($nbinfctdflsmail == 0 && $nbinfctdflsnext != 0 && $nextdetlnbr_enc != $old_nextvirdetect_enc) { $clamavperiodicdetect = "
"; } else { $clamavperiodicdetect = null; } // Get the log lines that contain virus detections in emails $mailstring = ' milter-hold: '; $maillog1init = $postfixlog; $maillog2init = $postfixlogsec; if (is_file($maillog1init)) { $maillog1sec = file_get_contents($maillog1init); } else { $maillog1sec = ''; } if (is_file($maillog2init)) { $maillog2sec = file_get_contents($maillog2init); } else { $maillog2sec = ''; } if ($lastreadmaillineinit != '') { if (strpos($maillog1sec, $lastreadmailline) !== false) { $maillog1tert = strstr($maillog1sec, $lastreadmailline); $maillog1fourth = preg_split('/\r\n|\r|\n/', $maillog1tert); $maillogexplall = $maillog1fourth; array_pop($maillog1fourth); $endoflstrdmlln = end($maillog1fourth); $reset1 = reset($maillog1fourth); $newlastreadmailline = base64_encode($endoflstrdmlln); } else { $maillog2tert = strstr($maillog2sec, $lastreadmailline); $maillog2fourth = preg_split('/\r\n|\r|\n/', $maillog2tert); $maillog2fifth = implode("\n", $maillog2fourth); $maillog2sixth = $maillog2fifth . $maillog1sec; $maillog2expl = preg_split('/\r\n|\r|\n/', $maillog2sixth); $maillogexplall = $maillog2expl; array_pop($maillog2expl); $endoflstrdmlln2 = end($maillog2expl); $reset2 = reset($maillog2expl); $newlastreadmailline = base64_encode($endoflstrdmlln2); } } else { if ($maillog1sec != '' || $maillog2sec != '') { $maillogconc = $maillog2sec . $maillog1sec; $maillogexplall = preg_split('/\r\n|\r|\n/', $maillogconc); if ($maillogexplall[0] == '') { $newlastreadinit = $maillogexplall[1]; } else { $newlastreadinit = $maillogexplall[0]; } $newlastreadmailline = base64_encode($newlastreadinit); } else { $newlastreadmailline = ''; $maillogexplall = []; } } $mailloglines = []; foreach($maillogexplall as $key => $value) { if (strpos($value, $mailstring) !== false) { $mailloglines[] = htmlentities($value); } } $maildetections = implode("

", $mailloglines); $maildetections_enc = base64_encode($maildetections); // Get the log lines that contain virus detections in files uploaded to Nextcloud $nextstring = 'Infected file deleted'; if (is_file($nextcloudlog)) { $nextlog1sec = file_get_contents($nextcloudlog); } else { $nextlog1sec = ''; } if (is_file($nextcloudlogsec)) { $nextlog2sec = file_get_contents($nextcloudlogsec); } else { $nextlog2sec = ''; } if ($lastreadnextlineinit != '') { if (strpos($nextlog1sec, $lastreadnextline) !== false) { $nextlog1tert = strstr($nextlog1sec, $lastreadnextline); $nextlog1fourth = preg_split('/\r\n|\r|\n/', $nextlog1tert); $nextlogexplall = $nextlog1fourth; array_pop($nextlog1fourth); $endoflstrdmlln = end($nextlog1fourth); $newlastreadnextline = base64_encode($endoflstrdmlln); } else { $nextlog2tert = strstr($nextlog2sec, $lastreadnextline); $nextlog2fourth = preg_split('/\r\n|\r|\n/', $nextlog2tert); $nextlog2fifth = implode("\n", $nextlog2fourth); $nextlog2sixth = $nextlog2fifth . $nextlog1sec; $nextlog2expl = preg_split('/\r\n|\r|\n/', $nextlog2sixth); $nextlogexplall = $nextlog2expl; array_pop($nextlog2expl); $endoflstrdmlln2 = end($nextlog2expl); $newlastreadnextline = base64_encode($endoflstrdmlln2); } } else { if ($nextlog1sec != '' || $nextlog2sec != '') { $nextlogconc = $nextlog2sec . $nextlog1sec; $nextlogexplall = preg_split('/\r\n|\r|\n/', $nextlogconc); if ($nextlogexplall[0] == '') { $newlastreadinit = $nextlogexplall[1]; } else { $newlastreadinit = $nextlogexplall[0]; } $newlastreadnextline = base64_encode($newlastreadinit); } else { $newlastreadnextline = ''; $nextlogexplall = []; } } $nextloglines = []; foreach ($nextlogexplall as $key2 => $value2) { if (strpos($value2, $nextstring) !== false) { $nextloglines[] = htmlentities($value2); } } $nextdetections = implode("

", $nextloglines); $nextdetections_enc = base64_encode($nextdetections); // Decide how the virus detections paragraph will look like if (count($mailloglines) != 0 && count($nextloglines) != 0 && $maildetections_enc != $old_maildetections_enc && $nextdetections_enc != $old_nextdetections_enc) { $clamavdetections = "
"; } elseif (count($mailloglines) != 0 && count($nextloglines) == 0 && $maildetections_enc != $old_maildetections_enc) { $clamavdetections = "
"; } elseif (count($mailloglines) == 0 && count($nextloglines) != 0 && $nextdetections_enc != $old_nextdetections_enc) { $clamavdetections = "