From 10cab278e653f00bd8ec0ee0e82d30e5c7798042 Mon Sep 17 00:00:00 2001
From: bmeeks8
Date: Wed, 19 Feb 2014 14:08:14 -0500
Subject: BETA version of Suricata 1.4.6 IDS package v0.1 for pfSense.
---
config/suricata/README.md | 6 +
config/suricata/suricata.inc | 2110 ++++++++++++++++++++
config/suricata/suricata.priv.inc | 46 +
config/suricata/suricata.xml | 241 +++
config/suricata/suricata_alerts.php | 578 ++++++
config/suricata/suricata_app_parsers.php | 340 ++++
config/suricata/suricata_barnyard.php | 503 +++++
config/suricata/suricata_check_cron_misc.inc | 109 +
.../suricata/suricata_check_for_rule_updates.php | 683 +++++++
config/suricata/suricata_define_vars.php | 290 +++
config/suricata/suricata_download_rules.php | 97 +
config/suricata/suricata_download_updates.php | 241 +++
config/suricata/suricata_flow_stream.php | 680 +++++++
config/suricata/suricata_generate_yaml.php | 515 +++++
config/suricata/suricata_global.php | 456 +++++
config/suricata/suricata_import_aliases.php | 272 +++
config/suricata/suricata_interfaces.php | 474 +++++
config/suricata/suricata_interfaces_edit.php | 911 +++++++++
config/suricata/suricata_libhtp_policy_engine.php | 329 +++
config/suricata/suricata_list_view.php | 100 +
config/suricata/suricata_log_view.php | 86 +
config/suricata/suricata_logs_browser.php | 217 ++
config/suricata/suricata_os_policy_engine.php | 275 +++
config/suricata/suricata_post_install.php | 139 ++
config/suricata/suricata_rules.php | 790 ++++++++
config/suricata/suricata_rules_edit.php | 154 ++
config/suricata/suricata_rules_flowbits.php | 306 +++
config/suricata/suricata_rulesets.php | 596 ++++++
config/suricata/suricata_select_alias.php | 226 +++
config/suricata/suricata_suppress.php | 172 ++
config/suricata/suricata_suppress_edit.php | 213 ++
config/suricata/suricata_uninstall.php | 133 ++
config/suricata/suricata_yaml_template.inc | 302 +++
33 files changed, 12590 insertions(+)
create mode 100644 config/suricata/README.md
create mode 100644 config/suricata/suricata.inc
create mode 100644 config/suricata/suricata.priv.inc
create mode 100644 config/suricata/suricata.xml
create mode 100644 config/suricata/suricata_alerts.php
create mode 100644 config/suricata/suricata_app_parsers.php
create mode 100644 config/suricata/suricata_barnyard.php
create mode 100644 config/suricata/suricata_check_cron_misc.inc
create mode 100644 config/suricata/suricata_check_for_rule_updates.php
create mode 100644 config/suricata/suricata_define_vars.php
create mode 100644 config/suricata/suricata_download_rules.php
create mode 100644 config/suricata/suricata_download_updates.php
create mode 100644 config/suricata/suricata_flow_stream.php
create mode 100644 config/suricata/suricata_generate_yaml.php
create mode 100644 config/suricata/suricata_global.php
create mode 100644 config/suricata/suricata_import_aliases.php
create mode 100644 config/suricata/suricata_interfaces.php
create mode 100644 config/suricata/suricata_interfaces_edit.php
create mode 100644 config/suricata/suricata_libhtp_policy_engine.php
create mode 100644 config/suricata/suricata_list_view.php
create mode 100644 config/suricata/suricata_log_view.php
create mode 100644 config/suricata/suricata_logs_browser.php
create mode 100644 config/suricata/suricata_os_policy_engine.php
create mode 100644 config/suricata/suricata_post_install.php
create mode 100644 config/suricata/suricata_rules.php
create mode 100644 config/suricata/suricata_rules_edit.php
create mode 100644 config/suricata/suricata_rules_flowbits.php
create mode 100644 config/suricata/suricata_rulesets.php
create mode 100644 config/suricata/suricata_select_alias.php
create mode 100644 config/suricata/suricata_suppress.php
create mode 100644 config/suricata/suricata_suppress_edit.php
create mode 100644 config/suricata/suricata_uninstall.php
create mode 100644 config/suricata/suricata_yaml_template.inc
diff --git a/config/suricata/README.md b/config/suricata/README.md
new file mode 100644
index 00000000..2ec1d9a3
--- /dev/null
+++ b/config/suricata/README.md
@@ -0,0 +1,6 @@
+pfsense-suricata
+================
+
+Suricata package port for pfSense
+
+This is a port of the Suricata package for pfSense 2.1 and higher. It is currently under development and is still considered BETA software. Use on production systems is not recommended.
diff --git a/config/suricata/suricata.inc b/config/suricata/suricata.inc
new file mode 100644
index 00000000..95b95711
--- /dev/null
+++ b/config/suricata/suricata.inc
@@ -0,0 +1,2110 @@
+&1 &");
+ }
+}
+
+function suricata_barnyard_reload_config($suricatacfg, $signal="HUP") {
+
+ /**************************************************************/
+ /* This function sends the passed SIGNAL to the Barnyard2 */
+ /* instance on the passed interface to cause Barnyard to */
+ /* reload and parse the running configuration without */
+ /* impacting packet processing. It also executes the reload */
+ /* as a background process and returns control immediately */
+ /* to the caller. */
+ /* */
+ /* $signal = HUP (default) parses and reloads config. */
+ /**************************************************************/
+ global $config, $g;
+
+ $suricatadir = SURICATADIR;
+ $suricata_uuid = $suricatacfg['uuid'];
+ $if_real = suricata_get_real_interface($suricatacfg['interface']);
+
+ /******************************************************/
+ /* Only send the SIGHUP if Barnyard2 is running and */
+ /* we can find a valid PID for the process. */
+ /******************************************************/
+ if (file_exists("{$g['varrun_path']}/barnyard2_{$if_real}{$suricata_uuid}.pid") && isvalidpid("{$g['varrun_path']}/barnyard2_{$if_real}{$suricata_uuid}.pid")) {
+ log_error("[Suricata] Barnyard2 CONFIG RELOAD initiated for {$suricatacfg['descr']} ({$if_real})...");
+ exec("/bin/pkill -{$signal} -F {$g['varrun_path']}/barnyard2_{$if_real}{$suricata_uuid}.pid 2>&1 &");
+ }
+}
+
+function suricata_get_friendly_interface($interface) {
+
+ if (function_exists('convert_friendly_interface_to_friendly_descr'))
+ $iface = convert_friendly_interface_to_friendly_descr($interface);
+ else {
+ if (!$interface || ($interface == "wan"))
+ $iface = "WAN";
+ else if(strtolower($interface) == "lan")
+ $iface = "LAN";
+ else if(strtolower($interface) == "pppoe")
+ $iface = "PPPoE";
+ else if(strtolower($interface) == "pptp")
+ $iface = "PPTP";
+ else
+ $iface = strtoupper($interface);
+ }
+
+ return $iface;
+}
+
+function suricata_get_real_interface($interface) {
+ global $config;
+
+ $lc_interface = strtolower($interface);
+ if (function_exists('get_real_interface'))
+ return get_real_interface($lc_interface);
+ else {
+ if ($lc_interface == "lan") {
+ if ($config['inerfaces']['lan'])
+ return $config['interfaces']['lan']['if'];
+ return $interface;
+ }
+ if ($lc_interface == "wan")
+ return $config['interfaces']['wan']['if'];
+ $ifdescrs = array();
+ for ($j = 1; isset($config['interfaces']['opt' . $j]); $j++) {
+ $ifname = "opt{$j}";
+ if(strtolower($ifname) == $lc_interface)
+ return $config['interfaces'][$ifname]['if'];
+ if(isset($config['interfaces'][$ifname]['descr']) && (strtolower($config['interfaces'][$ifname]['descr']) == $lc_interface))
+ return $config['interfaces'][$ifname]['if'];
+ }
+ }
+
+ return $interface;
+}
+
+function suricata_get_blocked_ips() {
+
+ return array();
+
+}
+
+/* func builds custom white lists */
+function suricata_find_list($find_name, $type = 'whitelist') {
+ global $config;
+
+ $suricataglob = $config['installedpackages']['suricata'];
+ if (!is_array($suricataglob[$type]))
+ return "";
+ if (!is_array($suricataglob[$type]['item']))
+ return "";
+
+ foreach ($suricataglob[$type]['item'] as $value) {
+ if ($value['name'] == $find_name)
+ return $value;
+ }
+
+ return array();
+}
+
+function suricata_build_list($suricatacfg, $listname = "", $whitelist = false) {
+
+ /***********************************************************/
+ /* The default is to build a HOME_NET variable unless */
+ /* '$whitelist' is set to 'true' when calling. */
+ /***********************************************************/
+
+ global $config, $g, $aliastable, $filterdns;
+ $home_net = array();
+
+ if ($listname == 'default' || empty($listname)) {
+ $localnet = 'yes'; $wanip = 'yes'; $wangw = 'yes'; $wandns = 'yes'; $vips = 'yes'; $vpns = 'yes';
+ } else {
+ $list = suricata_find_list($listname);
+ if (empty($list))
+ return $list;
+ $localnet = $list['localnets'];
+ $wanip = $list['wanips'];
+ $wangw = $list['wangateips'];
+ $wandns = $list['wandnsips'];
+ $vips = $list['vips'];
+ $vpns = $list['vpnips'];
+ if (!empty($list['address']) && is_alias($list['address']))
+ $home_net = explode(" ", trim(filter_expand_alias($list['address'])));
+ }
+
+ // Always add loopback to HOME_NET and whitelist (ftphelper)
+ if (!in_array("127.0.0.1", $home_net))
+ $home_net[] = "127.0.0.1";
+
+ /********************************************************************/
+ /* Always put the interface running Suricata in HOME_NET and */
+ /* whitelist unless it's the WAN. WAN options are handled further */
+ /* down. If the user specifically chose not to include LOCAL_NETS */
+ /* in the WHITELIST, then do not include the Suricata interface */
+ /* subnet in the WHITELIST. We do include the actual LAN interface */
+ /* IP for Suricata, though, to prevent locking out the firewall. */
+ /********************************************************************/
+ $suricataip = get_interface_ip($suricatacfg['interface']);
+ if (!$whitelist || $localnet == 'yes' || empty($localnet)) {
+ if (is_ipaddr($suricataip)) {
+ if ($suricatacfg['interface'] <> "wan") {
+ $sn = get_interface_subnet($suricatacfg['interface']);
+ $ip = gen_subnet($suricataip, $sn) . "/{$sn}";
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ }
+ }
+ else {
+ if (is_ipaddr($suricataip)) {
+ if (!in_array($suricataip, $home_net))
+ $home_net[] = $suricataip;
+ }
+ }
+
+ $suricataip = get_interface_ipv6($suricatacfg['interface']);
+ if (!$whitelist || $localnet == 'yes' || empty($localnet)) {
+ if (is_ipaddrv6($suricataip)) {
+ if ($suricatacfg['interface'] <> "wan") {
+ $sn = get_interface_subnetv6($suricatacfg['interface']);
+ $ip = gen_subnetv6($suricataip, $sn). "/{$sn}";
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ }
+ }
+ else {
+ if (is_ipaddrv6($suricataip)) {
+ if (!in_array($suricataip, $home_net))
+ $home_net[] = $suricataip;
+ }
+ }
+
+ if (!$whitelist || $localnet == 'yes' || empty($localnet)) {
+ /*************************************************************************/
+ /* Iterate through the interface list and write out whitelist items and */
+ /* also compile a HOME_NET list of all the local interfaces for suricata. */
+ /* Skip the WAN interface as we do not typically want that whole subnet */
+ /* whitelisted (just the i/f IP itself which was handled earlier). */
+ /*************************************************************************/
+ $int_array = get_configured_interface_list();
+ foreach ($int_array as $int) {
+ if ($int == "wan")
+ continue;
+ $subnet = get_interface_ip($int);
+ if (is_ipaddr($subnet)) {
+ $sn = get_interface_subnet($int);
+ $ip = gen_subnet($subnet, $sn) . "/{$sn}";
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ if ($int == "wan")
+ continue;
+ $subnet = get_interface_ipv6($int);
+ if (is_ipaddrv6($subnet)) {
+ $sn = get_interface_subnetv6($int);
+ $ip = gen_subnetv6($subnet, $sn). "/{$sn}";
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ }
+ }
+
+ if ($wanip == 'yes') {
+ $ip = get_interface_ip("wan");
+ if (is_ipaddr($ip)) {
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ $ip = get_interface_ipv6("wan");
+ if (is_ipaddrv6($ip)) {
+ if (!in_array($ip, $home_net))
+ $home_net[] = $ip;
+ }
+ }
+
+ if ($wangw == 'yes') {
+ // Grab the default gateway if set
+ $default_gw = exec("/sbin/route -n get default |grep 'gateway:' | /usr/bin/awk '{ print $2 }'");
+ if (is_ipaddr($default_gw) && !in_array($default_gw, $home_net))
+ $home_net[] = $default_gw;
+ if (is_ipaddrv6($default_gw) && !in_array($default_gw, $home_net))
+ $home_net[] = $default_gw;
+
+ // Get any other interface gateway and put in $HOME_NET if not there already
+ $gw = get_interface_gateway($suricatacfg['interface']);
+ if (is_ipaddr($gw) && !in_array($gw, $home_net))
+ $home_net[] = $gw;
+ $gw = get_interface_gateway_v6($suricatacfg['interface']);
+ if (is_ipaddrv6($gw) && !in_array($gw, $home_net))
+ $home_net[] = $gw;
+ }
+
+ if ($wandns == 'yes') {
+ // Add DNS server for WAN interface to whitelist
+ $dns_servers = get_dns_servers();
+ foreach ($dns_servers as $dns) {
+ if ($dns && !in_array($dns, $home_net))
+ $home_net[] = $dns;
+ }
+ }
+
+ if($vips == 'yes') {
+ // iterate all vips and add to whitelist
+ if (is_array($config['virtualip']) && is_array($config['virtualip']['vip'])) {
+ foreach($config['virtualip']['vip'] as $vip) {
+ if ($vip['subnet'] && $vip['mode'] != 'proxyarp') {
+ if (!in_array("{$vip['subnet']}/{$vip['subnet_bits']}", $home_net))
+ $home_net[] = "{$vip['subnet']}/{$vip['subnet_bits']}";
+ }
+ }
+ }
+ }
+
+ // grab a list of vpns and whitelist if user desires
+ if ($vpns == 'yes') {
+ $vpns_list = filter_get_vpns_list();
+ if (!empty($vpns_list)) {
+ // Convert the returned space-delimited string to an array
+ // and then add each VPN address to our HOME_NET array.
+ $vpns = explode(" ", $vpns_list);
+ foreach ($vpns as $vpn)
+ $home_net[] = trim($vpn);
+ unset($vpns, $vpns_list);
+ }
+ }
+
+ $valresult = array();
+ foreach ($home_net as $vald) {
+ if (empty($vald))
+ continue;
+ $vald = trim($vald);
+ if (empty($valresult[$vald]))
+ $valresult[$vald] = $vald;
+ }
+
+ // Release memory no longer required
+ unset($home_net);
+
+ // Sort the list and return it
+ natsort($valresult);
+ return $valresult;
+}
+
+function suricata_rules_up_install_cron($should_install) {
+ global $config, $g;
+
+ if(!$config['cron']['item'])
+ $config['cron']['item'] = array();
+
+ $x=0;
+ $is_installed = false;
+ foreach($config['cron']['item'] as $item) {
+ if (strstr($item['command'], "suricata_check_for_rule_updates.php")) {
+ $is_installed = true;
+ break;
+ }
+ $x++;
+ }
+ $suricata_rules_up_info_ck = $config['installedpackages']['suricata']['config'][0]['autoruleupdate'];
+
+ // See if a customized start time has been set for rule file updates
+ if (!empty($config['installedpackages']['suricata']['config'][0]['autoruleupdatetime']))
+ $suricata_rules_upd_time = $config['installedpackages']['suricata']['config'][0]['autoruleupdatetime'];
+ else
+ $suricata_rules_upd_time = "00:03";
+
+ if ($suricata_rules_up_info_ck == "6h_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $hour = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_hr = strval($hour);
+ for ($i=0; $i<3; $i++) {
+ $hour += 6;
+ if ($hour > 24)
+ $hour -= 24;
+ $suricata_rules_up_hr .= "," . strval($hour);
+ }
+ $suricata_rules_up_mday = "*";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ if ($suricata_rules_up_info_ck == "12h_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $hour = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_hr = strval($hour) . ",";
+ $hour += 12;
+ if ($hour > 24)
+ $hour -= 24;
+ $suricata_rules_up_hr .= strval($hour);
+ $suricata_rules_up_mday = "*";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ if ($suricata_rules_up_info_ck == "1d_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $suricata_rules_up_hr = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_mday = "*/1";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ if ($suricata_rules_up_info_ck == "4d_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $suricata_rules_up_hr = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_mday = "*/4";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ if ($suricata_rules_up_info_ck == "7d_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $suricata_rules_up_hr = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_mday = "*/7";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ if ($suricata_rules_up_info_ck == "28d_up") {
+ $suricata_rules_up_min = intval(substr($suricata_rules_upd_time, -2));
+ $suricata_rules_up_hr = intval(substr($suricata_rules_upd_time, 0, 2));
+ $suricata_rules_up_mday = "*/28";
+ $suricata_rules_up_month = "*";
+ $suricata_rules_up_wday = "*";
+ }
+ switch($should_install) {
+ case true:
+ $cron_item = array();
+ $cron_item['minute'] = $suricata_rules_up_min;
+ $cron_item['hour'] = $suricata_rules_up_hr;
+ $cron_item['mday'] = $suricata_rules_up_mday;
+ $cron_item['month'] = $suricata_rules_up_month;
+ $cron_item['wday'] = $suricata_rules_up_wday;
+ $cron_item['who'] = "root";
+ $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/bin/php -f /usr/local/www/suricata/suricata_check_for_rule_updates.php";
+
+ // Add cron job if not already installed, else just update the existing one
+ if (!$is_installed)
+ $config['cron']['item'][] = $cron_item;
+ elseif ($is_installed)
+ $config['cron']['item'][$x] = $cron_item;
+ break;
+ case false:
+ if($is_installed == true)
+ unset($config['cron']['item'][$x]);
+ break;
+ }
+}
+
+function suricata_loglimit_install_cron($should_install) {
+ global $config, $g;
+
+ if (!is_array($config['cron']['item']))
+ $config['cron']['item'] = array();
+
+ $x=0;
+ $is_installed = false;
+ foreach($config['cron']['item'] as $item) {
+ if (strstr($item['command'], 'suricata_check_cron_misc.inc')) {
+ $is_installed = true;
+ break;
+ }
+ $x++;
+ }
+
+ switch($should_install) {
+ case true:
+ if(!$is_installed) {
+ $cron_item = array();
+ $cron_item['minute'] = "*/5";
+ $cron_item['hour'] = "*";
+ $cron_item['mday'] = "*";
+ $cron_item['month'] = "*";
+ $cron_item['wday'] = "*";
+ $cron_item['who'] = "root";
+ $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/bin/php -f /usr/local/pkg/suricata/suricata_check_cron_misc.inc";
+ $config['cron']['item'][] = $cron_item;
+ }
+ break;
+ case false:
+ if($is_installed == true)
+ unset($config['cron']['item'][$x]);
+ break;
+ }
+}
+
+function sync_suricata_package_config() {
+ global $config, $g;
+
+ $suricatadir = SURICATADIR;
+ $rcdir = RCFILEPREFIX;
+
+ conf_mount_rw();
+
+ // Do not start config build if there are no Suricata-configured interfaces
+ if (!is_array($config['installedpackages']['suricata']) || !is_array($config['installedpackages']['suricata']['rule'])) {
+ @unlink("{$rcdir}/suricata.sh");
+ conf_mount_ro();
+ return;
+ }
+
+ $suricataconf = $config['installedpackages']['suricata']['rule'];
+ foreach ($suricataconf as $value) {
+ $if_real = suricata_get_real_interface($value['interface']);
+
+ // create a suricata.yaml file for interface
+ suricata_generate_yaml($value);
+
+ // create barnyard2.conf file for interface
+ if ($value['barnyard_enable'] == 'on')
+ suricata_generate_barnyard2_conf($value, $if_real);
+ }
+
+ // create suricata bootup file suricata.sh
+ suricata_create_rc();
+
+ $suricataglob = $config['installedpackages']['suricata']['config'][0];
+
+ suricata_loglimit_install_cron($suricataglob['suricataloglimit'] == 'on' ? true : false);
+
+ // set the suricata block hosts time IMPORTANT
+// suricata_rm_blocked_install_cron($suricataglob['rm_blocked'] != "never_b" ? true : false);
+
+ // set the suricata rules update time
+ suricata_rules_up_install_cron($suricataglob['autoruleupdate'] != "never_up" ? true : false);
+
+ write_config();
+ configure_cron();
+
+ // Do not attempt package sync if reinstalling package or booting
+// if (!$g['suricata_postinstall'] && !$g['booting'])
+// suricata_sync_on_changes();
+
+ conf_mount_ro();
+}
+
+function suricata_load_suppress_sigs($suricatacfg, $track_by=false) {
+
+ global $config;
+
+ /**********************************************************/
+ /* This function loads the GEN_ID and SIG_ID for all the */
+ /* suppressed alert entries from the Suppression List of */
+ /* the passed Suricata interface. The results are */
+ /* returned in an array with GEN_ID and SIG_ID as the */
+ /* primary keys. Any "track by_src" or "track by_dst" */
+ /* entries in the Suppression List are tacked on as */
+ /* additional keys in the array along with the IP address */
+ /* in either IPv4 or IPv6 format when $track_by is passed */
+ /* as true. */
+ /* */
+ /* Sample returned array: */
+ /* $suppress[1][2069] = "suppress" */
+ /* $suppress[1][2070]['by_src']['10.1.1.5'] = "suppress" */
+ /* $suppress[1][2070]['by_dst']['10.1.1.6'] = "suppress" */
+ /* */
+ /**********************************************************/
+
+ $suppress = array();
+
+ if (!is_array($config['installedpackages']['suricata']))
+ return;
+ if (!is_array($config['installedpackages']['suricata']['suppress']))
+ return;
+ if (!is_array($config['installedpackages']['suricata']['suppress']['item']))
+ return;
+ $a_suppress = $config['installedpackages']['suricata']['suppress']['item'];
+
+ foreach ($a_suppress as $a_id => $alist) {
+ if ($alist['name'] == $suricatacfg['suppresslistname']) {
+ if (!empty($alist['suppresspassthru'])) {
+ $tmplist = str_replace("\r", "", base64_decode($alist['suppresspassthru']));
+ $tmp = explode("\n", $tmplist);
+ foreach ($tmp as $line) {
+ // Skip any blank lines
+ if (trim($line, " \n") == "")
+ continue;
+ // Skip any comment lines
+ if (preg_match('/^\s*#/', $line))
+ continue;
+ /* See if entry suppresses GID:SID for all hosts */
+ if (preg_match('/\s*suppress\s*gen_id\b\s*(\d+),\s*sig_id\b\s*(\d+)\s*$/i', $line, $matches)) {
+ $genid = $matches[1];
+ $sigid = $matches[2];
+ if (!empty($genid) && !empty($sigid)) {
+ if (!is_array($suppress[$genid]))
+ $suppress[$genid] = array();
+ if (!is_array($suppress[$genid][$sigid]))
+ $suppress[$genid][$sigid] = array();
+ $suppress[$genid][$sigid] = "suppress";
+ }
+ }
+
+ /* Get "track by IP" entries if requested */
+ if ($track_by) {
+ /* See if entry suppresses only by SRC or DST IPv4 address */
+ if (preg_match('/\s*suppress\s*gen_id\b\s*(\d+),\s*sig_id\b\s*(\d+),\s*track\s*(by_src|by_dst),\s*ip\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*$/i', $line, $matches)) {
+ $genid = $matches[1];
+ $sigid = $matches[2];
+ $whichip = trim($matches[3]);
+ $ip = $matches[4];
+ if (!empty($genid) && !empty($sigid) && !empty($whichip) && !empty($ip)) {
+ if (!is_array($suppress[$genid]))
+ $suppress[$genid] = array();
+ if (!is_array($suppress[$genid][$sigid]))
+ $suppress[$genid][$sigid] = array();
+ if (!is_array($suppress[$genid][$sigid][$whichip]))
+ $suppress[$genid][$sigid][$whichip] = array();
+ if (!is_array($suppress[$genid][$sigid][$whichip][$ip]))
+ $suppress[$genid][$sigid][$whichip][$ip] = array();
+ $suppress[$genid][$sigid][$whichip][$ip] = "suppress";
+ }
+ }
+ /* See if entry suppresses only by SRC or DST IPv6 address */
+ if (preg_match('/\s*suppress\s*gen_id\b\s*(\d+),\s*sig_id\b\s*(\d+),\s*track\s*(by_src|by_dst),\s*ip\s*([0-9a-f\.:]+)\s*$/i', $line, $matches)) {
+ $genid = $matches[1];
+ $sigid = $matches[2];
+ $whichip = trim($matches[3]);
+ $ip = trim($matches[4]);
+ if (!empty($genid) && !empty($sigid) && !empty($whichip) && !empty($ip)) {
+ if (!is_array($suppress[$genid]))
+ $suppress[$genid] = array();
+ if (!is_array($suppress[$genid][$sigid]))
+ $suppress[$genid][$sigid] = array();
+ if (!is_array($suppress[$genid][$sigid][$whichip]))
+ $suppress[$genid][$sigid][$whichip] = array();
+ if (!is_array($suppress[$genid][$sigid][$whichip][$ip]))
+ $suppress[$genid][$sigid][$whichip][$ip] = array();
+ $suppress[$genid][$sigid][$whichip][$ip] = "suppress";
+ }
+ }
+ }
+ }
+ unset($tmp);
+ }
+ break;
+ }
+ }
+ unset($alist);
+ return $suppress;
+}
+
+function suricata_post_delete_logs($suricata_uuid = 0) {
+
+ /***********************************************/
+ /* This function cleans up related log files */
+ /* for the passed instance. These include */
+ /* Barnyard2 unified2 logs and pcap logs. */
+ /***********************************************/
+
+ global $config, $g;
+
+ // do nothing if no Suricata interfaces active
+ if (!is_array($config['installedpackages']['suricata']['rule']))
+ return;
+
+ foreach ($config['installedpackages']['suricata']['rule'] as $value) {
+ if ($value['uuid'] != $suricata_uuid)
+ continue;
+ $if_real = suricata_get_real_interface($value['interface']);
+ $suricata_log_dir = SURICATALOGDIR . "suricata_{$if_real}{$suricata_uuid}";
+
+ if ($if_real != '') {
+ /* Clean-up Barnyard2 files if any exist */
+ $filelist = glob("{$suricata_log_dir}/unified2.alert.*");
+ // Keep most recent file
+ unset($filelist[count($filelist) - 1]);
+ foreach ($filelist as $file)
+ @unlink($file);
+
+ /* Clean-up Barnyard2 archived files if any exist */
+ $filelist = glob("{$suricata_log_dir}/barnyard2/archive/unified2.alert.*");
+ foreach ($filelist as $file)
+ @unlink($file);
+
+ /* Clean-up packet capture files if any exist */
+ $filelist = glob("{$suricata_log_dir}/log.pcap.*");
+ // Keep most recent file
+ unset($filelist[count($filelist) - 1]);
+ foreach ($filelist as $file)
+ @unlink($file);
+ }
+ }
+}
+
+/* This returns size of passed directory or file in 1024-byte blocks */
+function suricata_Getdirsize($node) {
+ if(!is_readable($node))
+ return false;
+
+ $blah = exec( "/usr/bin/du -kdc $node" );
+ return substr( $blah, 0, strpos($blah, 9) );
+}
+
+function suricata_build_sid_msg_map($rules_path, $sid_file) {
+
+ /*************************************************************/
+ /* This function reads all the rules file in the passed */
+ /* $rules_path variable and produces a properly formatted */
+ /* sid-msg.map file for use by Suricata and/or barnyard2. */
+ /*************************************************************/
+
+ $sidMap = array();
+ $rule_files = array();
+
+ // First check if we were passed a directory, a single file
+ // or an array of filenames to read. Set our $rule_files
+ // variable accordingly. If we can't figure it out, return
+ // and don't write a sid_msg_map file.
+ if (is_string($rules_path)) {
+ if (is_dir($rules_path))
+ $rule_files = glob($rules_path . "*.rules");
+ elseif (is_file($rules_path))
+ $rule_files = (array)$rules_path;
+ }
+ elseif (is_array($rules_path))
+ $rule_files = $rules_path;
+ else
+ return;
+
+ // Read the rule files into an array, then iterate the list
+ foreach ($rule_files as $file) {
+
+ // Don't process files with "deleted" in the filename
+ if (stristr($file, "deleted"))
+ continue;
+
+ // Read the file into an array, skipping missing files.
+ if (!file_exists($file))
+ continue;
+
+ $rules_array = file($file, FILE_SKIP_EMPTY_LINES);
+ $record = "";
+ $b_Multiline = false;
+
+ // Read and process each line from the rules in the current file
+ foreach ($rules_array as $rule) {
+
+ // Skip any non-rule lines unless we're in multiline mode.
+ if (!preg_match('/^\s*#*\s*(alert|drop|pass)/i', $rule) && !$b_Multiline)
+ continue;
+
+ // Test for a multi-line rule, and reassemble the
+ // pieces back into a single line.
+ if (preg_match('/\\\\s*[\n]$/m', $rule)) {
+ $rule = substr($rule, 0, strrpos($rule, '\\'));
+ $record .= $rule;
+ $b_Multiline = true;
+ continue;
+ }
+ // If the last segment of a multiline rule, then
+ // append it onto the previous parts to form a
+ // single-line rule for further processing below.
+ elseif (!preg_match('/\\\\s*[\n]$/m', $rule) && $b_Multiline) {
+ $record .= $rule;
+ $rule = $record;
+ }
+ $b_Multiline = false;
+ $record = "";
+
+ // Parse the rule to find sid and any references.
+ $sid = '';
+ $msg = '';
+ $matches = '';
+ $sidEntry = '';
+ if (preg_match('/\bmsg\s*:\s*"(.+?)"\s*;/i', $rule, $matches))
+ $msg = trim($matches[1]);
+ if (preg_match('/\bsid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ $sid = trim($matches[1]);
+ if (!empty($sid) && !empty($msg)) {
+ $sidEntry = $sid . ' || ' . $msg;
+ preg_match_all('/\breference\s*:\s*([^\;]+)/i', $rule, $matches);
+ foreach ($matches[1] as $ref)
+ $sidEntry .= " || " . trim($ref);
+ $sidEntry .= "\n";
+ if (!is_array($sidMap[$sid]))
+ $sidMap[$sid] = array();
+ $sidMap[$sid] = $sidEntry;
+ }
+ }
+ }
+ // Sort the generated sid-msg map by sid
+ ksort($sidMap);
+
+ // Now print the result to the supplied file
+ @file_put_contents($sid_file, array_values($sidMap));
+}
+
+function suricata_merge_reference_configs($cfg_in, $cfg_out) {
+
+ /***********************************************************/
+ /* This function takes a list of "reference.config" files */
+ /* in the $cfg_in array and merges them into a single */
+ /* file specified by $cfg_out. The merging is done so */
+ /* no duplication of lines occurs in the output file. */
+ /***********************************************************/
+
+ $outMap = array();
+ foreach ($cfg_in as $file) {
+ if (!file_exists($file))
+ continue;
+ $in = file($file, FILE_SKIP_EMPTY_LINES);
+ foreach ($in as $line) {
+ /* Skip comment lines */
+ if (preg_match('/^\s*#/', $line))
+ continue;
+ if (preg_match('/(\:)\s*(\w+)\s*(.*)/', $line, $matches)) {
+ if (!empty($matches[2]) && !empty($matches[3])) {
+ $matches[2] = trim($matches[2]);
+ if (!array_key_exists($matches[2], $outMap)) {
+ if (!is_array($outMap[$matches[2]]))
+ $outMap[$matches[2]] = array();
+ $outMap[$matches[2]] = trim($matches[3]);
+ }
+ }
+ }
+ }
+ }
+ // Sort the new reference map.
+ uksort($outMap,'strnatcasecmp');
+
+ // Do NOT write an empty references.config file, just
+ // exit instead.
+ if (empty($outMap))
+ return false;
+
+ // Format and write it to the supplied output file.
+ $format = "config reference: %-12s %s\n";
+ foreach ($outMap as $key=>$value)
+ $outMap[$key] = sprintf($format, $key, $value);
+ @file_put_contents($cfg_out, array_values($outMap));
+ return true;
+}
+
+function suricata_merge_classification_configs($cfg_in, $cfg_out) {
+
+ /************************************************************/
+ /* This function takes a list of "classification.config" */
+ /* files in the $cfg_in array and merges them into a */
+ /* single file specified by $cfg_out. The merging is done */
+ /* so no duplication of lines occurs in the output file. */
+ /************************************************************/
+
+ $outMap = array();
+ foreach ($cfg_in as $file) {
+ if (!file_exists($file))
+ continue;
+ $in = file($file, FILE_SKIP_EMPTY_LINES);
+ foreach ($in as $line) {
+ if (preg_match('/(.*:)(\s*.*),(.*),(.*)/', $line, $matches)) {
+ /* Skip comment lines */
+ if (preg_match('/^\s*#/', $line))
+ continue;
+ if (!empty($matches[2]) && !empty($matches[3]) && !empty($matches[4])) {
+ $matches[2] = trim($matches[2]);
+ if (!array_key_exists($matches[2], $outMap)) {
+ if (!is_array($outMap[$matches[2]]))
+ $outMap[$matches[2]] = array();
+ $outMap[$matches[2]] = trim($matches[3]) . "," . trim($matches[4]);
+ }
+ }
+ }
+ }
+ }
+ // Sort the new classification map.
+ uksort($outMap,'strnatcasecmp');
+
+ // Do NOT write an empty classification.config file, just
+ // exit instead.
+ if (empty($outMap))
+ return false;
+
+ // Format and write it to the supplied output file.
+ $format = "config classification: %s,%s\n";
+ foreach ($outMap as $key=>$value)
+ $outMap[$key] = sprintf($format, $key, $value);
+ @file_put_contents($cfg_out, array_values($outMap));
+ return true;
+}
+
+function suricata_load_rules_map($rules_path) {
+
+ /***************************************************************/
+ /* This function loads and returns an array with all the rules */
+ /* found in the *.rules files in the passed rules path. */
+ /* */
+ /* $rules_path can be: */
+ /* a directory (assumed to contain *.rules files) */
+ /* a filename (identifying a specific *.rules file) */
+ /* an array of filenames (identifying *.rules files) */
+ /***************************************************************/
+
+ $map_ref = array();
+ $rule_files = array();
+
+ if (empty($rules_path))
+ return $map_ref;
+
+ /***************************************************************
+ * Read all the rules into the map array.
+ * The structure of the map array is:
+ *
+ * map[gid][sid]['rule']['category']['disabled']['flowbits']
+ *
+ * where:
+ * gid = Generator ID from rule, or 1 if general text
+ * rule
+ * sid = Signature ID from rule
+ * rule = Complete rule text
+ * category = File name of file containing the rule
+ * action = alert, drop, reject or pass
+ * disabled = 1 if rule is disabled (commented out), 0 if
+ * rule is enabled
+ * flowbits = Array of applicable flowbits if rule contains
+ * flowbits options
+ ***************************************************************/
+
+ // First check if we were passed a directory, a single file
+ // or an array of filenames to read. Set our $rule_files
+ // variable accordingly. If we can't figure it out, return
+ // an empty rules map array.
+ if (is_string($rules_path)) {
+ if (is_dir($rules_path))
+ $rule_files = glob($rules_path . "*.rules");
+ elseif (is_file($rules_path))
+ $rule_files = (array)$rules_path;
+ }
+ elseif (is_array($rules_path))
+ $rule_files = $rules_path;
+ else
+ return $map_ref;
+
+ // Read the rule files into an array, then iterate the list
+ // to process the rules from the files one-by-one.
+ foreach ($rule_files as $file) {
+
+ // Don't process files with "deleted" in the filename.
+ if (stristr($file, "deleted"))
+ continue;
+
+ // Read the file contents into an array, skipping
+ // missing files.
+ if (!file_exists($file))
+ continue;
+
+ $rules_array = file($file, FILE_SKIP_EMPTY_LINES);
+ $record = "";
+ $b_Multiline = false;
+
+ // Read and process each line from the rules in the
+ // current file into an array.
+ foreach ($rules_array as $rule) {
+
+ // Skip any lines that may be just spaces.
+ if (trim($rule, " \n") == "")
+ continue;
+
+ // Skip any non-rule lines unless we're in
+ // multiline mode.
+ if (!preg_match('/^\s*#*\s*(alert|drop|pass|reject)/i', $rule) && !$b_Multiline)
+ continue;
+
+ // Test for a multi-line rule; loop and reassemble
+ // the pieces back into a single line.
+ if (preg_match('/\\\\s*[\n]$/m', $rule)) {
+ $rule = substr($rule, 0, strrpos($rule, '\\'));
+ $record .= $rule;
+ $b_Multiline = true;
+ continue;
+ }
+ // If the last segment of a multiline rule, then
+ // append it onto the previous parts to form a
+ // single-line rule for further processing below.
+ elseif (!preg_match('/\\\\s*[\n]$/m', $rule) && $b_Multiline) {
+ $record .= $rule;
+ $rule = $record;
+ }
+
+ // We have an actual single-line rule, or else a
+ // re-assembled multiline rule that is now a
+ // single-line rule, so store it in our rules map.
+
+ // Get and test the SID. If we don't find one,
+ // ignore and skip this rule as it is invalid.
+ $sid = suricata_get_sid($rule);
+ if (empty($sid)) {
+ $b_Multiline = false;
+ $record = "";
+ continue;
+ }
+
+ $gid = suricata_get_gid($rule);
+ if (!is_array($map_ref[$gid]))
+ $map_ref[$gid] = array();
+ if (!is_array($map_ref[$gid][$sid]))
+ $map_ref[$gid][$sid] = array();
+ $map_ref[$gid][$sid]['rule'] = $rule;
+ $map_ref[$gid][$sid]['category'] = basename($file, ".rules");
+
+ // Grab the rule action
+ $matches = array();
+ if (preg_match('/^\s*#*\s*(alert|drop|pass|reject)/i', $rule, $matches))
+ $map_ref[$gid][$sid]['action'] = $matches[1];
+ else
+ $map_ref[$gid][$sid]['action'] = "";
+
+ // Determine if default state is "disabled"
+ if (preg_match('/^\s*\#+/', $rule))
+ $map_ref[$gid][$sid]['disabled'] = 1;
+ else
+ $map_ref[$gid][$sid]['disabled'] = 0;
+
+ // Grab any associated flowbits from the rule.
+ $map_ref[$gid][$sid]['flowbits'] = suricata_get_flowbits($rule);
+
+ // Reset our local flag and record variables
+ // for the next rule in the set.
+ $b_Multiline = false;
+ $record = "";
+ }
+
+ // Zero out our processing array and get the next file.
+ unset($rules_array);
+ }
+ return $map_ref;
+}
+
+function suricata_get_gid($rule) {
+
+ /****************************************************************/
+ /* If a gid is defined, then return it, else default to "1" for */
+ /* general text rules match. */
+ /****************************************************************/
+
+ if (preg_match('/\bgid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ return trim($matches[1]);
+ else
+ return "1";
+}
+
+function suricata_get_sid($rule) {
+
+ /***************************************************************/
+ /* If a sid is defined, then return it, else default to an */
+ /* empty value. */
+ /***************************************************************/
+
+ if (preg_match('/\bsid\s*:\s*(\d+)\s*;/i', $rule, $matches))
+ return trim($matches[1]);
+ else
+ return "";
+}
+
+function suricata_get_msg($rule) {
+
+ /**************************************************************/
+ /* Return the MSG section of the passed rule as a string. */
+ /**************************************************************/
+
+ $msg = "";
+ if (preg_match('/\bmsg\s*:\s*"(.+?)"\s*;/i', $rule, $matches))
+ $msg = trim($matches[1]);
+ return $msg;
+}
+
+function suricata_get_flowbits($rule) {
+
+ /*************************************************************/
+ /* This will pull out "flowbits:" options from the rule text */
+ /* and return them in an array (minus the "flowbits:" part). */
+ /*************************************************************/
+
+ $flowbits = array();
+
+ // Grab any "flowbits:set, setx, unset, isset or toggle" options first.
+ // Examine flowbits targets for logical operators to capture all targets.
+ if (preg_match_all('/flowbits\b\s*:\s*(set|setx|unset|toggle|isset|isnotset)\s*,([^;]+)/i', $rule, $matches)) {
+ $i = -1;
+ while (++$i < count($matches[1])) {
+ $action = trim($matches[1][$i]);
+ $target = preg_split('/[&|]/', $matches[2][$i]);
+ foreach ($target as $t)
+ $flowbits[] = "{$action}," . trim($t);
+ }
+ }
+
+ // Include the "flowbits:noalert or reset" options, if present.
+ if (preg_match_all('/flowbits\b\s*:\s*(noalert|reset)\b/i', $rule, $matches)) {
+ $i = -1;
+ while (++$i < count($matches[1])) {
+ $flowbits[] = trim($matches[1][$i]);
+ }
+ }
+
+ return $flowbits;
+}
+
+function suricata_get_checked_flowbits($rules_map) {
+
+ /*************************************************************/
+ /* This function checks all the currently enabled rules to */
+ /* find any checked flowbits, and returns the checked */
+ /* flowbit names in an array. */
+ /*************************************************************/
+
+ $checked_flowbits = array();
+ foreach ($rules_map as $rulem) {
+ if (!is_array($rulem))
+ continue;
+ foreach ($rulem as $rulem2) {
+ if (!is_array($rulem2))
+ continue;
+ if ($rulem2['disabled'] == 1)
+ continue;
+ if (empty($rulem2['flowbits']))
+ continue;
+ if (!is_array($rulem2['flowbits']))
+ continue;
+ foreach ($rulem2['flowbits'] as $flowbit) {
+ if (empty($flowbit))
+ continue;
+ // If no comma in flowbits option, then skip it.
+ $pos = strpos($flowbit, ",");
+ if ($pos === false)
+ continue;
+ $action = substr(strtolower($flowbit), 0, $pos);
+ if ($action == "isset" || $action == "isnotset") {
+ $target = preg_split('/[&|]/', substr($flowbit, $pos + 1));
+ foreach ($target as $t)
+ if (!empty($t) && !isset($checked_flowbits[$t])) {
+ if (!is_array($checked_flowbits[$t]))
+ $checked_flowbits[$t] = array();
+ $checked_flowbits[$t] = $action;
+ }
+ }
+ }
+ }
+ }
+ unset($rulem, $rulem2);
+ return $checked_flowbits;
+}
+
+function suricata_get_set_flowbits($rules_map) {
+
+ /*********************************************************/
+ /* This function checks all the currently enabled rules */
+ /* to find any set flowbits, and returns the flowbit */
+ /* names in an array. */
+ /*********************************************************/
+
+ $set_flowbits = array();
+ foreach ($rules_map as $rulem) {
+ if (!is_array($rulem))
+ continue;
+ foreach ($rulem as $rulem2) {
+ if ($rulem2['disabled'] == 1)
+ continue;
+ if (empty($rulem2['flowbits']))
+ continue;
+ if (!is_array($rulem2['flowbits']))
+ continue;
+ foreach ($rulem2['flowbits'] as $flowbit) {
+ if (empty($flowbit))
+ continue;
+ /* If no comma in flowbits option, then skip it. */
+ $pos = strpos($flowbit, ",");
+ if ($pos === false)
+ continue;
+ $action = substr(strtolower($flowbit), 0, $pos);
+ if ($action == "set" || $action == "toggle" || $action == "setx") {
+ $target = preg_split('/[&|]/', substr($flowbit, $pos + 1));
+ foreach ($target as $t)
+ if (!empty($t) && !isset($set_flowbits[$t])) {
+ if (!is_array($set_flowbits[$t]))
+ $set_flowbits[$t] = array();
+ $set_flowbits[$t] = $action;
+ }
+ }
+ }
+ }
+ }
+ unset($rulem, $rulem2);
+ return $set_flowbits;
+}
+
+function suricata_find_flowbit_required_rules($rules, $unchecked_flowbits) {
+
+ /********************************************************/
+ /* This function finds all rules that must be enabled */
+ /* in order to satisfy the "checked flowbits" used by */
+ /* the currently enabled rules. It returns the list */
+ /* of required rules in an array. */
+ /********************************************************/
+
+ $required_flowbits_rules = array();
+ foreach ($rules as $k1 => $rule) {
+ if (!is_array($rule))
+ continue;
+ foreach ($rule as $k2 => $rule2) {
+ if (empty($rule2['flowbits']))
+ continue;
+ if (!is_array($rule2['flowbits']))
+ continue;
+ foreach ($rule2['flowbits'] as $flowbit) {
+ if (empty($flowbit))
+ continue;
+ $action = substr($flowbit, 0, strpos($flowbit, ","));
+ if (!strcasecmp(substr($action, 0, 3), "set")) {
+ $tmp = substr($flowbit, strpos($flowbit, ",") +1 );
+ if (!empty($tmp) && isset($unchecked_flowbits[$tmp])) {
+ if (!is_array($required_flowbits_rules[$k1]))
+ $required_flowbits_rules[$k1] = array();
+ if (!is_array($required_flowbits_rules[$k1][$k2]))
+ $required_flowbits_rules[$k1][$k2] = array();
+ $required_flowbits_rules[$k1][$k2]['category'] = $rule2['category'];
+ if ($rule2['disabled'] == 0)
+ // If not disabled, just return the rule text "as is"
+ $required_flowbits_rules[$k1][$k2]['rule'] = ltrim($rule2['rule']);
+ else {
+ // If rule is disabled, remove leading '#' to enable it
+ $required_flowbits_rules[$k1][$k2]['rule'] = ltrim(substr($rule2['rule'], strpos($rule2['rule'], "#") + 1));
+ $required_flowbits_rules[$k1][$k2]['disabled'] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ unset($rule, $rule2);
+
+ return $required_flowbits_rules;
+}
+
+function suricata_resolve_flowbits($rules, $active_rules) {
+
+ /******************************************************/
+ /* This function auto-resolves flowbit requirements */
+ /* by finding all checked flowbits in the currently */
+ /* enabled rules, and then making sure all the "set" */
+ /* flowbit rules for those "checked" flowbits are */
+ /* enabled. For any that are not enabled, they are */
+ /* copied to an array, enabled, and returned. */
+ /* */
+ /* $active_rules --> Rules Map array containing */
+ /* the current rules for the */
+ /* interface to resolve flowbit */
+ /* dependencies for. */
+ /* */
+ /* $rules --> Rules Map array containing */
+ /* all the available rules. */
+ /******************************************************/
+
+ $suricatadir = SURICATADIR;
+
+ // Check $rules array to be sure it is filled.
+ if (empty($rules)) {
+ log_error(gettext("[Suricata] WARNING: Flowbit resolution not done - no rules in {$suricatadir}rules/ ..."));
+ return array();
+ }
+
+ // First, find all the "checked" and "set" flowbits.
+ $checked_flowbits = suricata_get_checked_flowbits($active_rules);
+ $set_flowbits = suricata_get_set_flowbits($active_rules);
+
+ // Next find any "checked" flowbits without matching
+ // "set" flowbit rules in the enabled rule set.
+ $delta_flowbits = array_diff_key($checked_flowbits, $set_flowbits);
+
+ // Cleanup and release the memory we no longer need.
+ unset($checked_flowbits);
+ unset($set_flowbits);
+
+ // Now find all the needed "set flowbit" rules from
+ // the master list of all rules.
+ $required_rules = suricata_find_flowbit_required_rules($rules, $delta_flowbits);
+
+ // Cleanup and release memory we no longer need.
+ unset($delta_flowbits);
+
+ return $required_rules;
+}
+
+function suricata_write_flowbit_rules_file($flowbit_rules, $rule_file) {
+
+ /************************************************/
+ /* This function takes an array of rules in the */
+ /* rules_map format and writes them to the file */
+ /* given. */
+ /* */
+ /* $flowbit_rules --> array of flowbit-required */
+ /* rules. */
+ /* */
+ /* $rule_file --> filename to write the */
+ /* flowbit-required rules */
+ /* to. */
+ /************************************************/
+
+ $flowbit_rules_file = FLOWBITS_FILENAME;
+
+ // See if we were passed a directory or full
+ // filename to write the rules to, and adjust
+ // the destination argument accordingly.
+ if (is_dir($rule_file))
+ $rule_file = rtrim($rule_file, '/')."/{$flowbit_rules_file}";
+
+ if (empty($flowbit_rules)) {
+ @file_put_contents($rule_file, "");
+ return;
+ }
+
+ $fp = fopen($rule_file, "w");
+ if ($fp) {
+ @fwrite($fp, "# These rules set flowbits checked by your other enabled rules. If the\n");
+ @fwrite($fp, "# dependent flowbits are not set, then some of your chosen rules may\n");
+ @fwrite($fp, "# not fire. Enabling all rules that set these dependent flowbits ensures\n");
+ @fwrite($fp, "# your chosen rules fire as intended.\n#\n");
+ @fwrite($fp, "# If you wish to prevent alerts from any of these rules, add the GID:SID\n");
+ @fwrite($fp, "# of the rule to the Suppression List for the interface.\n");
+ foreach ($flowbit_rules as $k1 => $rule) {
+ foreach ($rule as $k2 => $rule2) {
+ @fwrite($fp, "\n# Category: {$rule2['category']}");
+ @fwrite($fp, " GID:{$k1} SID:{$k2}\n");
+ @fwrite($fp, $rule2['rule']);
+ }
+ }
+ fclose($fp);
+ }
+}
+
+function suricata_load_vrt_policy($policy, $all_rules=null) {
+
+ /************************************************/
+ /* This function returns an array of all rules */
+ /* marked with the passed in $policy metadata. */
+ /* */
+ /* $policy --> desired VRT security policy */
+ /* 1. connectivity */
+ /* 2. balanced */
+ /* 3. security */
+ /* */
+ /* $all_rules --> optional Rules Map array of */
+ /* rules to scan for policy. */
+ /* If not provided, then an */
+ /* array will be created. */
+ /************************************************/
+
+ $suricatadir = SURICATADIR;
+ $vrt_policy_rules = array();
+
+ // Load a map of all the VRT rules if we were
+ // not passed a pre-loaded one to use.
+ if (is_null($all_rules)) {
+ /* Since only Snort VRT rules have IPS Policy metadata, */
+ /* limit our search to just those files. */
+ $suricata_file_pattern = VRT_FILE_PREFIX . "*.rules";
+ $suricata_vrt_files = glob("{$suricatadir}rules/{$suricata_file_pattern}");
+ $all_rules = suricata_load_rules_map($suricata_vrt_files);
+ }
+
+ // Now walk the rules list and find all those that are
+ // defined as active for the chosen security policy.
+ foreach ($all_rules as $k1 => $arulem) {
+ foreach ($arulem as $k2 => $arulem2) {
+ if (strripos($arulem2['rule'], "policy {$policy}-ips") !== false) {
+ if (!preg_match('/flowbits\s*:\s*noalert/i', $arulem2['rule'])) {
+ if (!is_array($vrt_policy_rules[$k1]))
+ $vrt_policy_rules[$k1] = array();
+ if (!is_array($vrt_policy_rules[$k1][$k2]))
+ $vrt_policy_rules[$k1][$k2] = array();
+ $vrt_policy_rules[$k1][$k2] = $arulem2;
+
+ // Enable the policy rule if disabled
+ if ($arulem2['disabled'] == 1) {
+ $vrt_policy_rules[$k1][$k2]['rule'] = ltrim(substr($arulem2['rule'], strpos($arulem2['rule'], "#") + 1));
+ $vrt_policy_rules[$k1][$k2]['disabled'] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Release memory we no longer need.
+ unset($arulem, $arulem2);
+
+ // Return all the rules that match the policy.
+ return $vrt_policy_rules;
+}
+
+function suricata_load_sid_mods($sids) {
+
+ /*****************************************/
+ /* This function parses the string of */
+ /* SID values in $sids and returns an */
+ /* array with the SID as the key and */
+ /* value. The SID values in $sids are */
+ /* assumed to be delimited by "||". */
+ /* */
+ /* $sids ==> string of SID values from */
+ /* saved config file. */
+ /* */
+ /* Returns ==> a multidimensional array */
+ /* with GID and SID as the */
+ /* keys ($result[GID][SID]) */
+ /*****************************************/
+
+ $result = array();
+ if (empty($sids))
+ return $result;
+ $tmp = explode("||", $sids);
+ foreach ($tmp as $v) {
+ if (preg_match('/(\d+)\s*:\s*(\d+)/', $v, $match)) {
+ if (!is_array($result[$match[1]]))
+ $result[$match[1]] = array();
+ $result[$match[1]][$match[2]] = "{$match[1]}:{$match[2]}";
+ }
+ }
+ unset($tmp);
+
+ return $result;
+}
+
+function suricata_modify_sids(&$rule_map, $suricatacfg) {
+
+ /*****************************************/
+ /* This function modifies the rules in */
+ /* the passed rules_map array based on */
+ /* values in the enablesid/disablesid */
+ /* configuration parameters. */
+ /* */
+ /* $rule_map = array of current rules */
+ /* $suricatacfg = config settings */
+ /*****************************************/
+
+ if (!isset($suricatacfg['rule_sid_on']) &&
+ !isset($suricatacfg['rule_sid_off']))
+ return;
+
+ // Load up our enablesid and disablesid
+ // arrays with lists of modified SIDs.
+ $enablesid = suricata_load_sid_mods($suricatacfg['rule_sid_on'], "enablesid");
+ $disablesid = suricata_load_sid_mods($suricatacfg['rule_sid_off'], "disablesid");
+
+ /* Turn on any rules that need to be */
+ /* forced "on" with enablesid mods. */
+ if (!empty($enablesid)) {
+ foreach ($rule_map as $k1 => $rulem) {
+ foreach ($rulem as $k2 => $v) {
+ if (isset($enablesid[$k1][$k2]) && $v['disabled'] == 1) {
+ $rule_map[$k1][$k2]['rule'] = ltrim($v['rule'], " \t#");
+ $rule_map[$k1][$k2]['disabled'] = 0;
+ }
+ }
+ }
+ }
+
+ /* Turn off any rules that need to be */
+ /* forced "off" with disablesid mods. */
+ if (!empty($disablesid)) {
+ foreach ($rule_map as $k1 => $rulem) {
+ foreach ($rulem as $k2 => $v) {
+ if (isset($disablesid[$k1][$k2]) && $v['disabled'] == 0) {
+ $rule_map[$k1][$k2]['rule'] = "# " . $v['rule'];
+ $rule_map[$k1][$k2]['disabled'] = 1;
+ }
+ }
+ }
+ }
+ unset($enablesid, $disablesid);
+}
+
+function suricata_prepare_rule_files($suricatacfg, $suricatacfgdir) {
+
+ /***********************************************************/
+ /* This function builds a new set of enforcing rules for */
+ /* Suricata and writes them to disk. */
+ /* */
+ /* $suricatacfg --> pointer to applicable section of */
+ /* config.xml containing settings for */
+ /* the interface. */
+ /* */
+ /* $suricatacfgdir --> pointer to physical directory on */
+ /* disk where Suricata configuration is */
+ /* to be written. */
+ /***********************************************************/
+
+ global $rebuild_rules;
+
+ $suricatadir = SURICATADIR;
+ $flowbit_rules_file = FLOWBITS_FILENAME;
+ $suricata_enforcing_rules_file = ENFORCING_RULES_FILENAME;
+ $no_rules_defined = true;
+
+ // If there is no reason to rebuild the rules, exit to save time.
+ if (!$rebuild_rules)
+ return;
+
+ // Log a message for rules rebuild in progress
+ log_error(gettext("[Suricata] Updating rules configuration for: " . suricata_get_friendly_interface($suricatacfg['interface']) . " ..."));
+
+ // Only rebuild rules if some are selected or an IPS Policy is enabled
+ if (!empty($suricatacfg['rulesets']) || $suricatacfg['ips_policy_enable'] == 'on') {
+ $enabled_rules = array();
+ $enabled_files = array();
+ $all_rules = array();
+ $no_rules_defined = false;
+
+ // Load up all the rules into a Rules Map array.
+ $all_rules = suricata_load_rules_map("{$suricatadir}rules/");
+
+ // Create an array with the filenames of the enabled
+ // rule category files if we have any.
+ if (!empty($suricatacfg['rulesets'])) {
+ foreach (explode("||", $suricatacfg['rulesets']) as $file){
+ $category = basename($file, ".rules");
+ if (!is_array($enabled_files[$category]))
+ $enabled_files[$category] = array();
+ $enabled_files[$category] = $file;
+ }
+
+ /****************************************************/
+ /* Walk the ALL_RULES map array and copy the rules */
+ /* matching our selected file categories to the */
+ /* ENABLED_RULES map array. */
+ /****************************************************/
+ foreach ($all_rules as $k1 => $rulem) {
+ foreach ($rulem as $k2 => $v) {
+ if (isset($enabled_files[$v['category']])) {
+ if (!is_array($enabled_rules[$k1]))
+ $enabled_rules[$k1] = array();
+ if (!is_array($enabled_rules[$k1][$k2]))
+ $enabled_rules[$k1][$k2] = array();
+ $enabled_rules[$k1][$k2]['rule'] = $v['rule'];
+ $enabled_rules[$k1][$k2]['category'] = $v['category'];
+ $enabled_rules[$k1][$k2]['disabled'] = $v['disabled'];
+ $enabled_rules[$k1][$k2]['flowbits'] = $v['flowbits'];
+ }
+ }
+ }
+
+ // Release memory we no longer need.
+ unset($enabled_files, $rulem, $v);
+ }
+
+ // Check if a pre-defined Snort VRT policy is selected. If so,
+ // add all the VRT policy rules to our enforcing rule set.
+ if (!empty($suricatacfg['ips_policy'])) {
+ $policy_rules = suricata_load_vrt_policy($suricatacfg['ips_policy'], $all_rules);
+ foreach ($policy_rules as $k1 => $policy) {
+ foreach ($policy as $k2 => $p) {
+ if (!is_array($enabled_rules[$k1]))
+ $enabled_rules[$k1] = array();
+ if (!is_array($enabled_rules[$k1][$k2]))
+ $enabled_rules[$k1][$k2] = array();
+ $enabled_rules[$k1][$k2]['rule'] = $p['rule'];
+ $enabled_rules[$k1][$k2]['category'] = $p['category'];
+ $enabled_rules[$k1][$k2]['disabled'] = $p['disabled'];
+ $enabled_rules[$k1][$k2]['flowbits'] = $p['flowbits'];
+ }
+ }
+ unset($policy_rules, $policy, $p);
+ }
+
+ // Process any enablesid or disablesid modifications for the selected rules.
+ suricata_modify_sids($enabled_rules, $suricatacfg);
+
+ // Write the enforcing rules file to the Suricata interface's "rules" directory.
+ suricata_write_enforcing_rules_file($enabled_rules, "{$suricatacfgdir}/rules/{$suricata_enforcing_rules_file}");
+
+ // If auto-flowbit resolution is enabled, generate the dependent flowbits rules file.
+ if ($suricatacfg['autoflowbitrules'] == 'on') {
+ log_error('[Suricata] Enabling any flowbit-required rules for: ' . suricata_get_friendly_interface($suricatacfg['interface']) . '...');
+ $fbits = suricata_resolve_flowbits($all_rules, $enabled_rules);
+
+ // Check for and disable any flowbit-required rules the user has
+ // manually forced to a disabled state.
+ suricata_modify_sids($fbits, $suricatacfg);
+ suricata_write_flowbit_rules_file($fbits, "{$suricatacfgdir}/rules/{$flowbit_rules_file}");
+ unset($fbits);
+ } else
+ // Just put an empty file to always have the file present
+ suricata_write_flowbit_rules_file(array(), "{$suricatacfgdir}/rules/{$flowbit_rules_file}");
+ } else {
+ suricata_write_enforcing_rules_file(array(), "{$suricatacfgdir}/rules/{$suricata_enforcing_rules_file}");
+ suricata_write_flowbit_rules_file(array(), "{$suricatacfgdir}/rules/{$flowbit_rules_file}");
+ }
+
+ if (!empty($suricatacfg['customrules'])) {
+ @file_put_contents("{$suricatacfgdir}/rules/custom.rules", base64_decode($suricatacfg['customrules']));
+ $no_rules_defined = false;
+ }
+ else
+ @file_put_contents("{$suricatacfgdir}/rules/custom.rules", "");
+
+ // Log a warning if the interface has no rules defined or enabled
+ if ($no_rules_defined)
+ log_error(gettext("[Suricata] Warning - no text rules selected for: " . suricata_get_friendly_interface($suricatacfg['interface']) . " ..."));
+
+ // Build a new sid-msg.map file from the enabled
+ // rules and copy it to the interface directory.
+ log_error(gettext("[Suricata] Building new sig-msg.map file for " . suricata_get_friendly_interface($suricatacfg['interface']) . "..."));
+ suricata_build_sid_msg_map("{$suricatacfgdir}/rules/", "{$suricatacfgdir}/sid-msg.map");
+}
+
+
+function suricata_write_enforcing_rules_file($rule_map, $rule_path) {
+
+ /************************************************/
+ /* This function takes a rules map array of */
+ /* the rules chosen for the active rule set */
+ /* and writes them out to the passed path. */
+ /* */
+ /* $rule_map --> Rules Map array of rules to */
+ /* write to disk. */
+ /* */
+ /* $rule_path --> filename or directory where */
+ /* rules file will be written. */
+ /************************************************/
+
+ $rule_file = "/" . ENFORCING_RULES_FILENAME;
+
+ // See if we were passed a directory or full
+ // filename to write the rules to, and adjust
+ // the destination argument accordingly.
+ if (is_dir($rule_path))
+ $rule_file = rtrim($rule_path, '/').$rule_file;
+ else
+ $rule_file = $rule_path;
+
+ // If the $rule_map array is empty, then exit.
+ if (empty($rule_map)) {
+ file_put_contents($rule_file, "");
+ return;
+ }
+
+ $fp = fopen($rule_file, "w");
+ if ($fp) {
+ @fwrite($fp, "# These rules are your current set of enforced rules for the protected\n");
+ @fwrite($fp, "# interface. This list was compiled from the categories selected on the\n");
+ @fwrite($fp, "# CATEGORIES tab of the Suricata configuration for the interface and/or any\n");
+ @fwrite($fp, "# chosen Snort VRT pre-defined IPS Policy.\n#\n");
+ @fwrite($fp, "# Any enablesid or disablesid customizations you made have been applied\n");
+ @fwrite($fp, "# to the rules in this file.\n\n");
+ foreach ($rule_map as $rulem) {
+ foreach ($rulem as $rulem2) {
+ /* No reason to write disabled rules to enforcing file, so skip them. */
+ if ($rulem2['disabled'] == 1)
+ continue;
+ @fwrite($fp, $rulem2['rule']);
+ }
+ }
+ fclose($fp);
+ }
+}
+
+function suricata_create_rc() {
+
+ /************************************************************/
+ /* This function builds the /usr/local/etc/rc.d/suricata.sh */
+ /* shell script for starting and stopping Suricata. The */
+ /* script is rebuilt on each package sync operation and */
+ /* after any changes to suricata.conf saved in the GUI. */
+ /************************************************************/
+
+ global $config, $g;
+
+ $suricatadir = SURICATADIR;
+ $suricatalogdir = SURICATALOGDIR;
+ $rcdir = RCFILEPREFIX;
+
+ // If no interfaces are configured for Suricata, exit
+ if (!is_array($config['installedpackages']['suricata']['rule']))
+ return;
+ $suricataconf = $config['installedpackages']['suricata']['rule'];
+ if (empty($suricataconf))
+ return;
+
+ // At least one interface is configured, so OK
+ $start_suricata_iface_start = array();
+ $start_suricata_iface_stop = array();
+
+ // Loop thru each configured interface and build
+ // the shell script.
+ foreach ($suricataconf as $value) {
+ $suricata_uuid = $value['uuid'];
+ $if_real = suricata_get_real_interface($value['interface']);
+
+ $start_barnyard = <</dev/null; do
+ sleep 1
+ time=\$((time+1))
+ if [ \$time -gt \$timeout ]; then
+ break
+ fi
+ done
+ if [ -f /var/run/barnyard2_{$if_real}{$suricata_uuid}.pid ]; then
+ /bin/rm /var/run/barnyard2_{$if_real}{$suricata_uuid}.pid
+ fi
+ fi
+ /usr/bin/logger -p daemon.info -i -t SuricataStartup "Barnyard2 START for {$value['descr']}({$suricata_uuid}_{$if_real})..."
+ /usr/local/bin/barnyard2 -r {$suricata_uuid} -f unified2.alert --pid-path {$g['varrun_path']} --nolock-pidfile -c {$suricatadir}suricata_{$suricata_uuid}_{$if_real}/barnyard2.conf -d {$suricatalogdir}suricata_{$if_real}{$suricata_uuid} -D -q
+
+EOE;
+ $stop_barnyard2 = <</dev/null; do
+ sleep 1
+ time=\$((time+1))
+ if [ \$time -gt \$timeout ]; then
+ break
+ fi
+ done
+ if [ -f /var/run/barnyard2_{$if_real}{$suricata_uuid}.pid ]; then
+ /bin/rm /var/run/barnyard2_{$if_real}{$suricata_uuid}.pid
+ fi
+ else
+ pid=`/bin/pgrep -f "barnyard2 -r {$suricata_uuid} "`
+ if [ ! -z \$pid ]; then
+ /bin/pkill -TERM -f "barnyard2 -r {$suricata_uuid} "
+ time=0 timeout=30
+ while /bin/kill -TERM \$pid 2>/dev/null; do
+ sleep 1
+ time=\$((time+1))
+ if [ \$time -gt \$timeout ]; then
+ break
+ fi
+ done
+ fi
+ fi
+
+EOE;
+ if ($value['barnyard_enable'] == 'on')
+ $start_barnyard2 = $start_barnyard;
+ else
+ $start_barnyard2 = $stop_barnyard2;
+
+ $start_suricata_iface_start[] = <</dev/null; do
+ sleep 1
+ time=\$((time+1))
+ if [ \$time -gt \$timeout ]; then
+ break
+ fi
+ done
+ if [ -f /var/run/suricata_{$if_real}{$suricata_uuid}.pid ]; then
+ /bin/rm /var/run/suricata_{$if_real}{$suricata_uuid}.pid
+ fi
+ else
+ pid=`/bin/pgrep -f "suricata -i {$if_real} "`
+ if [ ! -z \$pid ]; then
+ /usr/bin/logger -p daemon.info -i -t SuricataStartup "Suricata STOP for {$value['descr']}({$suricata_uuid}_{$if_real})..."
+ /bin/pkill -TERM -f "suricata -i {$if_real} "
+ time=0 timeout=30
+ while /bin/kill -TERM \$pid 2>/dev/null; do
+ sleep 1
+ time=\$((time+1))
+ if [ \$time -gt \$timeout ]; then
+ break
+ fi
+ done
+ fi
+ fi
+
+ sleep 2
+ {$stop_barnyard2}
+
+EOE;
+ }
+
+ $rc_start = implode("\n", $start_suricata_iface_start);
+ $rc_stop = implode("\n", $start_suricata_iface_stop);
+
+ $suricata_sh_text = << Suricata instance info in */
+ /* the config.xml master config */
+ /* file. */
+ /************************************************************/
+
+ global $config, $g;
+
+ $suricatadir = SURICATADIR;
+ $suricatalogdir = SURICATALOGDIR;
+ $flowbit_rules_file = FLOWBITS_FILENAME;
+ $suricata_enforcing_rules_file = ENFORCING_RULES_FILENAME;
+ $if_real = suricata_get_real_interface($suricatacfg['interface']);
+ $suricata_uuid = $suricatacfg['uuid'];
+ $suricatacfgdir = "{$suricatadir}suricata_{$suricata_uuid}_{$if_real}";
+
+ conf_mount_rw();
+
+ if (!is_array($config['installedpackages']['suricata']['rule']))
+ return;
+
+ // Pull in the PHP code that generates the suricata.yaml file
+ // variables that will be substitued further down below.
+ include("/usr/local/www/suricata/suricata_generate_yaml.php");
+
+ // Pull in the boilerplate template for the suricata.yaml
+ // configuration file. The contents of the template along
+ // with substituted variables is stored in $suricata_conf_text
+ // (which is defined in the included file).
+ include("/usr/local/pkg/suricata/suricata_yaml_template.inc");
+
+ // Now write out the conf file using $suricata_conf_text contents
+ $conf = fopen("{$suricatacfgdir}/suricata.yaml", "w");
+ if(!$conf) {
+ log_error("Could not open {$suricatacfgdir}/suricata.yaml for writing.");
+ return -1;
+ }
+ fwrite($conf, $suricata_conf_text);
+ fclose($conf);
+
+ conf_mount_ro();
+}
+
+?>
diff --git a/config/suricata/suricata.priv.inc b/config/suricata/suricata.priv.inc
new file mode 100644
index 00000000..a8f9807a
--- /dev/null
+++ b/config/suricata/suricata.priv.inc
@@ -0,0 +1,46 @@
+
\ No newline at end of file
diff --git a/config/suricata/suricata.xml b/config/suricata/suricata.xml
new file mode 100644
index 00000000..4c3e3e67
--- /dev/null
+++ b/config/suricata/suricata.xml
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+ Suricata IDS/IPS Package
+ None
+ suricata
+ 1.4.6 pkg v0.1-BETA
+ Services: Suricata IDS
+ /usr/local/pkg/suricata/suricata.inc
+
+
+ suricata
+ suricata.sh
+ suricata
+ Suricata IDS/IPS Daemon
+
+
+ /etc/inc/priv/
+ 077
+ - http://www.pfsense.com/packages/config/suricata/suricata.priv.inc
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata.inc
+ /usr/local/pkg/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_check_cron_misc.inc
+ /usr/local/pkg/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_yaml_template.inc
+ /usr/local/pkg/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_generate_yaml.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_download_updates.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_global.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_alerts.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_interfaces.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_interfaces_edit.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_download_rules.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_check_for_rule_updates.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_log_view.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_rules.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_rulesets.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_rules_flowbits.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_rules_edit.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_flow_stream.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_os_policy_engine.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_import_aliases.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_select_alias.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_suppress.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_suppress_edit.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_logs_browser.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_list_view.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_app_parsers.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_libhtp_policy_engine.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_uninstall.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_define_vars.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_barnyard.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_post_install.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ - http://www.pfsense.com/packages/config/suricata/suricata_uninstall.php
+ /usr/local/www/suricata/
+ 0755
+
+
+ ['installedpackages']['suricata']
+
+
+
+
+
+
+
+
+
+
+
+ sync_suricata_package_config();
+
+
+
+
diff --git a/config/suricata/suricata_alerts.php b/config/suricata/suricata_alerts.php
new file mode 100644
index 00000000..d21f7388
--- /dev/null
+++ b/config/suricata/suricata_alerts.php
@@ -0,0 +1,578 @@
+ suppression entry text */
+ /* */
+ /* Returns: */
+ /* TRUE if successful or FALSE on failure */
+ /************************************************/
+
+ global $config, $a_instance, $instanceid;
+
+ if (!is_array($config['installedpackages']['suricata']['suppress']))
+ $config['installedpackages']['suricata']['suppress'] = array();
+ if (!is_array($config['installedpackages']['suricata']['suppress']['item']))
+ $config['installedpackages']['suricata']['suppress']['item'] = array();
+ $a_suppress = &$config['installedpackages']['suricata']['suppress']['item'];
+
+ $found_list = false;
+
+ /* If no Suppress List is set for the interface, then create one with the interface name */
+ if (empty($a_instance[$instanceid]['suppresslistname']) || $a_instance[$instanceid]['suppresslistname'] == 'default') {
+ $s_list = array();
+ $s_list['uuid'] = uniqid();
+ $s_list['name'] = $a_instance[$instanceid]['interface'] . "suppress" . "_" . $s_list['uuid'];
+ $s_list['descr'] = "Auto-generated list for Alert suppression";
+ $s_list['suppresspassthru'] = base64_encode($suppress);
+ $a_suppress[] = $s_list;
+ $a_instance[$instanceid]['suppresslistname'] = $s_list['name'];
+ $found_list = true;
+ } else {
+ /* If we get here, a Suppress List is defined for the interface so see if we can find it */
+ foreach ($a_suppress as $a_id => $alist) {
+ if ($alist['name'] == $a_instance[$instanceid]['suppresslistname']) {
+ $found_list = true;
+ if (!empty($alist['suppresspassthru'])) {
+ $tmplist = base64_decode($alist['suppresspassthru']);
+ $tmplist .= "\n{$suppress}";
+ $alist['suppresspassthru'] = base64_encode($tmplist);
+ $a_suppress[$a_id] = $alist;
+ }
+ else {
+ $alist['suppresspassthru'] = base64_encode($suppress);
+ $a_suppress[$a_id] = $alist;
+ }
+ }
+ }
+ }
+
+ /* If we created a new list or updated an existing one, save the change, */
+ /* tell Snort to load it, and return true; otherwise return false. */
+ if ($found_list) {
+ write_config();
+ sync_suricata_package_config();
+ suricata_reload_config($a_instance[$instanceid]);
+ return true;
+ }
+ else
+ return false;
+}
+
+if ($_GET['instance'])
+ $instanceid = $_GET['instance'];
+if ($_POST['instance'])
+ $instanceid = $_POST['instance'];
+if (empty($instanceid))
+ $instanceid = 0;
+
+if (!is_array($config['installedpackages']['suricata']['rule']))
+ $config['installedpackages']['suricata']['rule'] = array();
+$a_instance = &$config['installedpackages']['suricata']['rule'];
+$suricata_uuid = $a_instance[$instanceid]['uuid'];
+$if_real = suricata_get_real_interface($a_instance[$instanceid]['interface']);
+$suricatalogdir = SURICATALOGDIR;
+
+// Load up the arrays of force-enabled and force-disabled SIDs
+$enablesid = suricata_load_sid_mods($a_instance[$instanceid]['rule_sid_on']);
+$disablesid = suricata_load_sid_mods($a_instance[$instanceid]['rule_sid_off']);
+
+$pconfig = array();
+if (is_array($config['installedpackages']['suricata']['alertsblocks'])) {
+ $pconfig['arefresh'] = $config['installedpackages']['suricata']['alertsblocks']['arefresh'];
+ $pconfig['alertnumber'] = $config['installedpackages']['suricata']['alertsblocks']['alertnumber'];
+}
+
+if (empty($pconfig['alertnumber']))
+ $pconfig['alertnumber'] = '250';
+if (empty($pconfig['arefresh']))
+ $pconfig['arefresh'] = 'off';
+$anentries = $pconfig['alertnumber'];
+
+if ($_POST['save']) {
+ if (!is_array($config['installedpackages']['suricata']['alertsblocks']))
+ $config['installedpackages']['suricata']['alertsblocks'] = array();
+ $config['installedpackages']['suricata']['alertsblocks']['arefresh'] = $_POST['arefresh'] ? 'on' : 'off';
+ $config['installedpackages']['suricata']['alertsblocks']['alertnumber'] = $_POST['alertnumber'];
+
+ write_config();
+
+ header("Location: /suricata/suricata_alerts.php?instance={$instanceid}");
+ exit;
+}
+
+//if ($_POST['todelete'] || $_GET['todelete']) {
+// $ip = "";
+// if($_POST['todelete'])
+// $ip = $_POST['todelete'];
+// else if($_GET['todelete'])
+// $ip = $_GET['todelete'];
+// if (is_ipaddr($ip)) {
+// exec("/sbin/pfctl -t snort2c -T delete {$ip}");
+// $savemsg = gettext("Host IP address {$ip} has been removed from the Blocked Table.");
+// }
+//}
+
+if ($_GET['act'] == "addsuppress" && is_numeric($_GET['sidid']) && is_numeric($_GET['gen_id'])) {
+ if (empty($_GET['descr']))
+ $suppress = "suppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}\n";
+ else
+ $suppress = "#{$_GET['descr']}\nsuppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}\n";
+
+ /* Add the new entry to the Suppress List */
+ if (suricata_add_supplist_entry($suppress))
+ $savemsg = gettext("An entry for 'suppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}' has been added to the Suppress List.");
+ else
+ $input_errors[] = gettext("Suppress List '{$a_instance[$instanceid]['suppresslistname']}' is defined for this interface, but it could not be found!");
+}
+
+if (($_GET['act'] == "addsuppress_srcip" || $_GET['act'] == "addsuppress_dstip") && is_numeric($_GET['sidid']) && is_numeric($_GET['gen_id'])) {
+ if ($_GET['act'] == "addsuppress_srcip")
+ $method = "by_src";
+ else
+ $method = "by_dst";
+
+ /* Check for valid IP addresses, exit if not valid */
+ if (is_ipaddr($_GET['ip']) || is_ipaddrv6($_GET['ip'])) {
+ if (empty($_GET['descr']))
+ $suppress = "suppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}, track {$method}, ip {$_GET['ip']}\n";
+ else
+ $suppress = "#{$_GET['descr']}\nsuppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}, track {$method}, ip {$_GET['ip']}\n";
+ }
+ else {
+ header("Location: /suricata/suricata_alerts.php?instance={$instanceid}");
+ exit;
+ }
+
+ /* Add the new entry to the Suppress List */
+ if (suricata_add_supplist_entry($suppress))
+ $savemsg = gettext("An entry for 'suppress gen_id {$_GET['gen_id']}, sig_id {$_GET['sidid']}, track {$method}, ip {$_GET['ip']}' has been added to the Suppress List.");
+ else
+ /* We did not find the defined list, so notify the user with an error */
+ $input_errors[] = gettext("Suppress List '{$a_instance[$instanceid]['suppresslistname']}' is defined for this interface, but it could not be found!");
+}
+
+if ($_GET['act'] == "togglesid" && is_numeric($_GET['sidid']) && is_numeric($_GET['gen_id'])) {
+ // Get the GID tag embedded in the clicked rule icon.
+ $gid = $_GET['gen_id'];
+
+ // Get the SID tag embedded in the clicked rule icon.
+ $sid= $_GET['sidid'];
+
+ // See if the target SID is in our list of modified SIDs,
+ // and toggle it if present.
+ if (isset($enablesid[$gid][$sid]))
+ unset($enablesid[$gid][$sid]);
+ if (isset($disablesid[$gid][$sid]))
+ unset($disablesid[$gid][$sid]);
+ elseif (!isset($disablesid[$gid][$sid]))
+ $disablesid[$gid][$sid] = "disablesid";
+
+ // Write the updated enablesid and disablesid values to the config file.
+ $tmp = "";
+ foreach (array_keys($enablesid) as $k1) {
+ foreach (array_keys($enablesid[$k1]) as $k2)
+ $tmp .= "{$k1}:{$k2}||";
+ }
+ $tmp = rtrim($tmp, "||");
+
+ if (!empty($tmp))
+ $a_instance[$instanceid]['rule_sid_on'] = $tmp;
+ else
+ unset($a_instance[$instanceid]['rule_sid_on']);
+
+ $tmp = "";
+ foreach (array_keys($disablesid) as $k1) {
+ foreach (array_keys($disablesid[$k1]) as $k2)
+ $tmp .= "{$k1}:{$k2}||";
+ }
+ $tmp = rtrim($tmp, "||");
+
+ if (!empty($tmp))
+ $a_instance[$instanceid]['rule_sid_off'] = $tmp;
+ else
+ unset($a_instance[$instanceid]['rule_sid_off']);
+
+ /* Update the config.xml file. */
+ write_config();
+
+ /*************************************************/
+ /* Update the suricata.yaml file and rebuild the */
+ /* rules for this interface. */
+ /*************************************************/
+ $rebuild_rules = true;
+ suricata_generate_yaml($a_instance[$instanceid]);
+ $rebuild_rules = false;
+
+ /* Signal Suricata to live-load the new rules */
+ suricata_reload_config($a_instance[$instanceid]);
+
+ $savemsg = gettext("The state for rule {$gid}:{$sid} has been modified. Suricata is 'live-reloading' the new rules list. Please wait at least 30 secs for the process to complete before toggling additional rules.");
+}
+
+if ($_GET['action'] == "clear" || $_POST['delete']) {
+ conf_mount_rw();
+ suricata_post_delete_logs($suricata_uuid);
+ $fd = @fopen("{$suricatalogdir}suricata_{$if_real}{$suricata_uuid}/alerts.log", "w+");
+ if ($fd)
+ fclose($fd);
+ conf_mount_ro();
+ /* XXX: This is needed if suricata is run as suricata user */
+ mwexec('/bin/chmod 660 {$suricatalogdir}*', true);
+ if (file_exists("{$g['varrun_path']}/suricata_{$if_real}{$suricata_uuid}.pid"))
+ mwexec("/bin/pkill -HUP -F {$g['varrun_path']}/suricata_{$if_real}{$suricata_uuid}.pid -a");
+ header("Location: /suricata/suricata_alerts.php?instance={$instanceid}");
+ exit;
+}
+
+if ($_POST['download']) {
+ $save_date = exec('/bin/date "+%Y-%m-%d-%H-%M-%S"');
+ $file_name = "suricata_logs_{$save_date}_{$if_real}.tar.gz";
+ exec("cd {$suricatalogdir}suricata_{$if_real}{$suricata_uuid} && /usr/bin/tar -czf /tmp/{$file_name} *");
+
+ if (file_exists("/tmp/{$file_name}")) {
+ ob_start(); //important or other posts will fail
+ if (isset($_SERVER['HTTPS'])) {
+ header('Pragma: ');
+ header('Cache-Control: ');
+ } else {
+ header("Pragma: private");
+ header("Cache-Control: private, must-revalidate");
+ }
+ header("Content-Type: application/octet-stream");
+ header("Content-length: " . filesize("/tmp/{$file_name}"));
+ header("Content-disposition: attachment; filename = {$file_name}");
+ ob_end_clean(); //important or other post will fail
+ readfile("/tmp/{$file_name}");
+
+ // Clean up the temp file
+ @unlink("/tmp/{$file_name}");
+ }
+ else
+ $savemsg = gettext("An error occurred while creating archive");
+}
+
+/* Load up an array with the current Suppression List GID,SID values */
+$supplist = suricata_load_suppress_sigs($a_instance[$instanceid], true);
+
+$pgtitle = gettext("Suricata: Alerts");
+include_once("head.inc");
+
+?>
+
+
+
+\n";
+?>
+
+' . $pgtitle . '
';}
+ /* Display Alert message */
+ if ($input_errors) {
+ print_input_errors($input_errors); // TODO: add checks
+ }
+ if ($savemsg) {
+ print_info_box($savemsg);
+ }
+?>
+
+
+
+
+