. * Copyright (C) 2003-2004 Manuel Kasper . * Copyright (C) 2006 Scott Ullrich * Copyright (C) 2009 Robert Zelaya Sr. Developer * Copyright (C) 2012 Ermal Luci * All rights reserved. * * Adapted for Suricata by: * Copyright (C) 2015 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("/usr/local/pkg/suricata/suricata.inc"); require_once("/usr/local/pkg/suricata/suricata_defs.inc"); global $g, $pkg_interface, $suricata_gui_include, $rebuild_rules, $static_output; $suricatadir = SURICATADIR; $suricatalogdir = SURICATALOGDIR; $mounted_rw = FALSE; /* 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']; $snort_filename = $config['installedpackages']['suricata']['config'][0]['snort_rules_file']; $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 = "{$g['tmp_path']}/suricata_rules_up"; /* Snort VRT Rules filenames and URL */ $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 */ if (!is_subsystem_dirty('mount')) { conf_mount_rw(); $mounted_rw = TRUE; } /* 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_if_exists("{$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_if_exists("{$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 = array( 100 => "100 Continue", 101 => "101 Switching Protocols", 200 => "200 OK", 201 => "201 Created", 202 => "202 Accepted", 203 => "203 Non-Authoritative Information", 204 => "204 No Content", 205 => "205 Reset Content", 206 => "206 Partial Content", 300 => "300 Multiple Choices", 301 => "301 Moved Permanently", 302 => "302 Found", 303 => "303 See Other", 304 => "304 Not Modified", 305 => "305 Use Proxy", 306 => "306 (Unused)", 307 => "307 Temporary Redirect", 400 => "400 Bad Request", 401 => "401 Unauthorized", 402 => "402 Payment Required", 403 => "403 Forbidden", 404 => "404 Not Found", 405 => "405 Method Not Allowed", 406 => "406 Not Acceptable", 407 => "407 Proxy Authentication Required", 408 => "408 Request Timeout", 409 => "409 Conflict", 410 => "410 Gone", 411 => "411 Length Required", 412 => "412 Precondition Failed", 413 => "413 Request Entity Too Large", 414 => "414 Request-URI Too Long", 415 => "415 Unsupported Media Type", 416 => "416 Requested Range Not Satisfiable", 417 => "417 Expectation Failed", 500 => "500 Internal Server Error", 501 => "501 Not Implemented", 502 => "502 Bad Gateway", 503 => "503 Service Unavailable", 504 => "504 Gateway Timeout", 505 => "505 HTTP Version Not Supported" ); // 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 // and to prevent useless spam from rules update cron job execution. This // prevents progress bar output during package sync and rules update cron task. if ($g['suricata_sync_in_progress'] || $pkg_interface == "console") 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 (Windows NT 6.1; WOW64) AppleWebKit/537.36 Chrome/43.0.2357.65 Safari/537.36"); curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, "TLSv1.2, TLSv1"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); 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 (isset($rfc2616[$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, $last_curl_error, $update_errors, $static_output; $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_LOGFILE); $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_LOGFILE); // 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_LOGFILE); return false; } else return true; } return true; } else { error_log(gettext("\t{$desc} md5 download failed.\n"), 3, SURICATA_RULES_UPD_LOGFILE); $suricata_err_msg = gettext("Server returned error code {$rc}."); if ($pkg_interface <> "console") { update_status(gettext("{$desc} md5 error ... Server returned error code {$rc} ...")); $static_output .= gettext("{$desc} will not be updated.\n\t{$suricata_err_msg}"); update_output_window($static_output); } 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_LOGFILE); if ($pkg_interface == "console") error_log(gettext("\tServer error message was: {$last_curl_error}\n"), 3, SURICATA_RULES_UPD_LOGFILE); error_log(gettext("\t{$desc} will not be updated.\n"), 3, SURICATA_RULES_UPD_LOGFILE); $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, $last_curl_error, $update_errors, $static_output; $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_LOGFILE); error_log(gettext("\tDownloading file '{$filename}'...\n"), 3, SURICATA_RULES_UPD_LOGFILE); $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_LOGFILE); // 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") { $static_output .= gettext("{$desc} file MD5 checksum failed!\n"); update_output_window($static_output); } 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_LOGFILE); error_log(gettext("\tDownloaded {$desc} file MD5: " . md5_file($file_dst) . "\n"), 3, SURICATA_RULES_UPD_LOGFILE); error_log(gettext("\tExpected {$desc} file MD5: {$file_md5}\n"), 3, SURICATA_RULES_UPD_LOGFILE); error_log(gettext("\t{$desc} file download failed. {$desc} will not be updated.\n"), 3, SURICATA_RULES_UPD_LOGFILE); $update_errors = true; return false; } return true; } else { if ($pkg_interface <> "console") { $static_output .= gettext("{$desc} file download failed!\n"); update_output_window($static_output); } 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_LOGFILE); if ($pkg_interface == "console") error_log(gettext("\tThe error text was: {$last_curl_error}\n"), 3, SURICATA_RULES_UPD_LOGFILE); error_log(gettext("\t{$desc} will not be updated.\n"), 3, SURICATA_RULES_UPD_LOGFILE); $update_errors = true; return false; } } /* Start of main code */ /* remove old $tmpfname files if present */ if (is_dir("{$tmpfname}")) rmdir_recursive("{$tmpfname}"); /* Make sure required suricatadirs exsist */ safe_mkdir("{$suricatadir}rules"); safe_mkdir("{$tmpfname}"); safe_mkdir("{$suricatalogdir}"); /* See if we need to automatically clear the Update Log based on 1024K size limit */ if (file_exists(SURICATA_RULES_UPD_LOGFILE)) { if (1048576 < filesize(SURICATA_RULES_UPD_LOGFILE)) unlink_if_exists("{SURICATA_RULES_UPD_LOGFILE}"); } /* 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_LOGFILE); $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 (empty($snort_filename)) { log_error(gettext("No snortrules-snapshot filename has been set on Snort pkg GLOBAL SETTINGS tab. Snort VRT rules cannot be updated.")); error_log(gettext("\tWARNING-- No snortrules-snapshot filename set on GLOBAL SETTINGS tab. Snort VRT rules cannot be updated!\n"), 3, SURICATA_RULES_UPD_LOGFILE); $snortdownload = 'off'; } elseif (suricata_check_rule_md5("{$snort_rule_url}{$snort_filename_md5}?oinkcode={$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}?oinkcode={$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...")); $static_output .= gettext("Installing {$et_name} rules..."); update_output_window($static_output); } error_log(gettext("\tExtracting and installing {$et_name} rules...\n"), 3, SURICATA_RULES_UPD_LOGFILE); 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", "dns-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...")); $static_output .= gettext(" done.\n"); update_output_window($static_output); } error_log(gettext("\tInstallation of {$et_name} rules completed.\n"), 3, SURICATA_RULES_UPD_LOGFILE); rmdir_recursive("{$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...")); $static_output .= gettext("Installing Sourcefire VRT rules..."); update_output_window($static_output); } error_log(gettext("\tExtracting and installing Snort VRT rules...\n"), 3, SURICATA_RULES_UPD_LOGFILE); /* 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}"); } rmdir_recursive("{$tmpfname}/snortrules"); /* extract base etc files */ if ($pkg_interface <> "console") { update_status(gettext("Extracting Snort VRT 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}"); } rmdir_recursive("{$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...")); $static_output .= gettext(" done.\n"); update_output_window($static_output); } error_log(gettext("\tInstallation of Snort VRT rules completed.\n"), 3, SURICATA_RULES_UPD_LOGFILE); } } /* 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...")); $static_output .= gettext("Installing Snort GPLv2 Community Rules..."); update_output_window($static_output); } error_log(gettext("\tExtracting and installing Snort GPLv2 Community Rules...\n"), 3, SURICATA_RULES_UPD_LOGFILE); 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...")); $static_output .= gettext(" done.\n"); update_output_window($static_output); } error_log(gettext("\tInstallation of Snort GPLv2 Community Rules completed.\n"), 3, SURICATA_RULES_UPD_LOGFILE); rmdir_recursive("{$tmpfname}/community"); } } // If removing deprecated rules categories, then do it if ($config['installedpackages']['suricata']['config'][0]['hide_deprecated_rules'] == "on") { log_error(gettext("[Suricata] Hide Deprecated Rules is enabled. Removing obsoleted rules categories.")); suricata_remove_dead_rules(); } 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_LOGFILE); /******************************************************************/ /* 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']) && count($config['installedpackages']['suricata']['rule']) > 0) { /* Set the flag to force rule rebuilds since we downloaded new rules, */ /* except when in post-install mode. Post-install does its own rebuild. */ if ($g['suricata_postinstall']) $rebuild_rules = false; else $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)); $static_output .= $tmp; update_output_window($static_output); } suricata_apply_customizations($value, $if_real); $tmp = "\t" . $tmp . "\n"; error_log($tmp, 3, SURICATA_RULES_UPD_LOGFILE); $static_output .= gettext(" done.\n"); update_output_window($static_output); } } else { if ($pkg_interface <> "console") { $static_output .= gettext("Warning: No interfaces configured for Suricata were found!\nNo interfaces currently have Suricata configured and enabled on them."); update_output_window($static_output); } error_log(gettext("\tWarning: No interfaces configured for Suricata were found...\n"), 3, SURICATA_RULES_UPD_LOGFILE); } /* Clear the rebuild rules flag. */ $rebuild_rules = false; /* Restart Suricata if already running and we are not in post-install, so as to pick up the new rules. */ if (is_process_running("suricata") && !$g['suricata_postinstall'] && count($config['installedpackages']['suricata']['rule']) > 0) { // 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('Signaling Suricata to live-load the new set of rules...')); } 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_LOGFILE); foreach ($config['installedpackages']['suricata']['rule'] as $value) { 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_LOGFILE); } 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_LOGFILE); } else { if ($pkg_interface <> "console") { update_status(gettext('Restarting Suricata to activate the new set of rules...')); $static_output .= gettext("Restarting Suricata..."); update_output_window($static_output); } error_log(gettext("\tRestarting Suricata to activate the new set of rules...\n"), 3, SURICATA_RULES_UPD_LOGFILE); restart_service("suricata"); if ($pkg_interface <> "console") { $static_output .= gettext(" done.\n"); update_output_window($static_output); } 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_LOGFILE); } } } // Remove old $tmpfname files if (is_dir("{$tmpfname}")) { if ($pkg_interface <> "console") { update_status(gettext("Cleaning up after rules extraction...")); $static_output .= gettext("Removing {$tmpfname} directory..."); update_output_window($static_output); } rmdir_recursive("{$tmpfname}"); $static_output .= gettext(" done.\n"); update_output_window($static_output); } if ($pkg_interface <> "console") { update_status(gettext("The Rules update has finished...")); $static_output .= gettext("Rules update task complete.\n"); update_output_window($static_output); } 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_LOGFILE); /* Remount filesystem read-only if we changed it in this module */ if ($mounted_rw == TRUE) 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("Suricata pkg: updated status for updated rules package(s) check.", FALSE); ?>