aboutsummaryrefslogtreecommitdiffstats
path: root/config/suricata/suricata_check_for_rule_updates.php
diff options
context:
space:
mode:
Diffstat (limited to 'config/suricata/suricata_check_for_rule_updates.php')
-rw-r--r--config/suricata/suricata_check_for_rule_updates.php721
1 files changed, 721 insertions, 0 deletions
diff --git a/config/suricata/suricata_check_for_rule_updates.php b/config/suricata/suricata_check_for_rule_updates.php
new file mode 100644
index 00000000..867a7efe
--- /dev/null
+++ b/config/suricata/suricata_check_for_rule_updates.php
@@ -0,0 +1,721 @@
+<?php
+/*
+ * suricata_check_for_rule_updates.php
+ *
+ * Copyright (C) 2014 Bill Meeks
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+require_once("functions.inc");
+require_once("service-utils.inc");
+require_once("guiconfig.inc");
+require_once("/usr/local/pkg/suricata/suricata.inc");
+
+global $g, $pkg_interface, $suricata_gui_include, $rebuild_rules;
+
+if (!defined("VRT_DNLD_URL"))
+ define("VRT_DNLD_URL", "https://www.snort.org/reg-rules/");
+if (!defined("ET_VERSION"))
+ define("ET_VERSION", "2.9.0");
+if (!defined("ET_BASE_DNLD_URL"))
+ define("ET_BASE_DNLD_URL", "http://rules.emergingthreats.net/");
+if (!defined("ETPRO_BASE_DNLD_URL"))
+ define("ETPRO_BASE_DNLD_URL", "https://rules.emergingthreatspro.com/");
+if (!defined("ET_DNLD_FILENAME"))
+ define("ET_DNLD_FILENAME", "emerging.rules.tar.gz");
+if (!defined("ETPRO_DNLD_FILENAME"))
+ define("ETPRO_DNLD_FILENAME", "etpro.rules.tar.gz");
+if (!defined("VRT_DNLD_FILENAME"))
+ define("VRT_DNLD_FILENAME", "snortrules-snapshot-edge.tar.gz");
+if (!defined("GPLV2_DNLD_FILENAME"))
+ define("GPLV2_DNLD_FILENAME", "community-rules.tar.gz");
+if (!defined("GPLV2_DNLD_URL"))
+ define("GPLV2_DNLD_URL", "https://s3.amazonaws.com/snort-org/www/rules/community/");
+if (!defined("RULES_UPD_LOGFILE"))
+ define("RULES_UPD_LOGFILE", SURICATALOGDIR . "/suricata_rules_update.log");
+if (!defined("VRT_FILE_PREFIX"))
+ define("VRT_FILE_PREFIX", "snort_");
+if (!defined("GPL_FILE_PREFIX"))
+ define("GPL_FILE_PREFIX", "GPLv2_");
+if (!defined("ET_OPEN_FILE_PREFIX"))
+ define("ET_OPEN_FILE_PREFIX", "emerging-");
+if (!defined("ET_PRO_FILE_PREFIX"))
+ define("ET_PRO_FILE_PREFIX", "etpro-");
+
+$suricatadir = SURICATADIR;
+$suricatalogdir = SURICATALOGDIR;
+$suricata_rules_upd_log = RULES_UPD_LOGFILE;
+
+/* Save the state of $pkg_interface so we can restore it */
+$pkg_interface_orig = $pkg_interface;
+if ($suricata_gui_include)
+ $pkg_interface = "";
+else
+ $pkg_interface = "console";
+
+/* define checks */
+$oinkid = $config['installedpackages']['suricata']['config'][0]['oinkcode'];
+$etproid = $config['installedpackages']['suricata']['config'][0]['etprocode'];
+$snortdownload = $config['installedpackages']['suricata']['config'][0]['enable_vrt_rules'] == 'on' ? 'on' : 'off';
+$etpro = $config['installedpackages']['suricata']['config'][0]['enable_etpro_rules'] == 'on' ? 'on' : 'off';
+$eto = $config['installedpackages']['suricata']['config'][0]['enable_etopen_rules'] == 'on' ? 'on' : 'off';
+$vrt_enabled = $config['installedpackages']['suricata']['config'][0]['enable_vrt_rules'] == 'on' ? 'on' : 'off';
+$snortcommunityrules = $config['installedpackages']['suricata']['config'][0]['snortcommunityrules'] == 'on' ? 'on' : 'off';
+
+/* Working directory for downloaded rules tarballs */
+$tmpfname = "/tmp/suricata_rules_up";
+
+/* Snort Edge VRT Rules filenames and URL */
+$snort_filename = VRT_DNLD_FILENAME;
+$snort_filename_md5 = "{$snort_filename}.md5";
+$snort_rule_url = VRT_DNLD_URL;
+
+/* Snort GPLv2 Community Rules filenames and URL */
+$snort_community_rules_filename = GPLV2_DNLD_FILENAME;
+$snort_community_rules_filename_md5 = GPLV2_DNLD_FILENAME . ".md5";
+$snort_community_rules_url = GPLV2_DNLD_URL;
+
+/* Mount the Suricata conf directories R/W so we can modify files there */
+conf_mount_rw();
+
+/* Set up Emerging Threats rules filenames and URL */
+if ($etpro == "on") {
+ $emergingthreats_filename = ETPRO_DNLD_FILENAME;
+ $emergingthreats_filename_md5 = ETPRO_DNLD_FILENAME . ".md5";
+ $emergingthreats_url = ETPRO_BASE_DNLD_URL;
+ $emergingthreats_url .= "{$etproid}/suricata/";
+ $et_name = "Emerging Threats Pro";
+ $et_md5_remove = ET_DNLD_FILENAME . ".md5";
+ @unlink("{$suricatadir}{$et_md5_remove}");
+}
+else {
+ $emergingthreats_filename = ET_DNLD_FILENAME;
+ $emergingthreats_filename_md5 = ET_DNLD_FILENAME . ".md5";
+ $emergingthreats_url = ET_BASE_DNLD_URL;
+ // If using Sourcefire VRT rules with ET, then we should use the open-nogpl ET rules
+ $emergingthreats_url .= $vrt_enabled == "on" ? "open-nogpl/" : "open/";
+ $emergingthreats_url .= "suricata/";
+ $et_name = "Emerging Threats Open";
+ $et_md5_remove = ETPRO_DNLD_FILENAME . ".md5";
+ @unlink("{$suricatadir}{$et_md5_remove}");
+}
+
+// Set a common flag for all Emerging Threats rules (open and pro).
+if ($etpro == 'on' || $eto == 'on')
+ $emergingthreats = 'on';
+else
+ $emergingthreats = 'off';
+
+function suricata_download_file_url($url, $file_out) {
+
+ /************************************************/
+ /* This function downloads the file specified */
+ /* by $url using the CURL library functions and */
+ /* saves the content to the file specified by */
+ /* $file. */
+ /* */
+ /* This is needed so console output can be */
+ /* suppressed to prevent XMLRPC sync errors. */
+ /* */
+ /* It provides logging of returned CURL errors. */
+ /************************************************/
+
+ global $g, $config, $pkg_interface, $last_curl_error, $fout, $ch, $file_size, $downloaded, $first_progress_update, $rfc2616;
+
+ // Initialize required variables for the pfSense "read_body()" function
+ $file_size = 1;
+ $downloaded = 1;
+ $first_progress_update = TRUE;
+ $last_curl_error = "";
+
+ $fout = fopen($file_out, "wb");
+ if ($fout) {
+ $ch = curl_init($url);
+ if (!$ch)
+ return false;
+ curl_setopt($ch, CURLOPT_FILE, $fout);
+
+ // NOTE: required to suppress errors from XMLRPC due to progress bar output
+ if ($g['suricata_sync_in_progress'])
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ else {
+ curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
+ curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'read_body');
+ }
+
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Win64; x64; Trident/6.0)");
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 0);
+
+ // Use the system proxy server setttings if configured
+ if (!empty($config['system']['proxyurl'])) {
+ curl_setopt($ch, CURLOPT_PROXY, $config['system']['proxyurl']);
+ if (!empty($config['system']['proxyport']))
+ curl_setopt($ch, CURLOPT_PROXYPORT, $config['system']['proxyport']);
+ if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
+ @curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY | CURLAUTH_ANYSAFE);
+ curl_setopt($ch, CURLOPT_PROXYUSERPWD, "{$config['system']['proxyuser']}:{$config['system']['proxypass']}");
+ }
+ }
+
+ $counter = 0;
+ $rc = true;
+ // Try up to 4 times to download the file before giving up
+ while ($counter < 4) {
+ $counter++;
+ $rc = curl_exec($ch);
+ if ($rc === true)
+ break;
+ log_error(gettext("[Suricata] Rules download error: " . curl_error($ch)));
+ log_error(gettext("[Suricata] Will retry in 15 seconds..."));
+ sleep(15);
+ }
+ if ($rc === false)
+ $last_curl_error = curl_error($ch);
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if (is_rfc2616_code($http_code))
+ $last_curl_error = $rfc2616[$http_code];
+ curl_close($ch);
+ fclose($fout);
+
+ // If we had to try more than once, log it
+ if ($counter > 1)
+ log_error(gettext("File '" . basename($file_out) . "' download attempts: {$counter} ..."));
+ return ($http_code == 200) ? true : $http_code;
+ }
+ else {
+ $last_curl_error = gettext("Failed to create file " . $file_out);
+ log_error(gettext("[Suricata] Failed to create file {$file_out} ..."));
+ return false;
+ }
+}
+
+function suricata_check_rule_md5($file_url, $file_dst, $desc = "") {
+
+ /**********************************************************/
+ /* This function attempts to download the passed MD5 hash */
+ /* file and compare its contents to the currently stored */
+ /* hash file to see if a new rules file has been posted. */
+ /* */
+ /* On Entry: $file_url = URL for md5 hash file */
+ /* $file_dst = Temp destination to store the */
+ /* downloaded hash file */
+ /* $desc = Short text string used to label */
+ /* log messages with rules type */
+ /* */
+ /* Returns: TRUE if new rule file download required. */
+ /* FALSE if rule download not required or an */
+ /* error occurred. */
+ /**********************************************************/
+
+ global $pkg_interface, $suricata_rules_upd_log, $last_curl_error, $update_errors;
+
+ $suricatadir = SURICATADIR;
+ $filename_md5 = basename($file_dst);
+
+ if ($pkg_interface <> "console")
+ update_status(gettext("Downloading {$desc} md5 file..."));
+ error_log(gettext("\tDownloading {$desc} md5 file {$filename_md5}...\n"), 3, $suricata_rules_upd_log);
+ $rc = suricata_download_file_url($file_url, $file_dst);
+
+ // See if download from URL was successful
+ if ($rc === true) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("Done downloading {$filename_md5}."));
+ error_log("\tChecking {$desc} md5 file...\n", 3, $suricata_rules_upd_log);
+
+ // check md5 hash in new file against current file to see if new download is posted
+ if (file_exists("{$suricatadir}{$filename_md5}")) {
+ $md5_check_new = file_get_contents($file_dst);
+ $md5_check_old = file_get_contents("{$suricatadir}{$filename_md5}");
+ if ($md5_check_new == $md5_check_old) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("{$desc} are up to date..."));
+ log_error(gettext("[Suricata] {$desc} are up to date..."));
+ error_log(gettext("\t{$desc} are up to date.\n"), 3, $suricata_rules_upd_log);
+ return false;
+ }
+ else
+ return true;
+ }
+ return true;
+ }
+ else {
+ error_log(gettext("\t{$desc} md5 download failed.\n"), 3, $suricata_rules_upd_log);
+ $suricata_err_msg = gettext("Server returned error code {$rc}.");
+ if ($pkg_interface <> "console") {
+ update_status(gettext("{$desc} md5 error ... Server returned error code {$rc} ..."));
+ update_output_window(gettext("{$desc} will not be updated.\n\t{$suricata_err_msg}"));
+ }
+ log_error(gettext("[Suricata] {$desc} md5 download failed..."));
+ log_error(gettext("[Suricata] Server returned error code {$rc}..."));
+ error_log(gettext("\t{$suricata_err_msg}\n"), 3, $suricata_rules_upd_log);
+ if ($pkg_interface == "console")
+ error_log(gettext("\tServer error message was: {$last_curl_error}\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\t{$desc} will not be updated.\n"), 3, $suricata_rules_upd_log);
+ $update_errors = true;
+ return false;
+ }
+}
+
+function suricata_fetch_new_rules($file_url, $file_dst, $file_md5, $desc = "") {
+
+ /**********************************************************/
+ /* This function downloads the passed rules file and */
+ /* compares its computed md5 hash to the passed md5 hash */
+ /* to verify the file's integrity. */
+ /* */
+ /* On Entry: $file_url = URL of rules file */
+ /* $file_dst = Temp destination to store the */
+ /* downloaded rules file */
+ /* $file_md5 = Expected md5 hash for the new */
+ /* downloaded rules file */
+ /* $desc = Short text string for use in */
+ /* log messages */
+ /* */
+ /* Returns: TRUE if download was successful. */
+ /* FALSE if download was not successful. */
+ /**********************************************************/
+
+ global $pkg_interface, $suricata_rules_upd_log, $last_curl_error, $update_errors;
+
+ $suricatadir = SURICATADIR;
+ $filename = basename($file_dst);
+
+ if ($pkg_interface <> "console")
+ update_status(gettext("There is a new set of {$desc} posted. Downloading..."));
+ log_error(gettext("[Suricata] There is a new set of {$desc} posted. Downloading {$filename}..."));
+ error_log(gettext("\tThere is a new set of {$desc} posted.\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\tDownloading file '{$filename}'...\n"), 3, $suricata_rules_upd_log);
+ $rc = suricata_download_file_url($file_url, $file_dst);
+
+ // See if the download from the URL was successful
+ if ($rc === true) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("Done downloading {$desc} file."));
+ log_error("[Suricata] {$desc} file update downloaded successfully");
+ error_log(gettext("\tDone downloading rules file.\n"),3, $suricata_rules_upd_log);
+
+ // Test integrity of the rules file. Turn off update if file has wrong md5 hash
+ if ($file_md5 != trim(md5_file($file_dst))){
+ if ($pkg_interface <> "console")
+ update_output_window(gettext("{$desc} file MD5 checksum failed..."));
+ log_error(gettext("[Suricata] {$desc} file download failed. Bad MD5 checksum..."));
+ log_error(gettext("[Suricata] Downloaded File MD5: " . md5_file($file_dst)));
+ log_error(gettext("[Suricata] Expected File MD5: {$file_md5}"));
+ error_log(gettext("\t{$desc} file download failed. Bad MD5 checksum.\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\tDownloaded {$desc} file MD5: " . md5_file($file_dst) . "\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\tExpected {$desc} file MD5: {$file_md5}\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\t{$desc} file download failed. {$desc} will not be updated.\n"), 3, $suricata_rules_upd_log);
+ $update_errors = true;
+ return false;
+ }
+ return true;
+ }
+ else {
+ if ($pkg_interface <> "console")
+ update_output_window(gettext("{$desc} file download failed..."));
+ log_error(gettext("[Suricata] {$desc} file download failed... server returned error '{$rc}'..."));
+ error_log(gettext("\t{$desc} file download failed. Server returned error {$rc}.\n"), 3, $suricata_rules_upd_log);
+ if ($pkg_interface == "console")
+ error_log(gettext("\tThe error text was: {$last_curl_error}\n"), 3, $suricata_rules_upd_log);
+ error_log(gettext("\t{$desc} will not be updated.\n"), 3, $suricata_rules_upd_log);
+ $update_errors = true;
+ return false;
+ }
+
+}
+
+/* Start of main code */
+
+/* remove old $tmpfname files if present */
+if (is_dir("{$tmpfname}"))
+ exec("/bin/rm -r {$tmpfname}");
+
+/* Make sure required suricatadirs exsist */
+exec("/bin/mkdir -p {$suricatadir}rules");
+exec("/bin/mkdir -p {$tmpfname}");
+exec("/bin/mkdir -p {$suricatalogdir}");
+
+/* See if we need to automatically clear the Update Log based on 1024K size limit */
+if (file_exists($suricata_rules_upd_log)) {
+ if (1048576 < filesize($suricata_rules_upd_log))
+ exec("/bin/rm -r {$suricata_rules_upd_log}");
+}
+
+/* Log start time for this rules update */
+error_log(gettext("Starting rules update... Time: " . date("Y-m-d H:i:s") . "\n"), 3, $suricata_rules_upd_log);
+$last_curl_error = "";
+$update_errors = false;
+
+/* Check for and download any new Emerging Threats Rules sigs */
+if ($emergingthreats == 'on') {
+ if (suricata_check_rule_md5("{$emergingthreats_url}{$emergingthreats_filename_md5}", "{$tmpfname}/{$emergingthreats_filename_md5}", "{$et_name} rules")) {
+ /* download Emerging Threats rules file */
+ $file_md5 = trim(file_get_contents("{$tmpfname}/{$emergingthreats_filename_md5}"));
+ if (!suricata_fetch_new_rules("{$emergingthreats_url}{$emergingthreats_filename}", "{$tmpfname}/{$emergingthreats_filename}", $file_md5, "{$et_name} rules"))
+ $emergingthreats = 'off';
+ }
+ else
+ $emergingthreats = 'off';
+}
+
+/* Check for and download any new Snort VRT sigs */
+if ($snortdownload == 'on') {
+ if (suricata_check_rule_md5("{$snort_rule_url}{$snort_filename_md5}/{$oinkid}/", "{$tmpfname}/{$snort_filename_md5}", "Snort VRT rules")) {
+ /* download snortrules file */
+ $file_md5 = trim(file_get_contents("{$tmpfname}/{$snort_filename_md5}"));
+ if (!suricata_fetch_new_rules("{$snort_rule_url}{$snort_filename}/{$oinkid}/", "{$tmpfname}/{$snort_filename}", $file_md5, "Snort VRT rules"))
+ $snortdownload = 'off';
+ }
+ else
+ $snortdownload = 'off';
+}
+
+/* Check for and download any new Snort GPLv2 Community Rules sigs */
+if ($snortcommunityrules == 'on') {
+ if (suricata_check_rule_md5("{$snort_community_rules_url}{$snort_community_rules_filename_md5}", "{$tmpfname}/{$snort_community_rules_filename_md5}", "Snort GPLv2 Community Rules")) {
+ /* download Snort GPLv2 Community Rules file */
+ $file_md5 = trim(file_get_contents("{$tmpfname}/{$snort_community_rules_filename_md5}"));
+ if (!suricata_fetch_new_rules("{$snort_community_rules_url}{$snort_community_rules_filename}", "{$tmpfname}/{$snort_community_rules_filename}", $file_md5, "Snort GPLv2 Community Rules"))
+ $snortcommunityrules = 'off';
+ }
+ else
+ $snortcommunityrules = 'off';
+}
+
+/* Untar Emerging Threats rules file to tmp if downloaded */
+if ($emergingthreats == 'on') {
+ safe_mkdir("{$tmpfname}/emerging");
+ if (file_exists("{$tmpfname}/{$emergingthreats_filename}")) {
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extracting {$et_name} rules..."));
+ update_output_window(gettext("Installing {$et_name} rules..."));
+ }
+ error_log(gettext("\tExtracting and installing {$et_name} rules...\n"), 3, $suricata_rules_upd_log);
+ exec("/usr/bin/tar xzf {$tmpfname}/{$emergingthreats_filename} -C {$tmpfname}/emerging rules/");
+
+ /* Remove the old Emerging Threats rules files */
+ $eto_prefix = ET_OPEN_FILE_PREFIX;
+ $etpro_prefix = ET_PRO_FILE_PREFIX;
+ unlink_if_exists("{$suricatadir}rules/{$eto_prefix}*.rules");
+ unlink_if_exists("{$suricatadir}rules/{$etpro_prefix}*.rules");
+ unlink_if_exists("{$suricatadir}rules/{$eto_prefix}*ips.txt");
+ unlink_if_exists("{$suricatadir}rules/{$etpro_prefix}*ips.txt");
+
+ // The code below renames ET files with a prefix, so we
+ // skip renaming the Suricata default events rule files
+ // that are also bundled in the ET rules.
+ $default_rules = array( "decoder-events.rules", "files.rules", "http-events.rules", "smtp-events.rules", "stream-events.rules", "tls-events.rules" );
+ $files = glob("{$tmpfname}/emerging/rules/*.rules");
+ // Determine the correct prefix to use based on which
+ // Emerging Threats rules package is enabled.
+ if ($etpro == "on")
+ $prefix = ET_PRO_FILE_PREFIX;
+ else
+ $prefix = ET_OPEN_FILE_PREFIX;
+ foreach ($files as $file) {
+ $newfile = basename($file);
+ if (in_array($newfile, $default_rules))
+ @copy($file, "{$suricatadir}rules/{$newfile}");
+ else {
+ if (strpos($newfile, $prefix) === FALSE)
+ @copy($file, "{$suricatadir}rules/{$prefix}{$newfile}");
+ else
+ @copy($file, "{$suricatadir}rules/{$newfile}");
+ }
+ }
+ /* IP lists for Emerging Threats rules */
+ $files = glob("{$tmpfname}/emerging/rules/*ips.txt");
+ foreach ($files as $file) {
+ $newfile = basename($file);
+ if ($etpro == "on")
+ @copy($file, "{$suricatadir}rules/" . ET_PRO_FILE_PREFIX . "{$newfile}");
+ else
+ @copy($file, "{$suricatadir}rules/" . ET_OPEN_FILE_PREFIX . "{$newfile}");
+ }
+ /* base etc files for Emerging Threats rules */
+ foreach (array("classification.config", "reference.config", "gen-msg.map", "unicode.map") as $file) {
+ if (file_exists("{$tmpfname}/emerging/rules/{$file}"))
+ @copy("{$tmpfname}/emerging/rules/{$file}", "{$tmpfname}/ET_{$file}");
+ }
+
+ /* Copy emergingthreats md5 sig to Suricata dir */
+ if (file_exists("{$tmpfname}/{$emergingthreats_filename_md5}")) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("Copying md5 signature to Suricata directory..."));
+ @copy("{$tmpfname}/{$emergingthreats_filename_md5}", "{$suricatadir}{$emergingthreats_filename_md5}");
+ }
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extraction of {$et_name} rules completed..."));
+ update_output_window(gettext("Installation of {$et_name} rules completed..."));
+ }
+ error_log(gettext("\tInstallation of {$et_name} rules completed.\n"), 3, $suricata_rules_upd_log);
+ exec("rm -r {$tmpfname}/emerging");
+ }
+}
+
+/* Untar Snort rules file to tmp */
+if ($snortdownload == 'on') {
+ if (file_exists("{$tmpfname}/{$snort_filename}")) {
+ /* Remove the old Snort rules files */
+ $vrt_prefix = VRT_FILE_PREFIX;
+ unlink_if_exists("{$suricatadir}rules/{$vrt_prefix}*.rules");
+
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extracting Snort VRT rules..."));
+ update_output_window(gettext("Installing Sourcefire VRT rules..."));
+ }
+ error_log(gettext("\tExtracting and installing Snort VRT rules...\n"), 3, $suricata_rules_upd_log);
+
+ /* extract snort.org rules and add prefix to all snort.org files */
+ safe_mkdir("{$tmpfname}/snortrules");
+ exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname}/snortrules rules/");
+ $files = glob("{$tmpfname}/snortrules/rules/*.rules");
+ foreach ($files as $file) {
+ $newfile = basename($file);
+ @copy($file, "{$suricatadir}rules/" . VRT_FILE_PREFIX . "{$newfile}");
+ }
+
+ /* IP lists */
+ $files = glob("{$tmpfname}/snortrules/rules/*.txt");
+ foreach ($files as $file) {
+ $newfile = basename($file);
+ @copy($file, "{$suricatadir}rules/{$newfile}");
+ }
+ exec("rm -r {$tmpfname}/snortrules");
+
+ /* extract base etc files */
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extracting Snort VRT config and map files..."));
+ update_output_window(gettext("Copying config and map files..."));
+ }
+ exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} etc/");
+ foreach (array("classification.config", "reference.config", "gen-msg.map", "unicode.map") as $file) {
+ if (file_exists("{$tmpfname}/etc/{$file}"))
+ @copy("{$tmpfname}/etc/{$file}", "{$tmpfname}/VRT_{$file}");
+ }
+ exec("rm -r {$tmpfname}/etc");
+ if (file_exists("{$tmpfname}/{$snort_filename_md5}")) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("Copying md5 signature to Suricata directory..."));
+ @copy("{$tmpfname}/{$snort_filename_md5}", "{$suricatadir}{$snort_filename_md5}");
+ }
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extraction of Snort VRT rules completed..."));
+ update_output_window(gettext("Installation of Sourcefire VRT rules completed..."));
+ }
+ error_log(gettext("\tInstallation of Snort VRT rules completed.\n"), 3, $suricata_rules_upd_log);
+ }
+}
+
+/* Untar Snort GPLv2 Community rules file to tmp */
+if ($snortcommunityrules == 'on') {
+ safe_mkdir("{$tmpfname}/community");
+ if (file_exists("{$tmpfname}/{$snort_community_rules_filename}")) {
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extracting Snort GPLv2 Community Rules..."));
+ update_output_window(gettext("Installing Snort GPLv2 Community Rules..."));
+ }
+ error_log(gettext("\tExtracting and installing Snort GPLv2 Community Rules...\n"), 3, $suricata_rules_upd_log);
+ exec("/usr/bin/tar xzf {$tmpfname}/{$snort_community_rules_filename} -C {$tmpfname}/community/");
+
+ $files = glob("{$tmpfname}/community/community-rules/*.rules");
+ foreach ($files as $file) {
+ $newfile = basename($file);
+ @copy($file, "{$suricatadir}rules/" . GPL_FILE_PREFIX . "{$newfile}");
+ }
+ /* base etc files for Snort GPLv2 Community rules */
+ foreach (array("classification.config", "reference.config", "gen-msg.map", "unicode.map") as $file) {
+ if (file_exists("{$tmpfname}/community/community-rules/{$file}"))
+ @copy("{$tmpfname}/community/community-rules/{$file}", "{$tmpfname}/" . GPL_FILE_PREFIX . "{$file}");
+ }
+ /* Copy snort community md5 sig to suricata dir */
+ if (file_exists("{$tmpfname}/{$snort_community_rules_filename_md5}")) {
+ if ($pkg_interface <> "console")
+ update_status(gettext("Copying md5 signature to suricata directory..."));
+ @copy("{$tmpfname}/{$snort_community_rules_filename_md5}", "{$suricatadir}{$snort_community_rules_filename_md5}");
+ }
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Extraction of Snort GPLv2 Community Rules completed..."));
+ update_output_window(gettext("Installation of Snort GPLv2 Community Rules file completed..."));
+ }
+ error_log(gettext("\tInstallation of Snort GPLv2 Community Rules completed.\n"), 3, $suricata_rules_upd_log);
+ exec("rm -r {$tmpfname}/community");
+ }
+}
+
+function suricata_apply_customizations($suricatacfg, $if_real) {
+
+ global $vrt_enabled, $rebuild_rules;
+ $suricatadir = SURICATADIR;
+
+ suricata_prepare_rule_files($suricatacfg, "{$suricatadir}suricata_{$suricatacfg['uuid']}_{$if_real}");
+
+ /* Copy the master config and map files to the interface directory */
+ @copy("{$suricatadir}classification.config", "{$suricatadir}suricata_{$suricatacfg['uuid']}_{$if_real}/classification.config");
+ @copy("{$suricatadir}reference.config", "{$suricatadir}suricata_{$suricatacfg['uuid']}_{$if_real}/reference.config");
+ @copy("{$suricatadir}gen-msg.map", "{$suricatadir}suricata_{$suricatacfg['uuid']}_{$if_real}/gen-msg.map");
+ @copy("{$suricatadir}unicode.map", "{$suricatadir}suricata_{$suricatacfg['uuid']}_{$if_real}/unicode.map");
+}
+
+if ($snortdownload == 'on' || $emergingthreats == 'on' || $snortcommunityrules == 'on') {
+
+ if ($pkg_interface <> "console")
+ update_status(gettext('Copying new config and map files...'));
+ error_log(gettext("\tCopying new config and map files...\n"), 3, $suricata_rules_upd_log);
+
+ /******************************************************************/
+ /* Build the classification.config and reference.config files */
+ /* using the ones from all the downloaded rules plus the default */
+ /* files installed with Suricata. */
+ /******************************************************************/
+ $cfgs = glob("{$tmpfname}/*reference.config");
+ $cfgs[] = "{$suricatadir}reference.config";
+ suricata_merge_reference_configs($cfgs, "{$suricatadir}reference.config");
+ $cfgs = glob("{$tmpfname}/*classification.config");
+ $cfgs[] = "{$suricatadir}classification.config";
+ suricata_merge_classification_configs($cfgs, "{$suricatadir}classification.config");
+
+ /* Determine which map files to use for the master copy. */
+ /* The Snort VRT ones are preferred, if available. */
+ if ($snortdownload == 'on')
+ $prefix = "VRT_";
+ elseif ($emergingthreats == 'on')
+ $prefix = "ET_";
+ elseif ($snortcommunityrules == 'on')
+ $prefix = GPL_FILE_PREFIX;
+ if (file_exists("{$tmpfname}/{$prefix}unicode.map"))
+ @copy("{$tmpfname}/{$prefix}unicode.map", "{$suricatadir}unicode.map");
+ if (file_exists("{$tmpfname}/{$prefix}gen-msg.map"))
+ @copy("{$tmpfname}/{$prefix}gen-msg.map", "{$suricatadir}gen-msg.map");
+
+ /* Start the rules rebuild proccess for each configured interface */
+ if (is_array($config['installedpackages']['suricata']['rule']) &&
+ !empty($config['installedpackages']['suricata']['rule'])) {
+
+ /* Set the flag to force rule rebuilds since we downloaded new rules */
+ $rebuild_rules = true;
+
+ /* Create configuration for each active Suricata interface */
+ foreach ($config['installedpackages']['suricata']['rule'] as $value) {
+ $if_real = get_real_interface($value['interface']);
+ // Make sure the interface subdirectory exists. We need to re-create
+ // it during a pkg reinstall on the intial rules set download.
+ if (!is_dir("{$suricatadir}suricata_{$value['uuid']}_{$if_real}"))
+ safe_mkdir("{$suricatadir}suricata_{$value['uuid']}_{$if_real}");
+ if (!is_dir("{$suricatadir}suricata_{$value['uuid']}_{$if_real}/rules"))
+ safe_mkdir("{$suricatadir}suricata_{$value['uuid']}_{$if_real}/rules");
+ $tmp = "Updating rules configuration for: " . convert_friendly_interface_to_friendly_descr($value['interface']) . " ...";
+ if ($pkg_interface <> "console"){
+ update_status(gettext($tmp));
+ update_output_window(gettext("Please wait while Suricata interface files are being updated..."));
+ }
+ suricata_apply_customizations($value, $if_real);
+ $tmp = "\t" . $tmp . "\n";
+ error_log($tmp, 3, $suricata_rules_upd_log);
+ }
+ }
+ else {
+ if ($pkg_interface <> "console") {
+ update_output_window(gettext("Warning: No interfaces configured for Suricata were found..."));
+ update_output_window(gettext("No interfaces currently have Suricata configured and enabled on them..."));
+ }
+ error_log(gettext("\tWarning: No interfaces configured for Suricata were found...\n"), 3, $suricata_rules_upd_log);
+ }
+
+ /* Clear the rebuild rules flag. */
+ $rebuild_rules = false;
+
+ /* Restart Suricata if already running and we are not rebooting to pick up the new rules. */
+ if (is_process_running("suricata") && !$g['booting'] &&
+ !empty($config['installedpackages']['suricata']['rule'])) {
+
+ // See if "Live Reload" is configured and signal each Suricata instance
+ // if enabled, else just do a hard restart of all the instances.
+ if ($config['installedpackages']['suricata']['config'][0]['live_swap_updates'] == 'on') {
+ if ($pkg_interface <> "console") {
+ update_status(gettext('Signalling Suricata to live-load the new set of rules...'));
+ update_output_window(gettext("Please wait ... the process should complete in a few seconds..."));
+ }
+ log_error(gettext("[Suricata] Live-Reload of rules from auto-update is enabled..."));
+ error_log(gettext("\tLive-Reload of updated rules is enabled...\n"), 3, $suricata_rules_upd_log);
+ foreach ($config['installedpackages']['suricata']['rule'] as $value) {
+ $if_real = get_real_interface($value['interface']);
+ suricata_reload_config($value);
+ error_log(gettext("\tLive swap of updated rules requested for " . convert_friendly_interface_to_friendly_descr($value['interface']) . ".\n"), 3, $suricata_rules_upd_log);
+ }
+ log_error(gettext("[Suricata] Live-Reload of updated rules completed..."));
+ error_log(gettext("\tLive-Reload of the updated rules is complete.\n"), 3, $suricata_rules_upd_log);
+ }
+ else {
+ if ($pkg_interface <> "console") {
+ update_status(gettext('Restarting Suricata to activate the new set of rules...'));
+ update_output_window(gettext("Please wait ... restarting Suricata will take some time..."));
+ }
+ error_log(gettext("\tRestarting Suricata to activate the new set of rules...\n"), 3, $suricata_rules_upd_log);
+ restart_service("suricata");
+ if ($pkg_interface <> "console")
+ update_output_window(gettext("Suricata has restarted with your new set of rules..."));
+ log_error(gettext("[Suricata] Suricata has restarted with your new set of rules..."));
+ error_log(gettext("\tSuricata has restarted with your new set of rules.\n"), 3, $suricata_rules_upd_log);
+ }
+ }
+ else {
+ if ($pkg_interface <> "console")
+ update_output_window(gettext("The rules update task is complete..."));
+ }
+}
+
+// Remove old $tmpfname files
+if (is_dir("{$tmpfname}")) {
+ if ($pkg_interface <> "console") {
+ update_status(gettext("Cleaning up after rules extraction..."));
+ update_output_window(gettext("Removing {$tmpfname} directory..."));
+ }
+ exec("/bin/rm -r {$tmpfname}");
+}
+
+if ($pkg_interface <> "console") {
+ update_status(gettext("The Rules update has finished..."));
+ update_output_window("");
+}
+log_error(gettext("[Suricata] The Rules update has finished."));
+error_log(gettext("The Rules update has finished. Time: " . date("Y-m-d H:i:s"). "\n\n"), 3, $suricata_rules_upd_log);
+conf_mount_ro();
+
+// Restore the state of $pkg_interface
+$pkg_interface = $pkg_interface_orig;
+
+/* Save this update status to the configuration file */
+if ($update_errors)
+ $config['installedpackages']['suricata']['config'][0]['last_rule_upd_status'] = gettext("failed");
+else
+ $config['installedpackages']['suricata']['config'][0]['last_rule_upd_status'] = gettext("success");
+$config['installedpackages']['suricata']['config'][0]['last_rule_upd_time'] = time();
+write_config();
+
+?>